#108
May 19, 2008

named_scope

The named_scope method in Rails 2.1 makes performing finds on models very elegant and convenient. See how in this episode.
Download (12.5 MB, 11:02)
alternative download for iPod & Apple TV (12.1 MB, 11:02)

Note: there’s a lot more to the named_scope method which I didn’t cover, please see the links below for additional info.

# models/product.rb
class Product < ActiveRecord::Base
  belongs_to :category
  named_scope :cheap, :conditions => { :price => 0..5 }
  named_scope :recent, lambda { |*args| {:conditions => ["released_at > ?", (args.first || 2.weeks.ago)]} }
  named_scope :visible, :include => :category, :conditions => { 'categories.hidden' => false }
end

RSS Feed for Episode Comments 75 comments

1. Adam Hill May 19, 2008 at 02:55

Wow, and I thought working with the DB couldn't get any easier, but this is seriously cool. Thanks for this excellent overview.

I wonder if these named scopes can be used in conjunction within existing search plugins like acts_as_ferret in one line...


2. Foo May 19, 2008 at 04:11

Another great screencast.

Does this essentially make all self.finds obsolete? Will the new conventions be named_scope instead of def self.find_some_criteria?


3. grigio May 19, 2008 at 04:21

Thanks for these Rails 2.1 tips.


4. hoksitan May 19, 2008 at 04:50

Very interesting screencast ! Cool new feature ! Thanks for this tip !


5. Mauricio May 19, 2008 at 06:12

What about the performance cost of this "magic"?


6. Marcel May 19, 2008 at 06:19

Wow, this looks really cool. I especially like the efficiency of the generated SQL query when you concatenated the named_scopes. Really amazing and definately a great feature for bigger, data-filled applications.

Thanks for another great screencast!


7. Clemens Kofler May 19, 2008 at 06:41

Foo:
I would guess that yes, pretty much all finds will be replaced by named scopes in near future. Think of your own experience and some of Ryan's previous screencasts: You shouldn't really have finds anyway as soon as you're using find conditions. Like, say, you'd have find_all_activated instead of find(:all, :conditions => "activated_at IS NOT NULL")

Mauricio:
As far as I can see in the source, there's not too much going on metaprogramming-wise. So I guess it's safe to say that scoping doesn't cause performance issues. And as Marcel points out, named scopes produce really clean and efficient SQL - so no problem there either. Plus, scopes have existed before in Rails (with_scope - see here: http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M001416).

Ryan:
Best episode so far, in my opinion. And not only because it was so long! ;-)


8. Ryan Bates May 19, 2008 at 08:01

@Adam, I doubt you can use this with acts_as_ferret, but other plugins which end up calling "find" should work. For example, will_paginate just adds a custom paginate class method. You can append this to the search. (Untested).

Product.recent.paginate(:page => 1)

@Foo, this definitely lowers the need to create custom "self.find..." methods. However I would hesitate getting too fancy here. If you have a complicated search which needs to dynamically build up the conditions based on the passed parameters, then I wouldn't use named_scope for this.

@Mauricio, to echo Clemens, I doubt performance will be much of an issue. There's probably a slight overhead on the ruby side to get the scoping stuff figured out, but beyond that it seems efficient.


9. Radarek May 19, 2008 at 12:15

@Ryan:
  in Ruby 1.9 you can use -> operator for lambda and put default values, like this:

named_scope :recent, ->(time = 2.weeks.ago) { {:conditions => ["released_at > ?", time]} }


10. Matt Beedle May 19, 2008 at 12:54

I've been using has_finder for a while now, and it's been a real pleasure to work with. I'm really glad it's made it into rails.


11. Jeremy May 19, 2008 at 14:07

I have been using edge rails as of late just for this amazing functionality. In addition to the new level of dynamism this feature adds to Rails, it also adds such simplicity.

My favorite has to be simply the Model.all, Model.first, and Model.last shortcuts... cleans up my controllers for sure!

Man, with each new update of Rails, so many new possibilities for refactoring!


12. Carl May 19, 2008 at 15:18

I have been trying to do some of the same things using with_scope and some of them were not working correctly (I have an idea why now after watching your screencast) and now it will be so much easier to do what I was trying to do. Thanks for telling me about this new feature!


13. Soleone May 19, 2008 at 22:58

Indeed a very cool screencast.

Thanks again for this great episode!


14. Mathijs May 19, 2008 at 23:34

I've been using the has_finder gem on which this is based for some time now, and it's really useful. One thing I still struggle with has to do with using named_scopes between models in associations.

In your example you are using 'visible' which is just a simple attribute. What if category had more advanced attributes, like hidden, private, public, read_only and stuff like that. Say Category already has some named_scopes, for example 'free_for_all' which means a category is public but not read-only.

Now it would make sense (maybe not in this example but in general) to be able to use these named_scopes in the Product model, not on instances only, but in named_scopes themselves as well. So Product.in_free_for_all can just use the 'free_for_all' scope in Category. Otherwise you would have to define this 'logic' twice, which isn't very DRY. I hope my explanation is clear enough :)

Of course I know that I could also use Category.free_for_all.map(&:products), but then all products get fetched right away which means sorting them (and other nice stuff finders have) will have to be done in ruby instead of in the DB.

Any 'nice' solutions for this?


15. Mathijs May 19, 2008 at 23:46

A solution I use at the moment is this, but I'm not fully satisfied with it yet, so any comments are very welcome...

class Product << ActiveRecord::Base
  belongs_to :category
  named_scope :in_free_for_all, lambda {{ :conditions => {:category_id => Category.free_for_all.map(&:id)} }}
end

Which will do 2 queries, which is default for :include in rails 2.1 anyway I believe. I could use :select => 'id' to speed up the query a bit since I only need id's, but in the end, it would be nicest if rails could somehow write a subquery for it. Along the lines of:

(example, doesn't work)

named_scope :in_free_for_all, :conditions => {:category => Category.free_for_all}

should translate to:
Select * from products where category_id in (select id from categories where public = 't' and read_only = 'f')

Any chance of getting behavior like this in the near future?


16. Ryan Bates May 20, 2008 at 07:55

@Mathijs, currently one named scope can't use another named scope, but I think many people are wanting this feature. There's already a couple tickets along this line:

http://rails.lighthouseapp.com/projects/8994/tickets/57
http://rails.lighthouseapp.com/projects/8994/tickets/223

However, neither of these mention using an association's scope. You may want to add a comment to those tickets about that.


17. Brad May 20, 2008 at 09:25

Thanks for a great post! My attention tends to drift after about 7 minutes, so i'll have to come back and watch again a couple times :)


18. ESPNDev May 20, 2008 at 16:26

This was a really great Railscast highlighting probably my favorite new feature of Rails. Those SQL queries are great! Can't wait to take advantage of this...

Thanks!


19. Bryan May 21, 2008 at 04:08

Wow! Truly awesome new feature!
Now I just need to get Hostgator to update to 2.1....


20. kino May 23, 2008 at 01:51

We have not simply lost immanent time for phenomenology; we retain it, even with respect to the evidence in which the phenomenon presents itself, by orienting noetic acts according to accured insights.


21. Bharat May 26, 2008 at 14:37

Hello Ryan (or anyone else who knows),
Great screencast. In your demo, you are typing the commands in the console and showing the SQL queries in the console below. How do you isolate the SQL Queries display in the window below? It is quite useful. A screencast summarizing the tips on using Rails Console for these kinds of tricks would be very useful.
Keep up the good work.
Regards,
Bharat


22. Olafski Jun 04, 2008 at 06:16

@Bharat: you could just keep a window with ``tail -f log/development.log'' open, that should show the queries. I prefer to have them in the same window though, and have explained how to achieve this here:
http://skionrails.wordpress.com/2008/05/22/loading-additional-files-when-starting-scriptconsole/

Hope that helps. Great work as always Ryan :)


23. Walter Horstman Jun 13, 2008 at 03:34

On my website there is a page to view standings for a so called soccer pool. There are member records with a name per pool, that can be left empty. Each member is linked to a user record with a name field as well. In case the name of the member is empty, the user's name is used in the standings.

When using named_scope, I see strange behavior, since the eager loading isn't quite as I expected. I guess things have been changed in Rails 2.1 in this area that have not necessarily have to do with named_scope.

Anyway, in my Member class this scope doesn't work:

named_scope :standings, :include => :user, :order => 'members.position, COALESCE(members.name, users.name)'

The query doesn't join with the users table, so a missing column error is returned. But when I add :conditions => 'users.id = users.id', it works! So is Rails looking in the conditions to see if a associated table is used?

Anybody see my points and have some clues/comments?


24. tom Jun 13, 2008 at 10:16

Hi
im getting this:
named_scope > NoMethodError

ruby-v
ruby 1.8.5 (2006-08-25) [i486-linux]

rails-v
Rails 2.1.0

gem1.8 list
*** LOCAL GEMS ***

actionmailer (2.1.0, 2.0.2)
actionpack (2.1.0, 2.0.2)
activerecord (2.1.0, 2.0.2)
activeresource (2.1.0, 2.0.2)
activesupport (2.1.0, 2.0.2)
jruby-openssl (0.2.3)
rails (2.1.0, 2.0.2)
rake (0.8.1)

my problem / question: as soon as i have a named_scope declaration within the class, im experiencing the error above ( NoMethodError)

what am i doing wrong/missing?

thx tom


25. tom Jun 13, 2008 at 10:46

never , i figures it out, its working now on the console.as a newbie i forgot to chnage envrinment.rb

thx


26. chris Jul 01, 2008 at 21:54

hi ryan, great job as usual. quick question for you... when you are working around the inability to pass in a default value for the lamdba, you splat args and then in the proc you use "args.first || 2.weeks.ago"...

my question is why? why not just leave the argument to the lambda as "t" and then in the proc use "t || 2.weeks.ago" ?

seems to work for me, but i'm wondering if you had some other rationale for using the splat/first method.


27. Nico Jul 09, 2008 at 22:02

You can also pass in a params hash into the named scope and use for example params[:newer_than] || 2.weeks.ago for the default parameter. I prefer this solution over using args when having more than one parameter as the named scope call becomes easier to understand and parameter order doesn't matter.


28. ryanL Jul 17, 2008 at 23:45

hey Ryan, still working on that image trouble. I'll let you know when i get it sorted out.

had a tricky scope question though.

Is it possible to order a find call my an associated model's attribute.

e.g.

@category.members.order_by_members_businesses_names do |member|

i would just do

@category.businesses.order_by_name do |business|

but i the member model has addtional info i need to access.

named_scope :order_by_members_businesses_names ...?


29. Matt Aug 01, 2008 at 10:33

So would this replace the need to use the scope out plugin?


30. Philip (flip) Kromer Aug 24, 2008 at 01:38

@chris ("why not just leave the argument to the lambda as "t" and then in the proc use "t || 2.weeks.ago" ?"):

It's so you can leave the arg out -- it fakes an optional parameter.

See:
  http://pastie.org/258863


31. Pete Sep 05, 2008 at 07:53

This is so easy, it's great! Thanks for the walkthrough.


32. Edgard Sep 25, 2008 at 10:26

Hi,

You didn't try the case where 2 named_scope have a block of code. In your screencast only :recent ha a block of code, but what about chaining it with :cheap that also had a block of code?

I can't make it work.


33. Zubin Oct 12, 2008 at 03:56

If anyone's interested, I've created a plugin which automagically generates common named scopes for all AR models and columns. See http://github.com/zubin/autoscope

>> Page
=> Page(id: integer, name: string, content: text, enabled: boolean, category_id: integer, created_at: datetime, updated_at: datetime)

...creates these (for example):
>> Page.enabled
>> Page.not_enabled
>> Page.named "foo"
>> Page.content_contains "bar"
>> Page.created_before 1.year.ago

Install:
./script/plugin install git://github.com/zubin/autoscope.git

Feedback welcome!


34. Achim Oct 12, 2008 at 16:00

Thank you very much for this screencast.
Your tip about working with optional arguments saved my live :-)

Really great screencast again Ryan


37. Steffen Dec 03, 2008 at 01:42

Thanks again for this great episode!
Just what I have been looking for!


38. Jason Dec 03, 2008 at 08:56

Hi Ryan,

Thanks so much for all your work. Question - I'm seeing that
scope = Product.scoped({})
is indeed doing a find which is undesired in my case. I'm on Rails 2.1. I'd like to build up the scopes before executing. Do I need to use your scope_builder?

Thanks!


39. Jason Dec 03, 2008 at 09:16

Per comment #44 by me:

I was in the console when testing this. Appears to be ok when running in Rails.

Sorry for any confusion. Still seems a bit weird though.


41. MTH Dec 31, 2008 at 00:19

In fact, I'm using your episodes instead of Rails API!

Thanks for sharing!


43. mırc Feb 04, 2009 at 14:24

Great post, one of the most informative posts on this subject online actuallly.

Thanks and keep up the great work.


48. Arjun Mar 04, 2009 at 23:50

Hi,

I have been trying to get the named scopes to work in my model without any success.
I have model called Contact.

And my named scope is like the follow:-
class Contact < ActiveRecord::Base
named_scope :guardians, {:conditions =>["relationship = ?", 'Guardian']}

And in the IRB:-
>contact = Contact.find(2)
>contact.guardians

gives the following error:
NoMethodError: undefined method `gaudians' for #<Contact:0x636d4e4>
        from F:/Projects/WorkshopProjects/testproject/vendor/rails/activerecord/lib/active_record/attribute_method
s.rb:256:in `method_missing'
        from (irb):7

I am not sure what is going wrong.

I even tried using lambda as follows, but same thing happens:-
named_scope :test_guardians, lambda { { :conditions => ['relationship = ?', 'Guardian'] } }

Can you help me out here ?

Thanks in advance.

-Arjun


52. Kandy Mar 18, 2009 at 11:38

I'll recommend to my friends. Thanks for your sharing.


52. 7battery Mar 18, 2009 at 11:41

Download it, very useful for me, thanks.


52. DM500C Mar 19, 2009 at 03:02

I have been trying to get the named scopes to work in my model without any success. How to do next?


52. netlog Mar 25, 2009 at 12:02

thanks.


53. seks Mar 26, 2009 at 20:13

thank you sex mex lak lak lak


54. rüya tabirleri Apr 06, 2009 at 08:14

Thank you


55. rüya tabirleri Apr 06, 2009 at 08:37

Thank You Kanks :)


56. ilahi dinle Apr 07, 2009 at 04:58

thank s admin


57. glass Apr 09, 2009 at 06:05

I was searching for some new scopes to work in my model Hope it will work fine.


58. adf Apr 10, 2009 at 00:16

wow gold:<a href=http://www.wowgoldbank.com/wow-gold/>wow gold</a>,wow gold website:<a href=http://www.mygamesale.net>wow gold</a> tks


59. sf Apr 10, 2009 at 00:17

 wow gold:[url=http://www.wowgoldbank.com/wow-gold/]wow gold[/url],wow gold website:[url=http://www.mygamesale.net]wow gold[/url] tks


60. ruby_seattle Apr 14, 2009 at 20:57

Ryan, or any other expert,

I'm such a fan of named scopes now and see so many opportunities to streamline code. I ran into a problem. How can I create a named scope that uses an association and a lambda expression for parameterization. For example how would you retrieve all the products that are from a certain category type, for example 'electronics'. When I try something analagous to this I get an error: wrong number of arguments (3 for2).

named_scope :retrieve_by_type, :include=>:category, lambda{|type_id| {:conditions=>['categories.type'=type_id]}

-Thanks in advance


61. Seks, Erotik Apr 15, 2009 at 10:51

When I try something analagous to this I get an error: wrong number of arguments (3 for2).


62. sağlıklı hayat May 04, 2009 at 12:07

Download it, very useful for me, thanks.


63. tiffany rings May 06, 2009 at 20:12

Download it, very useful for me, thanks.


64. fake Bvlgari Necklaces May 07, 2009 at 01:52

thank s admin


65. jack May 18, 2009 at 04:37

thanks for offering the information! When using named_scope, I see strange behavior, since the eager loading isn't quite as I expected
<a href="http://www.globalbattery.co.uk">www.globalbattery.co.uk</a>


67. mesos May 27, 2009 at 03:10

Sell MMORPG Gold And Money to us to get real money!
Dear player,
OGDEAL is buying mmorpg virtual currency. We will offer high price to buy your game gold.You can sell WOW gold, sell warhammer gold, sell eve online isk: http://www.runescapemoney.ws/ , sell silkroad gold, sell maple story mesos, sell lotro gold, sell lotro europe gold, sell aoc gold, sell Lineage 2 adena, sell EverQuest 2 platinum, sell FFXI gil, sell SWG credits, sell gaia gold, sell 2moons dil, sell cabal online alz, sell kalonline geons to : http://www.ogdeal.com/ .
Any questions please Contact our site’s live support chat or MSN: ogdeal@hotmail.com , AIM: ogdeal .Thanks !


67. konya chat May 28, 2009 at 05:43

thanks


68. Kadın Sağlık May 29, 2009 at 10:53

thank you very much http://kadinsaglik.blogcu.com/archive/


69. cc Jun 01, 2009 at 00:47

<a href="http://www.lszwjx.com">气动马达</a>
<a href="http://www.lszwjx.com">气动搅拌机</a>


73. ilahi dinle Jun 03, 2009 at 03:03

Good. I like how this added feature has made it easier. s


74. tomas Jun 11, 2009 at 08:05

Hi,
i'm new, how can i see generated queries like you do in the bottom terminal ?


75. sikis Jun 17, 2009 at 10:22

thank you admin


76. sikis Jun 17, 2009 at 10:23

thank you werry much.


77. Mike Jun 19, 2009 at 10:15

Since this was not clearly stated in text form I thought I would include this little snippet from the documentation on named_scope:

-----------------------
Unlike Shirt.find(...), however, the object returned by Shirt.red is not an Array; it resembles the association object constructed by a has_many declaration. For instance, you can invoke Shirt.red.find(:first), Shirt.red.count, Shirt.red.find(:all, :conditions => {:size => ‘small’}). Also, just as with the association objects, named \scopes act like an Array, implementing Enumerable; Shirt.red.each(&block), Shirt.red.first, and Shirt.red.inject(memo, &block) all behave as if Shirt.red really was an Array.


78. ilahi dinle Jun 20, 2009 at 00:47

thank you werry much.


79. dedektif Jun 23, 2009 at 03:59

thanks


80. evdeneve Jun 23, 2009 at 04:07

thank you


81. eniyimuzik Jun 23, 2009 at 04:08

thanks


82. dini videolar Jun 24, 2009 at 14:27

thank you
good night


83. ilahiler dinle Jun 24, 2009 at 14:28

thanks


84. sohbet odaları Jun 30, 2009 at 02:00

sğiteniz cok guzel. basarilarinizin dewamini diliyorum. turk Sohbet odalari. thankS


85. tiffanys Jun 30, 2009 at 22:58

cool


86. ed hardy Jun 30, 2009 at 22:59

cool

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player