What's New in Edge Rails: Dynamic Scope Methods

Posted by ryan
at 9:24 AM on Monday, December 29, 2008

This feature is scheduled for: Rails v2.3/3.0

For quite some time now you’ve been able to perform simple queries using dynamic find_all_by_xx_and_yy methods:

1
2
Article.find_by_published_and_user_id(true, 1)
  #=> "SELECT * FROM articles WHERE published = 1 AND user_id = 1"

These dynamic finders provide an easy way to quickly encapsulate non-reused query conditions (for commonly used query logic you should consider using named scopes). The downside, however, is that you can’t chain query conditions when using these dynamic finders.

With the recent addition of dynamic scopes, however, you now have a way to both quickly specify query logic and chain further conditions. The naming works in the same manner as dynamic finders and the chaining works in the same fashion as conventional named scopes:

1
2
Article.scoped_by_published_and_user_id(true, 1).find(:all, :limit => 5)
  #=> "SELECT * FROM articles WHERE published = 1 AND user_id = 1 LIMIT 5"

Note how you can hang further chainable query methods off the dynamic scope here? You could also have preceded the dynamic scope with another scope, or even another dynamic scope:

1
2
Article.scoped_by_published(true).scoped_by_user_id(1)
  #=> "SELECT * FROM articles WHERE published = 1 AND user_id = 1"

This is really just another tool to put in your toolbox based on the powerful named_scope functionality of ActiveRecord.

tags: ruby, rubyonrails

Comments

Leave a response

  1. jblancheDecember 29, 2008 @ 10:53 AM

    May be cool in some cases but for this one, I’ll prefer writting two named_scope (published and by_user) and write : Article.published.by_user(1)

    But as you just said, it’s one more tool in the toolbox :)

  2. Dan CroakDecember 29, 2008 @ 11:04 AM

    Awesome. Love it. Thanks, Ryan.

  3. JeffDecember 29, 2008 @ 11:15 AM

    I wonder if it would have made more sense to just change the find methods to return the same kind of proxy object that named_scope does already.

  4. archpolluxDecember 29, 2008 @ 11:56 AM

    Hehe, nice, ActiveRecord will finally have something that perl’s DBIx::Class (that Catalyst uses btw) had since its conception. Kudos RoR :P

  5. LoriDecember 29, 2008 @ 02:44 PM

    I like the idea, but I’d like to see it work with, say, will_paginate, in a seemless manner as well. That way I could seemlessly filter a list.

  6. Ryan DaigleDecember 29, 2008 @ 04:35 PM

    Lori, do you mean like this:

    
    Article.scoped_by_published(true).paginate(:page => 1)
    

    ? If so you’ll be happy to find that it does indeed work this way.

  7. Ash McKenzieDecember 29, 2008 @ 05:21 PM

    Anyone have any idea if this will work ?

    get_articles(‘2008’)

    def get_articles(year_check = ’’, month_check = ’’) a = Article a.year(year_check) unless year_check.blank? a.month(month_check) unless month_check.blank? end

    Or is this not a good way to approach this problem ?

  8. Luke RedpathJanuary 02, 2009 @ 07:01 PM

    Mmm, looks like something I’ll add to my “will almost never use” pile along with find_by_x_and_x.

    Maybe I’m missing something, but wouldn’t most of the above examples be better off making use of Rails existing association functionality? For instance, instead of Article.scoped_by_user_id(1), wouldn’t user.articles be more idiomatic, where user is User.find(1)?

    I’d also rather declare my named_scopes as I need them because it’s a) more intention revealing and b) the api is nicer (e.g. user.articles.published.

  9. Markus JaisJanuary 03, 2009 @ 03:36 PM

    Cool feature. There have been some cases when I needed just that for my ActiveRecord code. Thanks a lot for implementing it.

  10. Daniel TsadokJanuary 04, 2009 @ 05:31 AM

    @Luke – You’re right if you’re talking about foreign key queries, but I could definitely see myself doing something like: User.activated.scoped_by_login(‘foo’)

    I do like the semantics of “with_login” better, though, similar to what jblanche said…

  11. Yaroslav MarkinJanuary 05, 2009 @ 01:34 PM

    Hi guys,

    Glad that you like the patch.

    Luke, the idea of scoped_by_.., as well as find_by_… is that you don’t need custom finders or custom named scopes for things that are just too simple to code. I’ve noticed that I actually create a lot of named scopes for my models that are really simple and could just be generated on the fly.

    Less code => profit ;-)

  12. MikhailovJanuary 06, 2009 @ 03:15 AM

    What about create model’s method with with_scope? Isn’t it the same?