tags:

views:

499

answers:

2

This code works in irb:

irb(main):037:0> eval <<-EOS

irb(main):038:0" #{attribute} = "host"

irb(main):039:0" puts machine

irb(main):040:0" EOS

host

=> nil

irb(main):041:0> puts machine

host

=> nil

irb(main):042:0> puts attribute

machine

=> nil

irb(main):043:0>

however when I try to execute the same code as a ruby script I get the followinf error:

../autosys/convert_jil_to_zapp.rb:40: undefined local variable or method machine' for main:Object (NameError) from ../autosys/convert_jil_to_zapp.rb:29:in each_line' from ../autosys/convert_jil_to_zapp.rb:29 from ../autosys/convert_jil_to_zapp.rb:27:in each' from ../autosys/convert_jil_to_zapp.rb:27 pi929c1n10 /ms/user/h/hirscst/ruby/autosys 77$ gvim try.rb pi929c1n10 /ms/user/h/hirscst/ruby/autosys 78$ chmod +x try.rb pi929c1n10 /ms/user/h/hirscst/ruby/autosys 79$ ./try.rb host ./try.rb:8: undefined local variable or method machine' for main:Object (NameError)

can anyone explain why?

A: 

Because you're defining a method or variable named machine in IRB but not in your Ruby script.

Chuck
+6  A: 

It's because the machine variable was not already defined when eval was run. A more concise example:

Works in IRB but not as a script

eval 'x = 3'
puts x # throws an exception when run as a script
=> 3

Works in IRB and as a script

x = 1
eval 'x = 3'
puts x
=> 3

To quote Matz:

local variables should be determined at compile time, thus local variables defined first in the eval'ed string, can only be accessed from other eval'ed strings. In addition, they will be more ephemeral in Ruby2, so that these variables will not be accessed from outside.

The difference is that in IRB everything is being eval'd, so it's all in the same scope. That is, you're essentially doing this in IRB:

eval 'x = 3'
eval 'puts x'

Which works both in IRB and as a script.

Jordan Brough
I did not know that eval operates in a lower scope. But does defining x in the main script body causes the variables to be passed to eval by reference?
dmondark
Yes, it seems to behave a lot like this: `1.times {a = 3}; puts a; # NameError: undefined local variable or method 'a' for main:Object` versus `a = 1; 1.times {a = 3}; puts a; # 3` Judging by the linked thread, Matz made it work this way out of convenience of implementation.
Jordan Brough
Thanks makes sense. Thanks.
dmondark