开发者

Reusing named_scope to define another named_scope

The problem essence as I see it

One day, if I'm not mistaken, I have seen an example of reusing a named_scope to define another named_scope. Something like this (can't remember the exact syntax, but that's exactly my question):

named_scope :billable, :conditions => ...
named_scope :billable_by_tom, :conditions => {
    :billable => true, 
    :user => User.find_b开发者_如何学Cy_name('Tom')
}

The question is: what is the exact syntax, if it's possible at all? I can't find it back, and Google was of no help either.

Some explanations

Why I actually want it, is that I'm using Searchlogic to define a complex search, which can result in an expression like this:

Card.user_group_managers_salary_greater_than(100)

But it's too long to be put everywhere. Because, as far as I know, Searchlogic simply defines named_scopes on the fly, I would like to set a named_scope on the Card class like this:

named_scope from_big_guys, { user_group_managers_salary_greater_than(100) }

- this is where I would use that long Searchlogic method inside my named_scope. But, again, what would be the syntax? Can't figure it out.

Resume

So, is named_scope nesting (and I do not mean chaining) actually possible?


You can use proxy_options to recycle one named_scope into another:

class Thing
  #...
  named_scope :billable_by, lambda{|user| {:conditions => {:billable_id => user.id } } }
  named_scope :billable_by_tom, lambda{ self.billable_by(User.find_by_name('Tom').id).proxy_options }
  #...
end

This way it can be chained with other named_scopes.

I use this in my code and it works perfectly.

I hope it helps.


Refer to this question raised time ago here at SO. There is a patch at lighthouse to achieve your requirement.


Rails 3+

I had this same question and the good news is that over the last five years the Rails core team has made some good strides in the scopes department.

In Rails 3+ you can now do this, as you'd expect:

scope :billable,        where( due: true )
scope :billable_by_tom, -> { billable.where( user: User.find_by_name('Tom') ) }

Invoice.billable.to_sql         #=> "... WHERE due = 1 ..."
Invoice.billiable_by_tom.to_sql #=> "... WHERE due = 1 AND user_id = 5 ..."

FYI, Rails 3+ they've renamed named_scope to just scope. I'm also using Ruby 1.9 syntax.

Bonus Round: Generic Scope.

If there are multiple people that are "billable" besides just "Tom" then it might be useful to make a generic scope that accepts a name param that gets passed into the block:

scope :billable_by, lambda { |name| billable.where( user: User.find_by_name( name ) ) }

Then you can just call it with:

Invoice.billable_by( "Tom" ).to_sql #=> "... WHERE due = 1 AND user_id = 5 ..."

Btw, you can see I used the older lambda syntax in the bonus round. That's because I think the new syntax looks atrocious when you're passing a param to it: ->( name ) { ... }.


Chain Scopes.

Why not have a scope for stuff just by Tom in general, like:

scope :by_tom, where( user: User.find_by_name('Tom') )

And then you can get those records that are "billable by Tom" with:

Record.billable.by_tom


You can use a method to combine some named_scope like :


def self.from_big_guys
  self.class.user_group_managers_salary_greater_than(100)
end

This feature is add on Rails 3 with new syntax (http://m.onkey.org/2010/1/22/active-record-query-interface)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜