Named Scope: To Lambda or Not To Lambda, That Is The Question

December 18, 2008

One feature that was added to Rails in 2.1 is named scope. This is a really nice way to provide a flexible API for your controllers to query different types of data without resorting to a conditions hash or even worse, SQL. Here's how it works. Say we have a blog with an Article model, which has a boolean field for published. We could get all published articles like this:

Article.all(:conditions => {:published => true})

But with named scope, we can do this:

Article.published.all

This is a small example so it doesn't seem like much, but this really starts to pay of in terms of readability and encapsulation as things get more complicated. To set up the above named scope, we add this to your Article model:

named_scope :published, :conditions => {:published => true}

By doing this, we have abstracted away the implementation of what it means to be published from the controller and into the model. The advantage is that the code in the controller becomes more "intent revealing" and also if the logic for what it means to be published changes, we just change it in one place. For example, if we decide we need to keep track of when an article was published, we could change our published column from a boolean to a published_at datetime column and just change our named scope like this:

named_scope :published, :conditions => ["published_at is not null"]

If you need to further refine our query, we can chain the calls to multiple scopes together. For example, let's say we add this to our model:

named_scope :featured, :conditions => {:featured => true}

Which allows us to do this in our controllers:

Article.published.featured.all

Now let's say we want to create a named scope for all articles published before a given date. Ok, so we just add this to our model:

named_scope :published_before, :conditions => ["published_at < ?", ?????]

Oops. When we are defining the named scope, we don't know what the date will be. So what do we do? That's where lambda comes into play.

Before we do that, WTF is lambda? Lambda is a name for an anonymous function. The name comes from Lisp. The powerful thing about lambdas is that they are stored in an object and can be passed around as a variable and called at a later time. So here's a very simple lambda:

>> double = lambda{|x| x * 2}
=> #<Proc:0x010dc4b4@(irb):1>
>> double.call 21
=> 42    

We create a lambda that takes one argument and multiplies it by two. We can also call a lambda using the [] method. So in our previous example, we could have done double[21]. I prefer call over [], because mentally the first thing I think of when I see those [] is an Array or Hash, so the call is a little more explicit as to what is going on. To each his own, but it's good to know both ways, because you will come across both ways when reading Ruby code written by others.

This is a very powerful feature which Ruby has and many other mainstream languages lack or make it difficult to do. All functional languages that are gaining popularity now, such as Clojure, Erlang, Scala, and Haskell, also make this kind of thing easy to do.

So now that we know what lambda is, we can see how we can use it with named scope. Since we don't know the value of the date we are trying to compare with to do our published_before named scope, we will instead give named scope a lambda that it will use at the time the SQL query is constructed:

named_scope :published_before, lambda{|date| {:conditions => ["published_at < ?", date] } }

So there we go. This is a lambda that takes the data as a parameter and returns a Hash used for the conditions of the SQL query. Now if we need last year's featured articles, we can now do this:

Article.published_before(Time.now.beginning_of_year).featured.all

Posted in Technology | Tags Rails, Ruby

Comments Comments Feed

1. Nice writeup, clear and easy to follow.

# Posted By Brandon on Friday, December 19 2008 at 10:46 AM

2. With named scopes, you can do just Article.published, no need for Article.published.all unless you want to narrow it down further, e.g. Article.published.all(:limit => 2).

# Posted By Henrik N on Friday, December 19 2008 at 2:47 PM

3. Commented before I read the full post. Just wanted to add that I think this post doesn't really make explicit why the lambda is necessary in this case.

You could do
named_scope :published_before, :conditions => ["published_at < ?", Time.now.beginning_of_year]
but the time would be set at define-time, and not be calculated on each run; with a lambda, on the other hand, it's calculated each time it's run.

# Posted By Henrik N on Friday, December 19 2008 at 2:52 PM

4. Very interesting, nice write-up!

# Posted By Frederic Daoud on Friday, December 19 2008 at 2:53 PM

5. @Henrik N

The intent of published_before is to be able to pass in any date, not have it be always the beginning of the year.

But you bring up a good point. Even if you wanted to define a "published_last_year" scope, you might think you don't need lambda, because you don't need any arguments. But as you said, doing it without the lambda would make the value of beginning of the year be calculated only once, thus the value would never change, creating a nasty bug that you wouldn't find until the year after you deploy your app. :)

# Posted By Paul Barry on Friday, December 19 2008 at 4:01 PM

6. That was like poetry Paul. Thanks.

# Posted By Z on Saturday, December 20 2008 at 6:03 PM

7. Thank you for this informative read, I really appreciate sharing this great post. Keep up your work.

# Posted By suna dumankaya on Tuesday, May 12 2009 at 7:20 AM

8. I think this is an excellent article. Anyone got any more info about it?

# Posted By sbs sonuçlar? on Tuesday, May 12 2009 at 7:21 AM

9. it is easy to understand, especially for newbies like me. thanks. sarah james

# Posted By ygs on Friday, August 28 2009 at 5:22 PM

10.
Cheap nfl jerseys are your best choice, and retail nfl apparell and drop shipping are accepted as well. Grade cheap nfl sports jerseys,Competitive price and Best service, We do hope nfl jerseys usa to do long-term business with every clients and help you improvefootball jerseys more benefit from your market.

# Posted By fewrewree on Wednesday, September 1 2010 at 7:29 PM

11. As we all know, earrings are jewelry attached to the ear through a piercing in the earlobe or some other external part of the ear. Earrings are women's favorite. Proper Tiffany Bangles earrings can not only make a female look more beautiful, but also have the function of accommodating defects. Tiffany Cuff LinksSo it is necessary to know about how to wear earrings properly. Therefore, before you wear , you must take many factors into consideration, such as environment, your temperament, face shape, hairstyle, and your costume to reach the best effect.Wear Tiffany Accessories according to your face shape This will make your face look less plump. If you are long-faced, then you'd better choose Tiffany Sets hoop earrings or big earrings to make your face look more fetching. If you have a square-shape face, then dapper stud earrings, long drop earrings or exaggerated earrings can make you look livelier My discount silver tiffany
necklace
Blog.

# Posted By discount silver tiffany necklace on Thursday, September 2 2010 at 7:09 AM

Add a Comment

(If you leave this blank, your IP address will be displayed instead)

(Optional, will not be displayed on the site)