Ruby 1.9.2 Object.respond_to? :hello && Object.hello gives error, why? - ruby

While stepping through code today, I noticed something unexpected. This statement:
if Object.respond_to? :hello && Object.hello
#stuff
gives an undefined method error. But why? Obviously hello is not a valid method of Object, however given the short-circuit evaluation, shouldn't Object.hello be ignored whenever Object.respond_to? :hello is false?
I noticed this while playing with Authlogic, trying to figure out exactly why the UserSession class must define persisted? in Rails 3.
Thanks

The lack of parentheses is leading to a precedence issue:
>> Object.respond_to? :hello && Object.hello
NoMethodError: undefined method `hello' for Object:Class
from (irb):2
from /Users/john/.rvm/rubies/ruby-1.9.2-p136/bin/irb:16:in `<main>'
>> Object.respond_to?(:hello) && Object.hello
=> false

You have a precedence problem, the logical conjunction (&&) has a higher precedence than the method call so your example is executed like this:
if Object.respond_to?(:hello && Object.hello)
not like this:
if Object.respond_to?(:hello) && Object.hello
which is what you're assuming.

I believe you are running into an evaluation order problem. Consider the following:
Object.respond_to? :hello && true

If you are checking the condition of something with an if statement you are looking for a boolean response. Object.hello doesn't return false because the method doesn't exist. Your check of:
Object.respond_to? :hello
Is checking for that response though. There shouldn't be any reason, in my option for the second Object.hello.

Related

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.

New to Ruby: Defining Methods

I'm trying to teach myself Ruby. I don't know anyone who knows it, so I don't have anyone to help with my very simple, perhaps annoying problems. I'm running the following code and getting the following error:
def simon_says (command)
def echo (param)
puts "#{param}"
end
end
The error:
PS R:\learn_ruby\03_simon_says> rake
(in R:/learn_ruby)
You must use ANSICON 1.31 or later (http://adoxa.3eeweb.com/ansicon/) to use colour on Windows
Simon says
echo
should echo hello (FAILED - 1)
Failures:
1) Simon says echo should echo hello
Failure/Error: echo("hello").should == "hello"
NoMethodError:
undefined method `echo' for #RSpec::Core::ExampleGroup::Nested_1::Nested_1:0x2fb5690>
# ./03_simon_says/simon_says_spec.rb:19:in `block (3 levels) in <top (required)>'
Finished in 0.003 seconds
1 example, 1 failure
The Rspec code can be found here (Github): simon_says_spec.rb
I just really have no idea what's wrong. Why would it say 'echo' is undefined? It looks defined to me. I also tried just outputting "hello" itself instead of the method parameter (param). Same error. All help appreciated. Also, if anyone knows of a more level appropriate place I can get this kind of help, it'd be nice.
Your program says the following:
Define a method called simon_says. When that function is executed, it should define a method called echo.
Thus, before you invoke simon_says(), echo does not exist:
echo("foo")
# NoMethodError
simon_says("whatever")
# => nil
echo("bar")
# bar
# => nil
Now, I do not know why you wrapped def echo inside def simon_says, so I can't really offer any helpful advice as to what you should be doing instead of what you now are.
Looking to your spec, I guess simon_says is class. It is not method. You are defining method inside method. Try this
class SimonSays
def echo params
puts "#{params}"
end
end

Does should_receive do something I don't expect?

Consider the following two trivial models:
class Iq
def score
#Some Irrelevant Code
end
end
class Person
def iq_score
Iq.new(self).score #error here
end
end
And the following Rspec test:
describe "#iq_score" do
let(:person) { Person.new }
it "creates an instance of Iq with the person" do
Iq.should_receive(:new).with(person)
Iq.any_instance.stub(:score).and_return(100.0)
person.iq_score
end
end
When I run this test (or, rather, an analogous one), it appears the stub has not worked:
Failure/Error: person.iq_score
NoMethodError:
undefined method `iq_score' for nil:NilClass
The failure, as you might guess, is on the line marked "error here" above. When the should_receive line is commented out, this error disappears. What's going on?
Since RSpec has extended stubber functionality, now following way is correct:
Iq.should_receive(:new).with(person).and_call_original
It will (1) check expectation (2) return control to original function, not just return nil.
You're stubbing away the initializer:
Iq.should_receive(:new).with(person)
returns nil, so Iq.new is nil. To fix, just do this:
Iq.should_receive(:new).with(person).and_return(mock('iq', :iq_score => 34))
person.iq_score.should == 34 // assert it is really the mock you get

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

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