Is there a way to combine named scopes into a new named scope?
-
09-06-2019 - |
Question
I have
class Foo < ActiveRecord::Base
named_scope :a, lambda { |a| :conditions => { :a => a } }
named_scope :b, lambda { |b| :conditions => { :b => b } }
end
I'd like
class Foo < ActiveRecord::Base
named_scope :ab, lambda { |a,b| :conditions => { :a => a, :b => b } }
end
but I'd prefer to do it in a DRY fashion. I can get the same effect by using
Foo.a(something).b(something_else)
but it's not particularly lovely.
Solution
Well I'm still new to rails and I'm not sure exactly what you're going for here, but if you're just going for code reuse why not use a regular class method?
def self.ab(a, b)
a(a).b(b)
end
You could make that more flexible by taking *args instead of a and b, and then possibly make one or the other optional. If you're stuck on named_scope, can't you extend it to do much the same thing?
Let me know if I'm totally off base with what you're wanting to do.
OTHER TIPS
At least since 3.2 there is a clever solution :
scope :optional, ->() {where(option: true)}
scope :accepted, ->() {where(accepted: true)}
scope :optional_and_accepted, ->() { self.optional.merge(self.accepted) }
By making it a class method you won't be able to chain it to an association proxy, like:
@category.products.ab(x, y)
An alternative is applying this patch to enable a :through option for named_scope:
named_scope :a, :conditions => {}
named_scope :b, :conditions => {}
named_scope :ab, :through => [:a, :b]
Yes Reusing named_scope to define another named_scope
I copy it here for your convenience:
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.
@PJ: you know, I had considered that, but dismissed it because I thought I wouldn't be able to later chain on a third named scope, like so:
Foo.ab(x, y).c(z)
But since ab(x, y)
returns whatever b(y)
would return, I think the chain would work. Way to make me rethink the obvious!
Check out:
http://github.com/binarylogic/searchlogic
Impressive!
To be specific:
class Foo < ActiveRecord::Base
#named_scope :ab, lambda { |a,b| :conditions => { :a => a, :b => b } }
# alias_scope, returns a Scope defined procedurally
alias_scope :ab, lambda {
Foo.a.b
}
end