10

I am trying to dynamically create local variables in Ruby using eval and mutate the local-variables array. I am doing this in IRB.

eval "t = 2"
local_variables # => [:_]
eval "t"
# => NameError: undefined local variable or method `t' for main:Object
local_variables << "t".to_sym # => [:_, :t]
t
# => NameError: undefined local variable or method `t' for main:Object
sawa
  • 165,429
  • 45
  • 277
  • 381
ppone
  • 581
  • 8
  • 21

3 Answers3

17

You have to synchronize the evaluations with the same binding object. Otherwise, a single evaluation has its own scope.

b = binding
eval("t = 2", b)
eval("local_variables", b) #=> [:t, :b, :_]
eval("t", b) # => 2
b.eval('t') # => 2
Raj
  • 22,346
  • 14
  • 99
  • 142
sawa
  • 165,429
  • 45
  • 277
  • 381
  • Thanks. How do I get the binding for main Object. Everytime you run b = binding; it gives back a different binding object. – ppone Jul 24 '13 at 19:24
  • I was look for the outer binding to change. Not the b binding. When I type in t, it not part of the binding. I have to always type in eval(dosomethingwitht,b) – ppone Jul 24 '13 at 19:27
  • 2
    @ppone: again, what are you trying to accomplish with that? – Sergio Tulentsev Jul 24 '13 at 19:29
  • Please see Stefan answer. – ppone Jul 24 '13 at 19:34
  • 1
    He means why you are trying to dynamically set local variables at all. Your problem can probably be solved differently, without setting local variables dynamically and, above all, without using `eval`. – Patrick Oscity Jul 24 '13 at 19:42
  • 3
    I was just curious why it was not getting set. Your probably right there is a more efficient way to do it. Getting an answer to this question, helps me understand Ruby somewhat better. – ppone Jul 24 '13 at 19:45
  • 1
    I still don't see an answer anywhere here as to how, in general, to dynamically create a local variable in the existing context/binding, since `binding` always creates a new context. – Peter Alfvin Oct 07 '13 at 19:39
  • @PeterAlfvin `binding` does not create a new context. It refers to the current context. – sawa Oct 07 '13 at 19:46
  • So why does `eval('var=1',binding)` not create a new local variable `var`? (I saw you're most recent answer referring to Ruby 2.1 ...) – Peter Alfvin Oct 07 '13 at 19:49
  • @PeterAlfvin It does create `var`. I do not understand the relation to another answer of mine that you mention. – sawa Oct 07 '13 at 19:51
  • @PeterAlfvin I thought you were. – sawa Oct 07 '13 at 20:04
  • Anyway, in your answer to this question, `t` remains undefined in the current context (i.e. in the context the `eval` calls are being made). – Peter Alfvin Oct 07 '13 at 20:17
  • @PeterAlfvin That answer is correct, but is irrelevant to my answer. It is using `eval` without passing the binding argument, which is what the OP of this question is doing. – sawa Oct 07 '13 at 20:28
  • Just wanted to make sure you hadn't missed my earlier comment about `t` not being defined in the current context in your answer. :-) – Peter Alfvin Oct 07 '13 at 22:22
13

You have to use the correct binding. In IRB for example this would work:

irb(main):001:0> eval "t=2", IRB.conf[:MAIN_CONTEXT].workspace.binding
=> 2
irb(main):002:0> local_variables
=> [:t, :_]
irb(main):003:0> eval "t"
=> 2
irb(main):004:0> t
=> 2
Stefan
  • 109,145
  • 14
  • 143
  • 218
8

You could set instance variables like this:

instance_variable_set(:@a, 2)
@a
#=> 2
Patrick Oscity
  • 53,604
  • 17
  • 144
  • 168