PoVa
PoVa

Reputation: 1043

Does ruby stop evaluating if statements when first condition is false?

Does ruby stop evaluating if statements when first condition is false? I constantly get undefined method `ready' for nil:NilClass> if song = nil.

    if !song.nil? && song.ready && !song.has_been_downloaded_by(event.author)
      song.send_to_user(event.author)
      nil
    elsif !song.ready
      "The song is not ready yet. Try again once it is."
    elsif song.has_been_downloaded_by(event.author)
      "Yo, check your private messages, I've already sent you the song."
    else
      'Song with such index does not exist.'
    end

Upvotes: 9

Views: 6008

Answers (3)

DiegoSalazar
DiegoSalazar

Reputation: 13531

Ruby, and most other programming languages use short circuiting boolean expressions. Meaning any expression of the form false && puts("hi") will not run the right side of the expression puts("hi"). This goes for if conditions as well, anything with && really.

This is specially important to know because you always want to put faster or cheaper expressions/functions on the left side and more expensive expressions on the right side of a && operator.

Consider this

puts "hi" if expensive_method() && some_value

In the above example expensive_method will always run. But what if some_value is sometimes false? This would be more efficient:

puts "hi" if some_value && expensive_method()

Taking advantage of the possibility that some_value might sometimes be false, we spare ourselves from having to evaluate expensive_method in those cases.

In short, take advantage of boolean expression short circuiting.

https://en.wikipedia.org/wiki/Short-circuit_evaluation

Upvotes: 10

Cary Swoveland
Cary Swoveland

Reputation: 110725

For the exception to occur in the first line:

if !song.nil? && song.ready && !song.has_been_downloaded_by(event.author)

when song.ready is executed, song must equal nil, but to reach song.ready, !song.nil? must be true, meaning song is not nil, a contradiction. Therefore, we conclude that song must be nil, so the first elsif is executed:

elsif !song.ready

which is equivalent to

elsif !(nil.ready)

raising the exception.

More generally, error messages contain valuable information and deserve careful study. Yours would also identify the line where the exception occurred. The error message tells you that song is nil at that point. It therefore must have been nil in the first statement, so the first statement would have evaluated nil.

Consider putting your clause in a method and rewriting it as follows.

def process_song(song)
  return nil, "song is nil" if song.nil?
  return false, "The song is not ready yet. Try again once it is." unless song.ready
  return false, "Yo, check your private messages, I've already sent you the song." \
    if song.has_been_downloaded_by(event.author)
  song.send_to_user(event.author)
  true
end

to be called as follows.

outcome, msg = process_song(song)

Then, perhaps something like the following.

case outcome
when true
  ...
when false
  puts msg
  ...
when nil
  <raise exception with message that song is nil>
else
  <raise exception with message>
end

msg is nil when outcome is true. The first three lines of the method are called "guard clauses".

Upvotes: 4

davidhu
davidhu

Reputation: 10462

The two other answers have already mentioned why you are getting the error. To fix this it is very simple, you just need to change the order of your if statement. Like so:

if song.nil? 
  'Song with such index does not exist.'
elsif song.ready && !song.has_been_downloaded_by(event.author)
  song.send_to_user(event.author)
  nil
elsif !song.ready
  "The song is not ready yet. Try again once it is."
elsif song.has_been_downloaded_by(event.author)
  "Yo, check your private messages, I've already sent you the song."
end

So now, you are checking whether song == nil in your first statement, and if that is true, you get the message Song with such index does not exist. Now, you can move onto the rest of your elsif without having to worry about song being nil.

Upvotes: 1

Related Questions