aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/relation.rb
blob: 28f04e5d8554d32d7971d12171563db4dd391c41 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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
123
124
125
126
127
128
129
130
131
132
133
module ActiveRecord
  class Relation
    delegate :to_sql, :to => :relation
    delegate :length, :collect, :find, :map, :each, :to => :to_a
    attr_reader :relation, :klass

    def initialize(klass, relation, readonly = false, preload = [], eager_load = [])
      @klass, @relation = klass, relation
      @readonly = readonly
      @associations_to_preload = preload
      @eager_load_associations = eager_load
    end

    def preload(associations)
      @associations_to_preload << associations
      self
    end

    def eager_load(associations)
      @eager_load_associations += Array.wrap(associations)
      self
    end

    def readonly
      @readonly = true
      self
    end

    def to_a
      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

      records
    end

    def first
      @relation = @relation.take(1)
      to_a.first
    end

    def select(selects)
      selects.blank? ? self : create_new_relation(@relation.project(selects))
    end

    def group(groups)
      groups.blank? ? self : create_new_relation(@relation.group(groups))
    end

    def order(orders)
      orders.blank? ? self : create_new_relation(@relation.order(orders))
    end

    def limit(limits)
      limits.blank? ? self : create_new_relation(@relation.take(limits))
    end

    def offset(offsets)
      offsets.blank? ? self : create_new_relation(@relation.skip(offsets))
    end

    def on(join)
      join.blank? ? self : create_new_relation(@relation.on(join))
    end

    def joins(join, join_type = nil)
      if join.blank?
        self
      else
        join = 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
        create_new_relation(join)
      end
    end

    def conditions(conditions)
      if conditions.blank?
        self
      else
        conditions = @klass.send(:merge_conditions, conditions) if [String, Hash, Array].include?(conditions.class)
        create_new_relation(@relation.where(conditions))
      end
    end

    def respond_to?(method)
      @relation.respond_to?(method) || Array.method_defined?(method) || super
    end

    private

    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
      end
    end

    def create_new_relation(relation)
      Relation.new(@klass, relation, @readonly, @associations_to_preload, @eager_load_associations)
    end

  end
end