Reputation: 16365
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
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
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