In Ruby is there a way to define the context for load?

时间:2016-07-11 21:25:05

标签: ruby

I would like to load a file in a block and evaluate the statements in the context of an instance of a object. For example something like this:

I have a file called foo_file that contains just this one line (statement)

some_method

I would like this "statement" to be called in the context of an instance of Foo, as shown below.

class Foo
  def some_method
    puts "hello"
  end
end

foo = Foo.new

foo.instance_eval  do
    load "foo_file"     # load and execute statement some_method from a foo_file.  
end

This fails with:

 foo_file:1:in `<top (required)>': undefined local variable or method `some_method' for main:Object (NameError)

while this works fine:

foo.instance_eval do
  some_method
end

So it seems that while I call load in the block passed to instance_eval, the statements are loaded into the top context, not the instance foo.

I can get around this with the code shown below. I create a top level method that wraps a call to the instance method. I still need to provide the context, which I accomplish with a singleton.

require 'singleton'

class Context
  include Singleton
  attr_accessor :ctx 
end

def some_method
  ctx = Context.instance.ctx
  ctx.some_method
end

Context.instance.ctx = foo
load "bar_file"

I think this will work, but it seems like there should be a better way (like just loading into the correct context to begin with). Any suggestions?

1 个答案:

答案 0 :(得分:3)

In this particular case, getting what you want is quite easy, and doesn't even involve a call to load:

foo.instance_eval(File.read(file))

For example, if file just had the text length, then

"Hello".instance_eval(File.read(file))

would return 5.

More generally, if you want to specify an arbitrary context, that's basically the exact situation you'd want to use the optional Binding argument to Kernel#eval in:

bnd = 'Hello'.instance_exec { x = 3; binding }
eval('length + x',bnd) #=> 8