aboutsummaryrefslogblamecommitdiffstats
path: root/activerecord/lib/active_record/relation/query_methods.rb
blob: 5d7bf0b7bcbbbaf41d11871ca07380d774cec34c (plain) (tree)
1
2
3
4
5
6
7
8
9






                                                                         
                               
                                                                          

       







                                                                            



                                                            


























































































                                                                                               

                                                                   



                                                                                              

                                                                                    
          
                  

         
                                                                                                         

















                                            
module ActiveRecord
  module QueryMethods

    def preload(*associations)
      spawn.tap {|r| r.preload_associations += Array.wrap(associations) }
    end

    def includes(*associations)
      spawn.tap {|r| r.includes_associations += Array.wrap(associations) }
    end

    def eager_load(*associations)
      spawn.tap {|r| r.eager_load_associations += Array.wrap(associations) }
    end

    def readonly(status = true)
      spawn.tap {|r| r.readonly = status }
    end

    def create_with(attributes = {})
      spawn.tap {|r| r.create_with_attributes = attributes }
    end

    def select(selects)
      if selects.present?
        relation = spawn(@relation.project(selects))
        relation.readonly = @relation.joins(relation).present? ? false : @readonly
        relation
      else
        spawn
      end
    end

    def from(from)
      from.present? ? spawn(@relation.from(from)) : spawn
    end

    def having(*args)
      return spawn if args.blank?

      if [String, Hash, Array].include?(args.first.class)
        havings = @klass.send(:merge_conditions, args.size > 1 ? Array.wrap(args) : args.first)
      else
        havings = args.first
      end

      spawn(@relation.having(havings))
    end

    def group(groups)
      groups.present? ? spawn(@relation.group(groups)) : spawn
    end

    def order(orders)
      orders.present? ? spawn(@relation.order(orders)) : spawn
    end

    def lock(locks = true)
      case locks
      when String
        spawn(@relation.lock(locks))
      when TrueClass, NilClass
        spawn(@relation.lock)
      else
        spawn
      end
    end

    def reverse_order
      relation = spawn
      relation.instance_variable_set(:@orders, nil)

      order_clause = @relation.send(:order_clauses).join(', ')
      if order_clause.present?
        relation.order(reverse_sql_order(order_clause))
      else
        relation.order("#{@klass.table_name}.#{@klass.primary_key} DESC")
      end
    end

    def limit(limits)
      limits.present? ? spawn(@relation.take(limits)) : spawn
    end

    def offset(offsets)
      offsets.present? ? spawn(@relation.skip(offsets)) : spawn
    end

    def on(join)
      spawn(@relation.on(join))
    end

    def joins(join, join_type = nil)
      return spawn if join.blank?

      join_relation = case join
      when String
        @relation.join(join)
      when Hash, Array, Symbol
        if @klass.send(:array_of_strings?, join)
          @relation.join(join.join(' '))
        else
          @relation.join(@klass.send(:build_association_joins, join))
        end
      else
        @relation.join(join, join_type)
      end

      spawn(join_relation).tap { |r| r.readonly = true }
    end

    def where(*args)
      return spawn if args.blank?

      builder = PredicateBuilder.new(Arel::Sql::Engine.new(@klass))

      conditions = if [String, Array].include?(args.first.class)
        merged = @klass.send(:merge_conditions, args.size > 1 ? Array.wrap(args) : args.first)
        Arel::SqlLiteral.new(merged) if merged
      elsif args.first.is_a?(Hash)
        attributes = @klass.send(:expand_hash_conditions_for_aggregates, args.first)
        builder.build_from_hash(attributes, table)
      else
        args.first
      end

      conditions.is_a?(String) ? spawn(@relation.where(conditions)) : spawn(@relation.where(*conditions))
    end

    private

    def reverse_sql_order(order_query)
      order_query.to_s.split(/,/).each { |s|
        if s.match(/\s(asc|ASC)$/)
          s.gsub!(/\s(asc|ASC)$/, ' DESC')
        elsif s.match(/\s(desc|DESC)$/)
          s.gsub!(/\s(desc|DESC)$/, ' ASC')
        else
          s.concat(' DESC')
        end
      }.join(',')
    end

  end
end