activerecord 6.1.7 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +520 -1385
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +2 -12
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +60 -21
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +37 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +41 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +4 -4
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +46 -36
- data/lib/active_record/associations/collection_proxy.rb +44 -16
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +29 -19
- data/lib/active_record/associations/has_many_through_association.rb +12 -7
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +23 -15
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +212 -53
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -16
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +15 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +404 -509
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +2 -14
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +47 -27
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +14 -11
- data/lib/active_record/attribute_methods/serialization.rb +174 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -9
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +164 -52
- data/lib/active_record/attributes.rb +51 -49
- data/lib/active_record/autosave_association.rb +74 -57
- data/lib/active_record/base.rb +27 -5
- data/lib/active_record/callbacks.rb +18 -34
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -46
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +327 -612
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +377 -142
- data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
- data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +345 -166
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
- data/lib/active_record/connection_adapters/pool_config.rb +26 -16
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +401 -77
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +518 -251
- data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +130 -6
- data/lib/active_record/connection_handling.rb +132 -146
- data/lib/active_record/core.rb +276 -251
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -3
- data/lib/active_record/database_configurations/database_config.rb +34 -10
- data/lib/active_record/database_configurations/hash_config.rb +107 -31
- data/lib/active_record/database_configurations/url_config.rb +38 -13
- data/lib/active_record/database_configurations.rb +96 -60
- data/lib/active_record/delegated_type.rb +90 -20
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +4 -2
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +3 -3
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +170 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +56 -0
- data/lib/active_record/enum.rb +163 -63
- data/lib/active_record/errors.rb +210 -27
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +179 -112
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +85 -31
- data/lib/active_record/insert_all.rb +148 -32
- data/lib/active_record/integration.rb +14 -10
- data/lib/active_record/internal_metadata.rb +123 -23
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +43 -27
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +41 -29
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +113 -16
- data/lib/active_record/migration/compatibility.rb +235 -46
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +374 -177
- data/lib/active_record/model_schema.rb +143 -159
- data/lib/active_record/nested_attributes.rb +48 -21
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +282 -283
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +19 -25
- data/lib/active_record/query_logs.rb +189 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +44 -9
- data/lib/active_record/railtie.rb +234 -71
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +189 -256
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +325 -103
- data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +300 -111
- data/lib/active_record/relation/delegation.rb +33 -22
- data/lib/active_record/relation/finder_methods.rb +123 -52
- data/lib/active_record/relation/merger.rb +26 -19
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +29 -22
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +842 -150
- data/lib/active_record/relation/record_fetch_warning.rb +10 -9
- data/lib/active_record/relation/spawn_methods.rb +7 -6
- data/lib/active_record/relation/where_clause.rb +15 -36
- data/lib/active_record/relation.rb +736 -145
- data/lib/active_record/result.rb +67 -54
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +84 -34
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +90 -31
- data/lib/active_record/schema_migration.rb +74 -23
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +30 -9
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +277 -149
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +173 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +32 -19
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +118 -41
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -7
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -7
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +64 -15
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +444 -32
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +7 -2
- data/lib/arel/predications.rb +14 -4
- data/lib/arel/select_manager.rb +11 -5
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +20 -5
- data/lib/arel/visitors/dot.rb +81 -90
- data/lib/arel/visitors/mysql.rb +23 -5
- data/lib/arel/visitors/postgresql.rb +1 -22
- data/lib/arel/visitors/to_sql.rb +170 -36
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +23 -4
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +100 -14
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -3,7 +3,7 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
5
5
|
# = Active Record Belongs To Association
|
6
|
-
class BelongsToAssociation < SingularAssociation
|
6
|
+
class BelongsToAssociation < SingularAssociation # :nodoc:
|
7
7
|
def handle_dependency
|
8
8
|
return unless load_target
|
9
9
|
|
@@ -11,8 +11,13 @@ module ActiveRecord
|
|
11
11
|
when :destroy
|
12
12
|
raise ActiveRecord::Rollback unless target.destroy
|
13
13
|
when :destroy_async
|
14
|
-
|
15
|
-
|
14
|
+
if reflection.foreign_key.is_a?(Array)
|
15
|
+
primary_key_column = reflection.active_record_primary_key
|
16
|
+
id = reflection.foreign_key.map { |col| owner.public_send(col) }
|
17
|
+
else
|
18
|
+
primary_key_column = reflection.active_record_primary_key
|
19
|
+
id = owner.public_send(reflection.foreign_key)
|
20
|
+
end
|
16
21
|
|
17
22
|
enqueue_destroy_association(
|
18
23
|
owner_model_name: owner.class.to_s,
|
@@ -55,7 +60,8 @@ module ActiveRecord
|
|
55
60
|
|
56
61
|
def decrement_counters_before_last_save
|
57
62
|
if reflection.polymorphic?
|
58
|
-
|
63
|
+
model_type_was = owner.attribute_before_last_save(reflection.foreign_type)
|
64
|
+
model_was = owner.class.polymorphic_class_for(model_type_was) if model_type_was
|
59
65
|
else
|
60
66
|
model_was = klass
|
61
67
|
end
|
@@ -68,6 +74,14 @@ module ActiveRecord
|
|
68
74
|
end
|
69
75
|
|
70
76
|
def target_changed?
|
77
|
+
owner.attribute_changed?(reflection.foreign_key) || (!foreign_key_present? && target&.new_record?)
|
78
|
+
end
|
79
|
+
|
80
|
+
def target_previously_changed?
|
81
|
+
owner.attribute_previously_changed?(reflection.foreign_key)
|
82
|
+
end
|
83
|
+
|
84
|
+
def saved_change_to_target?
|
71
85
|
owner.saved_change_to_attribute?(reflection.foreign_key)
|
72
86
|
end
|
73
87
|
|
@@ -77,6 +91,8 @@ module ActiveRecord
|
|
77
91
|
raise_on_type_mismatch!(record)
|
78
92
|
set_inverse_instance(record)
|
79
93
|
@updated = true
|
94
|
+
elsif target
|
95
|
+
remove_inverse_instance(target)
|
80
96
|
end
|
81
97
|
|
82
98
|
replace_keys(record, force: true)
|
@@ -108,10 +124,21 @@ module ActiveRecord
|
|
108
124
|
end
|
109
125
|
|
110
126
|
def replace_keys(record, force: false)
|
111
|
-
|
127
|
+
reflection_fk = reflection.foreign_key
|
128
|
+
if reflection_fk.is_a?(Array)
|
129
|
+
target_key_values = record ? Array(primary_key(record.class)).map { |key| record._read_attribute(key) } : []
|
130
|
+
|
131
|
+
if force || reflection_fk.map { |fk| owner._read_attribute(fk) } != target_key_values
|
132
|
+
reflection_fk.each_with_index do |key, index|
|
133
|
+
owner[key] = target_key_values[index]
|
134
|
+
end
|
135
|
+
end
|
136
|
+
else
|
137
|
+
target_key_value = record ? record._read_attribute(primary_key(record.class)) : nil
|
112
138
|
|
113
|
-
|
114
|
-
|
139
|
+
if force || owner._read_attribute(reflection_fk) != target_key_value
|
140
|
+
owner[reflection_fk] = target_key_value
|
141
|
+
end
|
115
142
|
end
|
116
143
|
end
|
117
144
|
|
@@ -120,17 +147,16 @@ module ActiveRecord
|
|
120
147
|
end
|
121
148
|
|
122
149
|
def foreign_key_present?
|
123
|
-
|
150
|
+
Array(reflection.foreign_key).all? { |fk| owner._read_attribute(fk) }
|
124
151
|
end
|
125
152
|
|
126
153
|
def invertible_for?(record)
|
127
154
|
inverse = inverse_reflection_for(record)
|
128
|
-
inverse && (inverse.has_one? ||
|
155
|
+
inverse && (inverse.has_one? || inverse.klass.has_many_inversing)
|
129
156
|
end
|
130
157
|
|
131
158
|
def stale_state
|
132
|
-
|
133
|
-
result && result.to_s
|
159
|
+
owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
|
134
160
|
end
|
135
161
|
end
|
136
162
|
end
|
@@ -3,13 +3,21 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
5
5
|
# = Active Record Belongs To Polymorphic Association
|
6
|
-
class BelongsToPolymorphicAssociation < BelongsToAssociation
|
6
|
+
class BelongsToPolymorphicAssociation < BelongsToAssociation # :nodoc:
|
7
7
|
def klass
|
8
8
|
type = owner[reflection.foreign_type]
|
9
9
|
type.presence && owner.class.polymorphic_class_for(type)
|
10
10
|
end
|
11
11
|
|
12
12
|
def target_changed?
|
13
|
+
super || owner.attribute_changed?(reflection.foreign_type)
|
14
|
+
end
|
15
|
+
|
16
|
+
def target_previously_changed?
|
17
|
+
super || owner.attribute_previously_changed?(reflection.foreign_type)
|
18
|
+
end
|
19
|
+
|
20
|
+
def saved_change_to_target?
|
13
21
|
super || owner.saved_change_to_attribute?(reflection.foreign_type)
|
14
22
|
end
|
15
23
|
|
@@ -19,7 +27,7 @@ module ActiveRecord
|
|
19
27
|
|
20
28
|
target_type = record ? record.class.polymorphic_name : nil
|
21
29
|
|
22
|
-
if force || owner
|
30
|
+
if force || owner._read_attribute(reflection.foreign_type) != target_type
|
23
31
|
owner[reflection.foreign_type] = target_type
|
24
32
|
end
|
25
33
|
end
|
@@ -33,8 +41,9 @@ module ActiveRecord
|
|
33
41
|
end
|
34
42
|
|
35
43
|
def stale_state
|
36
|
-
foreign_key = super
|
37
|
-
|
44
|
+
if foreign_key = super
|
45
|
+
[foreign_key, owner[reflection.foreign_type]]
|
46
|
+
end
|
38
47
|
end
|
39
48
|
end
|
40
49
|
end
|
@@ -12,14 +12,14 @@
|
|
12
12
|
# - HasManyAssociation
|
13
13
|
|
14
14
|
module ActiveRecord::Associations::Builder # :nodoc:
|
15
|
-
class Association
|
15
|
+
class Association # :nodoc:
|
16
16
|
class << self
|
17
17
|
attr_accessor :extensions
|
18
18
|
end
|
19
19
|
self.extensions = []
|
20
20
|
|
21
21
|
VALID_OPTIONS = [
|
22
|
-
:class_name, :anonymous_class, :primary_key, :foreign_key, :dependent, :validate, :inverse_of, :strict_loading
|
22
|
+
:class_name, :anonymous_class, :primary_key, :foreign_key, :dependent, :validate, :inverse_of, :strict_loading, :query_constraints
|
23
23
|
].freeze # :nodoc:
|
24
24
|
|
25
25
|
def self.build(model, name, scope, options, &block)
|
@@ -33,6 +33,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
33
33
|
define_accessors model, reflection
|
34
34
|
define_callbacks model, reflection
|
35
35
|
define_validations model, reflection
|
36
|
+
define_change_tracking_methods model, reflection
|
36
37
|
reflection
|
37
38
|
end
|
38
39
|
|
@@ -117,14 +118,18 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
117
118
|
# noop
|
118
119
|
end
|
119
120
|
|
121
|
+
def self.define_change_tracking_methods(model, reflection)
|
122
|
+
# noop
|
123
|
+
end
|
124
|
+
|
120
125
|
def self.valid_dependent_options
|
121
126
|
raise NotImplementedError
|
122
127
|
end
|
123
128
|
|
124
129
|
def self.check_dependent_options(dependent, model)
|
125
130
|
if dependent == :destroy_async && !model.destroy_association_async_job
|
126
|
-
err_message = "
|
127
|
-
raise ActiveRecord::
|
131
|
+
err_message = "A valid destroy_association_async_job is required to use `dependent: :destroy_async` on associations"
|
132
|
+
raise ActiveRecord::ConfigurationError, err_message
|
128
133
|
end
|
129
134
|
unless valid_dependent_options.include? dependent
|
130
135
|
raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
|
@@ -158,6 +163,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
158
163
|
|
159
164
|
private_class_method :build_scope, :macro, :valid_options, :validate_options, :define_extensions,
|
160
165
|
:define_callbacks, :define_accessors, :define_readers, :define_writers, :define_validations,
|
161
|
-
:
|
166
|
+
:define_change_tracking_methods, :valid_dependent_options, :check_dependent_options,
|
167
|
+
:add_destroy_callbacks, :add_after_commit_jobs_callback
|
162
168
|
end
|
163
169
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord::Associations::Builder # :nodoc:
|
4
|
-
class BelongsTo < SingularAssociation
|
4
|
+
class BelongsTo < SingularAssociation # :nodoc:
|
5
5
|
def self.macro
|
6
6
|
:belongs_to
|
7
7
|
end
|
@@ -30,17 +30,18 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
30
30
|
model.after_update lambda { |record|
|
31
31
|
association = association(reflection.name)
|
32
32
|
|
33
|
-
if association.
|
33
|
+
if association.saved_change_to_target?
|
34
34
|
association.increment_counters
|
35
35
|
association.decrement_counters_before_last_save
|
36
36
|
end
|
37
37
|
}
|
38
38
|
|
39
39
|
klass = reflection.class_name.safe_constantize
|
40
|
-
klass.
|
40
|
+
klass._counter_cache_columns |= [cache_column] if klass && klass.respond_to?(:_counter_cache_columns)
|
41
|
+
model.counter_cached_association_names |= [reflection.name]
|
41
42
|
end
|
42
43
|
|
43
|
-
def self.touch_record(o, changes, foreign_key, name, touch
|
44
|
+
def self.touch_record(o, changes, foreign_key, name, touch) # :nodoc:
|
44
45
|
old_foreign_id = changes[foreign_key] && changes[foreign_key].first
|
45
46
|
|
46
47
|
if old_foreign_id
|
@@ -49,7 +50,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
49
50
|
if reflection.polymorphic?
|
50
51
|
foreign_type = reflection.foreign_type
|
51
52
|
klass = changes[foreign_type] && changes[foreign_type].first || o.public_send(foreign_type)
|
52
|
-
klass = klass
|
53
|
+
klass = o.class.polymorphic_class_for(klass)
|
53
54
|
else
|
54
55
|
klass = association.klass
|
55
56
|
end
|
@@ -58,9 +59,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
58
59
|
|
59
60
|
if old_record
|
60
61
|
if touch != true
|
61
|
-
old_record.
|
62
|
+
old_record.touch_later(touch)
|
62
63
|
else
|
63
|
-
old_record.
|
64
|
+
old_record.touch_later
|
64
65
|
end
|
65
66
|
end
|
66
67
|
end
|
@@ -68,9 +69,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
68
69
|
record = o.public_send name
|
69
70
|
if record && record.persisted?
|
70
71
|
if touch != true
|
71
|
-
record.
|
72
|
+
record.touch_later(touch)
|
72
73
|
else
|
73
|
-
record.
|
74
|
+
record.touch_later
|
74
75
|
end
|
75
76
|
end
|
76
77
|
end
|
@@ -81,13 +82,13 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
81
82
|
touch = reflection.options[:touch]
|
82
83
|
|
83
84
|
callback = lambda { |changes_method| lambda { |record|
|
84
|
-
BelongsTo.touch_record(record, record.send(changes_method), foreign_key, name, touch
|
85
|
+
BelongsTo.touch_record(record, record.send(changes_method), foreign_key, name, touch)
|
85
86
|
}}
|
86
87
|
|
87
88
|
if reflection.counter_cache_column
|
88
89
|
touch_callback = callback.(:saved_changes)
|
89
90
|
update_callback = lambda { |record|
|
90
|
-
instance_exec(record, &touch_callback) unless association(reflection.name).
|
91
|
+
instance_exec(record, &touch_callback) unless association(reflection.name).saved_change_to_target?
|
91
92
|
}
|
92
93
|
model.after_update update_callback, if: :saved_changes?
|
93
94
|
else
|
@@ -123,11 +124,37 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
123
124
|
super
|
124
125
|
|
125
126
|
if required
|
126
|
-
|
127
|
+
if ActiveRecord.belongs_to_required_validates_foreign_key
|
128
|
+
model.validates_presence_of reflection.name, message: :required
|
129
|
+
else
|
130
|
+
condition = lambda { |record|
|
131
|
+
foreign_key = reflection.foreign_key
|
132
|
+
foreign_type = reflection.foreign_type
|
133
|
+
|
134
|
+
record.read_attribute(foreign_key).nil? ||
|
135
|
+
record.attribute_changed?(foreign_key) ||
|
136
|
+
(reflection.polymorphic? && (record.read_attribute(foreign_type).nil? || record.attribute_changed?(foreign_type)))
|
137
|
+
}
|
138
|
+
|
139
|
+
model.validates_presence_of reflection.name, message: :required, if: condition
|
140
|
+
end
|
127
141
|
end
|
128
142
|
end
|
129
143
|
|
130
|
-
|
131
|
-
|
144
|
+
def self.define_change_tracking_methods(model, reflection)
|
145
|
+
model.generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
146
|
+
def #{reflection.name}_changed?
|
147
|
+
association(:#{reflection.name}).target_changed?
|
148
|
+
end
|
149
|
+
|
150
|
+
def #{reflection.name}_previously_changed?
|
151
|
+
association(:#{reflection.name}).target_previously_changed?
|
152
|
+
end
|
153
|
+
CODE
|
154
|
+
end
|
155
|
+
|
156
|
+
private_class_method :macro, :valid_options, :valid_dependent_options, :define_callbacks,
|
157
|
+
:define_validations, :define_change_tracking_methods, :add_counter_cache_callbacks,
|
158
|
+
:add_touch_callbacks, :add_default_callbacks, :add_destroy_callbacks
|
132
159
|
end
|
133
160
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "active_record/associations"
|
4
4
|
|
5
5
|
module ActiveRecord::Associations::Builder # :nodoc:
|
6
|
-
class CollectionAssociation < Association
|
6
|
+
class CollectionAssociation < Association # :nodoc:
|
7
7
|
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
|
8
8
|
|
9
9
|
def self.valid_options(options)
|
@@ -30,11 +30,18 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
30
30
|
def self.define_callback(model, callback_name, name, options)
|
31
31
|
full_callback_name = "#{callback_name}_for_#{name}"
|
32
32
|
|
33
|
-
|
33
|
+
callback_values = Array(options[callback_name.to_sym])
|
34
|
+
method_defined = model.respond_to?(full_callback_name)
|
35
|
+
|
36
|
+
# If there are no callbacks, we must also check if a superclass had
|
37
|
+
# previously defined this association
|
38
|
+
return if callback_values.empty? && !method_defined
|
39
|
+
|
40
|
+
unless method_defined
|
34
41
|
model.class_attribute(full_callback_name, instance_accessor: false, instance_predicate: false)
|
35
42
|
end
|
36
43
|
|
37
|
-
callbacks =
|
44
|
+
callbacks = callback_values.map do |callback|
|
38
45
|
case callback
|
39
46
|
when Symbol
|
40
47
|
->(method, owner, record) { owner.send(callback, record) }
|
@@ -20,6 +20,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
20
20
|
attr_accessor :right_reflection
|
21
21
|
end
|
22
22
|
|
23
|
+
@table_name = nil
|
23
24
|
def self.table_name
|
24
25
|
# Table name needs to be resolved lazily
|
25
26
|
# because RHS class might not have been loaded
|
@@ -41,14 +42,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
41
42
|
self.right_reflection = _reflect_on_association(rhs_name)
|
42
43
|
end
|
43
44
|
|
44
|
-
def self.
|
45
|
-
left_model.
|
45
|
+
def self.connection_pool
|
46
|
+
left_model.connection_pool
|
46
47
|
end
|
47
|
-
|
48
|
-
private
|
49
|
-
def self.suppress_composite_primary_key(pk)
|
50
|
-
pk unless pk.is_a?(Array)
|
51
|
-
end
|
52
48
|
}
|
53
49
|
|
54
50
|
join_model.name = "HABTM_#{association_name.to_s.camelize}"
|
@@ -1,15 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord::Associations::Builder # :nodoc:
|
4
|
-
class HasMany < CollectionAssociation
|
4
|
+
class HasMany < CollectionAssociation # :nodoc:
|
5
5
|
def self.macro
|
6
6
|
:has_many
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.valid_options(options)
|
10
|
-
valid = super + [:counter_cache, :join_table, :index_errors, :
|
11
|
-
valid += [:
|
12
|
-
valid += [:
|
10
|
+
valid = super + [:counter_cache, :join_table, :index_errors, :as, :through]
|
11
|
+
valid += [:foreign_type] if options[:as]
|
12
|
+
valid += [:source, :source_type, :disable_joins] if options[:through]
|
13
13
|
valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
|
14
14
|
valid
|
15
15
|
end
|
@@ -1,16 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord::Associations::Builder # :nodoc:
|
4
|
-
class HasOne < SingularAssociation
|
4
|
+
class HasOne < SingularAssociation # :nodoc:
|
5
5
|
def self.macro
|
6
6
|
:has_one
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.valid_options(options)
|
10
|
-
valid = super
|
11
|
-
valid += [:
|
10
|
+
valid = super + [:as, :through]
|
11
|
+
valid += [:foreign_type] if options[:as]
|
12
12
|
valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
|
13
|
-
valid += [:
|
13
|
+
valid += [:source, :source_type, :disable_joins] if options[:through]
|
14
14
|
valid
|
15
15
|
end
|
16
16
|
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# This class is inherited by the has_one and belongs_to association classes
|
4
4
|
|
5
5
|
module ActiveRecord::Associations::Builder # :nodoc:
|
6
|
-
class SingularAssociation < Association
|
6
|
+
class SingularAssociation < Association # :nodoc:
|
7
7
|
def self.valid_options(options)
|
8
8
|
super + [:required, :touch]
|
9
9
|
end
|
@@ -13,12 +13,16 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
13
13
|
mixin = model.generated_association_methods
|
14
14
|
name = reflection.name
|
15
15
|
|
16
|
-
define_constructors(mixin, name)
|
16
|
+
define_constructors(mixin, name) unless reflection.polymorphic?
|
17
17
|
|
18
18
|
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
19
19
|
def reload_#{name}
|
20
20
|
association(:#{name}).force_reload_reader
|
21
21
|
end
|
22
|
+
|
23
|
+
def reset_#{name}
|
24
|
+
association(:#{name}).reset
|
25
|
+
end
|
22
26
|
CODE
|
23
27
|
end
|
24
28
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/enumerable"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module Associations
|
5
7
|
# = Active Record Association Collection
|
@@ -14,7 +16,7 @@ module ActiveRecord
|
|
14
16
|
#
|
15
17
|
# The CollectionAssociation class provides common methods to the collections
|
16
18
|
# defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
|
17
|
-
# the
|
19
|
+
# the <tt>:through association</tt> option.
|
18
20
|
#
|
19
21
|
# You need to be careful with assumptions regarding the target: The proxy
|
20
22
|
# does not fetch records from the database until it needs them, but new
|
@@ -25,9 +27,13 @@ module ActiveRecord
|
|
25
27
|
#
|
26
28
|
# If you need to work on all current children, new and existing records,
|
27
29
|
# +load_target+ and the +loaded+ flag are your friends.
|
28
|
-
class CollectionAssociation < Association
|
30
|
+
class CollectionAssociation < Association # :nodoc:
|
31
|
+
attr_accessor :nested_attributes_target
|
32
|
+
|
29
33
|
# Implements the reader method, e.g. foo.items for Foo.has_many :items
|
30
34
|
def reader
|
35
|
+
ensure_klass_exists!
|
36
|
+
|
31
37
|
if stale_target?
|
32
38
|
reload
|
33
39
|
end
|
@@ -44,11 +50,11 @@ module ActiveRecord
|
|
44
50
|
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
|
45
51
|
def ids_reader
|
46
52
|
if loaded?
|
47
|
-
target.pluck(reflection.association_primary_key)
|
53
|
+
target.pluck(*reflection.association_primary_key)
|
48
54
|
elsif !target.empty?
|
49
|
-
load_target.pluck(reflection.association_primary_key)
|
55
|
+
load_target.pluck(*reflection.association_primary_key)
|
50
56
|
else
|
51
|
-
@association_ids ||= scope.pluck(reflection.association_primary_key)
|
57
|
+
@association_ids ||= scope.pluck(*reflection.association_primary_key)
|
52
58
|
end
|
53
59
|
end
|
54
60
|
|
@@ -57,14 +63,20 @@ module ActiveRecord
|
|
57
63
|
primary_key = reflection.association_primary_key
|
58
64
|
pk_type = klass.type_for_attribute(primary_key)
|
59
65
|
ids = Array(ids).compact_blank
|
60
|
-
ids.map! { |
|
66
|
+
ids.map! { |id| pk_type.cast(id) }
|
61
67
|
|
62
|
-
records = klass.
|
63
|
-
|
68
|
+
records = if klass.composite_primary_key?
|
69
|
+
klass.where(primary_key => ids).index_by do |record|
|
70
|
+
primary_key.map { |primary_key| record._read_attribute(primary_key) }
|
71
|
+
end
|
72
|
+
else
|
73
|
+
klass.where(primary_key => ids).index_by do |record|
|
74
|
+
record._read_attribute(primary_key)
|
75
|
+
end
|
64
76
|
end.values_at(*ids).compact
|
65
77
|
|
66
78
|
if records.size != ids.size
|
67
|
-
found_ids = records.map { |record| record.
|
79
|
+
found_ids = records.map { |record| record._read_attribute(primary_key) }
|
68
80
|
not_found_ids = ids - found_ids
|
69
81
|
klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids)
|
70
82
|
else
|
@@ -75,7 +87,7 @@ module ActiveRecord
|
|
75
87
|
def reset
|
76
88
|
super
|
77
89
|
@target = []
|
78
|
-
@replaced_or_added_targets = Set.new
|
90
|
+
@replaced_or_added_targets = Set.new.compare_by_identity
|
79
91
|
@association_ids = nil
|
80
92
|
end
|
81
93
|
|
@@ -115,28 +127,13 @@ module ActiveRecord
|
|
115
127
|
def concat(*records)
|
116
128
|
records = records.flatten
|
117
129
|
if owner.new_record?
|
118
|
-
load_target
|
130
|
+
skip_strict_loading { load_target }
|
119
131
|
concat_records(records)
|
120
132
|
else
|
121
133
|
transaction { concat_records(records) }
|
122
134
|
end
|
123
135
|
end
|
124
136
|
|
125
|
-
# Starts a transaction in the association class's database connection.
|
126
|
-
#
|
127
|
-
# class Author < ActiveRecord::Base
|
128
|
-
# has_many :books
|
129
|
-
# end
|
130
|
-
#
|
131
|
-
# Author.first.books.transaction do
|
132
|
-
# # same effect as calling Book.transaction
|
133
|
-
# end
|
134
|
-
def transaction(*args)
|
135
|
-
reflection.klass.transaction(*args) do
|
136
|
-
yield
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
137
|
# Removes all records from the association without calling callbacks
|
141
138
|
# on the associated records. It honors the +:dependent+ option. However
|
142
139
|
# if the +:dependent+ value is +:destroy+ then in that case the +:delete_all+
|
@@ -191,7 +188,7 @@ module ActiveRecord
|
|
191
188
|
end
|
192
189
|
|
193
190
|
# Deletes the +records+ and removes them from this association calling
|
194
|
-
# +before_remove
|
191
|
+
# +before_remove+, +after_remove+, +before_destroy+ and +after_destroy+ callbacks.
|
195
192
|
#
|
196
193
|
# Note that this method removes records from the database ignoring the
|
197
194
|
# +:dependent+ option.
|
@@ -233,7 +230,7 @@ module ActiveRecord
|
|
233
230
|
# loaded and you are going to fetch the records anyway it is better to
|
234
231
|
# check <tt>collection.length.zero?</tt>.
|
235
232
|
def empty?
|
236
|
-
if loaded? || @association_ids || reflection.
|
233
|
+
if loaded? || @association_ids || reflection.has_active_cached_counter?
|
237
234
|
size.zero?
|
238
235
|
else
|
239
236
|
target.empty? && !scope.exists?
|
@@ -244,7 +241,7 @@ module ActiveRecord
|
|
244
241
|
# and delete/add only records that have changed.
|
245
242
|
def replace(other_array)
|
246
243
|
other_array.each { |val| raise_on_type_mismatch!(val) }
|
247
|
-
original_target = load_target.dup
|
244
|
+
original_target = skip_strict_loading { load_target }.dup
|
248
245
|
|
249
246
|
if owner.new_record?
|
250
247
|
replace_records(other_array, original_target)
|
@@ -284,9 +281,11 @@ module ActiveRecord
|
|
284
281
|
end
|
285
282
|
|
286
283
|
def target=(record)
|
287
|
-
return super unless
|
284
|
+
return super unless reflection.klass.has_many_inversing
|
288
285
|
|
289
286
|
case record
|
287
|
+
when nil
|
288
|
+
# It's not possible to remove the record from the inverse association.
|
290
289
|
when Array
|
291
290
|
super
|
292
291
|
else
|
@@ -306,13 +305,21 @@ module ActiveRecord
|
|
306
305
|
|
307
306
|
def find_from_target?
|
308
307
|
loaded? ||
|
309
|
-
owner.strict_loading? ||
|
308
|
+
(owner.strict_loading? && owner.strict_loading_all?) ||
|
310
309
|
reflection.strict_loading? ||
|
311
310
|
owner.new_record? ||
|
312
311
|
target.any? { |record| record.new_record? || record.changed? }
|
313
312
|
end
|
314
313
|
|
314
|
+
def collection?
|
315
|
+
true
|
316
|
+
end
|
317
|
+
|
315
318
|
private
|
319
|
+
def transaction(&block)
|
320
|
+
reflection.klass.transaction(&block)
|
321
|
+
end
|
322
|
+
|
316
323
|
# We have some records loaded from the database (persisted) and some that are
|
317
324
|
# in-memory (memory). The same record may be represented in the persisted array
|
318
325
|
# and in the memory array.
|
@@ -325,13 +332,12 @@ module ActiveRecord
|
|
325
332
|
# * Otherwise, attributes should have the value found in the database
|
326
333
|
def merge_target_lists(persisted, memory)
|
327
334
|
return persisted if memory.empty?
|
328
|
-
return memory if persisted.empty?
|
329
335
|
|
330
336
|
persisted.map! do |record|
|
331
337
|
if mem_record = memory.delete(record)
|
332
338
|
|
333
|
-
((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save).each do |name|
|
334
|
-
mem_record
|
339
|
+
((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save - mem_record.class._attr_readonly).each do |name|
|
340
|
+
mem_record._write_attribute(name, record[name])
|
335
341
|
end
|
336
342
|
|
337
343
|
mem_record
|
@@ -345,7 +351,7 @@ module ActiveRecord
|
|
345
351
|
|
346
352
|
def _create_record(attributes, raise = false, &block)
|
347
353
|
unless owner.persisted?
|
348
|
-
raise ActiveRecord::RecordNotSaved
|
354
|
+
raise ActiveRecord::RecordNotSaved.new("You cannot call create unless the parent is saved", owner)
|
349
355
|
end
|
350
356
|
|
351
357
|
if attributes.is_a?(Array)
|
@@ -489,7 +495,11 @@ module ActiveRecord
|
|
489
495
|
|
490
496
|
def callbacks_for(callback_name)
|
491
497
|
full_callback_name = "#{callback_name}_for_#{reflection.name}"
|
492
|
-
owner.class.
|
498
|
+
if owner.class.respond_to?(full_callback_name)
|
499
|
+
owner.class.send(full_callback_name)
|
500
|
+
else
|
501
|
+
[]
|
502
|
+
end
|
493
503
|
end
|
494
504
|
|
495
505
|
def include_in_memory?(record)
|