When I use a variable in a loaded file that corresponds to a variable in
the local scope of the loading environment, I get an “undefined local
variable or method” error. I’m not sure why.
Here’s a short file to demonstrate, with a top-level instance variable
for comparison’s sake:
@var1 = 1
var2 = 2
puts “@var1: #{@var1}”
puts “var2: #{var2}”
load “external.rb”
Here’s external.rb:
puts “external.rb @var1: #{@var1}”
puts “external.rb var2: #{var2}”
The result is:
@var1: 1
var2: 2
external.rb @var1: 1
external.rb:2:in <top (required)>': undefined local variable or method
var2’ for main:Object (NameError)
. . .
Kernel.load documentation states: “In no circumstance will any local
variables in the loaded file be propagated to the loading environment.”
Are we to infer that it also works the other way around? If so, why?
Thanks,
Mark B.
Mark B. wrote in post #1014828:
Kernel.load documentation states: “In no circumstance will any local
variables in the loaded file be propagated to the loading environment.”
Are we to infer that it also works the other way around? If so, why?
Because local variables are local 
You get a new local scope inside every class definition introduced by
the ‘class’ keyword, and inside every method definition introduced by
the ‘def’ keyword. And as you found, whenever you load() a file.
The reason for keeping local variables local is very important, because
in ruby you can call methods just by their bare name, and also reference
local variables just by their bare name.
So if I were to write a piece of ruby code like this:
puts var2
Ruby has to decide if v2 is a local variable, or if it’s a method call.
Note that if you force a method call, there’s no ambiguity:
puts var2()
puts self.var2
But without this hint, it has to decide for itself. This decision is
made at parse-time, not at run-time (which would be too inefficient).
The rule is simple: inside the current scope, if you’ve seen a possible
assignment to this variable - even if it is never executed - then it’s a
variable.
if false
var2 = 123
end
puts var2 # nil
puts var3 # NameError: undefined local variable or method `v3’
Thanks, that confirms my suspicions.
I wonder why this is rarely mentioned in books and articles on Ruby
scope, even the best. For example, here’s a quote from a great Ruby book
I’m reading now (emphasis mine):
"There are exactly three places where a program leaves the previous
scope behind and opens a new one:
Class definitions
Module definitions
Methods"
That’s why I thought there might be some force at work other than
scoping mechanisms.
Mark B. wrote in post #1014954:
Thanks, that confirms my suspicions.
I wonder why this is rarely mentioned in books and articles on Ruby
scope, even the best. For example, here’s a quote from a great Ruby book
I’m reading now (emphasis mine):
"There are exactly three places where a program leaves the previous
scope behind and opens a new one:
Class definitions
Module definitions
Methods"
That’s why I thought there might be some force at work other than
scoping mechanisms.
One could argue that calling load() does not ‘open’ a new scope. load()
performs some tasks in another local scope and then returns, but did
load() ‘open’ a new
scope for you to write code in?
I know what book that is. 
Mark B. wrote in post #1014954:
"There are exactly three places where a program leaves the previous
scope behind and opens a new one:
Class definitions
Module definitions
Methods"
That’s not exactly true as written either, since you can define
classes and methods without starting a new scope.
x = 1234
Foo = Class.new do
define_method(:bar) do
puts x
end
end
puts Foo.new.bar
Hence why I said “class definitions introduced by the ‘class’ keyword”
etc.