activerecord 4.2.6 → 5.0.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1307 -1105
- data/MIT-LICENSE +2 -2
- data/README.rdoc +7 -8
- data/examples/performance.rb +2 -3
- data/examples/simple.rb +0 -1
- data/lib/active_record/aggregations.rb +37 -23
- data/lib/active_record/association_relation.rb +3 -3
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +11 -9
- data/lib/active_record/associations/association_scope.rb +73 -102
- data/lib/active_record/associations/belongs_to_association.rb +21 -32
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +43 -18
- data/lib/active_record/associations/builder/collection_association.rb +7 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +11 -6
- data/lib/active_record/associations/builder/singular_association.rb +3 -10
- data/lib/active_record/associations/collection_association.rb +50 -31
- data/lib/active_record/associations/collection_proxy.rb +69 -29
- data/lib/active_record/associations/foreign_association.rb +1 -1
- data/lib/active_record/associations/has_many_association.rb +20 -71
- data/lib/active_record/associations/has_many_through_association.rb +8 -47
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
- data/lib/active_record/associations/join_dependency.rb +29 -19
- data/lib/active_record/associations/preloader/association.rb +46 -52
- data/lib/active_record/associations/preloader/collection_association.rb +0 -6
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/through_association.rb +27 -14
- data/lib/active_record/associations/preloader.rb +14 -4
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +11 -3
- data/lib/active_record/associations.rb +317 -209
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +68 -18
- data/lib/active_record/attribute_assignment.rb +20 -141
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +46 -86
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +31 -59
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
- data/lib/active_record/attribute_methods/write.rb +14 -38
- data/lib/active_record/attribute_methods.rb +70 -45
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +6 -4
- data/lib/active_record/attribute_set.rb +30 -3
- data/lib/active_record/attributes.rb +199 -80
- data/lib/active_record/autosave_association.rb +49 -16
- data/lib/active_record/base.rb +32 -23
- data/lib/active_record/callbacks.rb +39 -43
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +378 -140
- data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +153 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +405 -362
- data/lib/active_record/connection_adapters/column.rb +28 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +25 -176
- data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +148 -203
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +37 -14
- data/lib/active_record/core.rb +89 -107
- data/lib/active_record/counter_cache.rb +13 -24
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +113 -76
- data/lib/active_record/errors.rb +87 -48
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +26 -5
- data/lib/active_record/fixtures.rb +76 -40
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +18 -2
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +15 -15
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +43 -21
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration.rb +364 -109
- data/lib/active_record/model_schema.rb +128 -38
- data/lib/active_record/nested_attributes.rb +58 -29
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +121 -80
- data/lib/active_record/query_cache.rb +15 -18
- data/lib/active_record/querying.rb +10 -9
- data/lib/active_record/railtie.rb +27 -18
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +58 -45
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +282 -115
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +139 -34
- data/lib/active_record/relation/calculations.rb +80 -102
- data/lib/active_record/relation/delegation.rb +7 -20
- data/lib/active_record/relation/finder_methods.rb +163 -81
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +16 -42
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -15
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +120 -107
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +308 -244
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +4 -7
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/relation.rb +176 -116
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +95 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +62 -38
- data/lib/active_record/schema_migration.rb +11 -17
- data/lib/active_record/scoping/default.rb +23 -9
- data/lib/active_record/scoping/named.rb +49 -28
- data/lib/active_record/scoping.rb +32 -15
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +16 -14
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +68 -0
- data/lib/active_record/tasks/database_tasks.rb +58 -41
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -20
- data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +20 -9
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +138 -56
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -41
- data/lib/active_record/type/date_time.rb +2 -49
- data/lib/active_record/type/internal/abstract_json.rb +29 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +15 -14
- data/lib/active_record/type/time.rb +10 -16
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +11 -12
- data/lib/active_record/validations/uniqueness.rb +30 -29
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record.rb +7 -2
- data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
- data/lib/rails/generators/active_record/migration.rb +7 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +58 -34
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -50
- data/lib/active_record/type/decimal_without_scale.rb +0 -11
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/text.rb +0 -11
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/unsigned_integer.rb +0 -15
- data/lib/active_record/type/value.rb +0 -105
@@ -28,15 +28,21 @@ module ActiveRecord
|
|
28
28
|
# Implements the reader method, e.g. foo.items for Foo.has_many :items
|
29
29
|
def reader(force_reload = false)
|
30
30
|
if force_reload
|
31
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
32
|
+
Passing an argument to force an association to reload is now
|
33
|
+
deprecated and will be removed in Rails 5.1. Please call `reload`
|
34
|
+
on the result collection proxy instead.
|
35
|
+
MSG
|
36
|
+
|
31
37
|
klass.uncached { reload }
|
32
38
|
elsif stale_target?
|
33
39
|
reload
|
34
40
|
end
|
35
41
|
|
36
|
-
if
|
42
|
+
if null_scope?
|
37
43
|
# Cache the proxy separately before the owner has an id
|
38
44
|
# or else a post-save proxy will still lack the id
|
39
|
-
@
|
45
|
+
@null_proxy ||= CollectionProxy.create(klass, self)
|
40
46
|
else
|
41
47
|
@proxy ||= CollectionProxy.create(klass, self)
|
42
48
|
end
|
@@ -54,17 +60,22 @@ module ActiveRecord
|
|
54
60
|
record.send(reflection.association_primary_key)
|
55
61
|
end
|
56
62
|
else
|
57
|
-
|
58
|
-
|
63
|
+
@association_ids ||= (
|
64
|
+
column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
|
65
|
+
scope.pluck(column)
|
66
|
+
)
|
59
67
|
end
|
60
68
|
end
|
61
69
|
|
62
70
|
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
63
71
|
def ids_writer(ids)
|
64
72
|
pk_type = reflection.primary_key_type
|
65
|
-
ids = Array(ids).reject
|
66
|
-
ids.map! { |i| pk_type.
|
67
|
-
|
73
|
+
ids = Array(ids).reject(&:blank?)
|
74
|
+
ids.map! { |i| pk_type.cast(i) }
|
75
|
+
records = klass.where(reflection.association_primary_key => ids).index_by do |r|
|
76
|
+
r.send(reflection.association_primary_key)
|
77
|
+
end.values_at(*ids)
|
78
|
+
replace(records)
|
68
79
|
end
|
69
80
|
|
70
81
|
def reset
|
@@ -125,6 +136,14 @@ module ActiveRecord
|
|
125
136
|
first_nth_or_last(:forty_two, *args)
|
126
137
|
end
|
127
138
|
|
139
|
+
def third_to_last(*args)
|
140
|
+
first_nth_or_last(:third_to_last, *args)
|
141
|
+
end
|
142
|
+
|
143
|
+
def second_to_last(*args)
|
144
|
+
first_nth_or_last(:second_to_last, *args)
|
145
|
+
end
|
146
|
+
|
128
147
|
def last(*args)
|
129
148
|
first_nth_or_last(:last, *args)
|
130
149
|
end
|
@@ -161,6 +180,7 @@ module ActiveRecord
|
|
161
180
|
# be chained. Since << flattens its argument list and inserts each record,
|
162
181
|
# +push+ and +concat+ behave identically.
|
163
182
|
def concat(*records)
|
183
|
+
records = records.flatten
|
164
184
|
if owner.new_record?
|
165
185
|
load_target
|
166
186
|
concat_records(records)
|
@@ -228,11 +248,7 @@ module ActiveRecord
|
|
228
248
|
|
229
249
|
# Count all records using SQL. Construct options and pass them with
|
230
250
|
# scope to the target class's +count+.
|
231
|
-
def count(column_name = nil
|
232
|
-
# TODO: Remove count_options argument as soon we remove support to
|
233
|
-
# activerecord-deprecated_finders.
|
234
|
-
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
|
235
|
-
|
251
|
+
def count(column_name = nil)
|
236
252
|
relation = scope
|
237
253
|
if association_scope.distinct_value
|
238
254
|
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
|
@@ -264,7 +280,7 @@ module ActiveRecord
|
|
264
280
|
_options = records.extract_options!
|
265
281
|
dependent = _options[:dependent] || options[:dependent]
|
266
282
|
|
267
|
-
records = find(records) if records.any? { |record| record.kind_of?(
|
283
|
+
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
268
284
|
delete_or_destroy(records, dependent)
|
269
285
|
end
|
270
286
|
|
@@ -275,7 +291,7 @@ module ActiveRecord
|
|
275
291
|
# +:dependent+ option.
|
276
292
|
def destroy(*records)
|
277
293
|
return if records.empty?
|
278
|
-
records = find(records) if records.any? { |record| record.kind_of?(
|
294
|
+
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
279
295
|
delete_or_destroy(records, :destroy)
|
280
296
|
end
|
281
297
|
|
@@ -299,7 +315,7 @@ module ActiveRecord
|
|
299
315
|
elsif !loaded? && !association_scope.group_values.empty?
|
300
316
|
load_target.size
|
301
317
|
elsif !loaded? && !association_scope.distinct_value && target.is_a?(Array)
|
302
|
-
unsaved_records = target.select
|
318
|
+
unsaved_records = target.select(&:new_record?)
|
303
319
|
unsaved_records.size + count_records
|
304
320
|
else
|
305
321
|
count_records
|
@@ -332,7 +348,8 @@ module ActiveRecord
|
|
332
348
|
end
|
333
349
|
|
334
350
|
# Returns true if the collections is not empty.
|
335
|
-
#
|
351
|
+
# If block given, loads all records and checks for one or more matches.
|
352
|
+
# Otherwise, equivalent to +!collection.empty?+.
|
336
353
|
def any?
|
337
354
|
if block_given?
|
338
355
|
load_target.any? { |*block_args| yield(*block_args) }
|
@@ -342,7 +359,8 @@ module ActiveRecord
|
|
342
359
|
end
|
343
360
|
|
344
361
|
# Returns true if the collection has more than 1 record.
|
345
|
-
#
|
362
|
+
# If block given, loads all records and checks for two or more matches.
|
363
|
+
# Otherwise, equivalent to +collection.size > 1+.
|
346
364
|
def many?
|
347
365
|
if block_given?
|
348
366
|
load_target.many? { |*block_args| yield(*block_args) }
|
@@ -371,6 +389,8 @@ module ActiveRecord
|
|
371
389
|
replace_common_records_in_memory(other_array, original_target)
|
372
390
|
if other_array != original_target
|
373
391
|
transaction { replace_records(other_array, original_target) }
|
392
|
+
else
|
393
|
+
other_array
|
374
394
|
end
|
375
395
|
end
|
376
396
|
end
|
@@ -405,12 +425,16 @@ module ActiveRecord
|
|
405
425
|
|
406
426
|
def replace_on_target(record, index, skip_callbacks)
|
407
427
|
callback(:before_add, record) unless skip_callbacks
|
428
|
+
|
429
|
+
was_loaded = loaded?
|
408
430
|
yield(record) if block_given?
|
409
431
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
432
|
+
unless !was_loaded && loaded?
|
433
|
+
if index
|
434
|
+
@target[index] = record
|
435
|
+
else
|
436
|
+
@target << record
|
437
|
+
end
|
414
438
|
end
|
415
439
|
|
416
440
|
callback(:after_add, record) unless skip_callbacks
|
@@ -510,7 +534,7 @@ module ActiveRecord
|
|
510
534
|
def delete_or_destroy(records, method)
|
511
535
|
records = records.flatten
|
512
536
|
records.each { |record| raise_on_type_mismatch!(record) }
|
513
|
-
existing_records = records.reject
|
537
|
+
existing_records = records.reject(&:new_record?)
|
514
538
|
|
515
539
|
if existing_records.empty?
|
516
540
|
remove_records(existing_records, records, method)
|
@@ -557,7 +581,7 @@ module ActiveRecord
|
|
557
581
|
def concat_records(records, should_raise = false)
|
558
582
|
result = true
|
559
583
|
|
560
|
-
records.
|
584
|
+
records.each do |record|
|
561
585
|
raise_on_type_mismatch!(record)
|
562
586
|
add_to_target(record) do |rec|
|
563
587
|
result &&= insert_record(rec, true, should_raise) unless owner.new_record?
|
@@ -601,13 +625,8 @@ module ActiveRecord
|
|
601
625
|
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
602
626
|
assoc = owner.association(reflection.through_reflection.name)
|
603
627
|
assoc.reader.any? { |source|
|
604
|
-
|
605
|
-
|
606
|
-
if target_association.respond_to?(:include?)
|
607
|
-
target_association.include?(record)
|
608
|
-
else
|
609
|
-
target_association == record
|
610
|
-
end
|
628
|
+
target_reflection = source.send(reflection.source_reflection.name)
|
629
|
+
target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
|
611
630
|
} || target.include?(record)
|
612
631
|
else
|
613
632
|
target.include?(record)
|
@@ -618,7 +637,7 @@ module ActiveRecord
|
|
618
637
|
# specified, then #find scans the entire collection.
|
619
638
|
def find_by_scan(*args)
|
620
639
|
expects_array = args.first.kind_of?(Array)
|
621
|
-
ids = args.flatten.compact.map
|
640
|
+
ids = args.flatten.compact.map(&:to_s).uniq
|
622
641
|
|
623
642
|
if ids.size == 1
|
624
643
|
id = ids.first
|
@@ -33,7 +33,7 @@ module ActiveRecord
|
|
33
33
|
|
34
34
|
def initialize(klass, association) #:nodoc:
|
35
35
|
@association = association
|
36
|
-
super klass, klass.arel_table
|
36
|
+
super klass, klass.arel_table, klass.predicate_builder
|
37
37
|
merge! association.scope(nullify: false)
|
38
38
|
end
|
39
39
|
|
@@ -112,7 +112,7 @@ module ActiveRecord
|
|
112
112
|
end
|
113
113
|
|
114
114
|
# Finds an object in the collection responding to the +id+. Uses the same
|
115
|
-
# rules as
|
115
|
+
# rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound
|
116
116
|
# error if the object cannot be found.
|
117
117
|
#
|
118
118
|
# class Person < ActiveRecord::Base
|
@@ -127,7 +127,7 @@ module ActiveRecord
|
|
127
127
|
# # ]
|
128
128
|
#
|
129
129
|
# person.pets.find(1) # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
|
130
|
-
# person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=4
|
130
|
+
# person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=4
|
131
131
|
#
|
132
132
|
# person.pets.find(2) { |pet| pet.name.downcase! }
|
133
133
|
# # => #<Pet id: 2, name: "fancy-fancy", person_id: 1>
|
@@ -171,32 +171,42 @@ module ActiveRecord
|
|
171
171
|
@association.first(*args)
|
172
172
|
end
|
173
173
|
|
174
|
-
# Same as
|
174
|
+
# Same as #first except returns only the second record.
|
175
175
|
def second(*args)
|
176
176
|
@association.second(*args)
|
177
177
|
end
|
178
178
|
|
179
|
-
# Same as
|
179
|
+
# Same as #first except returns only the third record.
|
180
180
|
def third(*args)
|
181
181
|
@association.third(*args)
|
182
182
|
end
|
183
183
|
|
184
|
-
# Same as
|
184
|
+
# Same as #first except returns only the fourth record.
|
185
185
|
def fourth(*args)
|
186
186
|
@association.fourth(*args)
|
187
187
|
end
|
188
188
|
|
189
|
-
# Same as
|
189
|
+
# Same as #first except returns only the fifth record.
|
190
190
|
def fifth(*args)
|
191
191
|
@association.fifth(*args)
|
192
192
|
end
|
193
193
|
|
194
|
-
# Same as
|
194
|
+
# Same as #first except returns only the forty second record.
|
195
195
|
# Also known as accessing "the reddit".
|
196
196
|
def forty_two(*args)
|
197
197
|
@association.forty_two(*args)
|
198
198
|
end
|
199
199
|
|
200
|
+
# Same as #first except returns only the third-to-last record.
|
201
|
+
def third_to_last(*args)
|
202
|
+
@association.third_to_last(*args)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Same as #first except returns only the second-to-last record.
|
206
|
+
def second_to_last(*args)
|
207
|
+
@association.second_to_last(*args)
|
208
|
+
end
|
209
|
+
|
200
210
|
# Returns the last record, or the last +n+ records, from the collection.
|
201
211
|
# If the collection is empty, the first form returns +nil+, and the second
|
202
212
|
# form returns an empty array.
|
@@ -227,6 +237,31 @@ module ActiveRecord
|
|
227
237
|
@association.last(*args)
|
228
238
|
end
|
229
239
|
|
240
|
+
# Gives a record (or N records if a parameter is supplied) from the collection
|
241
|
+
# using the same rules as <tt>ActiveRecord::Base.take</tt>.
|
242
|
+
#
|
243
|
+
# class Person < ActiveRecord::Base
|
244
|
+
# has_many :pets
|
245
|
+
# end
|
246
|
+
#
|
247
|
+
# person.pets
|
248
|
+
# # => [
|
249
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
250
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
251
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
252
|
+
# # ]
|
253
|
+
#
|
254
|
+
# person.pets.take # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
|
255
|
+
#
|
256
|
+
# person.pets.take(2)
|
257
|
+
# # => [
|
258
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
259
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>
|
260
|
+
# # ]
|
261
|
+
#
|
262
|
+
# another_person_without.pets # => []
|
263
|
+
# another_person_without.pets.take # => nil
|
264
|
+
# another_person_without.pets.take(2) # => []
|
230
265
|
def take(n = nil)
|
231
266
|
@association.take(n)
|
232
267
|
end
|
@@ -290,7 +325,7 @@ module ActiveRecord
|
|
290
325
|
@association.create(attributes, &block)
|
291
326
|
end
|
292
327
|
|
293
|
-
# Like
|
328
|
+
# Like #create, except that if the record is invalid, raises an exception.
|
294
329
|
#
|
295
330
|
# class Person
|
296
331
|
# has_many :pets
|
@@ -307,8 +342,8 @@ module ActiveRecord
|
|
307
342
|
end
|
308
343
|
|
309
344
|
# Add one or more records to the collection by setting their foreign keys
|
310
|
-
# to the association's primary key. Since
|
311
|
-
# inserts each record, +push+ and
|
345
|
+
# to the association's primary key. Since #<< flattens its argument list and
|
346
|
+
# inserts each record, +push+ and #concat behave identically. Returns +self+
|
312
347
|
# so method calls may be chained.
|
313
348
|
#
|
314
349
|
# class Person < ActiveRecord::Base
|
@@ -364,7 +399,7 @@ module ActiveRecord
|
|
364
399
|
# specified by the +:dependent+ option. If no +:dependent+ option is given,
|
365
400
|
# then it will follow the default strategy.
|
366
401
|
#
|
367
|
-
# For
|
402
|
+
# For <tt>has_many :through</tt> associations, the default deletion strategy is
|
368
403
|
# +:delete_all+.
|
369
404
|
#
|
370
405
|
# For +has_many+ associations, the default deletion strategy is +:nullify+.
|
@@ -399,7 +434,7 @@ module ActiveRecord
|
|
399
434
|
# # #<Pet id: 3, name: "Choo-Choo", person_id: nil>
|
400
435
|
# # ]
|
401
436
|
#
|
402
|
-
# Both +has_many+ and
|
437
|
+
# Both +has_many+ and <tt>has_many :through</tt> dependencies default to the
|
403
438
|
# +:delete_all+ strategy if the +:dependent+ option is set to +:destroy+.
|
404
439
|
# Records are not instantiated and callbacks will not be fired.
|
405
440
|
#
|
@@ -418,7 +453,7 @@ module ActiveRecord
|
|
418
453
|
# person.pets.delete_all
|
419
454
|
#
|
420
455
|
# Pet.find(1, 2, 3)
|
421
|
-
# # => ActiveRecord::RecordNotFound
|
456
|
+
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
|
422
457
|
#
|
423
458
|
# If it is set to <tt>:delete_all</tt>, all the objects are deleted
|
424
459
|
# *without* calling their +destroy+ method.
|
@@ -438,7 +473,7 @@ module ActiveRecord
|
|
438
473
|
# person.pets.delete_all
|
439
474
|
#
|
440
475
|
# Pet.find(1, 2, 3)
|
441
|
-
# # => ActiveRecord::RecordNotFound
|
476
|
+
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
|
442
477
|
def delete_all(dependent = nil)
|
443
478
|
@association.delete_all(dependent)
|
444
479
|
end
|
@@ -475,7 +510,7 @@ module ActiveRecord
|
|
475
510
|
# then it will follow the default strategy. Returns an array with the
|
476
511
|
# deleted records.
|
477
512
|
#
|
478
|
-
# For
|
513
|
+
# For <tt>has_many :through</tt> associations, the default deletion strategy is
|
479
514
|
# +:delete_all+.
|
480
515
|
#
|
481
516
|
# For +has_many+ associations, the default deletion strategy is +:nullify+.
|
@@ -532,7 +567,7 @@ module ActiveRecord
|
|
532
567
|
# # => [#<Pet id: 2, name: "Spook", person_id: 1>]
|
533
568
|
#
|
534
569
|
# Pet.find(1, 3)
|
535
|
-
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with
|
570
|
+
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 3)
|
536
571
|
#
|
537
572
|
# If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
|
538
573
|
# *without* calling their +destroy+ method.
|
@@ -560,9 +595,9 @@ module ActiveRecord
|
|
560
595
|
# # ]
|
561
596
|
#
|
562
597
|
# Pet.find(1)
|
563
|
-
# # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=1
|
598
|
+
# # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=1
|
564
599
|
#
|
565
|
-
# You can pass +
|
600
|
+
# You can pass +Integer+ or +String+ values, it finds the records
|
566
601
|
# responding to the +id+ and executes delete on them.
|
567
602
|
#
|
568
603
|
# class Person < ActiveRecord::Base
|
@@ -624,9 +659,9 @@ module ActiveRecord
|
|
624
659
|
# person.pets.size # => 0
|
625
660
|
# person.pets # => []
|
626
661
|
#
|
627
|
-
# Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with
|
662
|
+
# Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
|
628
663
|
#
|
629
|
-
# You can pass +
|
664
|
+
# You can pass +Integer+ or +String+ values, it finds the records
|
630
665
|
# responding to the +id+ and then deletes them from the database.
|
631
666
|
#
|
632
667
|
# person.pets.size # => 3
|
@@ -656,7 +691,7 @@ module ActiveRecord
|
|
656
691
|
# person.pets.size # => 0
|
657
692
|
# person.pets # => []
|
658
693
|
#
|
659
|
-
# Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with
|
694
|
+
# Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (4, 5, 6)
|
660
695
|
def destroy(*records)
|
661
696
|
@association.destroy(*records)
|
662
697
|
end
|
@@ -693,10 +728,8 @@ module ActiveRecord
|
|
693
728
|
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
694
729
|
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
695
730
|
# # ]
|
696
|
-
def count(column_name = nil
|
697
|
-
|
698
|
-
# activerecord-deprecated_finders.
|
699
|
-
@association.count(column_name, options)
|
731
|
+
def count(column_name = nil)
|
732
|
+
@association.count(column_name)
|
700
733
|
end
|
701
734
|
|
702
735
|
# Returns the size of the collection. If the collection hasn't been loaded,
|
@@ -783,7 +816,7 @@ module ActiveRecord
|
|
783
816
|
# person.pets.any? # => false
|
784
817
|
#
|
785
818
|
# person.pets << Pet.new(name: 'Snoop')
|
786
|
-
# person.pets.count # =>
|
819
|
+
# person.pets.count # => 1
|
787
820
|
# person.pets.any? # => true
|
788
821
|
#
|
789
822
|
# You can also pass a +block+ to define criteria. The behavior
|
@@ -858,7 +891,7 @@ module ActiveRecord
|
|
858
891
|
!!@association.include?(record)
|
859
892
|
end
|
860
893
|
|
861
|
-
def arel
|
894
|
+
def arel #:nodoc:
|
862
895
|
scope.arel
|
863
896
|
end
|
864
897
|
|
@@ -946,6 +979,10 @@ module ActiveRecord
|
|
946
979
|
end
|
947
980
|
alias_method :to_a, :to_ary
|
948
981
|
|
982
|
+
def records # :nodoc:
|
983
|
+
load_target
|
984
|
+
end
|
985
|
+
|
949
986
|
# Adds one or more +records+ to the collection by setting their foreign keys
|
950
987
|
# to the association's primary key. Returns +self+, so several appends may be
|
951
988
|
# chained together.
|
@@ -973,12 +1010,15 @@ module ActiveRecord
|
|
973
1010
|
alias_method :append, :<<
|
974
1011
|
|
975
1012
|
def prepend(*args)
|
976
|
-
raise NoMethodError, "prepend on association is not defined. Please use
|
1013
|
+
raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
|
977
1014
|
end
|
978
1015
|
|
979
1016
|
# Equivalent to +delete_all+. The difference is that returns +self+, instead
|
980
1017
|
# of an array with the deleted objects, so methods can be chained. See
|
981
1018
|
# +delete_all+ for more information.
|
1019
|
+
# Note that because +delete_all+ removes records by directly
|
1020
|
+
# running an SQL query into the database, the +updated_at+ column of
|
1021
|
+
# the object is not changed.
|
982
1022
|
def clear
|
983
1023
|
delete_all
|
984
1024
|
self
|
@@ -15,9 +15,16 @@ module ActiveRecord
|
|
15
15
|
|
16
16
|
when :restrict_with_error
|
17
17
|
unless empty?
|
18
|
-
record =
|
19
|
-
owner.errors.
|
20
|
-
|
18
|
+
record = owner.class.human_attribute_name(reflection.name).downcase
|
19
|
+
message = owner.errors.generate_message(:base, :'restrict_dependent_destroy.many', record: record, raise: true) rescue nil
|
20
|
+
if message
|
21
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
22
|
+
The error key `:'restrict_dependent_destroy.many'` has been deprecated and will be removed in Rails 5.1.
|
23
|
+
Please use `:'restrict_dependent_destroy.has_many'` instead.
|
24
|
+
MESSAGE
|
25
|
+
end
|
26
|
+
owner.errors.add(:base, message || :'restrict_dependent_destroy.has_many', record: record)
|
27
|
+
throw(:abort)
|
21
28
|
end
|
22
29
|
|
23
30
|
else
|
@@ -43,7 +50,7 @@ module ActiveRecord
|
|
43
50
|
end
|
44
51
|
|
45
52
|
def empty?
|
46
|
-
if has_cached_counter?
|
53
|
+
if reflection.has_cached_counter?
|
47
54
|
size.zero?
|
48
55
|
else
|
49
56
|
super
|
@@ -66,8 +73,8 @@ module ActiveRecord
|
|
66
73
|
# If the collection is empty the target is set to an empty array and
|
67
74
|
# the loaded flag is set to true as well.
|
68
75
|
def count_records
|
69
|
-
count = if has_cached_counter?
|
70
|
-
owner._read_attribute
|
76
|
+
count = if reflection.has_cached_counter?
|
77
|
+
owner._read_attribute reflection.counter_cache_column
|
71
78
|
else
|
72
79
|
scope.count
|
73
80
|
end
|
@@ -80,78 +87,20 @@ module ActiveRecord
|
|
80
87
|
[association_scope.limit_value, count].compact.min
|
81
88
|
end
|
82
89
|
|
83
|
-
|
84
|
-
# Returns whether a counter cache should be used for this association.
|
85
|
-
#
|
86
|
-
# The counter_cache option must be given on either the owner or inverse
|
87
|
-
# association, and the column must be present on the owner.
|
88
|
-
def has_cached_counter?(reflection = reflection())
|
89
|
-
if reflection.options[:counter_cache] || (inverse = inverse_which_updates_counter_cache(reflection)) && inverse.options[:counter_cache]
|
90
|
-
owner.attribute_present?(cached_counter_attribute_name(reflection))
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def cached_counter_attribute_name(reflection = reflection())
|
95
|
-
if reflection.options[:counter_cache]
|
96
|
-
reflection.options[:counter_cache].to_s
|
97
|
-
else
|
98
|
-
"#{reflection.name}_count"
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
90
|
def update_counter(difference, reflection = reflection())
|
103
|
-
|
104
|
-
|
105
|
-
end
|
106
|
-
|
107
|
-
def update_counter_in_database(difference, reflection = reflection())
|
108
|
-
if has_cached_counter?(reflection)
|
109
|
-
counter = cached_counter_attribute_name(reflection)
|
110
|
-
owner.class.update_counters(owner.id, counter => difference)
|
91
|
+
if reflection.has_cached_counter?
|
92
|
+
owner.increment!(reflection.counter_cache_column, difference)
|
111
93
|
end
|
112
94
|
end
|
113
95
|
|
114
96
|
def update_counter_in_memory(difference, reflection = reflection())
|
115
|
-
if counter_must_be_updated_by_has_many?
|
116
|
-
counter =
|
117
|
-
owner
|
118
|
-
owner.send(:
|
97
|
+
if reflection.counter_must_be_updated_by_has_many?
|
98
|
+
counter = reflection.counter_cache_column
|
99
|
+
owner.increment(counter, difference)
|
100
|
+
owner.send(:clear_attribute_change, counter) # eww
|
119
101
|
end
|
120
102
|
end
|
121
103
|
|
122
|
-
# This shit is nasty. We need to avoid the following situation:
|
123
|
-
#
|
124
|
-
# * An associated record is deleted via record.destroy
|
125
|
-
# * Hence the callbacks run, and they find a belongs_to on the record with a
|
126
|
-
# :counter_cache options which points back at our owner. So they update the
|
127
|
-
# counter cache.
|
128
|
-
# * In which case, we must make sure to *not* update the counter cache, or else
|
129
|
-
# it will be decremented twice.
|
130
|
-
#
|
131
|
-
# Hence this method.
|
132
|
-
def inverse_which_updates_counter_cache(reflection = reflection())
|
133
|
-
counter_name = cached_counter_attribute_name(reflection)
|
134
|
-
inverse_which_updates_counter_named(counter_name, reflection)
|
135
|
-
end
|
136
|
-
alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
|
137
|
-
|
138
|
-
def inverse_which_updates_counter_named(counter_name, reflection)
|
139
|
-
reflection.klass._reflections.values.find { |inverse_reflection|
|
140
|
-
inverse_reflection.belongs_to? &&
|
141
|
-
inverse_reflection.counter_cache_column == counter_name
|
142
|
-
}
|
143
|
-
end
|
144
|
-
alias inverse_updates_counter_named? inverse_which_updates_counter_named
|
145
|
-
|
146
|
-
def inverse_updates_counter_in_memory?(reflection)
|
147
|
-
inverse = inverse_which_updates_counter_cache(reflection)
|
148
|
-
inverse && inverse == reflection.inverse_of
|
149
|
-
end
|
150
|
-
|
151
|
-
def counter_must_be_updated_by_has_many?(reflection)
|
152
|
-
!inverse_updates_counter_in_memory?(reflection) && has_cached_counter?(reflection)
|
153
|
-
end
|
154
|
-
|
155
104
|
def delete_count(method, scope)
|
156
105
|
if method == :delete_all
|
157
106
|
scope.delete_all
|
@@ -169,7 +118,7 @@ module ActiveRecord
|
|
169
118
|
def delete_records(records, method)
|
170
119
|
if method == :destroy
|
171
120
|
records.each(&:destroy!)
|
172
|
-
update_counter(-records.length) unless inverse_updates_counter_cache?
|
121
|
+
update_counter(-records.length) unless reflection.inverse_updates_counter_cache?
|
173
122
|
else
|
174
123
|
scope = self.scope.where(reflection.klass.primary_key => records)
|
175
124
|
update_counter(-delete_count(method, scope))
|