query_methods.rb 6.5 KB
Newer Older
1
require 'active_support/core_ext/array/wrap'
2 3
require 'active_support/core_ext/object/blank'

4 5
module ActiveRecord
  module QueryMethods
6 7
    extend ActiveSupport::Concern

8
    attr_accessor :includes_values, :eager_load_values, :preload_values,
A
Aaron Patterson 已提交
9 10
                  :select_values, :group_values, :order_values, :joins_values,
                  :where_values, :having_values, :bind_values,
11
                  :limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value, :from_value
12

13
    def includes(*args)
14 15
      args.reject! {|a| a.blank? }

A
Aaron Patterson 已提交
16 17
      return clone if args.empty?

18
      relation = clone
A
Aaron Patterson 已提交
19
      relation.includes_values = (relation.includes_values + args).flatten.uniq
20
      relation
21
    end
22

23
    def eager_load(*args)
24
      relation = clone
25
      relation.eager_load_values += args unless args.blank?
26
      relation
27 28 29
    end

    def preload(*args)
30
      relation = clone
31
      relation.preload_values += args unless args.blank?
32
      relation
33
    end
34

35
    def select(value = Proc.new)
36
      if block_given?
37
        to_a.select {|*block_args| value.call(*block_args) }
38
      else
39
        relation = clone
40
        relation.select_values += Array.wrap(value)
41
        relation
S
Santiago Pastorino 已提交
42
      end
43
    end
S
Santiago Pastorino 已提交
44

45
    def group(*args)
46
      relation = clone
47
      relation.group_values += args.flatten unless args.blank?
48
      relation
49
    end
50

51
    def order(*args)
52
      relation = clone
53
      relation.order_values += args.flatten unless args.blank?
54
      relation
55
    end
56

57
    def joins(*args)
58 59
      relation = clone

A
Aaron Patterson 已提交
60 61
      args.flatten!
      relation.joins_values += args unless args.blank?
62 63

      relation
P
Pratik Naik 已提交
64 65
    end

A
Aaron Patterson 已提交
66 67 68 69 70 71
    def bind(value)
      relation = clone
      relation.bind_values += [value]
      relation
    end

72
    def where(opts, *rest)
73
      relation = clone
74
      relation.where_values += build_where(opts, rest) unless opts.blank?
75
      relation
76
    end
P
Pratik Naik 已提交
77

78
    def having(*args)
79
      relation = clone
80
      relation.having_values += build_where(*args) unless args.blank?
81
      relation
82 83
    end

84
    def limit(value)
85 86 87
      relation = clone
      relation.limit_value = value
      relation
88 89
    end

90
    def offset(value)
91 92 93
      relation = clone
      relation.offset_value = value
      relation
94 95 96
    end

    def lock(locks = true)
97 98
      relation = clone

99
      case locks
100
      when String, TrueClass, NilClass
101
        relation.lock_value = locks || true
102
      else
103
        relation.lock_value = false
104
      end
105 106

      relation
107 108
    end

109
    def readonly(value = true)
110 111 112
      relation = clone
      relation.readonly_value = value
      relation
113 114
    end

115
    def create_with(value)
116 117 118
      relation = clone
      relation.create_with_value = value
      relation
119 120
    end

121
    def from(value)
122 123 124
      relation = clone
      relation.from_value = value
      relation
125 126
    end

127 128
    def extending(*modules)
      modules << Module.new(&Proc.new) if block_given?
129

130 131
      return self if modules.empty?

132
      relation = clone
133
      relation.send(:apply_modules, modules.flatten)
134
      relation
135 136
    end

137
    def reverse_order
N
Neeraj Singh 已提交
138
      order_clause = arel.order_clauses
139

N
Neeraj Singh 已提交
140
      order = order_clause.empty? ?
141
        "#{@klass.table_name}.#{@klass.primary_key} DESC" :
N
Neeraj Singh 已提交
142
        reverse_sql_order(order_clause).join(', ')
143

N
Neeraj Singh 已提交
144
      except(:order).order(Arel::SqlLiteral.new(order))
145 146
    end

147 148
    def arel
      @arel ||= build_arel
149 150
    end

151
    def custom_join_sql(*joins)
A
Aaron Patterson 已提交
152
      arel = table.select_manager
153

154 155 156 157 158 159
      joins.each do |join|
        next if join.blank?

        @implicit_readonly = true

        case join
160
        when Array
161
          join = Arel.sql(join.join(' ')) if array_of_strings?(join)
162
        when String
163
          join = Arel.sql(join)
164
        end
A
Aaron Patterson 已提交
165 166

        arel.join(join)
167
      end
168

169 170 171
      arel.joins(arel)
    end

172 173
    def build_arel
      arel = table
174

175
      arel = build_joins(arel, @joins_values) unless @joins_values.empty?
176

177
      (@where_values - ['']).uniq.each do |where|
178 179
        where = Arel.sql(where) if String === where
        arel = arel.where(Arel::Nodes::Grouping.new(where))
180 181
      end

182
      arel = arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty?
183

184
      arel = arel.take(@limit_value) if @limit_value
185
      arel = arel.skip(@offset_value) if @offset_value
186

187
      arel = arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty?
188

189
      arel = arel.order(*@order_values.uniq.reject{|o| o.blank?}) unless @order_values.empty?
190

191
      arel = build_select(arel, @select_values.uniq)
192

193
      arel = arel.from(@from_value) if @from_value
194
      arel = arel.lock(@lock_value) if @lock_value
195 196

      arel
197 198
    end

A
Aaron Patterson 已提交
199 200
    private

201
    def build_where(opts, other = [])
A
Aaron Patterson 已提交
202 203
      case opts
      when String, Array
204
        [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
A
Aaron Patterson 已提交
205 206
      when Hash
        attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
207
        PredicateBuilder.build_from_hash(table.engine, attributes, table)
208
      else
209
        [opts]
210 211 212
      end
    end

213 214 215
    def build_joins(relation, joins)
      association_joins = []

216
      joins = joins.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq
217 218 219 220 221

      joins.each do |join|
        association_joins << join if [Hash, Array, Symbol].include?(join.class) && !array_of_strings?(join)
      end

222
      stashed_association_joins = joins.grep(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)
223

S
Santiago Pastorino 已提交
224
      non_association_joins = (joins - association_joins - stashed_association_joins)
225 226 227 228 229 230 231 232 233
      custom_joins = custom_join_sql(*non_association_joins)

      join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, custom_joins)

      join_dependency.graft(*stashed_association_joins)

      @implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty?

      join_dependency.join_associations.each do |association|
234
        relation = association.join_to(relation)
235 236 237 238 239
      end

      relation.join(custom_joins)
    end

240
    def build_select(arel, selects)
241
      unless selects.empty?
242
        @implicit_readonly = false
243
        arel.project(*selects)
244
      else
245
        arel.project(Arel.sql(@klass.quoted_table_name + '.*'))
246 247 248
      end
    end

P
Pratik Naik 已提交
249
    def apply_modules(modules)
250
      unless modules.empty?
251
        @extensions += modules
252
        modules.each {|extension| extend(extension) }
N
Neeraj Singh 已提交
253
      end
P
Pratik Naik 已提交
254 255
    end

256
    def reverse_sql_order(order_query)
257 258 259
      order_query.join(', ').split(',').collect do |s|
        s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
      end
260 261
    end

P
Pratik Naik 已提交
262 263 264 265
    def array_of_strings?(o)
      o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
    end

266 267
  end
end