aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/relation.rb
blob: 5f0eec754f42e18f898d456b2fd691d7f70f104d (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
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)
      @klass, @relation = klass, relation
      @readonly = false
      @associations_to_preload = []
      @eager_load_associations = []
    end

    def preload(association)
      @associations_to_preload += association
      self
    end

    def eager_load(association)
      @eager_load_associations += association
      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

      @klass.send(:preload_associations, records, @associations_to_preload) unless @associations_to_preload.empty?
      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 : Relation.new(@klass, @relation.project(selects))
    end

    def group(groups)
      groups.blank? ? self : Relation.new(@klass, @relation.group(groups))
    end

    def order(orders)
      orders.blank? ? self : Relation.new(@klass, @relation.order(orders))
    end

    def limit(limits)
      limits.blank? ? self : Relation.new(@klass, @relation.take(limits))
    end

    def offset(offsets)
      offsets.blank? ? self : Relation.new(@klass, @relation.skip(offsets))
    end

    def on(join)
      join.blank? ? self : Relation.new(@klass, @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
        Relation.new(@klass, join)
      end
    end

    def conditions(conditions)
      if conditions.blank?
        self
      else
        conditions = @klass.send(:merge_conditions, conditions) if [String, Hash, Array].include?(conditions.class)
        Relation.new(@klass, @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
  end
end