relation.rb 3.8 KB
Newer Older
1 2
module ActiveRecord
  class Relation
3
    delegate :to_sql, :to => :relation
4
    delegate :length, :collect, :find, :map, :each, :to => :to_a
5 6
    attr_reader :relation, :klass

7
    def initialize(klass, relation, readonly = false, preload = [], eager_load = [])
E
Emilio Tagua 已提交
8
      @klass, @relation = klass, relation
9 10 11
      @readonly = readonly
      @associations_to_preload = preload
      @eager_load_associations = eager_load
P
Pratik Naik 已提交
12
      @loaded = false
13 14
    end

15
    def preload(*associations)
16
      create_new_relation(@relation, @readonly, @associations_to_preload + Array.wrap(associations))
17 18
    end

19
    def eager_load(*associations)
20
      create_new_relation(@relation, @readonly, @associations_to_preload, @eager_load_associations + Array.wrap(associations))
21 22 23
    end

    def readonly
24
      create_new_relation(@relation, true)
25 26
    end

27
    def select(selects)
28
      create_new_relation(@relation.project(selects))
29 30
    end

31
    def group(groups)
32
      create_new_relation(@relation.group(groups))
33 34
    end

35
    def order(orders)
36
      create_new_relation(@relation.order(orders))
37 38
    end

39
    def limit(limits)
40
      create_new_relation(@relation.take(limits))
41 42
    end

43
    def offset(offsets)
44
      create_new_relation(@relation.skip(offsets))
45 46
    end

47
    def on(join)
48
      create_new_relation(@relation.on(join))
49 50
    end

51
    def joins(join, join_type = nil)
52 53 54 55 56 57
      join = case join
        when String
          @relation.join(join)
        when Hash, Array, Symbol
          if @klass.send(:array_of_strings?, join)
            @relation.join(join.join(' '))
58
          else
59 60 61 62
            @relation.join(@klass.send(:build_association_joins, join))
          end
        else
          @relation.join(join, join_type)
63
      end
64
      create_new_relation(join)
65 66
    end

67 68 69 70 71 72 73
    def where(*args)
      if [String, Hash, Array].include?(args.first.class)
        conditions = @klass.send(:merge_conditions, args.size > 1 ? Array.wrap(args) : args.first)
      else
        conditions = args.first
      end

74
      create_new_relation(@relation.where(conditions))
75 76
    end

77
    def respond_to?(method)
78
      @relation.respond_to?(method) || Array.method_defined?(method) || super
79 80
    end

P
Pratik Naik 已提交
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
    def to_a
      return @records if loaded?

      @records = if @eager_load_associations.any?
        catch :invalid_query do
          return @klass.send(:find_with_associations, {
            :select => @relation.send(:select_clauses).join(', '),
            :joins => @relation.joins(relation),
            :group => @relation.send(:group_clauses).join(', '),
            :order => @relation.send(:order_clauses).join(', '),
            :conditions => @relation.send(:where_clauses).join("\n\tAND "),
            :limit => @relation.taken,
            :offset => @relation.skipped
            },
            ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, @eager_load_associations, nil))
        end
        []
      else
        @klass.find_by_sql(@relation.to_sql)
      end

      @associations_to_preload.each {|associations| @klass.send(:preload_associations, @records, associations) }
      @records.each { |record| record.readonly! } if @readonly

      @loaded = true
      @records
    end

    alias all to_a

    def first
      if loaded?
        @records.first
      else
        @first ||= limit(1).to_a[0]
      end
    end

    def loaded?
      @loaded
    end

123
    private
124 125 126 127 128 129 130 131

    def method_missing(method, *args, &block)
      if @relation.respond_to?(method)
        @relation.send(method, *args, &block)
      elsif Array.method_defined?(method)
        to_a.send(method, *args, &block)
      else
        super
132
      end
133 134
    end

135 136
    def create_new_relation(relation, readonly = @readonly, preload = @associations_to_preload, eager_load = @eager_load_associations)
      Relation.new(@klass, relation, readonly, preload, eager_load)
137 138
    end

139 140
  end
end