Reputation: 1229
In Rails3 I have:
Class Teacher
# active :boolean
has_and_belongs_to_many :subjects
Class Subject
# active :boolean
has_and_belongs_to_many :teachers
I am trying to construct a Teacher scope that returns all Teachers
that are active
or are associated with a Subject
that is active
.
These scopes work individually, but how to combine them as a single scope with an OR?
scope :active_teachers, where(active: true)
scope :more_active_teachers, joins(:subjects).where(:subjects => {active: true})
I've tried this without success:
scope :active_teachers, where(active: true).or(joins(:subjects)
.where(:subjects => {active: true}))
UPDATE:
I thought I had a solution, but this no longer lazy loads, hits the database twice and — most importantly — returns an array rather than an AR object!
scope :active_teachers, where(active: true) |
joins(:subjects).where(:subjects => {active: true})
Upvotes: 2
Views: 2703
Reputation: 4802
There's a rails pull request for this (https://github.com/rails/rails/pull/9052), but in the meantime, some one has created a monkey patch that you can include in your initializers that will allow you to do this and still give you an ActiveRecord::Relation
:
https://gist.github.com/j-mcnally/250eaaceef234dd8971b
With that, you'll be able to OR your scopes like this
Teacher.active_teachers.or.more_active_teachers
or write a new scope
scope :combined_scopes, active_teachers.or.more_active_teachers
Upvotes: 1
Reputation: 3935
You can solve this by dropping to AREL. See this SO Question for how to do that.
Or from the AREL Source Code README.md. I think (but haven't verified) that this would translate to the following for your particular example.
teachers.where(teachers[:active].eq(true).or(subjects[:active].eq(true)))
Good Luck!
Upvotes: 3
Reputation: 37133
I think the short answer is you can't.
Oring in the code is going to break lazy loading ... really no way around it as you need the database to make the evaluation. ActiveRecord can't make the evaluations on the scopes without executing each subclause individually.
Something like this the following should work:
joins(:subjects).where("subjects.active = true OR teachers.active = true")
Not quite as elegant, but can be wrapped into a method for reuse.
Upvotes: 3