Combine two named scopes with OR (instead of AND)

前端 未结 5 1092
野的像风
野的像风 2020-12-03 08:56

I want to find all Annotations whose bodies are either:

  • Equal to \"?\"
  • or
  • Like \"[?]\"

What\'s t

5条回答
  •  有刺的猬
    2020-12-03 09:45

    I couldn't find any simple solutions, but this problem intrigued me, so I rolled my own solution:

    class ActiveRecord::Base
    
      def self.or_scopes(*scopes)
        # Cleanup input
        scopes.map! do |scope|
          scope = scope.respond_to?(:to_a) ? scope.to_a : [*scope]
          scope.unshift(scope.shift.to_sym)
        end
    
        # Check for existence of scopes
        scopes.each{|scope| raise ArgumentError, "invalid scope: #{scope.first}" unless self.scopes.has_key?(scope.first) }
    
        conditions = scopes.map do |scope|
          scope = self.scopes[scope.first].call(self, *scope[1..-1])
          self.merge_conditions(scope.proxy_options[:conditions])
        end
    
        or_conditions = conditions.compact.join(" OR ")
    
        merged_scopes = scopes.inject(self){|merged, scope| merged.scopes[scope.first].call(self, *scope[1..-1]) }
    
        # We ignore other scope types but so does named_scopes
        find_options = merged_scopes.scope(:find).merge(:conditions => or_conditions)
    
        self.scoped(find_options)
      end
    
    end
    

    Consider the following setup:

    class Person < ActiveRecord::Base
      named_scope :men,      :conditions => { :sex => 'M' }
      named_scope :women,    :conditions => { :sex => 'F' }
      named_scope :children, :conditions => "age < 18"
      named_scope :named, lambda{|name|
        { :conditions => { :name => name } }
      }
    end
    

    You call it with the names of a series of scopes as such:

    Person.or_scopes(:women, :children)
    

    This returns a scope like this:

    Person.or_scopes(:women, :children).proxy_options
    # => {:conditions=>"(`people`.`sex` = 'F') OR (age < 18)"}
    

    You can also call it with an array of arrays when the scope requires parameters:

    Person.or_scopes(:women, [:named, 'Sue']).proxy_options
    # => {:conditions=>"(`people`.`sex` = 'F') OR (`people`.`name` = 'Sue')"}
    

    In your case Horace, you could use the following:

    Annotation.or_scopes([:body_equals, '?'], [:body_like, '[?']).all
    

提交回复
热议问题