Reputation: 895
In /rails/lib/rails.rb there is an idiom with the logical &&
operator:
def root
application && application.config.root
end
What is the meaning of this idiom? It seems to do some control flow, to only return application.config.root if application exists or something like that?
Upvotes: 0
Views: 403
Reputation: 84353
Ruby code can throw a NoMethodError when invoking methods on an expression that evaluates to nil, or on an object that doesn't #respond_to? a particular method. While newer versions of Ruby support the safe navigation operator to guard against invoking methods on instances of NilClass, older code often needs this type of construct to avoid raising exceptions.
In peudocode:
define method "root":
if application does not evaluate to nil, then
invoke method config on application, then
invoke method root on application.config
In Ruby >= 2.3, you could potentially rewrite this method as:
def root
application&.config.root
end
However, there are cases where &.
isn't fully equivalent to obj && obj.method
. The former just protects against calling methods on nil. For example, if application.nil?
and application.respond_to? :config
are both false, using the safe navigator when calling application&.config
will still raise an exception.
Whether the logical AND in your example is necessary because of how the application variable is set or used, just a legacy idiom, or kept for backwards compatibility with earlier versions of Ruby is something you'd have to investigate in the Rails code base. In any case, the logical AND in your example guards against NoMethodError exceptions.
Upvotes: 2
Reputation: 10018
According to widely-used ruby style guides, https://github.com/rubocop-hq/ruby-style-guide#if-as-a-modifier, need to prefer modifier if/unless
usage when you have a single-line body. Another good alternative is the usage of control flow &&/||
(i.e. what you're asking about).
# bad
if some_condition
do_something
end
# good
do_something if some_condition
# another good option
some_condition && do_something
Upvotes: 2
Reputation: 28305
In ruby, logical operators are "lazy".
Consider the following:
def one
puts "Called method one"
false
end
def two
puts "Called method two"
true
end
puts "Calling one, then two:"
one && two
puts
puts "Calling two, then one:"
two && one
It gives this output:
Calling one, then two:
Called method one
Calling two, then one:
Called method two
Called method one
See what happened? When it called one
and got false
, it didn't even bother calling method two
-- because it already knew that the final result would be false
. (false && false
== false && true
== false
.)
Now let's look back at the original code you posted:
def root
application && application.config.root
end
What happens if application == nil
? This code doesn't even bother evaluating the right hand side. Since nil && literally_anything == nil
.
In other words, this is a safe-guard. It prevents the code from failing if application == nil
. It's (not exactly, but basically) equivalent to writing this:
def root
return nil if application == nil
application.config.root
end
Upvotes: 4