activerecord 4.2.11.3 → 5.0.7.2
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 +1638 -1132
- 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.rb +7 -2
- data/lib/active_record/aggregations.rb +34 -21
- data/lib/active_record/association_relation.rb +7 -4
- data/lib/active_record/associations.rb +347 -218
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +22 -10
- data/lib/active_record/associations/association_scope.rb +75 -104
- 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 +16 -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 +13 -11
- data/lib/active_record/associations/collection_association.rb +85 -69
- data/lib/active_record/associations/collection_proxy.rb +104 -46
- data/lib/active_record/associations/foreign_association.rb +1 -1
- data/lib/active_record/associations/has_many_association.rb +21 -78
- data/lib/active_record/associations/has_many_through_association.rb +6 -47
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency.rb +38 -22
- data/lib/active_record/associations/join_dependency/join_association.rb +15 -14
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +14 -4
- data/lib/active_record/associations/preloader/association.rb +52 -71
- data/lib/active_record/associations/preloader/collection_association.rb +0 -7
- 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/singular_association.rb +0 -1
- data/lib/active_record/associations/preloader/through_association.rb +36 -17
- data/lib/active_record/associations/singular_association.rb +13 -1
- data/lib/active_record/associations/through_association.rb +12 -4
- data/lib/active_record/attribute.rb +69 -19
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute_assignment.rb +19 -140
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods.rb +69 -44
- 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 +16 -3
- 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 +13 -37
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set.rb +32 -3
- data/lib/active_record/attribute_set/builder.rb +42 -16
- data/lib/active_record/attributes.rb +199 -81
- data/lib/active_record/autosave_association.rb +54 -17
- 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 +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +467 -189
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -62
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +86 -13
- 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 -188
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +407 -156
- data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -71
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +433 -399
- 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 +108 -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 -166
- data/lib/active_record/connection_adapters/postgresql/column.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -72
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +37 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -2
- 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 +13 -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/quoting.rb +56 -19
- 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 +250 -154
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +264 -170
- 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 +151 -194
- 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 +92 -108
- data/lib/active_record/counter_cache.rb +13 -24
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +116 -76
- data/lib/active_record/errors.rb +87 -48
- data/lib/active_record/explain.rb +20 -9
- 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 +77 -41
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +17 -14
- 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 +48 -24
- data/lib/active_record/migration.rb +362 -111
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/model_schema.rb +270 -73
- data/lib/active_record/nested_attributes.rb +58 -29
- data/lib/active_record/no_touching.rb +4 -0
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +152 -90
- data/lib/active_record/query_cache.rb +18 -23
- data/lib/active_record/querying.rb +12 -11
- data/lib/active_record/railtie.rb +23 -16
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +52 -41
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +302 -115
- data/lib/active_record/relation.rb +187 -120
- data/lib/active_record/relation/batches.rb +141 -36
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/calculations.rb +92 -117
- data/lib/active_record/relation/delegation.rb +8 -20
- data/lib/active_record/relation/finder_methods.rb +173 -89
- 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.rb +120 -107
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
- 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/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/result.rb +11 -4
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +105 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +54 -37
- data/lib/active_record/schema_migration.rb +11 -14
- data/lib/active_record/scoping.rb +34 -16
- data/lib/active_record/scoping/default.rb +28 -10
- data/lib/active_record/scoping/named.rb +59 -26
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +3 -5
- data/lib/active_record/statement_cache.rb +17 -15
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +69 -0
- data/lib/active_record/tasks/database_tasks.rb +66 -49
- data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
- data/lib/active_record/tasks/postgresql_database_tasks.rb +12 -3
- 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 +63 -0
- data/lib/active_record/transactions.rb +139 -57
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -45
- data/lib/active_record/type/date_time.rb +2 -49
- data/lib/active_record/type/internal/abstract_json.rb +33 -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_caster.rb +7 -0
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/validations.rb +33 -32
- 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 +33 -33
- data/lib/rails/generators/active_record/migration.rb +15 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -5
- 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/model/model_generator.rb +33 -16
- 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 -498
- 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 -64
- 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 -110
@@ -28,18 +28,19 @@ 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
|
-
|
37
|
-
|
38
|
-
# or else a post-save proxy will still lack the id
|
39
|
-
@new_record_proxy ||= CollectionProxy.create(klass, self)
|
40
|
-
else
|
41
|
-
@proxy ||= CollectionProxy.create(klass, self)
|
42
|
-
end
|
42
|
+
@proxy ||= CollectionProxy.create(klass, self)
|
43
|
+
@proxy.reset_scope
|
43
44
|
end
|
44
45
|
|
45
46
|
# Implements the writer method, e.g. foo.items= for Foo.has_many :items
|
@@ -54,27 +55,28 @@ module ActiveRecord
|
|
54
55
|
record.send(reflection.association_primary_key)
|
55
56
|
end
|
56
57
|
else
|
57
|
-
|
58
|
-
|
58
|
+
@association_ids ||= (
|
59
|
+
column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
|
60
|
+
scope.pluck(column)
|
61
|
+
)
|
59
62
|
end
|
60
63
|
end
|
61
64
|
|
62
65
|
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
63
66
|
def ids_writer(ids)
|
64
|
-
|
65
|
-
|
66
|
-
ids
|
67
|
-
pk_type.type_cast_from_user(i)
|
68
|
-
end
|
67
|
+
pk_type = reflection.association_primary_key_type
|
68
|
+
ids = Array(ids).reject(&:blank?)
|
69
|
+
ids.map! { |i| pk_type.cast(i) }
|
69
70
|
|
70
|
-
|
71
|
-
|
71
|
+
primary_key = reflection.association_primary_key
|
72
|
+
records = klass.where(primary_key => ids).index_by do |r|
|
73
|
+
r.public_send(primary_key)
|
72
74
|
end.values_at(*ids).compact
|
73
75
|
|
74
|
-
if
|
75
|
-
|
76
|
+
if records.size != ids.size
|
77
|
+
klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key)
|
76
78
|
else
|
77
|
-
|
79
|
+
replace(records)
|
78
80
|
end
|
79
81
|
end
|
80
82
|
|
@@ -136,6 +138,14 @@ module ActiveRecord
|
|
136
138
|
first_nth_or_last(:forty_two, *args)
|
137
139
|
end
|
138
140
|
|
141
|
+
def third_to_last(*args)
|
142
|
+
first_nth_or_last(:third_to_last, *args)
|
143
|
+
end
|
144
|
+
|
145
|
+
def second_to_last(*args)
|
146
|
+
first_nth_or_last(:second_to_last, *args)
|
147
|
+
end
|
148
|
+
|
139
149
|
def last(*args)
|
140
150
|
first_nth_or_last(:last, *args)
|
141
151
|
end
|
@@ -172,6 +182,7 @@ module ActiveRecord
|
|
172
182
|
# be chained. Since << flattens its argument list and inserts each record,
|
173
183
|
# +push+ and +concat+ behave identically.
|
174
184
|
def concat(*records)
|
185
|
+
records = records.flatten
|
175
186
|
if owner.new_record?
|
176
187
|
load_target
|
177
188
|
concat_records(records)
|
@@ -239,11 +250,7 @@ module ActiveRecord
|
|
239
250
|
|
240
251
|
# Count all records using SQL. Construct options and pass them with
|
241
252
|
# scope to the target class's +count+.
|
242
|
-
def count(column_name = nil
|
243
|
-
# TODO: Remove count_options argument as soon we remove support to
|
244
|
-
# activerecord-deprecated_finders.
|
245
|
-
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
|
246
|
-
|
253
|
+
def count(column_name = nil)
|
247
254
|
relation = scope
|
248
255
|
if association_scope.distinct_value
|
249
256
|
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
|
@@ -307,10 +314,10 @@ module ActiveRecord
|
|
307
314
|
else
|
308
315
|
target.size
|
309
316
|
end
|
310
|
-
elsif !
|
317
|
+
elsif !association_scope.group_values.empty?
|
311
318
|
load_target.size
|
312
|
-
elsif !
|
313
|
-
unsaved_records = target.select
|
319
|
+
elsif !association_scope.distinct_value && target.is_a?(Array)
|
320
|
+
unsaved_records = target.select(&:new_record?)
|
314
321
|
unsaved_records.size + count_records
|
315
322
|
else
|
316
323
|
count_records
|
@@ -343,7 +350,8 @@ module ActiveRecord
|
|
343
350
|
end
|
344
351
|
|
345
352
|
# Returns true if the collections is not empty.
|
346
|
-
#
|
353
|
+
# If block given, loads all records and checks for one or more matches.
|
354
|
+
# Otherwise, equivalent to +!collection.empty?+.
|
347
355
|
def any?
|
348
356
|
if block_given?
|
349
357
|
load_target.any? { |*block_args| yield(*block_args) }
|
@@ -353,7 +361,8 @@ module ActiveRecord
|
|
353
361
|
end
|
354
362
|
|
355
363
|
# Returns true if the collection has more than 1 record.
|
356
|
-
#
|
364
|
+
# If block given, loads all records and checks for two or more matches.
|
365
|
+
# Otherwise, equivalent to +collection.size > 1+.
|
357
366
|
def many?
|
358
367
|
if block_given?
|
359
368
|
load_target.many? { |*block_args| yield(*block_args) }
|
@@ -382,6 +391,8 @@ module ActiveRecord
|
|
382
391
|
replace_common_records_in_memory(other_array, original_target)
|
383
392
|
if other_array != original_target
|
384
393
|
transaction { replace_records(other_array, original_target) }
|
394
|
+
else
|
395
|
+
other_array
|
385
396
|
end
|
386
397
|
end
|
387
398
|
end
|
@@ -414,25 +425,9 @@ module ActiveRecord
|
|
414
425
|
replace_on_target(record, index, skip_callbacks, &block)
|
415
426
|
end
|
416
427
|
|
417
|
-
def
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
if index
|
422
|
-
@target[index] = record
|
423
|
-
else
|
424
|
-
@target << record
|
425
|
-
end
|
426
|
-
|
427
|
-
callback(:after_add, record) unless skip_callbacks
|
428
|
-
set_inverse_instance(record)
|
429
|
-
|
430
|
-
record
|
431
|
-
end
|
432
|
-
|
433
|
-
def scope(opts = {})
|
434
|
-
scope = super()
|
435
|
-
scope.none! if opts.fetch(:nullify, true) && null_scope?
|
428
|
+
def scope
|
429
|
+
scope = super
|
430
|
+
scope.none! if null_scope?
|
436
431
|
scope
|
437
432
|
end
|
438
433
|
|
@@ -441,7 +436,7 @@ module ActiveRecord
|
|
441
436
|
end
|
442
437
|
|
443
438
|
private
|
444
|
-
def get_records
|
439
|
+
def get_records(&block)
|
445
440
|
return scope.to_a if skip_statement_cache?
|
446
441
|
|
447
442
|
conn = klass.connection
|
@@ -453,13 +448,13 @@ module ActiveRecord
|
|
453
448
|
end
|
454
449
|
|
455
450
|
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
456
|
-
sc.execute
|
451
|
+
sc.execute(binds, klass, klass.connection, &block)
|
457
452
|
end
|
458
453
|
|
459
454
|
def find_target
|
460
|
-
|
461
|
-
|
462
|
-
|
455
|
+
get_records do |record|
|
456
|
+
set_inverse_instance(record)
|
457
|
+
end
|
463
458
|
end
|
464
459
|
|
465
460
|
# We have some records loaded from the database (persisted) and some that are
|
@@ -503,15 +498,19 @@ module ActiveRecord
|
|
503
498
|
transaction do
|
504
499
|
add_to_target(build_record(attributes)) do |record|
|
505
500
|
yield(record) if block_given?
|
506
|
-
insert_record(record, true, raise)
|
501
|
+
insert_record(record, true, raise) { @_was_loaded = loaded? }
|
507
502
|
end
|
508
503
|
end
|
509
504
|
end
|
510
505
|
end
|
511
506
|
|
512
507
|
# Do the relevant stuff to insert the given record into the association collection.
|
513
|
-
def insert_record(record, validate = true, raise = false)
|
514
|
-
raise
|
508
|
+
def insert_record(record, validate = true, raise = false, &block)
|
509
|
+
if raise
|
510
|
+
record.save!(validate: validate, &block)
|
511
|
+
else
|
512
|
+
record.save(validate: validate, &block)
|
513
|
+
end
|
515
514
|
end
|
516
515
|
|
517
516
|
def create_scope
|
@@ -521,7 +520,7 @@ module ActiveRecord
|
|
521
520
|
def delete_or_destroy(records, method)
|
522
521
|
records = records.flatten
|
523
522
|
records.each { |record| raise_on_type_mismatch!(record) }
|
524
|
-
existing_records = records.reject
|
523
|
+
existing_records = records.reject(&:new_record?)
|
525
524
|
|
526
525
|
if existing_records.empty?
|
527
526
|
remove_records(existing_records, records, method)
|
@@ -565,19 +564,41 @@ module ActiveRecord
|
|
565
564
|
end
|
566
565
|
end
|
567
566
|
|
568
|
-
def concat_records(records,
|
567
|
+
def concat_records(records, raise = false)
|
569
568
|
result = true
|
570
569
|
|
571
|
-
records.
|
570
|
+
records.each do |record|
|
572
571
|
raise_on_type_mismatch!(record)
|
573
|
-
add_to_target(record) do
|
574
|
-
result &&= insert_record(
|
572
|
+
add_to_target(record) do
|
573
|
+
result &&= insert_record(record, true, raise) { @_was_loaded = loaded? } unless owner.new_record?
|
575
574
|
end
|
576
575
|
end
|
577
576
|
|
578
577
|
result && records
|
579
578
|
end
|
580
579
|
|
580
|
+
def replace_on_target(record, index, skip_callbacks)
|
581
|
+
callback(:before_add, record) unless skip_callbacks
|
582
|
+
|
583
|
+
set_inverse_instance(record)
|
584
|
+
|
585
|
+
@_was_loaded = true
|
586
|
+
|
587
|
+
yield(record) if block_given?
|
588
|
+
|
589
|
+
if index
|
590
|
+
target[index] = record
|
591
|
+
elsif @_was_loaded || !loaded?
|
592
|
+
target << record
|
593
|
+
end
|
594
|
+
|
595
|
+
callback(:after_add, record) unless skip_callbacks
|
596
|
+
|
597
|
+
record
|
598
|
+
ensure
|
599
|
+
@_was_loaded = nil
|
600
|
+
end
|
601
|
+
|
581
602
|
def callback(method, record)
|
582
603
|
callbacks_for(method).each do |callback|
|
583
604
|
callback.call(method, owner, record)
|
@@ -612,13 +633,8 @@ module ActiveRecord
|
|
612
633
|
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
613
634
|
assoc = owner.association(reflection.through_reflection.name)
|
614
635
|
assoc.reader.any? { |source|
|
615
|
-
|
616
|
-
|
617
|
-
if target_association.respond_to?(:include?)
|
618
|
-
target_association.include?(record)
|
619
|
-
else
|
620
|
-
target_association == record
|
621
|
-
end
|
636
|
+
target_reflection = source.send(reflection.source_reflection.name)
|
637
|
+
target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
|
622
638
|
} || target.include?(record)
|
623
639
|
else
|
624
640
|
target.include?(record)
|
@@ -629,7 +645,7 @@ module ActiveRecord
|
|
629
645
|
# specified, then #find scans the entire collection.
|
630
646
|
def find_by_scan(*args)
|
631
647
|
expects_array = args.first.kind_of?(Array)
|
632
|
-
ids = args.flatten.compact.map
|
648
|
+
ids = args.flatten.compact.map(&:to_s).uniq
|
633
649
|
|
634
650
|
if ids.size == 1
|
635
651
|
id = ids.first
|
@@ -28,13 +28,12 @@ module ActiveRecord
|
|
28
28
|
# is computed directly through SQL and does not trigger by itself the
|
29
29
|
# instantiation of the actual post records.
|
30
30
|
class CollectionProxy < Relation
|
31
|
-
delegate(*(ActiveRecord::Calculations.public_instance_methods - [:count]), to: :scope)
|
32
|
-
delegate :find_nth, to: :scope
|
33
|
-
|
34
31
|
def initialize(klass, association) #:nodoc:
|
35
32
|
@association = association
|
36
|
-
super klass, klass.arel_table
|
37
|
-
|
33
|
+
super klass, klass.arel_table, klass.predicate_builder
|
34
|
+
|
35
|
+
extensions = association.extensions
|
36
|
+
extend(*extensions) if extensions.any?
|
38
37
|
end
|
39
38
|
|
40
39
|
def target
|
@@ -112,7 +111,7 @@ module ActiveRecord
|
|
112
111
|
end
|
113
112
|
|
114
113
|
# Finds an object in the collection responding to the +id+. Uses the same
|
115
|
-
# rules as
|
114
|
+
# rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound
|
116
115
|
# error if the object cannot be found.
|
117
116
|
#
|
118
117
|
# class Person < ActiveRecord::Base
|
@@ -127,7 +126,7 @@ module ActiveRecord
|
|
127
126
|
# # ]
|
128
127
|
#
|
129
128
|
# 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
|
129
|
+
# person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=4
|
131
130
|
#
|
132
131
|
# person.pets.find(2) { |pet| pet.name.downcase! }
|
133
132
|
# # => #<Pet id: 2, name: "fancy-fancy", person_id: 1>
|
@@ -171,32 +170,42 @@ module ActiveRecord
|
|
171
170
|
@association.first(*args)
|
172
171
|
end
|
173
172
|
|
174
|
-
# Same as
|
173
|
+
# Same as #first except returns only the second record.
|
175
174
|
def second(*args)
|
176
175
|
@association.second(*args)
|
177
176
|
end
|
178
177
|
|
179
|
-
# Same as
|
178
|
+
# Same as #first except returns only the third record.
|
180
179
|
def third(*args)
|
181
180
|
@association.third(*args)
|
182
181
|
end
|
183
182
|
|
184
|
-
# Same as
|
183
|
+
# Same as #first except returns only the fourth record.
|
185
184
|
def fourth(*args)
|
186
185
|
@association.fourth(*args)
|
187
186
|
end
|
188
187
|
|
189
|
-
# Same as
|
188
|
+
# Same as #first except returns only the fifth record.
|
190
189
|
def fifth(*args)
|
191
190
|
@association.fifth(*args)
|
192
191
|
end
|
193
192
|
|
194
|
-
# Same as
|
193
|
+
# Same as #first except returns only the forty second record.
|
195
194
|
# Also known as accessing "the reddit".
|
196
195
|
def forty_two(*args)
|
197
196
|
@association.forty_two(*args)
|
198
197
|
end
|
199
198
|
|
199
|
+
# Same as #first except returns only the third-to-last record.
|
200
|
+
def third_to_last(*args)
|
201
|
+
@association.third_to_last(*args)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Same as #first except returns only the second-to-last record.
|
205
|
+
def second_to_last(*args)
|
206
|
+
@association.second_to_last(*args)
|
207
|
+
end
|
208
|
+
|
200
209
|
# Returns the last record, or the last +n+ records, from the collection.
|
201
210
|
# If the collection is empty, the first form returns +nil+, and the second
|
202
211
|
# form returns an empty array.
|
@@ -227,6 +236,31 @@ module ActiveRecord
|
|
227
236
|
@association.last(*args)
|
228
237
|
end
|
229
238
|
|
239
|
+
# Gives a record (or N records if a parameter is supplied) from the collection
|
240
|
+
# using the same rules as <tt>ActiveRecord::Base.take</tt>.
|
241
|
+
#
|
242
|
+
# class Person < ActiveRecord::Base
|
243
|
+
# has_many :pets
|
244
|
+
# end
|
245
|
+
#
|
246
|
+
# person.pets
|
247
|
+
# # => [
|
248
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
249
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
250
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
251
|
+
# # ]
|
252
|
+
#
|
253
|
+
# person.pets.take # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
|
254
|
+
#
|
255
|
+
# person.pets.take(2)
|
256
|
+
# # => [
|
257
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
258
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>
|
259
|
+
# # ]
|
260
|
+
#
|
261
|
+
# another_person_without.pets # => []
|
262
|
+
# another_person_without.pets.take # => nil
|
263
|
+
# another_person_without.pets.take(2) # => []
|
230
264
|
def take(n = nil)
|
231
265
|
@association.take(n)
|
232
266
|
end
|
@@ -290,7 +324,7 @@ module ActiveRecord
|
|
290
324
|
@association.create(attributes, &block)
|
291
325
|
end
|
292
326
|
|
293
|
-
# Like
|
327
|
+
# Like #create, except that if the record is invalid, raises an exception.
|
294
328
|
#
|
295
329
|
# class Person
|
296
330
|
# has_many :pets
|
@@ -307,8 +341,8 @@ module ActiveRecord
|
|
307
341
|
end
|
308
342
|
|
309
343
|
# 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
|
344
|
+
# to the association's primary key. Since #<< flattens its argument list and
|
345
|
+
# inserts each record, +push+ and #concat behave identically. Returns +self+
|
312
346
|
# so method calls may be chained.
|
313
347
|
#
|
314
348
|
# class Person < ActiveRecord::Base
|
@@ -364,7 +398,7 @@ module ActiveRecord
|
|
364
398
|
# specified by the +:dependent+ option. If no +:dependent+ option is given,
|
365
399
|
# then it will follow the default strategy.
|
366
400
|
#
|
367
|
-
# For
|
401
|
+
# For <tt>has_many :through</tt> associations, the default deletion strategy is
|
368
402
|
# +:delete_all+.
|
369
403
|
#
|
370
404
|
# For +has_many+ associations, the default deletion strategy is +:nullify+.
|
@@ -399,7 +433,7 @@ module ActiveRecord
|
|
399
433
|
# # #<Pet id: 3, name: "Choo-Choo", person_id: nil>
|
400
434
|
# # ]
|
401
435
|
#
|
402
|
-
# Both +has_many+ and
|
436
|
+
# Both +has_many+ and <tt>has_many :through</tt> dependencies default to the
|
403
437
|
# +:delete_all+ strategy if the +:dependent+ option is set to +:destroy+.
|
404
438
|
# Records are not instantiated and callbacks will not be fired.
|
405
439
|
#
|
@@ -418,7 +452,7 @@ module ActiveRecord
|
|
418
452
|
# person.pets.delete_all
|
419
453
|
#
|
420
454
|
# Pet.find(1, 2, 3)
|
421
|
-
# # => ActiveRecord::RecordNotFound
|
455
|
+
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
|
422
456
|
#
|
423
457
|
# If it is set to <tt>:delete_all</tt>, all the objects are deleted
|
424
458
|
# *without* calling their +destroy+ method.
|
@@ -438,7 +472,7 @@ module ActiveRecord
|
|
438
472
|
# person.pets.delete_all
|
439
473
|
#
|
440
474
|
# Pet.find(1, 2, 3)
|
441
|
-
# # => ActiveRecord::RecordNotFound
|
475
|
+
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
|
442
476
|
def delete_all(dependent = nil)
|
443
477
|
@association.delete_all(dependent)
|
444
478
|
end
|
@@ -475,7 +509,7 @@ module ActiveRecord
|
|
475
509
|
# then it will follow the default strategy. Returns an array with the
|
476
510
|
# deleted records.
|
477
511
|
#
|
478
|
-
# For
|
512
|
+
# For <tt>has_many :through</tt> associations, the default deletion strategy is
|
479
513
|
# +:delete_all+.
|
480
514
|
#
|
481
515
|
# For +has_many+ associations, the default deletion strategy is +:nullify+.
|
@@ -532,7 +566,7 @@ module ActiveRecord
|
|
532
566
|
# # => [#<Pet id: 2, name: "Spook", person_id: 1>]
|
533
567
|
#
|
534
568
|
# Pet.find(1, 3)
|
535
|
-
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with
|
569
|
+
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 3)
|
536
570
|
#
|
537
571
|
# If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
|
538
572
|
# *without* calling their +destroy+ method.
|
@@ -560,7 +594,7 @@ module ActiveRecord
|
|
560
594
|
# # ]
|
561
595
|
#
|
562
596
|
# Pet.find(1)
|
563
|
-
# # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=1
|
597
|
+
# # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=1
|
564
598
|
#
|
565
599
|
# You can pass +Integer+ or +String+ values, it finds the records
|
566
600
|
# responding to the +id+ and executes delete on them.
|
@@ -624,7 +658,7 @@ module ActiveRecord
|
|
624
658
|
# person.pets.size # => 0
|
625
659
|
# person.pets # => []
|
626
660
|
#
|
627
|
-
# Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with
|
661
|
+
# Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
|
628
662
|
#
|
629
663
|
# You can pass +Integer+ or +String+ values, it finds the records
|
630
664
|
# responding to the +id+ and then deletes them from the database.
|
@@ -656,7 +690,7 @@ module ActiveRecord
|
|
656
690
|
# person.pets.size # => 0
|
657
691
|
# person.pets # => []
|
658
692
|
#
|
659
|
-
# Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with
|
693
|
+
# Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (4, 5, 6)
|
660
694
|
def destroy(*records)
|
661
695
|
@association.destroy(*records)
|
662
696
|
end
|
@@ -693,10 +727,16 @@ module ActiveRecord
|
|
693
727
|
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
694
728
|
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
695
729
|
# # ]
|
696
|
-
def count(column_name = nil
|
697
|
-
|
698
|
-
|
699
|
-
|
730
|
+
def count(column_name = nil)
|
731
|
+
@association.count(column_name)
|
732
|
+
end
|
733
|
+
|
734
|
+
def calculate(operation, column_name)
|
735
|
+
null_scope? ? scope.calculate(operation, column_name) : super
|
736
|
+
end
|
737
|
+
|
738
|
+
def pluck(*column_names)
|
739
|
+
null_scope? ? scope.pluck(*column_names) : super
|
700
740
|
end
|
701
741
|
|
702
742
|
# Returns the size of the collection. If the collection hasn't been loaded,
|
@@ -754,7 +794,7 @@ module ActiveRecord
|
|
754
794
|
# Returns +true+ if the collection is empty. If the collection has been
|
755
795
|
# loaded it is equivalent
|
756
796
|
# to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
|
757
|
-
# it is equivalent to <tt
|
797
|
+
# it is equivalent to <tt>!collection.exists?</tt>. If the collection has
|
758
798
|
# not already been loaded and you are going to fetch the records anyway it
|
759
799
|
# is better to check <tt>collection.length.zero?</tt>.
|
760
800
|
#
|
@@ -783,7 +823,7 @@ module ActiveRecord
|
|
783
823
|
# person.pets.any? # => false
|
784
824
|
#
|
785
825
|
# person.pets << Pet.new(name: 'Snoop')
|
786
|
-
# person.pets.count # =>
|
826
|
+
# person.pets.count # => 1
|
787
827
|
# person.pets.any? # => true
|
788
828
|
#
|
789
829
|
# You can also pass a +block+ to define criteria. The behavior
|
@@ -858,27 +898,14 @@ module ActiveRecord
|
|
858
898
|
!!@association.include?(record)
|
859
899
|
end
|
860
900
|
|
861
|
-
def arel
|
862
|
-
scope.arel
|
863
|
-
end
|
864
|
-
|
865
901
|
def proxy_association
|
866
902
|
@association
|
867
903
|
end
|
868
904
|
|
869
|
-
# We don't want this object to be put on the scoping stack, because
|
870
|
-
# that could create an infinite loop where we call an @association
|
871
|
-
# method, which gets the current scope, which is this object, which
|
872
|
-
# delegates to @association, and so on.
|
873
|
-
def scoping
|
874
|
-
@association.scope.scoping { yield }
|
875
|
-
end
|
876
|
-
|
877
905
|
# Returns a <tt>Relation</tt> object for the records in this association
|
878
906
|
def scope
|
879
|
-
@association.scope
|
907
|
+
@scope ||= @association.scope
|
880
908
|
end
|
881
|
-
alias spawn scope
|
882
909
|
|
883
910
|
# Equivalent to <tt>Array#==</tt>. Returns +true+ if the two arrays
|
884
911
|
# contain the same number of elements and if each element is equal
|
@@ -946,6 +973,10 @@ module ActiveRecord
|
|
946
973
|
end
|
947
974
|
alias_method :to_a, :to_ary
|
948
975
|
|
976
|
+
def records # :nodoc:
|
977
|
+
load_target
|
978
|
+
end
|
979
|
+
|
949
980
|
# Adds one or more +records+ to the collection by setting their foreign keys
|
950
981
|
# to the association's primary key. Returns +self+, so several appends may be
|
951
982
|
# chained together.
|
@@ -973,12 +1004,15 @@ module ActiveRecord
|
|
973
1004
|
alias_method :append, :<<
|
974
1005
|
|
975
1006
|
def prepend(*args)
|
976
|
-
raise NoMethodError, "prepend on association is not defined. Please use
|
1007
|
+
raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
|
977
1008
|
end
|
978
1009
|
|
979
1010
|
# Equivalent to +delete_all+. The difference is that returns +self+, instead
|
980
1011
|
# of an array with the deleted objects, so methods can be chained. See
|
981
1012
|
# +delete_all+ for more information.
|
1013
|
+
# Note that because +delete_all+ removes records by directly
|
1014
|
+
# running an SQL query into the database, the +updated_at+ column of
|
1015
|
+
# the object is not changed.
|
982
1016
|
def clear
|
983
1017
|
delete_all
|
984
1018
|
self
|
@@ -1004,7 +1038,7 @@ module ActiveRecord
|
|
1004
1038
|
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
|
1005
1039
|
def reload
|
1006
1040
|
proxy_association.reload
|
1007
|
-
|
1041
|
+
reset_scope
|
1008
1042
|
end
|
1009
1043
|
|
1010
1044
|
# Unloads the association. Returns +self+.
|
@@ -1026,8 +1060,32 @@ module ActiveRecord
|
|
1026
1060
|
def reset
|
1027
1061
|
proxy_association.reset
|
1028
1062
|
proxy_association.reset_scope
|
1063
|
+
reset_scope
|
1064
|
+
end
|
1065
|
+
|
1066
|
+
def reset_scope # :nodoc:
|
1067
|
+
@scope = nil
|
1029
1068
|
self
|
1030
1069
|
end
|
1070
|
+
|
1071
|
+
delegate_methods = [
|
1072
|
+
QueryMethods,
|
1073
|
+
SpawnMethods,
|
1074
|
+
].flat_map { |klass|
|
1075
|
+
klass.public_instance_methods(false)
|
1076
|
+
} - self.public_instance_methods(false) + [:scoping]
|
1077
|
+
|
1078
|
+
delegate(*delegate_methods, to: :scope)
|
1079
|
+
|
1080
|
+
private
|
1081
|
+
|
1082
|
+
def null_scope?
|
1083
|
+
@association.null_scope?
|
1084
|
+
end
|
1085
|
+
|
1086
|
+
def exec_queries
|
1087
|
+
load_target
|
1088
|
+
end
|
1031
1089
|
end
|
1032
1090
|
end
|
1033
1091
|
end
|