activerecord 4.0.13 → 4.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +745 -2700
  3. data/README.rdoc +2 -2
  4. data/examples/performance.rb +30 -18
  5. data/examples/simple.rb +4 -4
  6. data/lib/active_record.rb +2 -6
  7. data/lib/active_record/aggregations.rb +2 -1
  8. data/lib/active_record/association_relation.rb +0 -4
  9. data/lib/active_record/associations.rb +87 -43
  10. data/lib/active_record/associations/alias_tracker.rb +1 -3
  11. data/lib/active_record/associations/association.rb +8 -16
  12. data/lib/active_record/associations/association_scope.rb +5 -16
  13. data/lib/active_record/associations/belongs_to_association.rb +34 -25
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  15. data/lib/active_record/associations/builder/association.rb +78 -54
  16. data/lib/active_record/associations/builder/belongs_to.rb +91 -58
  17. data/lib/active_record/associations/builder/collection_association.rb +47 -45
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -25
  19. data/lib/active_record/associations/builder/has_many.rb +2 -2
  20. data/lib/active_record/associations/builder/has_one.rb +5 -7
  21. data/lib/active_record/associations/builder/singular_association.rb +6 -7
  22. data/lib/active_record/associations/collection_association.rb +68 -105
  23. data/lib/active_record/associations/collection_proxy.rb +12 -15
  24. data/lib/active_record/associations/has_many_association.rb +11 -9
  25. data/lib/active_record/associations/has_many_through_association.rb +16 -12
  26. data/lib/active_record/associations/has_one_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +204 -165
  28. data/lib/active_record/associations/join_dependency/join_association.rb +43 -101
  29. data/lib/active_record/associations/join_dependency/join_base.rb +6 -8
  30. data/lib/active_record/associations/join_dependency/join_part.rb +18 -37
  31. data/lib/active_record/associations/join_helper.rb +2 -11
  32. data/lib/active_record/associations/preloader.rb +89 -34
  33. data/lib/active_record/associations/preloader/association.rb +43 -25
  34. data/lib/active_record/associations/preloader/collection_association.rb +2 -2
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +58 -26
  38. data/lib/active_record/associations/singular_association.rb +6 -5
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +5 -2
  41. data/lib/active_record/attribute_methods.rb +45 -40
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
  43. data/lib/active_record/attribute_methods/dirty.rb +8 -22
  44. data/lib/active_record/attribute_methods/primary_key.rb +1 -7
  45. data/lib/active_record/attribute_methods/read.rb +55 -28
  46. data/lib/active_record/attribute_methods/serialization.rb +12 -33
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -13
  48. data/lib/active_record/attribute_methods/write.rb +37 -12
  49. data/lib/active_record/autosave_association.rb +207 -207
  50. data/lib/active_record/base.rb +5 -1
  51. data/lib/active_record/callbacks.rb +2 -2
  52. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +2 -7
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +11 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -14
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -5
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +84 -0
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -8
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +52 -83
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +0 -5
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +14 -97
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +58 -60
  63. data/lib/active_record/connection_adapters/column.rb +1 -35
  64. data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +3 -4
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +16 -15
  67. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +24 -18
  68. data/lib/active_record/connection_adapters/postgresql/cast.rb +20 -16
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +23 -43
  70. data/lib/active_record/connection_adapters/postgresql/oid.rb +19 -12
  71. data/lib/active_record/connection_adapters/postgresql/quoting.rb +28 -23
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +8 -30
  73. data/lib/active_record/connection_adapters/postgresql_adapter.rb +92 -75
  74. data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
  75. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +31 -64
  76. data/lib/active_record/connection_handling.rb +2 -2
  77. data/lib/active_record/core.rb +22 -43
  78. data/lib/active_record/counter_cache.rb +7 -7
  79. data/lib/active_record/enum.rb +100 -0
  80. data/lib/active_record/errors.rb +10 -5
  81. data/lib/active_record/fixture_set/file.rb +2 -1
  82. data/lib/active_record/fixtures.rb +171 -74
  83. data/lib/active_record/inheritance.rb +16 -22
  84. data/lib/active_record/integration.rb +52 -1
  85. data/lib/active_record/locking/optimistic.rb +7 -2
  86. data/lib/active_record/locking/pessimistic.rb +1 -1
  87. data/lib/active_record/log_subscriber.rb +5 -12
  88. data/lib/active_record/migration.rb +62 -46
  89. data/lib/active_record/migration/command_recorder.rb +7 -13
  90. data/lib/active_record/model_schema.rb +7 -14
  91. data/lib/active_record/nested_attributes.rb +10 -8
  92. data/lib/active_record/no_touching.rb +52 -0
  93. data/lib/active_record/null_relation.rb +3 -3
  94. data/lib/active_record/persistence.rb +16 -34
  95. data/lib/active_record/querying.rb +14 -12
  96. data/lib/active_record/railtie.rb +0 -50
  97. data/lib/active_record/railties/databases.rake +12 -15
  98. data/lib/active_record/readonly_attributes.rb +0 -6
  99. data/lib/active_record/reflection.rb +189 -75
  100. data/lib/active_record/relation.rb +69 -94
  101. data/lib/active_record/relation/batches.rb +57 -23
  102. data/lib/active_record/relation/calculations.rb +36 -43
  103. data/lib/active_record/relation/delegation.rb +54 -39
  104. data/lib/active_record/relation/finder_methods.rb +107 -62
  105. data/lib/active_record/relation/merger.rb +7 -20
  106. data/lib/active_record/relation/predicate_builder.rb +57 -38
  107. data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
  108. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  109. data/lib/active_record/relation/query_methods.rb +110 -98
  110. data/lib/active_record/relation/spawn_methods.rb +1 -2
  111. data/lib/active_record/result.rb +45 -6
  112. data/lib/active_record/runtime_registry.rb +5 -0
  113. data/lib/active_record/sanitization.rb +6 -8
  114. data/lib/active_record/schema_dumper.rb +16 -5
  115. data/lib/active_record/schema_migration.rb +24 -25
  116. data/lib/active_record/scoping/default.rb +5 -18
  117. data/lib/active_record/scoping/named.rb +8 -29
  118. data/lib/active_record/store.rb +56 -28
  119. data/lib/active_record/tasks/database_tasks.rb +8 -4
  120. data/lib/active_record/timestamp.rb +4 -4
  121. data/lib/active_record/transactions.rb +8 -10
  122. data/lib/active_record/validations/presence.rb +1 -1
  123. data/lib/active_record/validations/uniqueness.rb +1 -6
  124. data/lib/active_record/version.rb +1 -1
  125. data/lib/rails/generators/active_record.rb +2 -8
  126. data/lib/rails/generators/active_record/migration.rb +18 -0
  127. data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
  128. data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
  129. metadata +32 -45
  130. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
  131. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  132. data/lib/active_record/tasks/firebird_database_tasks.rb +0 -56
  133. data/lib/active_record/tasks/oracle_database_tasks.rb +0 -45
  134. data/lib/active_record/tasks/sqlserver_database_tasks.rb +0 -48
  135. data/lib/active_record/test_case.rb +0 -102
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
 
8
8
  MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
9
9
  :order, :joins, :where, :having, :bind, :references,
10
- :extending]
10
+ :extending, :unscope]
11
11
 
12
12
  SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering,
13
13
  :reverse_order, :distinct, :create_with, :uniq]
@@ -17,18 +17,14 @@ module ActiveRecord
17
17
  include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
18
18
 
19
19
  attr_reader :table, :klass, :loaded
20
- attr_accessor :default_scoped
21
20
  alias :model :klass
22
21
  alias :loaded? :loaded
23
- alias :default_scoped? :default_scoped
24
22
 
25
23
  def initialize(klass, table, values = {})
26
- @klass = klass
27
- @table = table
28
- @values = values
29
- @implicit_readonly = nil
30
- @loaded = false
31
- @default_scoped = false
24
+ @klass = klass
25
+ @table = table
26
+ @values = values
27
+ @loaded = false
32
28
  end
33
29
 
34
30
  def initialize_copy(other)
@@ -39,7 +35,7 @@ module ActiveRecord
39
35
  reset
40
36
  end
41
37
 
42
- def insert(values)
38
+ def insert(values) # :nodoc:
43
39
  primary_key_value = nil
44
40
 
45
41
  if primary_key && Hash === values
@@ -56,16 +52,7 @@ module ActiveRecord
56
52
  im = arel.create_insert
57
53
  im.into @table
58
54
 
59
- conn = @klass.connection
60
-
61
- substitutes = values.sort_by { |arel_attr,_| arel_attr.name }
62
- binds = substitutes.map do |arel_attr, value|
63
- [@klass.columns_hash[arel_attr.name], value]
64
- end
65
-
66
- substitutes.each_with_index do |tuple, i|
67
- tuple[1] = conn.substitute_at(binds[i][0], i)
68
- end
55
+ substitutes, binds = substitute_values values
69
56
 
70
57
  if values.empty? # empty insert
71
58
  im.values = Arel.sql(connection.empty_insert_statement_value)
@@ -73,7 +60,7 @@ module ActiveRecord
73
60
  im.insert substitutes
74
61
  end
75
62
 
76
- conn.insert(
63
+ @klass.connection.insert(
77
64
  im,
78
65
  'SQL',
79
66
  primary_key,
@@ -82,6 +69,29 @@ module ActiveRecord
82
69
  binds)
83
70
  end
84
71
 
72
+ def update_record(values, id, id_was) # :nodoc:
73
+ substitutes, binds = substitute_values values
74
+ um = @klass.unscoped.where(@klass.arel_table[@klass.primary_key].eq(id_was || id)).arel.compile_update(substitutes, @klass.primary_key)
75
+
76
+ @klass.connection.update(
77
+ um,
78
+ 'SQL',
79
+ binds)
80
+ end
81
+
82
+ def substitute_values(values) # :nodoc:
83
+ substitutes = values.sort_by { |arel_attr,_| arel_attr.name }
84
+ binds = substitutes.map do |arel_attr, value|
85
+ [@klass.columns_hash[arel_attr.name], value]
86
+ end
87
+
88
+ substitutes.each_with_index do |tuple, i|
89
+ tuple[1] = @klass.connection.substitute_at(binds[i][0], i)
90
+ end
91
+
92
+ [substitutes, binds]
93
+ end
94
+
85
95
  # Initializes new record from relation while maintaining the current
86
96
  # scope.
87
97
  #
@@ -234,8 +244,13 @@ module ActiveRecord
234
244
  def empty?
235
245
  return @records.empty? if loaded?
236
246
 
237
- c = count
238
- c.respond_to?(:zero?) ? c.zero? : c.empty?
247
+ if limit_value == 0
248
+ true
249
+ else
250
+ # FIXME: This count is not compatible with #select('authors.*') or other select narrows
251
+ c = count
252
+ c.respond_to?(:zero?) ? c.zero? : c.empty?
253
+ end
239
254
  end
240
255
 
241
256
  # Returns true if there are any records.
@@ -300,7 +315,7 @@ module ActiveRecord
300
315
  stmt.table(table)
301
316
  stmt.key = table[primary_key]
302
317
 
303
- if with_default_scope.joins_values.any?
318
+ if joins_values.any?
304
319
  @klass.connection.join_to_update(stmt, arel)
305
320
  else
306
321
  stmt.take(arel.limit)
@@ -425,7 +440,7 @@ module ActiveRecord
425
440
  stmt = Arel::DeleteManager.new(arel.engine)
426
441
  stmt.from(table)
427
442
 
428
- if with_default_scope.joins_values.any?
443
+ if joins_values.any?
429
444
  @klass.connection.join_to_delete(stmt, arel, table[primary_key])
430
445
  else
431
446
  stmt.wheres = arel.constraints
@@ -491,19 +506,34 @@ module ActiveRecord
491
506
  # User.where(name: 'Oscar').to_sql
492
507
  # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
493
508
  def to_sql
494
- @to_sql ||= klass.connection.to_sql(arel, bind_values.dup)
509
+ @to_sql ||= begin
510
+ relation = self
511
+ connection = klass.connection
512
+ visitor = connection.visitor
513
+
514
+ if eager_loading?
515
+ find_with_associations { |rel| relation = rel }
516
+ end
517
+
518
+ ast = relation.arel.ast
519
+ binds = relation.bind_values.dup
520
+ visitor.accept(ast) do
521
+ connection.quote(*binds.shift.reverse)
522
+ end
523
+ end
495
524
  end
496
525
 
497
526
  # Returns a hash of where conditions.
498
527
  #
499
528
  # User.where(name: 'Oscar').where_values_hash
500
529
  # # => {name: "Oscar"}
501
- def where_values_hash(relation_table_name = table_name)
502
- equalities = with_default_scope.where_values.grep(Arel::Nodes::Equality).find_all { |node|
503
- node.left.relation.name == relation_table_name
530
+ def where_values_hash
531
+ equalities = where_values.grep(Arel::Nodes::Equality).find_all { |node|
532
+ node.left.relation.name == table_name
504
533
  }
505
534
 
506
535
  binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
536
+ binds.merge!(Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }])
507
537
 
508
538
  Hash[equalities.map { |where|
509
539
  name = where.left.name
@@ -539,8 +569,6 @@ module ActiveRecord
539
569
  # Compares two relations for equality.
540
570
  def ==(other)
541
571
  case other
542
- when Associations::CollectionProxy, AssociationRelation
543
- self == other.to_a
544
572
  when Relation
545
573
  other.to_sql == to_sql
546
574
  when Array
@@ -552,16 +580,6 @@ module ActiveRecord
552
580
  q.pp(self.to_a)
553
581
  end
554
582
 
555
- def with_default_scope #:nodoc:
556
- if default_scoped? && default_scope = klass.send(:build_default_scope)
557
- default_scope = default_scope.merge(self)
558
- default_scope.default_scoped = false
559
- default_scope
560
- else
561
- self
562
- end
563
- end
564
-
565
583
  # Returns true if relation is blank.
566
584
  def blank?
567
585
  to_a.blank?
@@ -581,34 +599,24 @@ module ActiveRecord
581
599
  private
582
600
 
583
601
  def exec_queries
584
- default_scoped = with_default_scope
585
-
586
- if default_scoped.equal?(self)
587
- @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, bind_values)
588
-
589
- preload = preload_values
590
- preload += includes_values unless eager_loading?
591
- preload.each do |associations|
592
- ActiveRecord::Associations::Preloader.new(@records, associations).run
593
- end
602
+ @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, bind_values)
594
603
 
595
- # @readonly_value is true only if set explicitly. @implicit_readonly is true if there
596
- # are JOINS and no explicit SELECT.
597
- readonly = readonly_value.nil? ? @implicit_readonly : readonly_value
598
- @records.each { |record| record.readonly! } if readonly
599
- else
600
- @records = default_scoped.to_a
604
+ preload = preload_values
605
+ preload += includes_values unless eager_loading?
606
+ preloader = ActiveRecord::Associations::Preloader.new
607
+ preload.each do |associations|
608
+ preloader.preload @records, associations
601
609
  end
602
610
 
611
+ @records.each { |record| record.readonly! } if readonly_value
612
+
603
613
  @loaded = true
604
614
  @records
605
615
  end
606
616
 
607
617
  def references_eager_loaded_tables?
608
618
  joined_tables = arel.join_sources.map do |join|
609
- if join.is_a?(Arel::Nodes::StringJoin)
610
- tables_in_string(join.left)
611
- else
619
+ unless join.is_a?(Arel::Nodes::StringJoin)
612
620
  [join.left.table_name, join.left.table_alias]
613
621
  end
614
622
  end
@@ -617,41 +625,8 @@ module ActiveRecord
617
625
 
618
626
  # always convert table names to downcase as in Oracle quoted table names are in uppercase
619
627
  joined_tables = joined_tables.flatten.compact.map { |t| t.downcase }.uniq
620
- string_tables = tables_in_string(to_sql)
621
628
 
622
- if (references_values - joined_tables).any?
623
- true
624
- elsif !ActiveRecord::Base.disable_implicit_join_references &&
625
- (string_tables - joined_tables).any?
626
- ActiveSupport::Deprecation.warn(
627
- "It looks like you are eager loading table(s) (one of: #{string_tables.join(', ')}) " \
628
- "that are referenced in a string SQL snippet. For example: \n" \
629
- "\n" \
630
- " Post.includes(:comments).where(\"comments.title = 'foo'\")\n" \
631
- "\n" \
632
- "Currently, Active Record recognizes the table in the string, and knows to JOIN the " \
633
- "comments table to the query, rather than loading comments in a separate query. " \
634
- "However, doing this without writing a full-blown SQL parser is inherently flawed. " \
635
- "Since we don't want to write an SQL parser, we are removing this functionality. " \
636
- "From now on, you must explicitly tell Active Record when you are referencing a table " \
637
- "from a string:\n" \
638
- "\n" \
639
- " Post.includes(:comments).where(\"comments.title = 'foo'\").references(:comments)\n" \
640
- "\n" \
641
- "If you don't rely on implicit join references you can disable the feature entirely " \
642
- "by setting `config.active_record.disable_implicit_join_references = true`."
643
- )
644
- true
645
- else
646
- false
647
- end
648
- end
649
-
650
- def tables_in_string(string)
651
- return [] if string.blank?
652
- # always convert table names to downcase as in Oracle quoted table names are in uppercase
653
- # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
654
- string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
629
+ (references_values - joined_tables).any?
655
630
  end
656
631
  end
657
632
  end
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  # The #find_each method uses #find_in_batches with a batch size of 1000 (or as
12
12
  # specified by the +:batch_size+ option).
13
13
  #
14
- # Person.all.find_each do |person|
14
+ # Person.find_each do |person|
15
15
  # person.do_awesome_stuff
16
16
  # end
17
17
  #
@@ -19,40 +19,71 @@ module ActiveRecord
19
19
  # person.party_all_night!
20
20
  # end
21
21
  #
22
- # You can also pass the +:start+ option to specify
23
- # an offset to control the starting point.
24
- def find_each(options = {})
25
- find_in_batches(options) do |records|
26
- records.each { |record| yield record }
27
- end
28
- end
29
-
30
- # Yields each batch of records that was found by the find +options+ as
31
- # an array. The size of each batch is set by the +:batch_size+
32
- # option; the default is 1000.
22
+ # If you do not provide a block to #find_each, it will return an Enumerator
23
+ # for chaining with other methods:
33
24
  #
34
- # You can control the starting point for the batch processing by
35
- # supplying the +:start+ option. This is especially useful if you
36
- # want multiple workers dealing with the same processing queue. You can
37
- # make worker 1 handle all the records between id 0 and 10,000 and
38
- # worker 2 handle from 10,000 and beyond (by setting the +:start+
39
- # option on that worker).
25
+ # Person.find_each.with_index do |person, index|
26
+ # person.award_trophy(index + 1)
27
+ # end
40
28
  #
41
- # It's not possible to set the order. That is automatically set to
29
+ # ==== Options
30
+ # * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
31
+ # * <tt>:start</tt> - Specifies the starting point for the batch processing.
32
+ # This is especially useful if you want multiple workers dealing with
33
+ # the same processing queue. You can make worker 1 handle all the records
34
+ # between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
35
+ # (by setting the +:start+ option on that worker).
36
+ #
37
+ # # Let's process for a batch of 2000 records, skipping the first 2000 rows
38
+ # Person.find_each(start: 2000, batch_size: 2000) do |person|
39
+ # person.party_all_night!
40
+ # end
41
+ #
42
+ # NOTE: It's not possible to set the order. That is automatically set to
42
43
  # ascending on the primary key ("id ASC") to make the batch ordering
43
44
  # work. This also means that this method only works with integer-based
44
- # primary keys. You can't set the limit either, that's used to control
45
+ # primary keys.
46
+ #
47
+ # NOTE: You can't set the limit either, that's used to control
45
48
  # the batch sizes.
49
+ def find_each(options = {})
50
+ if block_given?
51
+ find_in_batches(options) do |records|
52
+ records.each { |record| yield record }
53
+ end
54
+ else
55
+ enum_for :find_each, options
56
+ end
57
+ end
58
+
59
+ # Yields each batch of records that was found by the find +options+ as
60
+ # an array.
46
61
  #
47
62
  # Person.where("age > 21").find_in_batches do |group|
48
63
  # sleep(50) # Make sure it doesn't get too crowded in there!
49
64
  # group.each { |person| person.party_all_night! }
50
65
  # end
51
66
  #
67
+ # ==== Options
68
+ # * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
69
+ # * <tt>:start</tt> - Specifies the starting point for the batch processing.
70
+ # This is especially useful if you want multiple workers dealing with
71
+ # the same processing queue. You can make worker 1 handle all the records
72
+ # between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
73
+ # (by setting the +:start+ option on that worker).
74
+ #
52
75
  # # Let's process the next 2000 records
53
- # Person.all.find_in_batches(start: 2000, batch_size: 2000) do |group|
76
+ # Person.find_in_batches(start: 2000, batch_size: 2000) do |group|
54
77
  # group.each { |person| person.party_all_night! }
55
78
  # end
79
+ #
80
+ # NOTE: It's not possible to set the order. That is automatically set to
81
+ # ascending on the primary key ("id ASC") to make the batch ordering
82
+ # work. This also means that this method only works with integer-based
83
+ # primary keys.
84
+ #
85
+ # NOTE: You can't set the limit either, that's used to control
86
+ # the batch sizes.
56
87
  def find_in_batches(options = {})
57
88
  options.assert_valid_keys(:start, :batch_size)
58
89
 
@@ -71,13 +102,16 @@ module ActiveRecord
71
102
  while records.any?
72
103
  records_size = records.size
73
104
  primary_key_offset = records.last.id
74
- raise "Primary key not included in the custom select clause" unless primary_key_offset
75
105
 
76
106
  yield records
77
107
 
78
108
  break if records_size < batch_size
79
109
 
80
- records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
110
+ if primary_key_offset
111
+ records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
112
+ else
113
+ raise "Primary key not included in the custom select clause"
114
+ end
81
115
  end
82
116
  end
83
117
 
@@ -20,6 +20,8 @@ module ActiveRecord
20
20
  # Person.group(:city).count
21
21
  # # => { 'Rome' => 5, 'Paris' => 3 }
22
22
  def count(column_name = nil, options = {})
23
+ # TODO: Remove options argument as soon we remove support to
24
+ # activerecord-deprecated_finders.
23
25
  column_name, options = nil, column_name if column_name.is_a?(Hash)
24
26
  calculate(:count, column_name, options)
25
27
  end
@@ -27,8 +29,10 @@ module ActiveRecord
27
29
  # Calculates the average value on a given column. Returns +nil+ if there's
28
30
  # no row. See +calculate+ for examples with options.
29
31
  #
30
- # Person.average('age') # => 35.8
32
+ # Person.average(:age) # => 35.8
31
33
  def average(column_name, options = {})
34
+ # TODO: Remove options argument as soon we remove support to
35
+ # activerecord-deprecated_finders.
32
36
  calculate(:average, column_name, options)
33
37
  end
34
38
 
@@ -36,8 +40,10 @@ module ActiveRecord
36
40
  # with the same data type of the column, or +nil+ if there's no row. See
37
41
  # +calculate+ for examples with options.
38
42
  #
39
- # Person.minimum('age') # => 7
43
+ # Person.minimum(:age) # => 7
40
44
  def minimum(column_name, options = {})
45
+ # TODO: Remove options argument as soon we remove support to
46
+ # activerecord-deprecated_finders.
41
47
  calculate(:minimum, column_name, options)
42
48
  end
43
49
 
@@ -45,8 +51,10 @@ module ActiveRecord
45
51
  # with the same data type of the column, or +nil+ if there's no row. See
46
52
  # +calculate+ for examples with options.
47
53
  #
48
- # Person.maximum('age') # => 93
54
+ # Person.maximum(:age) # => 93
49
55
  def maximum(column_name, options = {})
56
+ # TODO: Remove options argument as soon we remove support to
57
+ # activerecord-deprecated_finders.
50
58
  calculate(:maximum, column_name, options)
51
59
  end
52
60
 
@@ -54,17 +62,9 @@ module ActiveRecord
54
62
  # with the same data type of the column, 0 if there's no row. See
55
63
  # +calculate+ for examples with options.
56
64
  #
57
- # Person.sum('age') # => 4562
65
+ # Person.sum(:age) # => 4562
58
66
  def sum(*args)
59
- if block_given?
60
- ActiveSupport::Deprecation.warn(
61
- "Calling #sum with a block is deprecated and will be removed in Rails 4.1. " \
62
- "If you want to perform sum calculation over the array of elements, use `to_a.sum(&block)`."
63
- )
64
- self.to_a.sum(*args) {|*block_args| yield(*block_args)}
65
- else
66
- calculate(:sum, *args)
67
- end
67
+ calculate(:sum, *args)
68
68
  end
69
69
 
70
70
  # This calculates aggregate values in the given column. Methods for count, sum, average,
@@ -99,19 +99,17 @@ module ActiveRecord
99
99
  #
100
100
  # Person.sum("2 * age")
101
101
  def calculate(operation, column_name, options = {})
102
- relation = with_default_scope
102
+ # TODO: Remove options argument as soon we remove support to
103
+ # activerecord-deprecated_finders.
104
+ if column_name.is_a?(Symbol) && attribute_alias?(column_name)
105
+ column_name = attribute_alias(column_name)
106
+ end
103
107
 
104
- if relation.equal?(self)
105
- if has_include?(column_name)
106
- construct_relation_for_association_calculations.calculate(operation, column_name, options)
107
- else
108
- perform_calculation(operation, column_name, options)
109
- end
108
+ if has_include?(column_name)
109
+ construct_relation_for_association_calculations.calculate(operation, column_name, options)
110
110
  else
111
- relation.calculate(operation, column_name, options)
111
+ perform_calculation(operation, column_name, options)
112
112
  end
113
- rescue ThrowResult
114
- 0
115
113
  end
116
114
 
117
115
  # Use <tt>pluck</tt> as a shortcut to select one or more attributes without
@@ -149,10 +147,10 @@ module ActiveRecord
149
147
  #
150
148
  def pluck(*column_names)
151
149
  column_names.map! do |column_name|
152
- if column_name.is_a?(Symbol) && self.column_names.include?(column_name.to_s)
153
- "#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}"
150
+ if column_name.is_a?(Symbol) && attribute_alias?(column_name)
151
+ attribute_alias(column_name)
154
152
  else
155
- column_name
153
+ column_name.to_s
156
154
  end
157
155
  end
158
156
 
@@ -160,22 +158,20 @@ module ActiveRecord
160
158
  construct_relation_for_association_calculations.pluck(*column_names)
161
159
  else
162
160
  relation = spawn
163
- relation.select_values = column_names
161
+ relation.select_values = column_names.map { |cn|
162
+ columns_hash.key?(cn) ? arel_table[cn] : cn
163
+ }
164
164
  result = klass.connection.select_all(relation.arel, nil, bind_values)
165
165
  columns = result.columns.map do |key|
166
166
  klass.column_types.fetch(key) {
167
- result.column_types.fetch(key) {
168
- Class.new { def type_cast(v); v; end }.new
169
- }
167
+ result.column_types.fetch(key) { result.identity_type }
170
168
  }
171
169
  end
172
170
 
173
171
  result = result.map do |attributes|
174
172
  values = klass.initialize_attributes(attributes).values
175
173
 
176
- columns.zip(values).map do |column, value|
177
- column.type_cast(value)
178
- end
174
+ columns.zip(values).map { |column, value| column.type_cast value }
179
175
  end
180
176
  columns.one? ? result.map!(&:first) : result
181
177
  end
@@ -196,26 +192,22 @@ module ActiveRecord
196
192
  end
197
193
 
198
194
  def perform_calculation(operation, column_name, options = {})
195
+ # TODO: Remove options argument as soon we remove support to
196
+ # activerecord-deprecated_finders.
199
197
  operation = operation.to_s.downcase
200
198
 
201
199
  # If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count)
202
200
  distinct = self.distinct_value
203
- if options.has_key?(:distinct)
204
- ActiveSupport::Deprecation.warn "The :distinct option for `Relation#count` is deprecated. " \
205
- "Please use `Relation#distinct` instead. (eg. `relation.distinct.count`)"
206
- distinct = options[:distinct]
207
- end
208
201
 
209
202
  if operation == "count"
210
- column_name ||= (select_for_count || :all)
203
+ column_name ||= select_for_count
211
204
 
212
205
  unless arel.ast.grep(Arel::Nodes::OuterJoin).empty?
213
206
  distinct = true
214
207
  end
215
208
 
216
209
  column_name = primary_key if column_name == :all && distinct
217
-
218
- distinct = nil if column_name =~ /\s*DISTINCT\s+/i
210
+ distinct = nil if column_name =~ /\s*DISTINCT[\s(]+/i
219
211
  end
220
212
 
221
213
  if group_values.any?
@@ -381,8 +373,9 @@ module ActiveRecord
381
373
  # TODO: refactor to allow non-string `select_values` (eg. Arel nodes).
382
374
  def select_for_count
383
375
  if select_values.present?
384
- select = select_values.join(", ")
385
- select if select !~ /[,*]/
376
+ select_values.join(", ")
377
+ else
378
+ :all
386
379
  end
387
380
  end
388
381