Can you eval code in the context of a caller in Ruby?
There is no built-in way to get a callers binding in Ruby in 1.8.X or 1.9.X.
You can use https://github.com/banister/binding_of_caller to work around.
In MRI 2.0 you can use RubyVM::DebugInspector, see: https://github.com/banister/binding_of_caller/blob/master/lib/binding_of_caller/mri2.rb
Working sample in MRI 2.0:
require 'debug_inspector'
def bar(symbol)
RubyVM::DebugInspector.open do |inspector|
val = eval(symbol.to_s, inspector.frame_binding(2))
puts "#{symbol}: #{val}"
end
end
def foo
a = 100
bar(:a)
end
foo
# a: 100
Ruby - Automatically pass binding() to method like eval?
Okay, so it turns out there's no easy answer. I've compiled a list here:
Ruby: Binding of Caller Solutions
And it breaks down to the following possibilities (expanded on in the link above):
- Use binding_of_caller gem
- Use a really sneaky pure ruby trick that only works if your method takes no args (involving define_singleton_method and then composing (>>) Object::method(:binding) with your method). See examples at https://bugs.ruby-lang.org/issues/18487
- Use TracePoint API for a pure ruby solution that is amazingly slow
- Call rb_eval_string_protect("binding") inside of a C extension which, until Ruby 3.2.0, will get you all of the binding (but with a corrupted receiver)
- Use my cut down C extension that takes the important bits out of the gem that binding_of_caller depends upon (debug_inspector)
Maintaining local variables between eval of code
You need to store the Binding
and re-use the same one. If you call binding
repeatedly—even in the same scope—you will get a new binding. So, for the demo function, we can do this:
class Foo
def initialize; @b = binding; end
def bar; 42; end
def run(code)
@b.eval( code, '(yourcode)' )
end
end
f = Foo.new
f.run "x=bar ; p x" #=> 42
f.run "p x" #=> 42
evaluate a proc object in different context
You must pass the Proc as an argument so it's executed in that context.
Practice.instance_eval(&p)
Is there an eval in ruby
Googling "ruby eval" quickly reveals that the answer is yes.
eval(string [, binding [, filename [,lineno]]]) → obj
Evaluates the Ruby expression(s) in string. If binding is given, which
must be aBinding
object, the evaluation is performed in its context.
If the optional filename and lineno parameters are present, they will
be used when reporting syntax errors.
When is `eval` in Ruby justified?
The only case I know of (other than "I have this string and I want to execute it") is dynamically dealing with local and global variables. Ruby has methods to get the names of local and global variables, but it lacks methods to get or set their values based on these names. The only way to do AFAIK is with eval
.
Any other use is almost certainly wrong. I'm no guru and can't state categorically that there are no others, but every other use case I've ever seen where somebody said "You need eval for this," I've found a solution that didn't.
Note that I'm talking about string eval here, by the way. Ruby also has instance_eval
, which can take either a string or a block to execute in the context of the receiver. The block form of this method is fast, safe and very useful.
when calling instance_eval(&lambda) to pass current context got error 'wrong number of arguments'
You are actually correct in your assumption. Self is being passed to the Proc
and to the lambda as it is being instance_eval
'ed. A major difference between Procs and lambdas is that lambdas check the arity of the block being being passed to them.
So:
class Rule
def get_rule
lambda { |s| puts s.inspect; puts name; }
end
end
class Person
attr_accessor :name
def init_rule
@name = "ruby"
instance_eval(&Rule.new.get_rule)
end
end
p = Person.new
p.init_rule
#<Person:0x007fd1099f53d0 @name="ruby">
ruby
Here I told the lambda to expect a block with arity 1 and as you see in the argument inspection, the argument is indeed the self
instance of Person class.
ExecJS: keeping the context between two calls
Only the context you create when you call ExecJS.compile
is preserved between evals. Anything you want preserved needs to be part of the initial compile.
Related Topics
Prevent Rails Test from Deleting Seed Data
When Would a Ruby Flip-Flop Be Useful
Ruby 1.9 + Sinatra Incompatible Character Encodings: Ascii-8Bit and Utf-8
Why Isn't 'Method=' Treated the Same as Any Other Method
Best Solution for Authentication in Ruby on Rails
Differencebetween Class and Klass in Ruby
Ruby String to Date Conversion
Difference Between @Instance_Variable and Attr_Accessor
Spinning Background Tasks in Rails
How to Unzip a File in Ruby on Rails
String#Encode Not Fixing "Invalid Byte Sequence in Utf-8" Error
Ruby on Rails Rake Assets:Precompile Error
How to Download via Http Only Piece of Big File with Ruby
How to Embed Regular Expressions in Other Regular Expressions in Ruby
Where to Define Custom Error Types in Ruby And/Or Rails
Create Module Variables in Ruby