aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/relation.rb
blob: 853103a6060723617a26ca4f3690906ddb81e905 (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
134
135
136
137
138
139
140
141
142
143
144
145
146
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
      @loaded = false
    end

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

    def eager_load(*associations)
      create_new_relation(@relation, @readonly, @associations_to_preload, @eager_load_associations + Array.wrap(associations))
    end

    def readonly
      create_new_relation(@relation, true)
    end

    def select(selects)
      create_new_relation(@relation.project(selects))
    end

    def group(groups)
      create_new_relation(@relation.group(groups))
    end

    def order(orders)
      create_new_relation(@relation.order(orders))
    end

    def limit(limits)
      create_new_relation(@relation.take(limits))
    end

    def offset(offsets)
      create_new_relation(@relation.skip(offsets))
    end

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

    def joins(join, join_type = nil)
      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

    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

      create_new_relation(@relation.where(conditions))
    end

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

    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

    def reload
      @loaded = false
      @records = @first = nil
      self
    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, readonly = @readonly, preload = @associations_to_preload, eager_load = @eager_load_associations)
      Relation.new(@klass, relation, readonly, preload, eager_load)
    end

  end
end