[#114181] [Ruby master Bug#19767] [Not really a bug, but more a not ideal notification] "historical binary regexp match" when using the "n" modifier in a ruby regex — "rubyFeedback (robert heiler) via ruby-core" <ruby-core@...>
SXNzdWUgIzE5NzY3IGhhcyBiZWVuIHJlcG9ydGVkIGJ5IHJ1YnlGZWVkYmFjayAocm9iZXJ0IGhl
3 messages
2023/07/14
[ruby-core:114075] [Ruby master Feature#19755] Module#class_eval and Binding#eval use caller location by default
From:
"Eregon (Benoit Daloze) via ruby-core" <ruby-core@...>
Date:
2023-07-03 12:01:48 UTC
List:
ruby-core #114075
Issue #19755 has been updated by Eregon (Benoit Daloze).
`#<UnboundMethod: Foo#foo() (eval in /tmp/foo.rb):10>` sounds great to me, +1.
The `(eval` makes it clear it's an eval in that file and so the line of an exception inside might not be exact.
----------------------------------------
Feature #19755: Module#class_eval and Binding#eval use caller location by default
https://bugs.ruby-lang.org/issues/19755#change-103747
* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Background
In Ruby we're very reliant on `Method#source_location` as well as `caller_locations` to locate source code.
However, code generated via `Binding#eval`, `Module#class_eval` etc defeat this if called without a location:
```ruby
Foo.class_eval <<~RUBY
def bar
end
RUBY
p Foo.instance_method(:bar).source_location # => ["(eval)", 1]
```
The overwhelming majority of open source code properly pass a `filename` and `lineno`, however a small minority doesn't and make locating the code much harder than it needs to be.
Here's some example of anonymous eval uses I fixed in the past:
- https://github.com/ruby/mutex_m/pull/11
- https://github.com/rails/execjs/pull/120
- https://github.com/jnunemaker/httparty/pull/776
- https://github.com/SiftScience/sift-ruby/pull/76
- https://github.com/activemerchant/active_merchant/pull/4675
- https://github.com/rails/thor/pull/807
- https://github.com/dry-rb/dry-initializer/pull/104
- https://github.com/rmosolgo/graphql-ruby/pull/4288
### Proposal
I suggest that instead of defaulting to `"(eval)"`, the optional `filename` argument of the eval family of methods should instead default to: `"(eval in #{caller_locations(1, 1).first.path})"` and `lineno` to `caller_locations(1, 1).first.lineno`.
Which can pretty much be monkey patched as this:
```ruby
module ModuleEval
def class_eval(code, location = "(eval in #{caller_locations(1, 1).first.path})", lineno = caller_locations(1, 1).first.lineno)
super
end
end
Module.prepend(ModuleEval)
module Foo
class_eval <<~RUBY
def foo
end
RUBY
end
p Foo.instance_method(:foo)
```
before:
```
#<UnboundMethod: Foo#foo() (eval):1>
```
after:
```
#<UnboundMethod: Foo#foo() (eval in /tmp/foo.rb):10>
```
Of course the `lineno` part is likely to not be fully correct, but the reasoning is that it's better than defaulting to 0 or 1.
Another possiblity would be to include the caller `lineo` inside the `filename` part, and leave the actual lineo default to `1`:
```
#<UnboundMethod: Foo#foo() (eval at /tmp/foo.rb:10):1>
```
--
https://bugs.ruby-lang.org/
______________________________________________
ruby-core mailing list -- [email protected]
To unsubscribe send an email to [email protected]
ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/