Tip #13 - BangBang Transformations!

Tue Apr 22 18:02:21 -0700 2008

Ruby is marvelous, everything evaluates. Which means a lot of the time, you can get away with things like ‘if @user…” and just depend on the existence of the @user var. But what if you just really need a Boolean true or false? Here is a little pattern you can use to do this…

Where two wrongs make a right?

Ruby you have the case that EVERYTHING except nil and false are “truthy” (which means, they evaluate to true in a condition).

So that is why something like this can work in Ruby:

irb(main):001:0> user = "Mikel" 
=> "Mikel" 
irb(main):002:0> if user
irb(main):003:1>   puts "Hello #{user}" 
irb(main):004:1> end 
Hello Mikel

But the “value” of users in that if statement is still ‘Mikel’

What if you had a method in a rails model like this:

1
2
3
def logged_in?
  true if logged_in_date != nil
end

That says “Return true if the logged_in_date is not nil, otherwise return nil.

The problem with this, is that if the logged_in_date is already nil, the method will return nil, not false. And nil does not equal false in all cases.

A better way to write this would be to use the logical NOT operator, you might know that a ! in front of any variable is a logical NOT operator. This means, it takes whatever the value is and returns the opposite as a true or false. So we get stuff like this:

irb(main):001:0> true
=> true
irb(main):002:0> !true
=> false
irb(main):003:0> 1 == 1
=> true
irb(main):004:0> !(1 == 1)
=> false

Easy enough.

So NOT true equals false. But NOT NOT true equals true. And NOT NOT (anything except nil and false) equals true. And NOT NOT anything that is nil or false equals false. Confused? Let me show you the same “logged_in?” method with some ruby goodness:

1
2
3
def logged_in?
  !!logged_in_date
end

What this now says is that if the logged_in_date is nil, then apply NOT to that (which gives you true) and apply NOT to that (which will give you false) so in that case it will return false, which is what you want!

On the other hand, if logged_in_date is ANY value (string, date, anything) then you apply NOT to that and you get false, apply NOT again and you get true. Which is also what you want.

So instead of returning true or nil, that method will now return true or false, just as we expect.

Not bad for two exclamation marks!

blogLater

Mikel

  1. Rémi Vanicat Says:

    what is wrong with:

    def logged_in? logged_in_date != nil end

    logged_in_date != nil is something whose value is false if logged_in_date is nil, and true otherwise, so why not just return it. And it seem so more logical and simple to me than this not not.

    Well there is a difference between foo != nil and !!foo when foo’s value is false : - the first one is useful when you want to check if the value is initialized, because if its value is false, this mean it have been initialize, and then false is different from nil - the second one test the truth value of foo

  2. Mikel Says:

    Exactly, this method handles it if it is false or nil and always returns true or false.

    Mikel

  3. Luke Says:

    I’m still digesting the !! example, but it seems a bit unnecessary. we love Ruby b/c the first example does work. clients can faithfully check for authenticated users with “do_something if user.logged_in?” b/c false and nil are interchangeable for just this type of scenario.

    so, other than possibly changing the line to “true unless logged_in_date.nil?”, I think I’d use it over a double-negation solution.

    still a good tip and I’ll probably end up using b/c it does seem “smarter” :)

  4. Mikel Says:

    Luke: I know what you mean. But sometimes (not just logged in?) maybe also checking to see if an attribute or value is set or not.

    .blank? does a good job on this though, but then you have to call blank? every where :) Plus, it is a lot more typing :)

    Mikel

Leave a Reply