Skip to content

Commit 1bd8041

Browse files
transclaude
andcommitted
Rewrite callstack/require_all to use caller_locations
Replace fragile caller string parsing with caller_locations, which returns structured Thread::Backtrace::Location objects. No more regex matching against Ruby version-specific caller formats. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f03722c commit 1bd8041

File tree

3 files changed

+15
-45
lines changed

3 files changed

+15
-45
lines changed

HISTORY.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ Changes:
2727
* Adapt `Module#attr_setter` to frozen-string-literal. (PR#287)
2828
* Update `Binding#__LINE__` and `__FILE__` to use `source_location`.
2929
* Replace `URI.escape`/`URI.unescape` with `CGI.escape`/`CGI.unescape`.
30-
* Match Ruby 3.4 caller syntax in `Kernel#__DIR__`, `#callstack`, `#require_all`. (PR#308)
30+
* Rewrite `Kernel#callstack`, `#require_all`, `#load_all` to use `caller_locations`
31+
instead of parsing caller strings with regex.
3132
* Switch CI from Travis to GitHub Actions.
3233

3334
* Bug Fixes

lib/core/facets/kernel/callstack.rb

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,22 @@ module Kernel
33
alias_method :pp_callstack, :caller
44
alias_method :pp_call_stack, :caller
55

6-
# Parse a caller string and break it into its components,
7-
# returning an array composed of:
8-
#
9-
# * file (String)
10-
# * lineno (Integer)
11-
# * method (Symbol)
6+
# Returns the call stack as an array of [file, lineno, method] entries.
127
#
138
# For example, from irb
149
#
1510
# callstack(1)
1611
#
1712
# _produces_ ...
1813
#
19-
# [["(irb)", 2, :irb_binding],
20-
# ["/usr/lib/ruby/1.8/irb/workspace.rb", 52, :irb_binding],
21-
# ["/usr/lib/ruby/1.8/irb/workspace.rb", 52, nil]]
22-
#
23-
# Note: If the user decides to redefine caller() to output data
24-
# in a different format, _prior_ to requiring this, then the
25-
# results will be indeterminate.
14+
# [["(irb)", 2, :irb_binding], ...]
2615
#
2716
# CREDIT: Trans
2817

2918
def callstack(level = 1)
30-
call_str_array = pp_callstack(level)
31-
stack = []
32-
call_str_array.each{ |call_str|
33-
file, lineno, method = call_str.split(':')
34-
if method =~ /in [`'](.*)'/ then
35-
method = $1.intern()
36-
end
37-
stack << [file, lineno.to_i, method]
38-
}
39-
stack
19+
caller_locations(level).map do |loc|
20+
[loc.path, loc.lineno, loc.label&.to_sym]
21+
end
4022
end
4123

4224
alias_method :call_stack, :callstack
@@ -53,4 +35,3 @@ def callstack(level=1)
5335

5436
alias_method :call_stack, :callstack
5537
end
56-
Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,15 @@
11
module Kernel
22

3-
# Require a pattern of files relatvie to the current file.
4-
# This makes is easy to require an entire directory, for instance:
3+
# Require a pattern of files relative to the current file.
4+
# This makes it easy to require an entire directory, for instance:
55
#
66
# require_all 'core_ext/*'
77
#
8-
# NOTE: This method used to allow glob-based requires from the $LOAD_PATH,
9-
# but this was deprecated in favor of relative requiring only, as it is
10-
# consider the typical usecase, and globbing from the $LOAD_PATH is a
11-
# bit dangerous. Better options exist for globbing the $LOAD_PATH such as
12-
# the +plugins+ gem.
138

149
def require_all(pattern)
15-
c = caller.first
16-
fail "Can't parse #{c}" unless c.rindex(/:\d+(:in [`'].*')?$/)
17-
file = $` # File.dirname(c)
18-
if /\A\((.*)\)/ =~ file # eval, etc.
19-
raise LoadError, "require_relative is called in #{$1}"
20-
end
10+
loc = caller_locations(1, 1).first
11+
file = loc.absolute_path || loc.path
12+
raise LoadError, "require_all is called in #{loc.label}" unless file
2113
glob = File.expand_path(pattern, File.dirname(file))
2214
Dir.glob(glob).each do |absolute|
2315
require absolute
@@ -26,17 +18,13 @@ def require_all(pattern)
2618

2719
# Same as #require_all, but for #load.
2820
def load_all(pattern, safe=nil)
29-
c = caller.first
30-
fail "Can't parse #{c}" unless c.rindex(/:\d+(:in [`'].*')?$/)
31-
file = $` # File.dirname(c)
32-
if /\A\((.*)\)/ =~ file # eval, etc.
33-
raise LoadError, "require_relative is called in #{$1}"
34-
end
21+
loc = caller_locations(1, 1).first
22+
file = loc.absolute_path || loc.path
23+
raise LoadError, "load_all is called in #{loc.label}" unless file
3524
glob = File.expand_path(pattern, File.dirname(file))
3625
Dir.glob(glob).each do |absolute|
3726
load absolute, safe
3827
end
3928
end
4029

4130
end
42-

0 commit comments

Comments
 (0)