query_methods.rb 28.0 KB
Newer Older
1
require 'active_support/core_ext/array/wrap'
2

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

7 8 9 10 11 12 13 14 15 16
    # WhereChain objects act as placeholder for queries in which #where does not have any parameter.
    # In this case, #where must be chained with either #not, #like, or #not_like to return a new relation.
    class WhereChain
      def initialize(scope)
        @scope = scope
      end

      # Returns a new relation expressing WHERE + NOT condition
      # according to the conditions in the arguments.
      #
17 18 19 20
      # #not accepts conditions in one of these formats: String, Array, Hash.
      # See #where for more details on each format.
      #
      #    User.where.not("name = 'Jon'")
21
      #    # SELECT * FROM users WHERE NOT (name = 'Jon')
22 23
      #
      #    User.where.not(["name = ?", "Jon"])
24
      #    # SELECT * FROM users WHERE NOT (name = 'Jon')
25
      #
26
      #    User.where.not(name: "Jon")
27
      #    # SELECT * FROM users WHERE name != 'Jon'
28 29 30 31
      #
      #    User.where.not(name: nil)
      #    # SELECT * FROM users WHERE name IS NOT NULL
      #
32
      #    User.where.not(name: %w(Ko1 Nobu))
33 34 35 36 37 38
      #    # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
      def not(opts, *rest)
        where_value = @scope.send(:build_where, opts, rest).map do |rel|
          case rel
          when Arel::Nodes::In
            Arel::Nodes::NotIn.new(rel.left, rel.right)
39 40
          when Arel::Nodes::Equality
            Arel::Nodes::NotEqual.new(rel.left, rel.right)
41 42 43 44 45 46 47 48 49 50 51
          when String
            Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(rel))
          else
            Arel::Nodes::Not.new(rel)
          end
        end
        @scope.where_values += where_value
        @scope
      end
    end

52 53
    Relation::MULTI_VALUE_METHODS.each do |name|
      class_eval <<-CODE, __FILE__, __LINE__ + 1
54 55 56 57 58 59 60 61
        def #{name}_values                   # def select_values
          @values[:#{name}] || []            #   @values[:select] || []
        end                                  # end
                                             #
        def #{name}_values=(values)          # def select_values=(values)
          raise ImmutableRelation if @loaded #   raise ImmutableRelation if @loaded
          @values[:#{name}] = values         #   @values[:select] = values
        end                                  # end
62 63 64 65 66
      CODE
    end

    (Relation::SINGLE_VALUE_METHODS - [:create_with]).each do |name|
      class_eval <<-CODE, __FILE__, __LINE__ + 1
67 68 69
        def #{name}_value                    # def readonly_value
          @values[:#{name}]                  #   @values[:readonly]
        end                                  # end
70 71 72
      CODE
    end

73 74 75 76 77 78 79
    Relation::SINGLE_VALUE_METHODS.each do |name|
      class_eval <<-CODE, __FILE__, __LINE__ + 1
        def #{name}_value=(value)            # def readonly_value=(value)
          raise ImmutableRelation if @loaded #   raise ImmutableRelation if @loaded
          @values[:#{name}] = value          #   @values[:readonly] = value
        end                                  # end
      CODE
80 81
    end

O
Oscar Del Ben 已提交
82
    def create_with_value # :nodoc:
83
      @values[:create_with] || {}
84
    end
85 86

    alias extensions extending_values
87

O
Oscar Del Ben 已提交
88 89 90 91 92 93 94 95 96 97
    # Specify relationships to be included in the result set. For
    # example:
    #
    #   users = User.includes(:address)
    #   users.each do |user|
    #     user.address.city
    #   end
    #
    # allows you to access the +address+ attribute of the +User+ model without
    # firing an additional query. This will often result in a
98 99 100 101 102 103 104 105 106 107 108 109
    # performance improvement over a simple +join+.
    #
    # === conditions
    #
    # If you want to add conditions to your included models you'll have
    # to explicitly reference them. For example:
    #
    #   User.includes(:posts).where('posts.name = ?', 'example')
    #
    # Will throw an error, but this will work:
    #
    #   User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
110
    def includes(*args)
J
Jon Leighton 已提交
111
      args.empty? ? self : spawn.includes!(*args)
112
    end
113

J
Jon Leighton 已提交
114
    def includes!(*args) # :nodoc:
115
      args.reject! {|a| a.blank? }
A
Aaron Patterson 已提交
116

117 118
      self.includes_values = (includes_values + args).flatten.uniq
      self
119
    end
120

121 122 123 124 125 126
    # Forces eager loading by performing a LEFT OUTER JOIN on +args+:
    #
    #   User.eager_load(:posts)
    #   => SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ...
    #   FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
    #   "users"."id"
127
    def eager_load(*args)
J
Jon Leighton 已提交
128
      args.blank? ? self : spawn.eager_load!(*args)
129
    end
130

J
Jon Leighton 已提交
131
    def eager_load!(*args) # :nodoc:
132 133
      self.eager_load_values += args
      self
134 135
    end

136 137 138 139
    # Allows preloading of +args+, in the same way that +includes+ does:
    #
    #   User.preload(:posts)
    #   => SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
140
    def preload(*args)
J
Jon Leighton 已提交
141
      args.blank? ? self : spawn.preload!(*args)
142
    end
143

J
Jon Leighton 已提交
144
    def preload!(*args) # :nodoc:
145 146
      self.preload_values += args
      self
147
    end
148

149 150 151 152 153 154 155 156 157
    # Used to indicate that an association is referenced by an SQL string, and should
    # therefore be JOINed in any query rather than loaded separately.
    #
    #   User.includes(:posts).where("posts.name = 'foo'")
    #   # => Doesn't JOIN the posts table, resulting in an error.
    #
    #   User.includes(:posts).where("posts.name = 'foo'").references(:posts)
    #   # => Query now knows the string references posts, so adds a JOIN
    def references(*args)
J
Jon Leighton 已提交
158
      args.blank? ? self : spawn.references!(*args)
159
    end
160

J
Jon Leighton 已提交
161
    def references!(*args) # :nodoc:
162 163 164
      args.flatten!

      self.references_values = (references_values + args.map!(&:to_s)).uniq
165
      self
166 167
    end

168
    # Works in two unique ways.
169
    #
170 171
    # First: takes a block so it can be used just like Array#select.
    #
172
    #   Model.all.select { |m| m.field == value }
173 174 175 176 177
    #
    # This will build an array of objects from the database for the scope,
    # converting them into an array and iterating through them using Array#select.
    #
    # Second: Modifies the SELECT statement for the query so that only certain
V
Vijay Dev 已提交
178
    # fields are retrieved:
179
    #
180 181
    #   Model.select(:field)
    #   # => [#<Model field:value>]
182 183
    #
    # Although in the above example it looks as though this method returns an
V
Vijay Dev 已提交
184
    # array, it actually returns a relation object and can have other query
185 186
    # methods appended to it, such as the other methods in ActiveRecord::QueryMethods.
    #
187
    # The argument to the method can also be an array of fields.
188
    #
189 190
    #   Model.select(:field, :other_field, :and_one_more)
    #   # => [#<Model field: "value", other_field: "value", and_one_more: "value">]
191
    #
192 193
    # Accessing attributes of an object that do not have fields retrieved by a select
    # will throw <tt>ActiveModel::MissingAttributeError</tt>:
194
    #
195 196 197
    #   Model.select(:field).first.other_field
    #   # => ActiveModel::MissingAttributeError: missing attribute: other_field
    def select(*fields)
198
      if block_given?
199
        to_a.select { |*block_args| yield(*block_args) }
200
      else
201 202
        raise ArgumentError, 'Call this with at least one field' if fields.empty?
        spawn.select!(*fields)
203 204 205
      end
    end

J
Jon Leighton 已提交
206
    def select!(*fields) # :nodoc:
207
      self.select_values += fields.flatten
208
      self
209
    end
S
Santiago Pastorino 已提交
210

O
Oscar Del Ben 已提交
211 212 213 214 215
    # Allows to specify a group attribute:
    #
    #   User.group(:name)
    #   => SELECT "users".* FROM "users" GROUP BY name
    #
216
    # Returns an array with distinct records based on the +group+ attribute:
O
Oscar Del Ben 已提交
217 218 219 220 221 222
    #
    #   User.select([:id, :name])
    #   => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">
    #
    #   User.group(:name)
    #   => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
223
    def group(*args)
J
Jon Leighton 已提交
224
      args.blank? ? self : spawn.group!(*args)
225
    end
226

J
Jon Leighton 已提交
227
    def group!(*args) # :nodoc:
228 229 230
      args.flatten!

      self.group_values += args
231
      self
232
    end
233

O
Oscar Del Ben 已提交
234 235 236 237 238 239 240 241 242 243
    # Allows to specify an order attribute:
    #
    #   User.order('name')
    #   => SELECT "users".* FROM "users" ORDER BY name
    #
    #   User.order('name DESC')
    #   => SELECT "users".* FROM "users" ORDER BY name DESC
    #
    #   User.order('name DESC, email')
    #   => SELECT "users".* FROM "users" ORDER BY name DESC, email
244
    #
245 246
    #   User.order(:name)
    #   => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
247
    #
248 249
    #   User.order(email: :desc)
    #   => SELECT "users".* FROM "users" ORDER BY "users"."email" DESC
250
    #
251 252
    #   User.order(:name, email: :desc)
    #   => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
253
    def order(*args)
J
Jon Leighton 已提交
254
      args.blank? ? self : spawn.order!(*args)
255
    end
256

J
Jon Leighton 已提交
257
    def order!(*args) # :nodoc:
258
      args.flatten!
259
      validate_order_args args
260

261
      references = args.reject { |arg| Arel::Node === arg }
262
      references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
263
      references!(references) if references.any?
264

265
      self.order_values = args + self.order_values
266
      self
267
    end
268

269 270 271 272 273 274 275 276
    # Replaces any existing order defined on the relation with the specified order.
    #
    #   User.order('email DESC').reorder('id ASC') # generated SQL has 'ORDER BY id ASC'
    #
    # Subsequent calls to order on the same relation will be appended. For example:
    #
    #   User.order('email DESC').reorder('id ASC').order('name ASC')
    #
277
    # generates a query with 'ORDER BY name ASC, id ASC'.
S
Sebastian Martinez 已提交
278
    def reorder(*args)
J
Jon Leighton 已提交
279
      args.blank? ? self : spawn.reorder!(*args)
280
    end
281

J
Jon Leighton 已提交
282
    def reorder!(*args) # :nodoc:
283
      args.flatten!
284
      validate_order_args args
285

286
      self.reordering_value = true
287
      self.order_values = args
288
      self
S
Sebastian Martinez 已提交
289 290
    end

291 292 293 294
    # Performs a joins on +args+:
    #
    #   User.joins(:posts)
    #   => SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
295
    def joins(*args)
296
      args.compact.blank? ? self : spawn.joins!(*args.flatten)
297
    end
298

J
Jon Leighton 已提交
299
    def joins!(*args) # :nodoc:
300 301
      self.joins_values += args
      self
P
Pratik Naik 已提交
302 303
    end

A
Aaron Patterson 已提交
304
    def bind(value)
J
Jon Leighton 已提交
305
      spawn.bind!(value)
306 307
    end

J
Jon Leighton 已提交
308
    def bind!(value) # :nodoc:
309 310
      self.bind_values += [value]
      self
A
Aaron Patterson 已提交
311 312
    end

313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
    # Returns a new relation, which is the result of filtering the current relation
    # according to the conditions in the arguments.
    #
    # #where accepts conditions in one of several formats. In the examples below, the resulting
    # SQL is given as an illustration; the actual query generated may be different depending
    # on the database adapter.
    #
    # === string
    #
    # A single string, without additional arguments, is passed to the query
    # constructor as a SQL fragment, and used in the where clause of the query.
    #
    #    Client.where("orders_count = '2'")
    #    # SELECT * from clients where orders_count = '2';
    #
    # Note that building your own string from user input may expose your application
    # to injection attacks if not done properly. As an alternative, it is recommended
    # to use one of the following methods.
    #
    # === array
    #
    # If an array is passed, then the first element of the array is treated as a template, and
    # the remaining elements are inserted into the template to generate the condition.
    # Active Record takes care of building the query to avoid injection attacks, and will
    # convert from the ruby type to the database type where needed. Elements are inserted
    # into the string in the order in which they appear.
    #
    #   User.where(["name = ? and email = ?", "Joe", "joe@example.com"])
    #   # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
    #
    # Alternatively, you can use named placeholders in the template, and pass a hash as the
    # second element of the array. The names in the template are replaced with the corresponding
    # values from the hash.
    #
    #   User.where(["name = :name and email = :email", { name: "Joe", email: "joe@example.com" }])
    #   # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
    #
    # This can make for more readable code in complex queries.
    #
    # Lastly, you can use sprintf-style % escapes in the template. This works slightly differently
    # than the previous methods; you are responsible for ensuring that the values in the template
    # are properly quoted. The values are passed to the connector for quoting, but the caller
    # is responsible for ensuring they are enclosed in quotes in the resulting SQL. After quoting,
    # the values are inserted using the same escapes as the Ruby core method <tt>Kernel::sprintf</tt>.
    #
    #   User.where(["name = '%s' and email = '%s'", "Joe", "joe@example.com"])
    #   # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
    #
    # If #where is called with multiple arguments, these are treated as if they were passed as
    # the elements of a single array.
    #
    #   User.where("name = :name and email = :email", { name: "Joe", email: "joe@example.com" })
    #   # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
    #
    # When using strings to specify conditions, you can use any operator available from
    # the database. While this provides the most flexibility, you can also unintentionally introduce
    # dependencies on the underlying database. If your code is intended for general consumption,
    # test with multiple database backends.
    #
    # === hash
    #
    # #where will also accept a hash condition, in which the keys are fields and the values
    # are values to be searched for.
    #
    # Fields can be symbols or strings. Values can be single values, arrays, or ranges.
    #
    #    User.where({ name: "Joe", email: "joe@example.com" })
    #    # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com'
    #
    #    User.where({ name: ["Alice", "Bob"]})
    #    # SELECT * FROM users WHERE name IN ('Alice', 'Bob')
    #
    #    User.where({ created_at: (Time.now.midnight - 1.day)..Time.now.midnight })
    #    # SELECT * FROM users WHERE (created_at BETWEEN '2012-06-09 07:00:00.000000' AND '2012-06-10 07:00:00.000000')
    #
388 389 390 391 392 393
    # In the case of a belongs_to relationship, an association key can be used
    # to specify the model if an ActiveRecord object is used as the value.
    #
    #    author = Author.find(1)
    #
    #    # The following queries will be equivalent:
A
AvnerCohen 已提交
394 395
    #    Post.where(author: author)
    #    Post.where(author_id: author)
396 397 398
    #
    # This also works with polymorphic belongs_to relationships:
    #
A
AvnerCohen 已提交
399 400
    #    treasure = Treasure.create(name: 'gold coins')
    #    treasure.price_estimates << PriceEstimate.create(price: 125)
401 402
    #
    #    # The following queries will be equivalent:
A
AvnerCohen 已提交
403 404
    #    PriceEstimate.where(estimate_of: treasure)
    #    PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: treasure)
405
    #
406 407 408 409 410 411 412 413 414 415
    # === Joins
    #
    # If the relation is the result of a join, you may create a condition which uses any of the
    # tables in the join. For string and array conditions, use the table name in the condition.
    #
    #    User.joins(:posts).where("posts.created_at < ?", Time.now)
    #
    # For hash conditions, you can either use the table name in the key, or use a sub-hash.
    #
    #    User.joins(:posts).where({ "posts.published" => true })
A
AvnerCohen 已提交
416
    #    User.joins(:posts).where({ posts: { published: true } })
417
    #
418
    # === no argument
419
    #
420 421
    # If no argument is passed, #where returns a new instance of WhereChain, that
    # can be chained with #not to return a new relation that negates the where clause.
422 423
    #
    #    User.where.not(name: "Jon")
424
    #    # SELECT * FROM users WHERE name != 'Jon'
425
    #
426
    # See WhereChain for more details on #not.
427
    #
428
    # === blank condition
429
    #
430
    # If the condition is any blank-ish object, then #where is a no-op and returns
431
    # the current relation.
432 433
    def where(opts = :chain, *rest)
      if opts == :chain
434 435 436 437 438 439
        WhereChain.new(spawn)
      elsif opts.blank?
        self
      else
        spawn.where!(opts, *rest)
      end
440 441
    end

442 443
    # #where! is identical to #where, except that instead of returning a new relation, it adds
    # the condition to the existing relation.
444 445
    def where!(opts = :chain, *rest) # :nodoc:
      if opts == :chain
446 447 448
        WhereChain.new(self)
      else
        references!(PredicateBuilder.references(opts)) if Hash === opts
449

450 451 452
        self.where_values += build_where(opts, rest)
        self
      end
453
    end
P
Pratik Naik 已提交
454

455 456 457 458
    # Allows to specify a HAVING clause. Note that you can't use HAVING
    # without also specifying a GROUP clause.
    #
    #   Order.having('SUM(price) > 30').group('user_id')
459
    def having(opts, *rest)
J
Jon Leighton 已提交
460
      opts.blank? ? self : spawn.having!(opts, *rest)
461 462
    end

J
Jon Leighton 已提交
463
    def having!(opts, *rest) # :nodoc:
464
      references!(PredicateBuilder.references(opts)) if Hash === opts
465

466 467
      self.having_values += build_where(opts, rest)
      self
468 469
    end

470
    # Specifies a limit for the number of records to retrieve.
471 472 473 474
    #
    #   User.limit(10) # generated SQL has 'LIMIT 10'
    #
    #   User.limit(10).limit(20) # generated SQL has 'LIMIT 20'
475
    def limit(value)
J
Jon Leighton 已提交
476
      spawn.limit!(value)
477 478
    end

J
Jon Leighton 已提交
479
    def limit!(value) # :nodoc:
480 481
      self.limit_value = value
      self
482 483
    end

484 485 486 487
    # Specifies the number of rows to skip before returning rows.
    #
    #   User.offset(10) # generated SQL has "OFFSET 10"
    #
488
    # Should be used with order.
489
    #
490
    #   User.offset(10).order("name ASC")
491
    def offset(value)
J
Jon Leighton 已提交
492
      spawn.offset!(value)
493 494
    end

J
Jon Leighton 已提交
495
    def offset!(value) # :nodoc:
496 497
      self.offset_value = value
      self
498 499
    end

500
    # Specifies locking settings (default to +true+). For more information
501
    # on locking, please see +ActiveRecord::Locking+.
502
    def lock(locks = true)
J
Jon Leighton 已提交
503
      spawn.lock!(locks)
504
    end
505

J
Jon Leighton 已提交
506
    def lock!(locks = true) # :nodoc:
507
      case locks
508
      when String, TrueClass, NilClass
509
        self.lock_value = locks || true
510
      else
511
        self.lock_value = false
512
      end
513

514
      self
515 516
    end

517
    # Returns a chainable relation with zero records, specifically an
V
Vijay Dev 已提交
518
    # instance of the <tt>ActiveRecord::NullRelation</tt> class.
519
    #
V
Vijay Dev 已提交
520 521 522
    # The returned <tt>ActiveRecord::NullRelation</tt> inherits from Relation and implements the
    # Null Object pattern. It is an object with defined null behavior and always returns an empty
    # array of records without quering the database.
523 524 525 526
    #
    # Any subsequent condition chained to the returned relation will continue
    # generating an empty relation and will not fire any query to the database.
    #
527 528
    # Used in cases where a method or scope could return zero records but the
    # result needs to be chainable.
529 530 531
    #
    # For example:
    #
A
AvnerCohen 已提交
532
    #   @posts = current_user.visible_posts.where(name: params[:name])
533
    #   # => the visible_posts method is expected to return a chainable Relation
534 535 536
    #
    #   def visible_posts
    #     case role
537
    #     when 'Country Manager'
A
AvnerCohen 已提交
538
    #       Post.where(country: country)
539
    #     when 'Reviewer'
540
    #       Post.published
541
    #     when 'Bad User'
542 543 544 545 546
    #       Post.none # => returning [] instead breaks the previous code
    #     end
    #   end
    #
    def none
547
      extending(NullRelation)
548 549
    end

J
Jon Leighton 已提交
550
    def none! # :nodoc:
551 552 553
      extending!(NullRelation)
    end

554 555 556 557 558 559
    # Sets readonly attributes for the returned relation. If value is
    # true (default), attempting to update a record will result in an error.
    #
    #   users = User.readonly
    #   users.first.save
    #   => ActiveRecord::ReadOnlyRecord: ActiveRecord::ReadOnlyRecord
560
    def readonly(value = true)
J
Jon Leighton 已提交
561
      spawn.readonly!(value)
562 563
    end

J
Jon Leighton 已提交
564
    def readonly!(value = true) # :nodoc:
565 566
      self.readonly_value = value
      self
567 568
    end

569 570 571 572 573 574 575 576 577 578 579 580 581
    # Sets attributes to be used when creating new records from a
    # relation object.
    #
    #   users = User.where(name: 'Oscar')
    #   users.new.name # => 'Oscar'
    #
    #   users = users.create_with(name: 'DHH')
    #   users.new.name # => 'DHH'
    #
    # You can pass +nil+ to +create_with+ to reset attributes:
    #
    #   users = users.create_with(nil)
    #   users.new.name # => 'Oscar'
582
    def create_with(value)
J
Jon Leighton 已提交
583
      spawn.create_with!(value)
584 585
    end

J
Jon Leighton 已提交
586
    def create_with!(value) # :nodoc:
587 588
      self.create_with_value = value ? create_with_value.merge(value) : {}
      self
589 590
    end

591 592 593 594 595 596 597
    # Specifies table from which the records will be fetched. For example:
    #
    #   Topic.select('title').from('posts')
    #   #=> SELECT title FROM posts
    #
    # Can accept other relation objects. For example:
    #
598
    #   Topic.select('title').from(Topic.approved)
599 600
    #   # => SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
    #
601
    #   Topic.select('a.title').from(Topic.approved, :a)
602 603 604 605
    #   # => SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
    #
    def from(value, subquery_name = nil)
      spawn.from!(value, subquery_name)
606 607
    end

608
    # Like #from, but modifies relation in place.
J
Jon Leighton 已提交
609
    def from!(value, subquery_name = nil) # :nodoc:
610
      self.from_value = [value, subquery_name]
611
      self
612 613
    end

614 615 616 617 618 619 620 621 622 623 624
    # Specifies whether the records should be unique or not. For example:
    #
    #   User.select(:name)
    #   # => Might return two records with the same name
    #
    #   User.select(:name).uniq
    #   # => Returns 1 record per unique name
    #
    #   User.select(:name).uniq.uniq(false)
    #   # => You can also remove the uniqueness
    def uniq(value = true)
J
Jon Leighton 已提交
625
      spawn.uniq!(value)
626 627
    end

628
    # Like #uniq, but modifies relation in place.
J
Jon Leighton 已提交
629
    def uniq!(value = true) # :nodoc:
630 631
      self.uniq_value = value
      self
632 633
    end

634
    # Used to extend a scope with additional methods, either through
635 636
    # a module or through a block provided.
    #
637 638 639 640 641 642 643 644 645 646
    # The object returned is a relation, which can be further extended.
    #
    # === Using a module
    #
    #   module Pagination
    #     def page(number)
    #       # pagination code goes here
    #     end
    #   end
    #
647
    #   scope = Model.all.extending(Pagination)
648 649
    #   scope.page(params[:page])
    #
V
Vijay Dev 已提交
650
    # You can also pass a list of modules:
651
    #
652
    #   scope = Model.all.extending(Pagination, SomethingElse)
653 654 655
    #
    # === Using a block
    #
656
    #   scope = Model.all.extending do
657
    #     def page(number)
658
    #       # pagination code goes here
659 660 661 662 663 664
    #     end
    #   end
    #   scope.page(params[:page])
    #
    # You can also use a block and a module list:
    #
665
    #   scope = Model.all.extending(Pagination) do
666
    #     def per_page(number)
667
    #       # pagination code goes here
668 669
    #     end
    #   end
670 671
    def extending(*modules, &block)
      if modules.any? || block
J
Jon Leighton 已提交
672
        spawn.extending!(*modules, &block)
673 674 675 676
      else
        self
      end
    end
677

J
Jon Leighton 已提交
678
    def extending!(*modules, &block) # :nodoc:
679
      modules << Module.new(&block) if block_given?
680

J
Jon Leighton 已提交
681
      self.extending_values += modules.flatten
682
      extend(*extending_values) if extending_values.any?
683

684
      self
685 686
    end

687 688 689
    # Reverse the existing order clause on the relation.
    #
    #   User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
690
    def reverse_order
J
Jon Leighton 已提交
691
      spawn.reverse_order!
692 693
    end

J
Jon Leighton 已提交
694
    def reverse_order! # :nodoc:
695 696
      self.reverse_order_value = !reverse_order_value
      self
697 698
    end

699
    # Returns the Arel object associated with the relation.
700
    def arel
701
      @arel ||= with_default_scope.build_arel
702 703
    end

704
    # Like #arel, but ignores the default scope of the model.
705
    def build_arel
706
      arel = Arel::SelectManager.new(table.engine, table)
707

708
      build_joins(arel, joins_values) unless joins_values.empty?
709

710
      collapse_wheres(arel, (where_values - ['']).uniq)
711

712
      arel.having(*having_values.uniq.reject{|h| h.blank?}) unless having_values.empty?
713

714 715
      arel.take(connection.sanitize_limit(limit_value)) if limit_value
      arel.skip(offset_value.to_i) if offset_value
A
Aaron Patterson 已提交
716

717
      arel.group(*group_values.uniq.reject{|g| g.blank?}) unless group_values.empty?
718

719
      build_order(arel)
720

721
      build_select(arel, select_values.uniq)
722

723
      arel.distinct(uniq_value)
724
      arel.from(build_from) if from_value
725
      arel.lock(lock_value) if lock_value
726 727

      arel
728 729
    end

730 731
    private

732
    def custom_join_ast(table, joins)
733 734
      joins = joins.reject { |join| join.blank? }

735
      return [] if joins.empty?
736 737 738

      @implicit_readonly = true

739
      joins.map do |join|
740 741 742 743 744 745
        case join
        when Array
          join = Arel.sql(join.join(' ')) if array_of_strings?(join)
        when String
          join = Arel.sql(join)
        end
746
        table.create_string_join(join)
747 748 749
      end
    end

750 751 752
    def collapse_wheres(arel, wheres)
      equalities = wheres.grep(Arel::Nodes::Equality)

A
Aaron Patterson 已提交
753
      arel.where(Arel::Nodes::And.new(equalities)) unless equalities.empty?
754 755 756

      (wheres - equalities).each do |where|
        where = Arel.sql(where) if String === where
757
        arel.where(Arel::Nodes::Grouping.new(where))
758 759 760
      end
    end

761
    def build_where(opts, other = [])
A
Aaron Patterson 已提交
762 763
      case opts
      when String, Array
764
        [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
A
Aaron Patterson 已提交
765
      when Hash
766
        attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
767
        PredicateBuilder.build_from_hash(klass, attributes, table)
768
      else
769
        [opts]
770 771 772
      end
    end

773 774 775 776 777 778 779 780 781 782 783
    def build_from
      opts, name = from_value
      case opts
      when Relation
        name ||= 'subquery'
        opts.arel.as(name.to_s)
      else
        opts
      end
    end

784
    def build_joins(manager, joins)
A
Aaron Patterson 已提交
785 786 787
      buckets = joins.group_by do |join|
        case join
        when String
788
          :string_join
A
Aaron Patterson 已提交
789
        when Hash, Symbol, Array
790
          :association_join
791
        when ActiveRecord::Associations::JoinDependency::JoinAssociation
792
          :stashed_join
793
        when Arel::Nodes::Join
794
          :join_node
A
Aaron Patterson 已提交
795 796 797
        else
          raise 'unknown class: %s' % join.class.name
        end
798 799
      end

800 801 802 803
      association_joins         = buckets[:association_join] || []
      stashed_association_joins = buckets[:stashed_join] || []
      join_nodes                = (buckets[:join_node] || []).uniq
      string_joins              = (buckets[:string_join] || []).map { |x|
A
Aaron Patterson 已提交
804 805
        x.strip
      }.uniq
806

807
      join_list = join_nodes + custom_join_ast(manager, string_joins)
808

809
      join_dependency = ActiveRecord::Associations::JoinDependency.new(
810 811 812 813
        @klass,
        association_joins,
        join_list
      )
814 815 816 817 818

      join_dependency.graft(*stashed_association_joins)

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

A
Aaron Patterson 已提交
819
      # FIXME: refactor this to build an AST
820
      join_dependency.join_associations.each do |association|
821
        association.join_to(manager)
822 823
      end

824
      manager.join_sources.concat join_list
825 826

      manager
827 828
    end

829
    def build_select(arel, selects)
830
      unless selects.empty?
831
        @implicit_readonly = false
832
        arel.project(*selects)
833
      else
834
        arel.project(@klass.arel_table[Arel.star])
835 836 837
      end
    end

838
    def reverse_sql_order(order_query)
B
Brian Mathiyakom 已提交
839 840
      order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?

841
      order_query.flat_map do |o|
842
        case o
843
        when Arel::Nodes::Ordering
844
          o.reverse
845
        when String
846 847 848 849
          o.to_s.split(',').collect do |s|
            s.strip!
            s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
          end
850
        when Symbol
851
          { o => :desc }
852
        when Hash
853
          o.each_with_object({}) do |(field, dir), memo|
854 855
            memo[field] = (dir == :asc ? :desc : :asc )
          end
856 857 858
        else
          o
        end
859
      end
860 861
    end

P
Pratik Naik 已提交
862 863 864
    def array_of_strings?(o)
      o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
    end
865

866 867 868
    def build_order(arel)
      orders = order_values
      orders = reverse_sql_order(orders) if reverse_order_value
869

870
      orders = orders.uniq.reject(&:blank?).flat_map do |order|
871 872 873 874 875
        case order
        when Symbol
          table[order].asc
        when Hash
          order.map { |field, dir| table[field].send(dir) }
876
        else
877 878
          order
        end
879
      end
880

881 882
      arel.order(*orders) unless orders.empty?
    end
883

884 885 886 887 888 889 890
    def validate_order_args(args)
      args.select { |a| Hash === a  }.each do |h|
        unless (h.values - [:asc, :desc]).empty?
          raise ArgumentError, 'Direction should be :asc or :desc'
        end
      end
    end
P
Pratik Naik 已提交
891

892 893
  end
end