von spotz
von spotz

Reputation: 895

What does this logical AND operator do?

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

Answers (3)

Todd A. Jacobs
Todd A. Jacobs

Reputation: 84353

Avoiding NoMethodError

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

mikdiet
mikdiet

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

Tom Lord
Tom Lord

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

Related Questions