Last Updated: February 25, 2016
·
6.4K
· hopsoft

Chain Rails scopes with OR

One of the shortcoming of scopes in ActiveRecord is the inability to chain them into an "OR" clause. Here's one way to do it. The usage isn't particularly elegant, but it gets the job done.

Concern with OR behavior

module ModelHasScopeHelpers
  extend ActiveSupport::Concern

  module ClassMethods

    # Creates a new scope with with all where clauses joined by "OR"
    # Preserves selects & orders but ignores all other query elements.
    def or_scopes(*scopes)
      selects = []
      orders = []
      wheres = scopes.map do |scope|
        selects << scope.projections
        orders << scope.orders
        if scope.arel.where_sql.present?
          scope.arel.where_sql.gsub(/\AWHERE /i, "")
        else
          nil
        end
      end
      scope = where(wheres.compact.join(" OR "))
      selects.flatten.each { |s| scope = scope.select(s) }
      orders.flatten.each { |o| scope = scope.order(o) }
      scope
    end

  end

end

Model

model User < ActiveRecord::Base
  include ModelHasScopeHelpers
end

Usage

users = User.or_scopes(
  User.where(name: "Bob"), 
  User.where(name: "Bobby"), 
  User.where(name: "Bobbie")
)

1 Response
Add your response

Hi Nathan, this is great! I've been looking for a solution for a good while :)
Given you've been probably using this for a while, did you find any drawbacks or edge cases in which this does not work properly?
Great work BTW - annoying how the rails core team is not acknowledging how useful this is..

over 1 year ago ·