Using OR in ActiveRecord models' scopes can lead to some weird behaviour when these scopes are chained. Assume we have two conditions that we want to disjunctively check, and to also have the scope in which the check occurs be chainable. Given a model with the following scopes:
class Test < ActiveRecord::Base
scope :test1, lambda { where(SOME_CONDITION).or(SOME_OTHER_CONDITION) }
scope :test2, lambda { where(id: Test.where(SOME_CONDITION).or(SOME_OTHER_CONDITION)) }
end
Then there's a subtle difference between them. The former behaves correctly when called as: Test.test1
, but if prior to test1
any other scopes are called, the or
causes some unintended behaviour, since the prior scopes now only work conjunctively with SOME_CONDITION. Using test2
fixes this issue, but generates a very hacky looking query, even for the simple case Test.test2
. Additionally, the latter just looks very hacky. Is there any recommended way to tackle this problem?
Due to the fact that the alternative would likely be to try and determine whether or not conditions already existed (e.g. where
was already called), which would be even "hackier" (sp?) than the sub-query in my opinion, your best bet is probably to use Arel
to create the condition.
class Test < ActiveRecord::Base
scope :test, -> (val1,val2) {
where(
arel_table[:column].in(val1).or(
arel_table[:column2].in(val2)
)
)
}
end
This option will be order independent when chaining scopes together