Evaluation in binding differs from ruby execution
In specific situation, I found that result of string evaluation in Binding returns different from ruby execution result.
In following sample code, ruby evaluates
method_or_local_var as method call and returns
method_or_local_var as local variable and returns
Here is sample code.
def method_or_local_var 'method' end if true puts "execute method_or_local_var:" p method_or_local_var #=> "method" puts "execute method_or_local_var by bind.eval('method_or_local_var'):" p binding.eval('method_or_local_var') #=> nil else method_or_local_var = 'local variable' end
and here is results of execute sample code.
❯ ruby sample_code.rb execute method_or_local_var: "method" execute method_or_local_var by bind.eval('method_or_local_var'): nil
I expect evaluation result of
method_or_local_var in binding to be method, and returns
Is this the expected behavior?
Updated by mame (Yusuke Endoh) 12 months ago
I have no idea whether this is a spec or undefined behavior, but the current implementation is actually intentional.
def x "x" end def foo eval("x = 1") # foo has no variable named x, so 1 is assigned to a temporal variable p eval("x") #=> "x" # there is no variable named x in this scope, so it is regarded as a method call end foo def bar eval("x = 1") # foo has a variable named x, so 1 is assigned to the variable p eval("x") #=> 1 # foo has a variable named x, so it is regarded as a variable reference x = nil end bar
IMO, you'd better not to depend upon the behavior in any way.
Updated by kernigh (George Koehler) 11 months ago
This is a simpler example of the behavior:
$ ruby -e 'p x; x = 6' Traceback (most recent call last): -e:1:in `<main>': undefined local variable or method `x' for main:Object (NameError) $ ruby -e 'p eval("x"); x = 6' nil $ ruby -e 'p binding.eval("x"); x = 6' nil
x raises NameError, but an
binding.eval("x") fetches nil from the local variable x. I expect this behavior, because Ruby parses a script before running it:
- Ruby parses
x = 6and adds x to the binding.
- Ruby runs
eval("x")with x in the binding.
- Ruby runs
x = 6and changes the value of x from nil to 6.
Binding#local_variables reveals the local variable named x:
$ ruby -e 'p binding.local_variables; x = 6' [:x]
Because x exists,
binding.eval("x") has the expected behavior, so there is no bug.