When you activate a refinement inside an a string using eval with a binding, the refinement is sill active the next time you call eval with the same binding.
Strangely enough, this will only happen if there is an assignment at the code evaluated the first time. If you delete the assignment everything works as expected.
moduleMrefineStringdodeffoobar;puts'foobar';endendendsome_binding=classA;binding;endstr1=<<EOF
a = 'something' # Without this everything works as expected
using M
'str'.foobar # Works fine
EOFstr2=<<EOF
'str'.foobar # This time should fail but it doesn't
EOFevalstr1,some_bindingevalstr2,some_binding
Acording to the RefinmentsSpec: "when main.using is invoked in a string given as the first argument of Kernel#eval, Kernel#instance_eval, or Module#module_eval, the end of the scope is the end of the string."
eval('a = 42')eval('p a')# bar.rb:2:in `eval': undefined local variable or method `a' for main:Object (NameError)# from bar.rb:2:in `eval'# from bar.rb:2:in `<main>'
But Binding#eval behaves like:
b=bindingb.eval("a = 42")b.eval("p a")# => 42
So, I expect following code works fine, but it raises NoMethodError in ruby 2.1.5, 2.2.0
classFoo;endmoduleMrefine(Foo)dodefbar42endendendb=bindingb.eval('using M')putsb.eval('Foo.new.bar')# expected:# 42## actual 2.0.0:# refinement.rb:4: warning: Refinements are experimental, and the behavior may change in future versions of Ruby!# 42## actual 2.1.5:# refinement.rb:11:in `<main>': undefined method `bar' for #<Foo:0x007fe3040fd568> (NoMethodError)# from refinement.rb:13:in `eval'# from refinement.rb:13:in `<main>'## actual 2.2.0# refinement.rb:11:in `<main>': undefined method `bar' for #<Foo:0x007f9fba04e488> (NoMethodError)# from refinement.rb:13:in `eval'# from refinement.rb:13:in `<main>'
When call Kernel.#eval with binding returns same result.
classFoo;endmoduleMrefine(Foo)dodefbar42endendendb=bindingeval('using M',b)putseval('Foo.new.bar',b)# expected:# 42## actual 2.0.0:# refinement2.rb:4: warning: Refinements are experimental, and the behavior may change in future versions of Ruby!# 42## actual 2.1.5:# refinement2.rb:11:in `<main>': undefined method `bar' for #<Foo:0x007fc47c865568> (NoMethodError)# from refinement2.rb:13:in `eval'# from refinement2.rb:13:in `<main>'## actual 2.2.0:# refinement2.rb:11:in `<main>': undefined method `bar' for #<Foo:0x007fa1f0852138> (NoMethodError)# from refinement2.rb:13:in `eval'# from refinement2.rb:13:in `<main>'
So, I expect following code works fine, but it raises NoMethodError in ruby 2.1.5, 2.2.0
Refinements should be activated in a lexical scope, so NoMethodError should be raised in that case.
The problem originally reported is considered to be a bug, but it's difficult to fix because
the environment of binding is updated by eval to share local variables (see vm_eval.c:1293),
and refinement activation information is stored in the same area.
Objects of class Binding encapsulate the execution context at some particular place in the code and retain this context for future use. The variables, methods, value of self, and possibly an iterator block that can be accessed in this context are all retained.
Does it make any difference that the refinement at Seiei's example was already active before the string evaluation?.
If eval('using M', b) in Seiei's example is changed to eval('x = 1; using M', b),
refinements are activated in the subsequent eval.
It's because b's environment is updated to capture the new local variable x.
Objects of class Binding encapsulate the execution context at some particular place in the code and retain this context for future use. The variables, methods, value of self, and possibly an iterator block that can be accessed in this context are all retained.
Evaluates the Ruby expression(s) in string, in the binding’s context.
It's sounds good to retain refinements in binding's context.
The documentation of Binding should not justify the behavior of refinements,
because it was written before refinements are introduced into Ruby.
The original version of refinements are designed to be a more dynamic feature,
but it was changed to be more static to avoid confusion caused by implicit
refinement activation.
Does it make any difference that the refinement at Seiei's example was already active before the string evaluation?.
If eval('using M', b) in Seiei's example is changed to eval('x = 1; using M', b),
refinements are activated in the subsequent eval.
It's because b's environment is updated to capture the new local variable x.
I followed you there, but I meant the example where he activated the refinement outside the string and then stored the binding at the global variable. Sorry if I wasn't clear enough.
Assignee changed from matz (Yukihiro Matsumoto) to shugo (Shugo Maeda)
Shugo Maeda wrote:
I'd like to hear Matz's opinion.
I talked with Matz, and he said that a binding should keep refinements activation
information and the refinements should be activated in subsequent eval calls with
the binding.
So I'll change the behavior of eval as follows:
moduleMrefineStringdodeffoobar;puts'foobar';endendendsome_binding=classA;binding;endstr1=<<EOF
using M
'str'.foobar
EOFstr2=<<EOF
'str'.foobar
EOFevalstr1,some_binding# foobarevalstr2,some_binding# foobar (No exception is raised)
vm_eval.c (eval_string_with_cref): A binding should keep
refinements activation information and the refinements should be
activated in subsequent eval calls with the binding. [ruby-core:67945] [Bug #10818]