Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Double ampersand in Ruby

Tags:

ruby

I am using the authlogic gem with Ruby on Rails, and I have been using the following to obtain the id of the user that is currently logged in:

current_user = UserSession.find
id = current_user && current_user.record.id

I'm not understanding how current_user && current_user.record.id returns the current user id. I would think this would return a boolean. Can someone explain how this works?

like image 543
Chad Johnson Avatar asked Jan 01 '10 05:01

Chad Johnson


2 Answers

There is no Boolean type in Ruby; Ruby has a rather simple view of truth (or more precisely, it has a rather simple view of falsehood).

  • the false object, which is the singleton instance of FalseClass is considered falsy
  • the nil object, which is the singleton instance of NilClass is falsy
  • every other object is truthy (including, obviously, the true object, which is the singleton instance of TrueClass)

[BTW: this means that a lot of objects that are considered falsy in some other languages, are actually truthy in Ruby, like the integer 0, the real value 0.0, the empty string, the empty array, the empty hash, the character 'F']

So, the Boolean operators &&, ||, and and or do not return Boolean values. Instead they return the first object that determines the outcome of the expression.

(They are also short-circuiting, which means that they only evaluate the minimum sub-expressions that are needed to determine the outcome of the expression. So, an alternate formulation would be that they return the result of the last expression that was evaluated. Which, in turn, is analogous to what blocks, methods, class bodies and module bodies do.)

So, what does it mean to return the first object that determines the outcome? That's simple, really: the result of the expression

 a && b

is truthy if both a and b are truthy, otherwise it is falsy. So, if a is falsy, it is completely irrelevant what b is: the result will be falsy either way. So, we might just as well simply return a. (Remember, a doesn't have to be false, it could also be nil and the programmer might want to know which one of the two it was.)

If, OTOH, a is truthy (IOW it is neither nil nor false), then the result of the whole expression is solely dependent on b: if b is truthy, the whole result will be truthy, otherwise if b is falsy, the whole result will be falsy. So, we might just as well return b itself, instead of first converting it to a Boolean.

|| and or are analogous or more precisely dual to && and and.

You posted this example:

id = current_user && current_user.record.id

Here, the author isn't even expecting current_user to be a Boolean value! Instead, he expects it to be either a User or nil. But that's perfectly fine, because a User is truthy and nil is falsy, so they still can be used in a Boolean expression.

The basic intention is that the author wants to prevent a NoMethodError exception being raised, if he tries to call #record on nil.

An alternative way of expressing this would be

id = current_user.record.id unless current_user.nil?

If you want all the gory details, check out Section 11.1 (page 36) of the Draft ISO Ruby Specification or the excutable specifications of the RubySpec project. (Here's the one for &&.)

I wrote a pure Ruby implementation of Ruby's Boolean operators and conditional expressions once for fun. The meat of the implementations is these two mixins.

like image 151
Jörg W Mittag Avatar answered Nov 11 '22 07:11

Jörg W Mittag


The logical and is short circuiting. That means that if the construct is X && Y, and X is false then Y never gets checked because the whole thing is certainly going to be yield false.

That code is saying, essentially:

if (current_user is TRUE) {
    id = current_user.record.id;
]

Here's some console output showing you get the second value if the first is true:

irb(main):005:0> true && 9
=> 9

and nil if the first is nil:

irb(main):008:0> nil && 9
=> nil
like image 45
Ry4an Brase Avatar answered Nov 11 '22 06:11

Ry4an Brase