Regardless of whether a variable is defined, it depends not only on the scope, but also on its location in the ruby ββscript. That is, a variable is defined only if it was previously defined in parsing, and not in execution.
Here is an example:
begin puts "Is foo defined? #{defined?(foo).inspect}"
Output:
Is foo defined? nil but now foo is 1 Is foo defined? nil but now foo is 2 Is foo defined? nil but now foo is 3
Since foo was not previously defined in the script in the first line of the loop, at this point it is undefined and remains undefined, even if it is assigned the same line returns to a later point at run time.
This is why foo in the while condition of the question is always undefined:
while defined?(foo).nil?
and will be cyclically forever. In contrast, this loop runs only once:
begin foo = 1 end while defined?(foo).nil?
since foo is assigned earlier in parsing.
Edit:
Only loops that require a block appear to be isolated local variables from life outside of it. For example. loop , upto , each , inject , map , times , etc. To do this, they all require the use of the do and end or curly braces that bound the block. On the contrary, while , until and for do not, and therefore the variables defined inside them continue to live outside of them. This is shown here:
while true foo_while = 1 break end puts "foo_while: #{defined?(foo_while).inspect}" until false foo_until = 1 break end puts "foo_until: #{defined?(foo_until).inspect}" for i in 0..2 foo_for = 1 break end puts "foo_for: #{defined?(foo_for).inspect}" loop do foo_loop = 1 break end puts "foo_loop: #{defined?(foo_loop).inspect}" 1.upto(2) do |i| foo_upto = 1 break end puts "foo_upto: #{defined?(foo_upto).inspect}" [1,2,3].each do |i| foo_each = 1 break end puts "foo_each: #{defined?(foo_each).inspect}" [1,2,3].inject do |i,j| foo_inject = 1 break end puts "foo_inject: #{defined?(foo_inject).inspect}" [1,2,3].map do |i| foo_map = 1 break end puts "foo_map: #{defined?(foo_map).inspect}" 3.times do foo_times = 1 break end puts "foo_times: #{defined?(foo_times).inspect}"
Output:
foo_while: "local-variable" foo_until: "local-variable" foo_for: "local-variable" foo_loop: nil foo_upto: nil foo_each: nil foo_inject: nil foo_map: nil foo_times: nil