Can You Eval Code in the Context of a Caller in Ruby

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):

  1. Use binding_of_caller gem
  2. 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
  3. Use TracePoint API for a pure ruby solution that is amazingly slow
  4. 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)
  5. 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 a Binding 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



Leave a reply



Submit