activerecord 4.1.16 → 4.2.11.3

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 (185) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1162 -1801
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +15 -8
  5. data/lib/active_record/association_relation.rb +13 -0
  6. data/lib/active_record/associations/alias_tracker.rb +3 -12
  7. data/lib/active_record/associations/association.rb +16 -4
  8. data/lib/active_record/associations/association_scope.rb +83 -38
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -10
  10. data/lib/active_record/associations/builder/association.rb +15 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/collection_association.rb +5 -1
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
  14. data/lib/active_record/associations/builder/has_many.rb +1 -1
  15. data/lib/active_record/associations/builder/has_one.rb +2 -2
  16. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  17. data/lib/active_record/associations/collection_association.rb +63 -27
  18. data/lib/active_record/associations/collection_proxy.rb +29 -35
  19. data/lib/active_record/associations/foreign_association.rb +11 -0
  20. data/lib/active_record/associations/has_many_association.rb +83 -22
  21. data/lib/active_record/associations/has_many_through_association.rb +49 -26
  22. data/lib/active_record/associations/has_one_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
  24. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  25. data/lib/active_record/associations/join_dependency.rb +26 -13
  26. data/lib/active_record/associations/preloader/association.rb +14 -11
  27. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  28. data/lib/active_record/associations/preloader.rb +36 -26
  29. data/lib/active_record/associations/singular_association.rb +17 -2
  30. data/lib/active_record/associations/through_association.rb +5 -12
  31. data/lib/active_record/associations.rb +158 -49
  32. data/lib/active_record/attribute.rb +163 -0
  33. data/lib/active_record/attribute_assignment.rb +19 -11
  34. data/lib/active_record/attribute_decorators.rb +66 -0
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  36. data/lib/active_record/attribute_methods/dirty.rb +107 -43
  37. data/lib/active_record/attribute_methods/primary_key.rb +7 -8
  38. data/lib/active_record/attribute_methods/query.rb +1 -1
  39. data/lib/active_record/attribute_methods/read.rb +22 -59
  40. data/lib/active_record/attribute_methods/serialization.rb +16 -150
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
  42. data/lib/active_record/attribute_methods/write.rb +9 -24
  43. data/lib/active_record/attribute_methods.rb +56 -94
  44. data/lib/active_record/attribute_set/builder.rb +106 -0
  45. data/lib/active_record/attribute_set.rb +81 -0
  46. data/lib/active_record/attributes.rb +147 -0
  47. data/lib/active_record/autosave_association.rb +19 -12
  48. data/lib/active_record/base.rb +13 -24
  49. data/lib/active_record/callbacks.rb +6 -6
  50. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
  62. data/lib/active_record/connection_adapters/column.rb +29 -240
  63. data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
  64. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  65. data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
  66. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  67. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  68. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
  69. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  97. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  98. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
  101. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  102. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
  103. data/lib/active_record/connection_handling.rb +1 -1
  104. data/lib/active_record/core.rb +163 -39
  105. data/lib/active_record/counter_cache.rb +60 -6
  106. data/lib/active_record/enum.rb +9 -11
  107. data/lib/active_record/errors.rb +53 -30
  108. data/lib/active_record/explain.rb +1 -1
  109. data/lib/active_record/explain_subscriber.rb +1 -1
  110. data/lib/active_record/fixtures.rb +55 -69
  111. data/lib/active_record/gem_version.rb +4 -4
  112. data/lib/active_record/inheritance.rb +35 -10
  113. data/lib/active_record/integration.rb +4 -4
  114. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  115. data/lib/active_record/locking/optimistic.rb +46 -26
  116. data/lib/active_record/migration/command_recorder.rb +19 -2
  117. data/lib/active_record/migration/join_table.rb +1 -1
  118. data/lib/active_record/migration.rb +71 -46
  119. data/lib/active_record/model_schema.rb +52 -58
  120. data/lib/active_record/nested_attributes.rb +5 -5
  121. data/lib/active_record/no_touching.rb +1 -1
  122. data/lib/active_record/persistence.rb +46 -26
  123. data/lib/active_record/query_cache.rb +3 -3
  124. data/lib/active_record/querying.rb +10 -7
  125. data/lib/active_record/railtie.rb +18 -11
  126. data/lib/active_record/railties/databases.rake +50 -51
  127. data/lib/active_record/readonly_attributes.rb +0 -1
  128. data/lib/active_record/reflection.rb +273 -114
  129. data/lib/active_record/relation/batches.rb +0 -2
  130. data/lib/active_record/relation/calculations.rb +41 -37
  131. data/lib/active_record/relation/finder_methods.rb +70 -47
  132. data/lib/active_record/relation/merger.rb +39 -29
  133. data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
  134. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
  135. data/lib/active_record/relation/predicate_builder.rb +16 -8
  136. data/lib/active_record/relation/query_methods.rb +114 -65
  137. data/lib/active_record/relation/spawn_methods.rb +3 -0
  138. data/lib/active_record/relation.rb +57 -25
  139. data/lib/active_record/result.rb +18 -7
  140. data/lib/active_record/sanitization.rb +12 -2
  141. data/lib/active_record/schema.rb +0 -1
  142. data/lib/active_record/schema_dumper.rb +59 -28
  143. data/lib/active_record/schema_migration.rb +5 -4
  144. data/lib/active_record/scoping/default.rb +6 -4
  145. data/lib/active_record/scoping/named.rb +4 -0
  146. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  147. data/lib/active_record/statement_cache.rb +95 -10
  148. data/lib/active_record/store.rb +5 -5
  149. data/lib/active_record/tasks/database_tasks.rb +61 -6
  150. data/lib/active_record/tasks/mysql_database_tasks.rb +20 -11
  151. data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
  152. data/lib/active_record/timestamp.rb +9 -7
  153. data/lib/active_record/transactions.rb +53 -27
  154. data/lib/active_record/type/big_integer.rb +13 -0
  155. data/lib/active_record/type/binary.rb +50 -0
  156. data/lib/active_record/type/boolean.rb +31 -0
  157. data/lib/active_record/type/date.rb +50 -0
  158. data/lib/active_record/type/date_time.rb +54 -0
  159. data/lib/active_record/type/decimal.rb +64 -0
  160. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  161. data/lib/active_record/type/decorator.rb +14 -0
  162. data/lib/active_record/type/float.rb +19 -0
  163. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  164. data/lib/active_record/type/integer.rb +59 -0
  165. data/lib/active_record/type/mutable.rb +16 -0
  166. data/lib/active_record/type/numeric.rb +36 -0
  167. data/lib/active_record/type/serialized.rb +62 -0
  168. data/lib/active_record/type/string.rb +40 -0
  169. data/lib/active_record/type/text.rb +11 -0
  170. data/lib/active_record/type/time.rb +26 -0
  171. data/lib/active_record/type/time_value.rb +38 -0
  172. data/lib/active_record/type/type_map.rb +64 -0
  173. data/lib/active_record/type/unsigned_integer.rb +15 -0
  174. data/lib/active_record/type/value.rb +110 -0
  175. data/lib/active_record/type.rb +23 -0
  176. data/lib/active_record/validations/associated.rb +5 -3
  177. data/lib/active_record/validations/presence.rb +5 -3
  178. data/lib/active_record/validations/uniqueness.rb +25 -29
  179. data/lib/active_record/validations.rb +25 -19
  180. data/lib/active_record.rb +4 -0
  181. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  182. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  183. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  184. metadata +66 -11
  185. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -20,17 +20,31 @@ module ActiveRecord
20
20
  # Person.group(:city).count
21
21
  # # => { 'Rome' => 5, 'Paris' => 3 }
22
22
  #
23
+ # If +count+ is used with +group+ for multiple columns, it returns a Hash whose
24
+ # keys are an array containing the individual values of each column and the value
25
+ # of each key would be the +count+.
26
+ #
27
+ # Article.group(:status, :category).count
28
+ # # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
29
+ # ["published", "business"]=>0, ["published", "technology"]=>2}
30
+ #
23
31
  # If +count+ is used with +select+, it will count the selected columns:
24
32
  #
25
33
  # Person.select(:age).count
26
34
  # # => counts the number of different age values
27
35
  #
28
36
  # Note: not all valid +select+ expressions are valid +count+ expressions. The specifics differ
29
- # between databases. In invalid cases, an error from the databsae is thrown.
37
+ # between databases. In invalid cases, an error from the database is thrown.
30
38
  def count(column_name = nil, options = {})
39
+ if options.present? && !ActiveRecord.const_defined?(:DeprecatedFinders)
40
+ raise ArgumentError, "Relation#count does not support finder options anymore. " \
41
+ "Please build a scope and then call count on it or use the " \
42
+ "activerecord-deprecated_finders gem to enable this functionality."
43
+
44
+ end
45
+
31
46
  # TODO: Remove options argument as soon we remove support to
32
47
  # activerecord-deprecated_finders.
33
- column_name, options = nil, column_name if column_name.is_a?(Hash)
34
48
  calculate(:count, column_name, options)
35
49
  end
36
50
 
@@ -80,7 +94,7 @@ module ActiveRecord
80
94
  #
81
95
  # There are two basic forms of output:
82
96
  #
83
- # * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
97
+ # * Single aggregate value: The single value is type cast to Integer for COUNT, Float
84
98
  # for AVG, and the given column's type for everything else.
85
99
  #
86
100
  # * Grouped values: This returns an ordered hash of the values and groups them. It
@@ -169,22 +183,8 @@ module ActiveRecord
169
183
  relation.select_values = column_names.map { |cn|
170
184
  columns_hash.key?(cn) ? arel_table[cn] : cn
171
185
  }
172
- result = klass.connection.select_all(relation.arel, nil, bind_values)
173
- columns = result.columns.map do |key|
174
- klass.column_types.fetch(key) {
175
- result.column_types.fetch(key) { result.identity_type }
176
- }
177
- end
178
-
179
- result = result.rows.map do |values|
180
- values = result.columns.zip(values).map do |column_name, value|
181
- single_attr_hash = { column_name => value }
182
- klass.initialize_attributes(single_attr_hash).values.first
183
- end
184
-
185
- columns.zip(values).map { |column, value| column.type_cast value }
186
- end
187
- columns.one? ? result.map!(&:first) : result
186
+ result = klass.connection.select_all(relation.arel, nil, relation.arel.bind_values + bind_values)
187
+ result.cast_values(klass.column_types)
188
188
  end
189
189
  end
190
190
 
@@ -246,27 +246,32 @@ module ActiveRecord
246
246
 
247
247
  column_alias = column_name
248
248
 
249
+ bind_values = nil
250
+
249
251
  if operation == "count" && (relation.limit_value || relation.offset_value)
250
252
  # Shortcut when limit is zero.
251
253
  return 0 if relation.limit_value == 0
252
254
 
253
255
  query_builder = build_count_subquery(relation, column_name, distinct)
256
+ bind_values = query_builder.bind_values + relation.bind_values
254
257
  else
255
258
  column = aggregate_column(column_name)
256
259
 
257
260
  select_value = operation_over_aggregate_column(column, operation, distinct)
258
261
 
259
262
  column_alias = select_value.alias
263
+ column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
260
264
  relation.select_values = [select_value]
261
265
 
262
266
  query_builder = relation.arel
267
+ bind_values = query_builder.bind_values + relation.bind_values
263
268
  end
264
269
 
265
- result = @klass.connection.select_all(query_builder, nil, relation.bind_values)
270
+ result = @klass.connection.select_all(query_builder, nil, bind_values)
266
271
  row = result.first
267
272
  value = row && row.values.first
268
273
  column = result.column_types.fetch(column_alias) do
269
- column_for(column_name)
274
+ type_for(column_name)
270
275
  end
271
276
 
272
277
  type_cast_calculated_value(value, column, operation)
@@ -276,12 +281,13 @@ module ActiveRecord
276
281
  group_attrs = group_values
277
282
 
278
283
  if group_attrs.first.respond_to?(:to_sym)
279
- association = @klass._reflect_on_association(group_attrs.first.to_sym)
280
- associated = group_attrs.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
284
+ association = @klass._reflect_on_association(group_attrs.first)
285
+ associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations
281
286
  group_fields = Array(associated ? association.foreign_key : group_attrs)
282
287
  else
283
288
  group_fields = group_attrs
284
289
  end
290
+ group_fields = arel_columns(group_fields)
285
291
 
286
292
  group_aliases = group_fields.map { |field|
287
293
  column_alias_for(field)
@@ -304,7 +310,7 @@ module ActiveRecord
304
310
  operation,
305
311
  distinct).as(aggregate_alias)
306
312
  ]
307
- select_values += select_values unless having_values.empty?
313
+ select_values += self.select_values unless having_values.empty?
308
314
 
309
315
  select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
310
316
  if field.respond_to?(:as)
@@ -318,7 +324,7 @@ module ActiveRecord
318
324
  relation.group_values = group
319
325
  relation.select_values = select_values
320
326
 
321
- calculated_data = @klass.connection.select_all(relation, nil, bind_values)
327
+ calculated_data = @klass.connection.select_all(relation, nil, relation.arel.bind_values + bind_values)
322
328
 
323
329
  if association
324
330
  key_ids = calculated_data.collect { |row| row[group_aliases.first] }
@@ -329,14 +335,14 @@ module ActiveRecord
329
335
  Hash[calculated_data.map do |row|
330
336
  key = group_columns.map { |aliaz, col_name|
331
337
  column = calculated_data.column_types.fetch(aliaz) do
332
- column_for(col_name)
338
+ type_for(col_name)
333
339
  end
334
340
  type_cast_calculated_value(row[aliaz], column)
335
341
  }
336
342
  key = key.first if key.size == 1
337
343
  key = key_records[key] if associated
338
344
 
339
- column_type = calculated_data.column_types.fetch(aggregate_alias) { column_for(column_name) }
345
+ column_type = calculated_data.column_types.fetch(aggregate_alias) { type_for(column_name) }
340
346
  [key, type_cast_calculated_value(row[aggregate_alias], column_type, operation)]
341
347
  end]
342
348
  end
@@ -363,24 +369,20 @@ module ActiveRecord
363
369
  @klass.connection.table_alias_for(table_name)
364
370
  end
365
371
 
366
- def column_for(field)
372
+ def type_for(field)
367
373
  field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
368
- @klass.columns_hash[field_name]
374
+ @klass.type_for_attribute(field_name)
369
375
  end
370
376
 
371
- def type_cast_calculated_value(value, column, operation = nil)
377
+ def type_cast_calculated_value(value, type, operation = nil)
372
378
  case operation
373
379
  when 'count' then value.to_i
374
- when 'sum' then type_cast_using_column(value || 0, column)
380
+ when 'sum' then type.type_cast_from_database(value || 0)
375
381
  when 'average' then value.respond_to?(:to_d) ? value.to_d : value
376
- else type_cast_using_column(value, column)
382
+ else type.type_cast_from_database(value)
377
383
  end
378
384
  end
379
385
 
380
- def type_cast_using_column(value, column)
381
- column ? column.type_cast(value) : value
382
- end
383
-
384
386
  # TODO: refactor to allow non-string `select_values` (eg. Arel nodes).
385
387
  def select_for_count
386
388
  if select_values.present?
@@ -396,9 +398,11 @@ module ActiveRecord
396
398
 
397
399
  aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias)
398
400
  relation.select_values = [aliased_column]
399
- subquery = relation.arel.as(subquery_alias)
401
+ arel = relation.arel
402
+ subquery = arel.as(subquery_alias)
400
403
 
401
404
  sm = Arel::SelectManager.new relation.engine
405
+ sm.bind_values = arel.bind_values
402
406
  select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
403
407
  sm.project(select_value).from(subquery)
404
408
  end
@@ -1,3 +1,6 @@
1
+ require 'active_support/deprecation'
2
+ require 'active_support/core_ext/string/filters'
3
+
1
4
  module ActiveRecord
2
5
  module FinderMethods
3
6
  ONE_AS_ONE = '1 AS one'
@@ -79,12 +82,16 @@ module ActiveRecord
79
82
  # Post.find_by "published_at < ?", 2.weeks.ago
80
83
  def find_by(*args)
81
84
  where(*args).take
85
+ rescue RangeError
86
+ nil
82
87
  end
83
88
 
84
89
  # Like <tt>find_by</tt>, except that if no record is found, raises
85
90
  # an <tt>ActiveRecord::RecordNotFound</tt> error.
86
91
  def find_by!(*args)
87
92
  where(*args).take!
93
+ rescue RangeError
94
+ raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range value"
88
95
  end
89
96
 
90
97
  # Gives a record (or N records if a parameter is supplied) without any implied
@@ -101,42 +108,30 @@ module ActiveRecord
101
108
  # Same as +take+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
102
109
  # is found. Note that <tt>take!</tt> accepts no arguments.
103
110
  def take!
104
- take or raise RecordNotFound
111
+ take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
105
112
  end
106
113
 
107
114
  # Find the first record (or first N records if a parameter is supplied).
108
115
  # If no order is defined it will order by primary key.
109
116
  #
110
- # Person.first # returns the first object fetched by SELECT * FROM people
117
+ # Person.first # returns the first object fetched by SELECT * FROM people ORDER BY people.id LIMIT 1
111
118
  # Person.where(["user_name = ?", user_name]).first
112
119
  # Person.where(["user_name = :u", { u: user_name }]).first
113
120
  # Person.order("created_on DESC").offset(5).first
114
- # Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3
115
- #
116
- # ==== Rails 3
117
- #
118
- # Person.first # SELECT "people".* FROM "people" LIMIT 1
119
- #
120
- # NOTE: Rails 3 may not order this query by the primary key and the order
121
- # will depend on the database implementation. In order to ensure that behavior,
122
- # use <tt>User.order(:id).first</tt> instead.
123
- #
124
- # ==== Rails 4
125
- #
126
- # Person.first # SELECT "people".* FROM "people" ORDER BY "people"."id" ASC LIMIT 1
121
+ # Person.first(3) # returns the first three objects fetched by SELECT * FROM people ORDER BY people.id LIMIT 3
127
122
  #
128
123
  def first(limit = nil)
129
124
  if limit
130
- find_nth_with_limit(offset_value, limit)
125
+ find_nth_with_limit(offset_index, limit)
131
126
  else
132
- find_nth(:first, offset_value)
127
+ find_nth(0, offset_index)
133
128
  end
134
129
  end
135
130
 
136
131
  # Same as +first+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
137
132
  # is found. Note that <tt>first!</tt> accepts no arguments.
138
133
  def first!
139
- first or raise RecordNotFound
134
+ find_nth! 0
140
135
  end
141
136
 
142
137
  # Find the last record (or last N records if a parameter is supplied).
@@ -169,7 +164,7 @@ module ActiveRecord
169
164
  # Same as +last+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
170
165
  # is found. Note that <tt>last!</tt> accepts no arguments.
171
166
  def last!
172
- last or raise RecordNotFound
167
+ last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
173
168
  end
174
169
 
175
170
  # Find the second record.
@@ -179,13 +174,13 @@ module ActiveRecord
179
174
  # Person.offset(3).second # returns the second object from OFFSET 3 (which is OFFSET 4)
180
175
  # Person.where(["user_name = :u", { u: user_name }]).second
181
176
  def second
182
- find_nth(:second, offset_value ? offset_value + 1 : 1)
177
+ find_nth(1, offset_index)
183
178
  end
184
179
 
185
180
  # Same as +second+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
186
181
  # is found.
187
182
  def second!
188
- second or raise RecordNotFound
183
+ find_nth! 1
189
184
  end
190
185
 
191
186
  # Find the third record.
@@ -195,13 +190,13 @@ module ActiveRecord
195
190
  # Person.offset(3).third # returns the third object from OFFSET 3 (which is OFFSET 5)
196
191
  # Person.where(["user_name = :u", { u: user_name }]).third
197
192
  def third
198
- find_nth(:third, offset_value ? offset_value + 2 : 2)
193
+ find_nth(2, offset_index)
199
194
  end
200
195
 
201
196
  # Same as +third+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
202
197
  # is found.
203
198
  def third!
204
- third or raise RecordNotFound
199
+ find_nth! 2
205
200
  end
206
201
 
207
202
  # Find the fourth record.
@@ -211,13 +206,13 @@ module ActiveRecord
211
206
  # Person.offset(3).fourth # returns the fourth object from OFFSET 3 (which is OFFSET 6)
212
207
  # Person.where(["user_name = :u", { u: user_name }]).fourth
213
208
  def fourth
214
- find_nth(:fourth, offset_value ? offset_value + 3 : 3)
209
+ find_nth(3, offset_index)
215
210
  end
216
211
 
217
212
  # Same as +fourth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
218
213
  # is found.
219
214
  def fourth!
220
- fourth or raise RecordNotFound
215
+ find_nth! 3
221
216
  end
222
217
 
223
218
  # Find the fifth record.
@@ -227,13 +222,13 @@ module ActiveRecord
227
222
  # Person.offset(3).fifth # returns the fifth object from OFFSET 3 (which is OFFSET 7)
228
223
  # Person.where(["user_name = :u", { u: user_name }]).fifth
229
224
  def fifth
230
- find_nth(:fifth, offset_value ? offset_value + 4 : 4)
225
+ find_nth(4, offset_index)
231
226
  end
232
227
 
233
228
  # Same as +fifth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
234
229
  # is found.
235
230
  def fifth!
236
- fifth or raise RecordNotFound
231
+ find_nth! 4
237
232
  end
238
233
 
239
234
  # Find the forty-second record. Also known as accessing "the reddit".
@@ -243,13 +238,13 @@ module ActiveRecord
243
238
  # Person.offset(3).forty_two # returns the forty-second object from OFFSET 3 (which is OFFSET 44)
244
239
  # Person.where(["user_name = :u", { u: user_name }]).forty_two
245
240
  def forty_two
246
- find_nth(:forty_two, offset_value ? offset_value + 41 : 41)
241
+ find_nth(41, offset_index)
247
242
  end
248
243
 
249
244
  # Same as +forty_two+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
250
245
  # is found.
251
246
  def forty_two!
252
- forty_two or raise RecordNotFound
247
+ find_nth! 41
253
248
  end
254
249
 
255
250
  # Returns +true+ if a record exists in the table that matches the +id+ or
@@ -280,7 +275,14 @@ module ActiveRecord
280
275
  # Person.exists?(false)
281
276
  # Person.exists?
282
277
  def exists?(conditions = :none)
283
- conditions = conditions.id if Base === conditions
278
+ if Base === conditions
279
+ conditions = conditions.id
280
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
281
+ You are passing an instance of ActiveRecord::Base to `exists?`.
282
+ Please pass the id of the object by calling `.id`
283
+ MSG
284
+ end
285
+
284
286
  return false if !conditions
285
287
 
286
288
  relation = apply_join_dependency(self, construct_join_dependency)
@@ -292,10 +294,12 @@ module ActiveRecord
292
294
  when Array, Hash
293
295
  relation = relation.where(conditions)
294
296
  else
295
- relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none
297
+ unless conditions == :none
298
+ relation = relation.where(primary_key => conditions)
299
+ end
296
300
  end
297
301
 
298
- connection.select_value(relation, "#{name} Exists", relation.bind_values) ? true : false
302
+ connection.select_value(relation, "#{name} Exists", relation.arel.bind_values + relation.bind_values) ? true : false
299
303
  end
300
304
 
301
305
  # This method is called whenever no records are found with either a single
@@ -322,6 +326,10 @@ module ActiveRecord
322
326
 
323
327
  private
324
328
 
329
+ def offset_index
330
+ offset_value || 0
331
+ end
332
+
325
333
  def find_with_associations
326
334
  # NOTE: the JoinDependency constructed here needs to know about
327
335
  # any joins already present in `self`, so pass them in
@@ -344,7 +352,8 @@ module ActiveRecord
344
352
  if ActiveRecord::NullRelation === relation
345
353
  []
346
354
  else
347
- rows = connection.select_all(relation.arel, 'SQL', relation.bind_values.dup)
355
+ arel = relation.arel
356
+ rows = connection.select_all(arel, 'SQL', arel.bind_values + relation.bind_values)
348
357
  join_dependency.instantiate(rows, aliases)
349
358
  end
350
359
  end
@@ -387,8 +396,9 @@ module ActiveRecord
387
396
  "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
388
397
 
389
398
  relation = relation.except(:select).select(values).distinct!
399
+ arel = relation.arel
390
400
 
391
- id_rows = @klass.connection.select_all(relation.arel, 'SQL', relation.bind_values)
401
+ id_rows = @klass.connection.select_all(arel, 'SQL', arel.bind_values + relation.bind_values)
392
402
  id_rows.map {|row| row[primary_key]}
393
403
  end
394
404
 
@@ -415,15 +425,20 @@ module ActiveRecord
415
425
  else
416
426
  find_some(ids)
417
427
  end
428
+ rescue RangeError
429
+ raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
418
430
  end
419
431
 
420
432
  def find_one(id)
421
- id = id.id if ActiveRecord::Base === id
433
+ if ActiveRecord::Base === id
434
+ id = id.id
435
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
436
+ You are passing an instance of ActiveRecord::Base to `find`.
437
+ Please pass the id of the object by calling `.id`
438
+ MSG
439
+ end
422
440
 
423
- column = columns_hash[primary_key]
424
- substitute = connection.substitute_at(column, bind_values.length)
425
- relation = where(table[primary_key].eq(substitute))
426
- relation.bind_values += [[column, id]]
441
+ relation = where(primary_key => id)
427
442
  record = relation.take
428
443
 
429
444
  raise_record_not_found_exception!(id, 0, 1) unless record
@@ -432,7 +447,7 @@ module ActiveRecord
432
447
  end
433
448
 
434
449
  def find_some(ids)
435
- result = where(table[primary_key].in(ids)).to_a
450
+ result = where(primary_key => ids).to_a
436
451
 
437
452
  expected_size =
438
453
  if limit_value && ids.size > limit_value
@@ -461,20 +476,28 @@ module ActiveRecord
461
476
  end
462
477
  end
463
478
 
464
- def find_nth(ordinal, offset)
479
+ def find_nth(index, offset)
465
480
  if loaded?
466
- @records.send(ordinal)
481
+ @records[index]
467
482
  else
483
+ offset += index
468
484
  @offsets[offset] ||= find_nth_with_limit(offset, 1).first
469
485
  end
470
486
  end
471
487
 
488
+ def find_nth!(index)
489
+ find_nth(index, offset_index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
490
+ end
491
+
472
492
  def find_nth_with_limit(offset, limit)
473
- if order_values.empty? && primary_key
474
- order(arel_table[primary_key].asc).limit(limit).offset(offset).to_a
475
- else
476
- limit(limit).offset(offset).to_a
477
- end
493
+ relation = if order_values.empty? && primary_key
494
+ order(arel_table[primary_key].asc)
495
+ else
496
+ self
497
+ end
498
+
499
+ relation = relation.offset(offset) unless offset.zero?
500
+ relation.limit(limit).to_a
478
501
  end
479
502
 
480
503
  def find_last
@@ -13,7 +13,7 @@ module ActiveRecord
13
13
  @hash = hash
14
14
  end
15
15
 
16
- def merge
16
+ def merge #:nodoc:
17
17
  Merger.new(relation, other).merge
18
18
  end
19
19
 
@@ -51,7 +51,8 @@ module ActiveRecord
51
51
 
52
52
  NORMAL_VALUES = Relation::SINGLE_VALUE_METHODS +
53
53
  Relation::MULTI_VALUE_METHODS -
54
- [:joins, :where, :order, :bind, :reverse_order, :lock, :create_with, :reordering, :from] # :nodoc:
54
+ [:includes, :preload, :joins, :where, :order, :bind, :reverse_order, :lock, :create_with, :reordering, :from] # :nodoc:
55
+
55
56
 
56
57
  def normal_values
57
58
  NORMAL_VALUES
@@ -75,6 +76,7 @@ module ActiveRecord
75
76
 
76
77
  merge_multi_values
77
78
  merge_single_values
79
+ merge_preloads
78
80
  merge_joins
79
81
 
80
82
  relation
@@ -82,13 +84,34 @@ module ActiveRecord
82
84
 
83
85
  private
84
86
 
87
+ def merge_preloads
88
+ return if other.preload_values.empty? && other.includes_values.empty?
89
+
90
+ if other.klass == relation.klass
91
+ relation.preload!(*other.preload_values) unless other.preload_values.empty?
92
+ relation.includes!(other.includes_values) unless other.includes_values.empty?
93
+ else
94
+ reflection = relation.klass.reflect_on_all_associations.find do |r|
95
+ r.class_name == other.klass.name
96
+ end || return
97
+
98
+ unless other.preload_values.empty?
99
+ relation.preload! reflection.name => other.preload_values
100
+ end
101
+
102
+ unless other.includes_values.empty?
103
+ relation.includes! reflection.name => other.includes_values
104
+ end
105
+ end
106
+ end
107
+
85
108
  def merge_joins
86
- return if values[:joins].blank?
109
+ return if other.joins_values.blank?
87
110
 
88
111
  if other.klass == relation.klass
89
- relation.joins!(*values[:joins])
112
+ relation.joins!(*other.joins_values)
90
113
  else
91
- joins_dependency, rest = values[:joins].partition do |join|
114
+ joins_dependency, rest = other.joins_values.partition do |join|
92
115
  case join
93
116
  when Hash, Symbol, Array
94
117
  true
@@ -108,49 +131,36 @@ module ActiveRecord
108
131
 
109
132
  def merge_multi_values
110
133
  lhs_wheres = relation.where_values
111
- rhs_wheres = values[:where] || []
134
+ rhs_wheres = other.where_values
112
135
 
113
136
  lhs_binds = relation.bind_values
114
- rhs_binds = values[:bind] || []
137
+ rhs_binds = other.bind_values
115
138
 
116
139
  removed, kept = partition_overwrites(lhs_wheres, rhs_wheres)
117
140
 
118
141
  where_values = kept + rhs_wheres
119
142
  bind_values = filter_binds(lhs_binds, removed) + rhs_binds
120
143
 
121
- conn = relation.klass.connection
122
- bv_index = 0
123
- where_values.map! do |node|
124
- if Arel::Nodes::Equality === node && Arel::Nodes::BindParam === node.right
125
- substitute = conn.substitute_at(bind_values[bv_index].first, bv_index)
126
- bv_index += 1
127
- Arel::Nodes::Equality.new(node.left, substitute)
128
- else
129
- node
130
- end
131
- end
132
-
133
144
  relation.where_values = where_values
134
145
  relation.bind_values = bind_values
135
146
 
136
- if values[:reordering]
147
+ if other.reordering_value
137
148
  # override any order specified in the original relation
138
- relation.reorder! values[:order]
139
- elsif values[:order]
149
+ relation.reorder! other.order_values
150
+ elsif other.order_values
140
151
  # merge in order_values from relation
141
- relation.order! values[:order]
152
+ relation.order! other.order_values
142
153
  end
143
154
 
144
- relation.extend(*values[:extending]) unless values[:extending].blank?
155
+ relation.extend(*other.extending_values) unless other.extending_values.blank?
145
156
  end
146
157
 
147
158
  def merge_single_values
148
- relation.from_value = values[:from] unless relation.from_value
149
- relation.lock_value = values[:lock] unless relation.lock_value
150
- relation.reverse_order_value = values[:reverse_order]
159
+ relation.from_value = other.from_value unless relation.from_value
160
+ relation.lock_value = other.lock_value unless relation.lock_value
151
161
 
152
- unless values[:create_with].blank?
153
- relation.create_with_value = (relation.create_with_value || {}).merge(values[:create_with])
162
+ unless other.create_with_value.blank?
163
+ relation.create_with_value = (relation.create_with_value || {}).merge(other.create_with_value)
154
164
  end
155
165
  end
156
166
 
@@ -1,29 +1,48 @@
1
+ require 'active_support/core_ext/string/filters'
2
+
1
3
  module ActiveRecord
2
4
  class PredicateBuilder
3
5
  class ArrayHandler # :nodoc:
4
6
  def call(attribute, value)
5
7
  values = value.map { |x| x.is_a?(Base) ? x.id : x }
6
- ranges, values = values.partition { |v| v.is_a?(Range) }
8
+ nils, values = values.partition(&:nil?)
9
+
10
+ if values.any? { |val| val.is_a?(Array) }
11
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
12
+ Passing a nested array to Active Record finder methods is
13
+ deprecated and will be removed. Flatten your array before using
14
+ it for 'IN' conditions.
15
+ MSG
7
16
 
8
- values_predicate = if values.include?(nil)
9
- values = values.compact
17
+ flat_values = values.flatten
18
+ values = flat_values unless flat_values.include?(nil)
19
+ end
20
+
21
+ return attribute.in([]) if values.empty? && nils.empty?
10
22
 
23
+ ranges, values = values.partition { |v| v.is_a?(Range) }
24
+
25
+ values_predicate =
11
26
  case values.length
12
- when 0
13
- attribute.eq(nil)
14
- when 1
15
- attribute.eq(values.first).or(attribute.eq(nil))
16
- else
17
- attribute.in(values).or(attribute.eq(nil))
27
+ when 0 then NullPredicate
28
+ when 1 then attribute.eq(values.first)
29
+ else attribute.in(values)
18
30
  end
19
- else
20
- attribute.in(values)
31
+
32
+ unless nils.empty?
33
+ values_predicate = values_predicate.or(attribute.eq(nil))
21
34
  end
22
35
 
23
- array_predicates = ranges.map { |range| attribute.in(range) }
24
- array_predicates << values_predicate
36
+ array_predicates = ranges.map { |range| attribute.between(range) }
37
+ array_predicates.unshift(values_predicate)
25
38
  array_predicates.inject { |composite, predicate| composite.or(predicate) }
26
39
  end
40
+
41
+ module NullPredicate # :nodoc:
42
+ def self.or(other)
43
+ other
44
+ end
45
+ end
27
46
  end
28
47
  end
29
48
  end
@@ -6,11 +6,7 @@ module ActiveRecord
6
6
  value = value.select(value.klass.arel_table[value.klass.primary_key])
7
7
  end
8
8
 
9
- value = value.dup
10
- value.where_values = value.where_values.map do |node|
11
- node.dup rescue node
12
- end
13
- attribute.in(value.arel.ast)
9
+ attribute.in(value.arel)
14
10
  end
15
11
  end
16
12
  end