Collective Idea

Collective Idea Logo

Steve Richert

The Billy Baldwin of Conditional Assignment

By Steve Richert on June 21, 2011 in conditional assignment, operator, and ruby

For those of you who don’t know, Billy Baldwin is the lesser-known and generally-less-useful little brother of famed actor Alec Baldwin.

In the world of Ruby’s conditional assignment operators, ||= is Alec Baldwin; charming and versatile. But not many people know about ||=’s little brother… the &&= operator.

Meet Billy

The ||= conditional assignment operator is popular in Ruby and for good reason. One common usage is for simple memoization. Ryan Bates outlined this technique in his very first RailsCast.

def current_user
  @current_user ||= User.find(session[:user_id])
end

The purpose here is to avoid re-finding the user every time the current_user method is called. The ||= operator above has the same effect as:

def current_user
  @current_user = @current_user || User.find(session[:user_id])
end

If @current_user is both defined and truthy (not false or nil), that value is simply returned. Otherwise the instance variable is set and returned.

Enough about Alec. What about Billy?

The magic of the &&= operator is that it only sets variables that are… already set! The usefulness of this may not be immediately apparent, but maybe you’ve written code like this:

class Article < ActiveRecord::Base
  validates :title, :body, :presence => true

  before_save :clean_summary

  private
    def clean_summary
      self.summary = summary.squish
    end
end

The problem with this code is that the article summary isn’t required. What if an article’s summary is nil? We’ll get a NoMethodError trying to call squish1 on nil. Oftentimes, the fix looks like this:

def clean_summary
  self.summary = summary && summary.squish
end

which looks eerily similar to our current_user memoization expansion. So instead, try:

def clean_summary
  self.summary &&= summary.squish
end

Sure, it’s a bit of a one-trick pony, but sometimes that one trick is exactly what you need. Have you ever seen Backdraft? Every dog has its day.

1 String#squish changes consecutive whitespace to single spaces.

By Steve Richert on June 21, 2011 in conditional assignment, operator, and ruby

12 Comments

  1. Florian Gilcher

    Florian Gilcher June 21, 2011

    Tiny nitpick: ||= has the same effect as:

        @value || @value = ….

    This avoids assigning the variable when its already set and a double lookup if you use ||= when accessing a Hash member.

    Otherwise: thanks for the reminder!

  2. Steve Richert

    Steve Richert June 21, 2011 http://collectiveidea.com

    Florian: Absolutely right, thank you! I’d originally written the current_user expansion just like that but ultimately wanted to keep the effect clearer for the sake of comparison to &&=.

  3. Dan

    Dan June 21, 2011

    First, hilarious title / comparison

    Second, I wasn’t aware of this, thanks for sharing!

    Dan

  4. Luigi Montanez

    Luigi Montanez June 21, 2011 http://luigimontanez.com

    Just make sure you don’t use ||= and &&= for boolean values like flags. For that reason, I prefer to explicitly check for .nil?, even though it’s more verbose.

  5. p8

    p8 June 21, 2011 http://deheus.net/petrik

    You can also use the xor one for :
    a ^= true # true
    a ^= true # false
    a ^= true # true
    a ^= true # false

  6. Dave Fayram

    Dave Fayram June 21, 2011 http://kirindave.tumblr.com

    It’s sad to me that more rubyists don’t realize what terrific tools &&= and ||= are. 

    &&= chaining is actually almost like a limited “Maybe Monad” for Ruby. It’s really great when you’re dealing with a long string of computations that may fail, you can just write them in a really naive fashion and avoid writing if(x != nil) guards. 

  7. Brian Armstrong

    Brian Armstrong June 21, 2011 http://www.startbreakingfree.com

    I think this syntax is more clear:

    self.summary = summary.squish if summary.present?

    But I hadn’t seen the squish method, very cool!

  8. Mark Wilden

    Mark Wilden June 21, 2011 http://mwilden.blogspot.com/

    You can also use && in a similar way to mimic #try:

    name = user && user.name

  9. dude

    dude June 21, 2011

    In rails you can just do:

        self.summary = summary.try(:squish)

    But yes, quite good :)

  10. xn

    xn June 22, 2011

    Fun! How about this?
    array_of_methods.inject(obj, :try)

  11. jarmo

    jarmo March 21, 2013

    In Objective-C just go ahead and call summary.squish (actually [summary squish]).  If summary is nil, it will do nothing.  No need to pre-test if (summary != nil) or have null pointer exception handling.  Surprising how useful this behavior actually is and how quickly you accept it.

  12. Chong

    Chong April 07, 2014 http://Learn-programming-Quickly.blogspot.co.uk/

    Heya pretty much started programming in the ruby programming
    language so I am quite a bit of a newcommer!
    However, have found your internet site very interesting and instructive.
    Appreciate it!

Post a Comment

Contact Us

Find us on Google Maps
Collective Idea
44 East 8th Street, Suite 410
Holland, Michigan 49423 USA 42.790334-86.105251

Follow us on the Interwebs

We are currently available for medium and long term projects. Please get in touch if we can be of service.