I am trying to understanding eval and binding contexts in Ruby.
Consinder the following in irb
irb(main):001:0> eval "a = 42"
=> 42
irb(main):002:0> a
NameError: undefined local variable or method `a' for main:Object
from (irb):2
from /Users/niels/.rbenv/versions/2.1.3/bin/irb:11:in `<main>'
irb(main):003:0>
Why is a not defined?
If I declare a prior to evalling, the value 42 is assigned to a.
It appears to me that some sort of block scope applies where local variables are available inside the eval context, but any variables declared are only declared in the block scope.
How do I eval code without creating a new scope?
Why is a not defined?
a is defined within the binding of the eval'd code, but not outside of it. That's just how local variables work. They are local to the scope they are defined in. That's why they are called "local" variables, after all.
If I declare a prior to evalling, the value 42 is assigned to a.
Yes, eval's scope nests, just like block scope.
How do I eval code without creating a new scope?
You can't. In Ruby 1.8 and prior, eval would indeed leak variables into the surrounding scope, but that leak was fixed in 1.9 and onwards.
That is because a is not on the same context than irb.
look at this code
2.2.1 :001 > eval "a = 42"
=> 42
2.2.1 :002 > a
NameError: undefined local variable or method `a' for main:Object
from (irb):2
from /home/hbranciforte/.rvm/rubies/ruby-2.2.1/bin/irb:11:in `<main>'
it's the same as blocks
2.2.1 :001 > 1.times{|a| b=1}
=> 1
2.2.1 :002 > b
NameError: undefined local variable or method `b' for main:Object
from (irb):2
from /home/hbranciforte/.rvm/rubies/ruby-2.2.1/bin/irb:11:in `<main>'
But...
2.2.1 :001 > a=nil
=> nil
2.2.1 :002 > eval "a = 42"
=> 42
2.2.1 :003 > a
=> 42
it's like
2.2.1 :001 > b=nil
=> nil
2.2.1 :002 > 1.times{|a| b=1}
=> 1
2.2.1 :003 > b
=> 1
2.2.1 :004 >
Instances variables work because it is part of "self"
2.2.1 :001 > eval "#b = 42"
=> 42
2.2.1 :002 > #b
=> 42
2.2.1 :003 >
Take a look how to context could be sent to a method. from http://ruby-doc.org/core-2.2.0/Binding.html
def get_binding(param)
return binding
end
b = get_binding("hello")
b.eval("param") #=> "hello"
I don't know if I was clear but, that I want to say is that the real thing that really matters is the context (or scope) where ruby will allocate/deallocate memory and his access.
Related
Due to some sloppy coding on my part, I've noticed that undeclared instance variables seem to evaluate nil where undeclared local variables do not. Is this default nill value for instance variables intended behavior that I can exploit (like with a conditional to check whether the variable has been set true) or is this just a quirk that should be left alone?
2.6.6 :001 > puts #test
=> nil
2.6.6 :002 > puts test
Traceback (most recent call last):
2: from (irb):2
1: from (irb):2:in `test'
ArgumentError (wrong number of arguments (given 0, expected 2..3))
Per the Ruby doc:
Instance variables of ruby do not need declaration. This implies a flexible structure of objects. In fact, each instance variable is dynamically appended to an object when it is first referenced.
https://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/instancevars.html
You can also get a list of all previously defined instance variables:
ClassName.instance_variables
or
a = ClassName.new
a.instance_variables #specific to instance
tl;dr Yes, this is documented in Assignment.
Whether a thing is defined,and what it is defined as, can be had with the defined? keyword.
2.6.5 :003 > defined?(fnord)
=> nil
2.6.5 :004 > fnord = 42
=> 42
2.6.5 :005 > defined?(fnord)
=> "local-variable"
2.6.5 :006 > defined?($fnord)
=> nil
2.6.5 :007 > $fnord = 42
=> 42
2.6.5 :008 > defined?($fnord)
=> "global-variable"
2.6.5 :009 > defined?(#fnord)
=> nil
2.6.5 :010 > #fnord = 42
=> 42
2.6.5 :011 > defined?(#fnord)
=> "instance-variable"
2.6.5 :012 > defined?(FNORD)
=> nil
2.6.5 :013 > FNORD = 42
=> 42
2.6.5 :014 > defined?(FNORD)
=> "constant"
This is useful for debugging, but I'd discourage "exploiting" it in application code.
First, let's clear up your example. test is a method of main. It is inherited from Kernel#test.
2.6.5 :004 > defined?(test)
=> "method"
2.6.5 :005 > method(:test)
=> #<Method: main.test>
A proper example looks like this.
2.6.5 :008 > puts #fnord
=> nil
2.6.5 :009 > puts fnord
Traceback (most recent call last):
4: from /Users/schwern/.rvm/rubies/ruby-2.6.5/bin/irb:23:in `<main>'
3: from /Users/schwern/.rvm/rubies/ruby-2.6.5/bin/irb:23:in `load'
2: from /Users/schwern/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
1: from (irb):9
NameError (undefined local variable or method `fnord' for main:Object)
Local variables
The error hints at what's happening: ambiguity. fnord could be the local variable fnord or self.fnord. Neither have been declared, Ruby won't guess at what you meant, so you get a NameError.
In Ruby local variable names and method names are nearly identical. If you have not assigned to one of these ambiguous names ruby will assume you wish to call a method. Once you have assigned to the name ruby will assume you wish to reference a local variable.
Declared local variables can be discovered with local_variables.
2.6.5 :012 > foo = 42
=> 42
2.6.5 :013 > Kernel.local_variables
=> [:foo, :_]
Instance variables
An uninitialized instance variable has a value of nil. If you run Ruby with warnings enabled, you will get a warning when accessing an uninitialized instance variable.
There are various methods for introspecting instance variables like instance_variables and instance_variables_defined?. While there are some instances this might be necessary when writing libraries, I strongly discourage exploiting the distinction between nil and "undefined" in application code; it makes things fragile.
Class variables
Accessing an uninitialized class variable will raise a NameError exception.
Global variables
An uninitialized global variable has a value of nil.
This question already has an answer here:
In Ruby, why does nil[1]=1 evaluate to nil?
(1 answer)
Closed 6 years ago.
I've just noticed a very puzzling change in behaviour between Ruby 2.2.4 and Ruby 2.3.0: trying to use [] on nil in an assignment does not raise a NoMethodError anymore.
Ruby 2.2.4:
box:~ jfoeh$ irb
2.2.4 :001 > a = nil
=> nil
2.2.4 :002 > a[:b] = 1
NoMethodError: undefined method `[]=' for nil:NilClass
from (irb):2
from /Users/jfoeh/.rvm/rubies/ruby-2.2.4/bin/irb:11:in `<main>'
In contrast Ruby 2.3.0:
box:~ jfoeh$ irb
2.3.0 :001 > a = nil
=> nil
2.3.0 :002 > a[:b] = 1
=> nil
Is that behaviour expected, or is this a regression of sorts?
We originally noticed this when we found such an assignment seemingly swallowing exceptions in 2.3:
2.3.0 :001 > require 'date'
=> true
2.3.0 :002 > a = nil
=> nil
2.3.0 :003 > a[:b] = Date.parse(nil)
=> nil
whereas Ruby 2.2 would execute the right-hand side first and raise a TypeError as one would expect.
This was a bug introduced in ruby version 2.3.0. It has since been fixed, as of version 2.3.1.
Here is the original issue that was raised, on ruby-lang.org, and here is the commit which resolves the issue.
This question already has answers here:
Why can I refer to a variable outside of an if/unless/case statement that never ran?
(3 answers)
Closed 2 years ago.
I noticed a strange behavior with Ruby local variables in the code below. It seems that Ruby runs into the false part and sets params to nil. The code in irb is below:
2.1.2 :001 > def params
2.1.2 :002?> {a:1}
2.1.2 :003?> end
2.1.2 :014 > def go1!
2.1.2 :015?> p params
2.1.2 :016?> if false
2.1.2 :017?> params = 1
2.1.2 :018?> end
2.1.2 :019?> p params
2.1.2 :020?> end
=> :go1!
2.1.2 :021 > go1!
{:a=>1}
nil
=> nil
Can anyone explain this?
Ruby determines the lifetime of local variables while it parses the code, so even if params = 1 assignment wouldn't be reached, params will be interpreted as local variable (and set to nil by default) in this scope.
Here's the link to documentation:
http://docs.ruby-lang.org/en/2.1.0/syntax/assignment_rdoc.html#label-Local+Variables+and+Methods
courtesy of irb:
2.0.0-p0 :006 > #instance_variable = "from an instance variable"
=> "from an instance variable"
2.0.0-p0 :007 > variable = "from a variable"
=> "from a variable"
2.0.0-p0 :008 > instance_variable_get(:#instance_variable)
=> "from an instance variable"
2.0.0-p0 :009 > variable_get(:variable)
NoMethodError: undefined method `variable_get' for main:Object
from (irb):9
from /usr/local/rvm/rubies/ruby-2.0.0-p0/bin/irb:16:in `<main>'
2.0.0-p0 :010 >
I'm simply trying to programmatically 'query' for a variable and return its contents. Exactly like instance_variable_get but for a variable.
It's for a custom rspec matcher. Not some crazy workaround :)
In Ruby 2.1 and later, you can use Binding#local_variable_get.
In prior versions of Ruby, you have to use eval. If you want to do some sanity-checking before evaluating a supposed variable name, you can check whether the named variable is in local_variables.
You could use eval:
irb(main):007:0> variable = "from a variable"
=> "from a variable"
irb(main):008:0> eval 'variable'
=> "from a variable"
Some undeclared variables are nil, some throw an error. How come?
$ irb
1.9.3p0 :001 > asdf # local
NameError: undefined local variable or method `asdf' for main:Object
from (irb):1
from /Users/saizai/.rvm/rubies/ruby-1.9.3-p0/bin/irb:16:in `<main>'
1.9.3p0 :002 >#asdf # instance
=> nil
1.9.3p0 :003 >##asdf # class
NameError: uninitialized class variable ##asdf in Object
from (irb):3
from /Users/saizai/.rvm/rubies/ruby-1.9.3-p0/bin/irb:16:in `<main>'
1.9.3p0 :004 > $asdf # global
=> nil
Class variables must always be assigned or else they will return a NameError when you attempt to use them. I do not currently have the details as to why this is.
Instance and Global variables will return nil even if they are not assigned. However, they will raise a warning if you run the script with the -w flag.
I do, however, have the answer in regards to the local variables. The reason local variables act like this comes in the fact that they do not have any punctuation in front of them. This means the variable could be either a variable or a method call (since Ruby does not require () after a method call with no parameters).
something # could be a variable named 'something' or a method called 'something()'
If there is no value assigned to something variable then the Ruby interpreter assumes it is a method invocation. If there is no method by that name then it raises NameError. That is why you will get this message:
NameError: undefined local variable or method 'something' for main:Object
from (irb):1
from path/to/Ruby/bin/irb:12 in '<main>'
So, it is important for the Ruby interpreter to treat local variables in this manner just in case it is actually a method you are referring to.
As an interesting side note:
There is one quirkâa variable comes into existence when the Ruby
interpreter sees an assignment expression for that variable. This is
the case even if that assignment is not actually executed. A variable
that exists but has not been assigned a value is given the default
value nil.
Which means that:
if false
z = "Something"
end
z.nil? #=> true
never_assigned.nil? #=> NameError
The above quote is from The Ruby Programming Language by David Flanagan and Yukihiro Matsumoto section 4.2