Ruby: why does an undeclared instance variable return nil - ruby

Given the following class:
class Something
def initialize
#test = "test"
end
end
Why does ruby return nil when invoking an undeclared instance variable?
thingy = Something.new
thingy.instance_variable_get(:#var) # nil
thingy.instance_variable_get(:#test) # "test"
As opposed to some error message indicating the variable is missing from the instance. The answer I am hoping for is an explanation as to the reasoning behind ruby's implementation of instance variables in this way.

It's just the way instance variables implemented in Ruby. Same for global variables. Also you can see warnings for such things if you pass -w parameter to ruby.
✗ irb -w
2.3.1 :001 > #a
(irb):1: warning: instance variable #a not initialized
=> nil

Because, instead of throwing an error, objects are allowed to have empty values. Such as a user, must have a name and address but email address is optional, a check can be done for nil before continuing.
if customer1.name == nil #personalized error code here
if customer1.email == nil customer1.email = "not given"

Related

Behaviours of a Ruby local variable shadowing an instance method

I recently read a blog post about Ruby's behaviours with regards to a local variable shadowing a method (different to, say, a block variable shadowing a method local variable, which is also talked about in this StackOverflow thread), and I found some behaviour that I don't quite understand.
Ruby's documentation says that:
[V]ariable 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.
So, given the following example class
# person.rb
class Person
attr_accessor :name
def initialize(name = nil)
#name = name
end
def say_name
if name.nil?
name = "Unknown"
end
puts "My name is #{name.inspect}"
end
end
and given what I now know from reading the information from the links above, I would expect the following:
The name.nil? statement would still refer to the name instance method provided by attr_accessor
When the Ruby parser sees the name = "Unknown" assignment line in the #say_name method, it will consider any reference to name used after the assignment to refer to the local variable
Therefore, even if the Person had a name assigned to it on initialisation, the name referenced in the final line of #say_name method would be nil
And it looks like this can be confirmed in an irb console:
irb(main):001:0> require "./person.rb"
true
# `name.nil?` using instance method fails,
# `name` local variable not assigned
irb(main):002:0> Person.new("Paul").say_name
My name is nil
nil
# `name.nil?` using instance method succeeds
# as no name given on initialisation,
# `name` local variable gets assigned
irb(main):003:0> Person.new.say_name
My name is "Unknown"
nil
However, if I do some inline debugging and use Pry to attempt to trace how the referencing of name changes, I get the following:
irb(main):002:0> Person.new("Paul").say_name
From: /Users/paul/person.rb # line 13 Person#say_name:
10: def say_name
11: binding.pry
12:
=> 13: p name
14: if name.nil?
15: name = "Unknown"
16: end
17:
18: puts "My name is #{name.inspect}"
19: end
[1] pry(#<Person>)> next
"Paul"
Okay, that makes sense as I'm assuming name is referring to the instance method. So, let's check the value of name directly...
From: /Users/paul/person.rb # line 14 Person#say_name:
10: def say_name
11: binding.pry
12:
13: p name
=> 14: if name.nil?
15: name = "Unknown"
16: end
17:
18: puts "My name is #{name.inspect}"
19: end
[2] pry(#<Person>)> name
nil
Err... that was unexpected at this point. I'm currently looking at a reference to name above the assignment line, so I would have thought it would still reference the instance method and not the local variable, so now I'm confused... I guess somehow the name = "Unknown" assignment will run, then...?
[3] pry(#<Person>)> exit
My name is nil
nil
Nope, same return value as before. So, what is going on here?
Was I wrong in my assumptions about name.nil? referencing the name instance method? What is it referencing?
Is all this something related to being in the Pry environment?
Something else I've missed?
For reference:
➜ [ruby]$ ruby -v
ruby 2.4.2p198 (2017-09-14 revision 59899) [x86_64-darwin16]
Edit
The example code in this question is meant to be illustrative of the (I think) unexpected behaviour I'm seeing, and not in any way illustrative of actual good code.
I know that this shadowing issue is easily avoided by re-naming the local variable to something else.
Even with the shadowing, I know that it is still possible to avoid the problem by specifically invoking the method, rather than reference the local variable, with self.name or name().
Playing around with this further, I'm starting to think it's perhaps an issue around Pry's environment. When running Person.new("Paul").say_name:
From: /Users/paul/person.rb # line 13 Person#say_name:
10: def say_name
11: binding.pry
12:
=> 13: p name
14: if name.nil?
15: name = "Unknown"
16: end
17:
18: puts "My name is #{name.inspect}"
19: end
At this point, the p statement hasn't run yet, so let's see what Pry says the value of name is:
[1] pry(#<Person>)> name
nil
This is unexpected given that Ruby's documentation says that since no assignment has been made yet, the method call should be invoked. Let's now let the p statement run...
[2] pry(#<Person>)> next
"Paul"
...and the value of the method name is returned, which is expected.
So, what is Pry seeing here? Is it modifying the scope somehow? Why is it that when Pry runs name it gives a different return value to when Ruby itself runs name?
Once Ruby has decided that name is a variable and not a method call that information applies to the totality of the scope it appears within. In this case it's taking it to mean the whole method. The trouble is if you have a method and a variable with the same name the variable only seems to take hold on the line where the variable has been potentially assigned to and this re-interpretation affects all subsequent lines within that method.
Unlike in other languages where method calls are made clear either by some kind of prefix, suffix or other indicator, in Ruby name the variable and name the method call look identical in code and the only difference is how they're interpreted at "compile" time proior to execution.
So what's happening here is a little confusing and subtle but you can see how name is being interpreted with local_variables:
def say_name_local_variable
p defined?(name) # => "method"
p local_variables # => [:name] so Ruby's aware of the variable already
if name.nil? # <- Method call
name = "Unknown" # ** From this point on name refers to the variable
end # even if this block never runs.
p defined?(name) # => "local-variable"
p name # <- Variable value
puts "My name is #{name.inspect}"
end
I'm quite surprised that, given how obnoxiously particular Ruby can be with the -w flag enabled, that this particular situation generates no warnings at all. This is likely something the'll have to emit a warning for, a strange partial shadowing of methods with variables.
To avoid method ambiguity you'll need to prefix it to force it to be a method call:
def say_name
name = self.name || 'Unknown'
puts "My name is #{name.inspect}"
end
One thing to note here is that in Ruby there are only two logically false values, literal nil and false. Everything else, including empty strings, 0, empty arrays and hashes, or objects of any kind are logically true. That means unless there's a chance name is valid as literal false then || is fine for defaults.
Using nil? is only necessary when you're trying to distinguish between nil and false, a situation that might arise if you have a three-state checkbox, checked, unchecked, or no answer given yet.
What looks like inconsistent return values for name during runtime and while debugging doesn't seem to related to Pry, but more about binding itself encapsulating the entire execution context of a method, versus the progressive change in what shadowed variables reference at runtime. To build on the example method with some more debugging code:
def say_name
puts "--- Before assignment of name: ---"
puts "defined?(name) : #{defined?(name).inspect}"
puts "binding.local_variable_defined?(:name) : #{binding.local_variable_defined?(:name).inspect}"
puts "local_variables : #{local_variables.inspect}"
puts "binding.local_variables : #{binding.local_variables.inspect}"
puts "name : #{name.inspect}"
puts "binding.eval('name') : #{binding.eval('name').inspect}"
if name.nil?
name = "Unknown"
end
puts "--- After assignment of name: ---"
puts "defined?(name) : #{defined?(name).inspect}"
puts "binding.local_variable_defined?(:name) : #{binding.local_variable_defined?(:name).inspect}"
puts "local_variables : #{local_variables.inspect}"
puts "binding.local_variables : #{binding.local_variables.inspect}"
puts "name : #{name.inspect}"
puts "binding.eval('name') : #{binding.eval('name').inspect}"
puts "My name is #{name.inspect}"
end
Now, running Person.new("Paul").say_name outputs:
--- Before assignment of name: ---
defined?(name) : "method"
binding.local_variable_defined?(:name) : true
local_variables : [:name]
binding.local_variables : [:name]
name : "Paul"
binding.eval('name') : nil
--- After assignment of name: ---
defined?(name) : "local-variable"
binding.local_variable_defined?(:name) : true
local_variables : [:name]
binding.local_variables : [:name]
name : nil
binding.eval('name') : nil
My name is nil
which shows that binding never references the method call of name and only ever the eventually-assigned name variable.

What does &. (ampersand dot) mean in Ruby?

I came across this line of ruby code. What does &. mean in this?
#object&.method
It is called the Safe Navigation Operator. Introduced in Ruby 2.3.0, it lets you call methods on objects without worrying that the object may be nil(Avoiding an undefined method for nil:NilClass error), similar to the try method in Rails.
So you can write
#person&.spouse&.name
instead of
#person.spouse.name if #person && #person.spouse
From the Docs:
my_object.my_method
This sends the my_method message to my_object. Any
object can be a receiver but depending on the method's visibility
sending a message may raise a NoMethodError.
You may use &. to designate a receiver, then my_method is not invoked
and the result is nil when the receiver is nil. In that case, the
arguments of my_method are not evaluated.
Note: Even though #Santosh gave a clear and full answer, I would like add some more background and add an important note regarding its use with non instance variables.
It is called "Safe Navigation Operator" (aka "Optional chaining operator", "Null-conditional operator", etc.). Matz seems to call it "lonely operator". It was introduced in Ruby 2.3. It sends a method to an object only if it is not nil.
Example:
# Call method `.profile` on `user` only if `user` is not `nil`
#user&.profile
# Equivalent to
unless #user.nil?
#user.profile
end
"Edge case" with local variables:
Please note, above code uses instance variables. If you want to use safe navigation operator with local variables, you will have to check that your local variables are defined first.
# `user` local variable is not defined previous
user&.profile
# This code would throw the following error:
NameError: undefined local variable or method `user' for main:Object
To fix this issue, check if your local variable is defined first or set it to nil:
# Option 1: Check the variable is defined
if defined?(user)
user&.profile
end
# Option 2: Define your local variable. Example, set it to nil
user = nil
user&.profile # Works and does not throw any errors
Method background
Rails has try method that basically does the same. It uses send method internally to call a method. Matz suggested that it is slow and this should be a built-in language feature.
Many other programming languages have similar features: Objective C, Swift, Scala, CoffeeScript, etc. However, a common syntax is ?. (question dot). But, this syntax could not be adopted by Ruby. Because ? was allowed in method names and thus, ?. symbol sequence is already a valid Ruby code. For example:
2.even?.class # => TrueClass
That's why Ruby community had to come up with different syntax. It was an active discussion and different options were considered (.?, ?, &&, etc.). Here is a list of some considerations:
u.?profile.?thumbnails
u\profile\thumbnails
u!profile!thumbnails
u ? .profile ? .thumbnails
u && .profile && .thumbnails
# And finally
u&.profile&.thumbnails
While choosing the syntax, developers looked at different edge cases and the discussion is quite useful to go through. If you want to go through all variants and nuance of the operator, please see this feature introduction discussion on official Ruby issue tracker.
Be wary! Though the safe navigation operator is convenient it can also be easy to trick yourself into changing your logic with it. I recommend avoiding the use of it in flow control. Example:
str = nil
puts "Hello" if str.nil? || str.empty?
# The above line is different than the below line
puts "Hello" if str&.empty?
In the first example, str.nil? returns true and str.empty? is never called, causing the puts statement to be executed. In the second example however, str&.empty? returns nil which is falsey, and the puts statement is never executed.
safe navigation operator (&.): tells Ruby to only call the next method if the receiver isn’t nil. Otherwise, the expression returns nil.
Practical In Action
Let’s construct a Roster object for a Sports team. The Roster will contain multiple Player objects.
class Roster
attr_accessor :players
end
class Player
attr_accessor :name, :position
def initialize(name, position)
#name = name
#position = position
end
end
With these two objects, we can create a roster for a 2-on-2 women’s basketball tournament:
moore = Player.new("Maya Moore", "Forward")
taurasi = Player.new("Diana Taurasi", "Guard")
tourney_roster1 = Roster.new
tourney_roster1.players = [moore, taurasi]
If we want to know the forward for our 2-on-2 team, we might find the name this way:
if tourney_roster1.players.first.position == "Forward"
puts "Forward: #{tourney_roster1.players.first.name}"
end
But what if our opposing roster isn’t set correctly?
tourney_roster2 = Roster.new
if tourney_roster2.players.first.position == "Forward"
puts "Forward: #{tourney_roster1.players.first.name}"
end
tourney_roster2 hasn’t yet been set with any players. The preceding code will raise a NoMethodError because tourney_roster2.players returns nil. We can add conditional statements to avoid this, but it makes our if statement verbose and unclear:
if tourney_roster2.players &&
tourney_roster2.players.first &&
tourney_roster2.players.first.position == "Forward"
Instead, we can use the safe navigation operator to avoid the NoMethodError:
if tourney_roster2.players&.first&.position == "Forward"
puts "Forward: #{tourney_roster1.players.first.name}"
end
Thus,
>> tourney_roster2.players&.first == nil
#=> true
>> tourney_roster2.players&.first&.position == nil
#=> true
Some legitimate use cases: The safe navigation operator comes in handy when working with multiple objects, as shown here, and when chaining methods together.
it used for nil check, such as in kotlin and swift
For example;
with Object -> Swift and Kotlin
model = car?.model
this model can be nil(Swift) or null(Kotlin) if we have not defined the model value in car class.
we use that ampersand instead of question mark in ruby
model = car&.model
if use car.model without ampersand and if model is nil the system cannot continue running.
Here's a short-read (3 mins) I found on this - it is pretty good.
To add to the above, it acts like the try! method in Rails, not the try method.
Because it will raise a NoMethodError exception if the receiver is not nil and does not implement the tried method.
Example taken from the above article:
account = Account.new(owner: Object.new)
account&.owner&.address
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`
account.try(:owner).try(:address)
# => nil
account.try!(:owner).try!(:address)
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`
For all of those who came here from Typescript, it's the same as the ? operator
what does mean object&.an_attribute in ruby on rails?
I am new in Ruby on rails and I saw this kind of code but I don't understand it:
In Ruby, like in most mainstream programming languages, user code cannot modify the fundamental workings of the programming languages, nor can it change the programming language's syntax.
Since Ruby on Rails is just Ruby code, it should be immediately obvious that this cannot possibly have anything to do with Ruby on Rails.
Therefore, we need to look at Ruby for an explanation, not Ruby on Rails.
The safe navigation operator or safe navigator is specified in language/safe_navigator_spec.rb of the ruby/spec, in particular here:
context "when context is nil" do
it "always returns nil" do
eval("nil&.unknown").should == nil
eval("[][10]&.unknown").should == nil
end
it "can be chained" do
eval("nil&.one&.two&.three").should == nil
end
it "doesn't evaluate arguments" do
obj = Object.new
obj.should_not_receive(:m)
eval("nil&.unknown(obj.m) { obj.m }")
end
end
It is documented in the Calling Methods section of the Ruby Syntax documentation:
&., called “safe navigation operator”, allows to skip method call when receiver is nil. It returns nil and doesn't evaluate method's arguments if the call is skipped.

Why do undeclared Ruby local, instance, class, & global variables have different behavior?

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

How to disable warning for redefining a constant when loading a file

Is there a way to disable warning: already initialized constant when loading particular files?
The solution to your problem depends on what is causing it.
1 - You are changing the value of a constant that was set before somewhere in your code, or are trying to define a constant with the same name as an existant class or module. Solution: don't use constants if you know in advance that the value of the constant will change; don't define constants with the same name as class/modules.
2 - You are in a situation where you want to redefine a constant for good reasons, without getting warnings. There are two options.
First, you could undefine the constant before redefining it (this requires a helper method, because remove_const is a private function):
Object.module_eval do
# Unset a constant without private access.
def self.const_unset(const)
self.instance_eval { remove_const(const) }
end
end
Or, you could just tell the Ruby interpreter to shut up (this suppresses all warnings):
# Runs a block of code without warnings.
def silence_warnings(&block)
warn_level = $VERBOSE
$VERBOSE = nil
result = block.call
$VERBOSE = warn_level
result
end
3 - You are requiring an external library that defines a class/module whose name clashes with a new constant or class/module you are creating. Solution: wrap your code inside a top-level module-namespace to prevent the name clash.
class SomeClass; end
module SomeModule
SomeClass = '...'
end
4 - Same as above, but you absolutely need to define a class with the same name as the gem/library's class. Solution: you can assign the library's class name to a variable, and then clear it for your later use:
require 'clashing_library'
some_class_alias = SomeClass
SomeClass = nil
# You can now define your own class:
class SomeClass; end
# Or your own constant:
SomeClass = 'foo'
Try this :
Kernel::silence_warnings { MY_CONSTANT = 'my value '}
To suppress warnings, use the following code at the top of the script:
$VERBOSE = nil
The accepted answer to this question was helpful. I looked at the Rails source to get the following. Before and after loading the file, I can insert these lines:
# Supress warning messages.
original_verbose, $VERBOSE = $VERBOSE, nil
load(file_in_question)
# Activate warning messages again.
$VERBOSE = original_verbose
Using user2398029's reply the simplest way for me to remove warnings was to add this line:
before { described_class.instance_eval { remove_const(:CONSTANT_NAME) } }

Ruby equivalent operators at "OrElse" and "AndAlso" of Vb.net

There are operators in Ruby similar to "OrElse"and "AndAlso" in VB.NET?
For example in Ruby NoMethodError exception is raised when active_record is nil:
if active_record.nil? || active_record.errors.count == 0
...
end
In VB.net i can do:
If active_record Is Nothing OrElse active_record.errors.count = 0
...
End
That does not generate an exception because it is only checked the first expression
In this case there will be no exception raised (because only the first term in || will be evaluated). However you might be interested in reading about Object#try from ActiveSupport, which can be helpful when dealing with objects that can be nil.
in ruby, there is a big difference between something that is nil and something that is undefined. Considering the following, from IRB:
ruby-1.9.2-p0 :002 > active_record
NameError: undefined local variable or method `active_record' for main:Object
from (irb):2
from /Users/jed/.rvm/rubies/ruby-1.9.2-p0/bin/irb:16:in `<main>'
ruby-1.9.2-p0 :003 > active_record = nil
=> nil
ruby-1.9.2-p0 :004 > active_record.class
=> NilClass
ruby-1.9.2-p0 :006 > active_record.nil?
=> true
So, an object that is nil is an instance of NilClass and therefore responds to the message nil? will return true, but without declaring the variable (as in your code) Ruby doesn't know what you are calling.
A couple of options here:
Ruby's || operator is a strict operator, whereas the or keyword is less strict, so I don't know where the vb operation compares to these two or flow options.
you could use a neat little gem callled 'andand'
require 'andand'
active_record.andand.errors.count == 0
but, generally when you are dealing with this situation in rails, you would use another means to determine the situation above, consider:
#post = Post.new(:my_key => "my value") #=> an ActiveRecord object
if #post.valid?
# do something meaningful
else
puts #post.errors.full_messages.to_sentence
end
and if you mean to assign something based on if it possibly undefined, you would want to use memoization:
#post ||= Post.new
which will declare the object if undefined or use the existing object
Ruby || is short circuit evaluation operator, so it should evaluate only first condition, therefore your if should not raise any exception.
I assume active_record.nil? returns boolean true.

Resources