evfwcqcg
evfwcqcg

Reputation: 16365

Is it possible to access block's scope in method?

I'd like to write the method (define_variables) which can get a block and use the variables defined in it. Is it possible? For example, I'd like to get 5 in output:

module A
  def self.define_variables
    yield
    puts a # not 5 :(
  end
end

A::define_variables do
  a = 5
end

Maybe there is some tricks with eval, but haven't found anyone yet.

Upvotes: 2

Views: 256

Answers (2)

Andrew Marshall
Andrew Marshall

Reputation: 97004

In short, no. After you've called yield those variables defined in the block are gone (sort of, as we shall see), except for what is returned—that's just how scope works. In your example, the 5 is still there in that it is returned by the block, and thus puts yield would print 5. Using this you could return a hash from the block {:a => 5}, and then access multiple "variables" that way. In Ruby 1.8 (in IRb only) you can do:

eval "a = 5"
a # => 5

Though I don't know of anyway to eval the contents of a block. Regardless, in Ruby 1.9 the scope of eval was isolated and this will give you a NameError. You can do an eval within the context of a Binding though:

def foo
  b = yield
  eval(a, b) + 2
end

foo do
  a = 5
  binding
end # => 7

It seems to me that what you're trying to do is emulate macros in Ruby, which is just not possible (at least not pure Ruby), and I discourage the use of any of the "workarounds" I've mentioned above.

Upvotes: 5

brymck
brymck

Reputation: 7663

Agreed that this is a bit backwards, and Andrew's explanation is correct. If your use case is defining variables, however, there are already class_variable_set and instance_variable_set methods that are great for this:

module A
  def self.define_variables(vars = {})
    vars.each { |n, v| class_variable_set n, v }
    puts @@a
  end
end

A::define_variables :@@a => 5

The above is more of an example of how it would work within the code you've posted rather than a recommendation.

Upvotes: 2

Related Questions