activerecord 6.0.0 → 7.2.3
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 +996 -594
- data/MIT-LICENSE +1 -1
- data/README.rdoc +34 -34
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +22 -20
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +41 -30
- data/lib/active_record/associations/association.rb +106 -41
- data/lib/active_record/associations/association_scope.rb +30 -21
- data/lib/active_record/associations/belongs_to_association.rb +69 -14
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
- data/lib/active_record/associations/builder/association.rb +39 -6
- data/lib/active_record/associations/builder/belongs_to.rb +47 -17
- data/lib/active_record/associations/builder/collection_association.rb +14 -6
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
- data/lib/active_record/associations/builder/has_many.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +13 -16
- data/lib/active_record/associations/builder/singular_association.rb +7 -3
- data/lib/active_record/associations/collection_association.rb +90 -53
- data/lib/active_record/associations/collection_proxy.rb +54 -19
- 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 +21 -1
- data/lib/active_record/associations/has_many_association.rb +41 -10
- data/lib/active_record/associations/has_many_through_association.rb +29 -12
- data/lib/active_record/associations/has_one_association.rb +33 -9
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/join_dependency.rb +97 -54
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +237 -54
- 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 +51 -17
- data/lib/active_record/associations/preloader.rb +55 -121
- data/lib/active_record/associations/singular_association.rb +16 -4
- data/lib/active_record/associations/through_association.rb +26 -15
- data/lib/active_record/associations.rb +454 -440
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +11 -14
- data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +75 -34
- data/lib/active_record/attribute_methods/primary_key.rb +53 -31
- data/lib/active_record/attribute_methods/query.rb +31 -22
- data/lib/active_record/attribute_methods/read.rb +16 -17
- data/lib/active_record/attribute_methods/serialization.rb +177 -35
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
- data/lib/active_record/attribute_methods/write.rb +16 -28
- data/lib/active_record/attribute_methods.rb +227 -100
- data/lib/active_record/attributes.rb +94 -56
- data/lib/active_record/autosave_association.rb +119 -73
- data/lib/active_record/base.rb +31 -21
- data/lib/active_record/callbacks.rb +168 -55
- 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 -25
- 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 +367 -565
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
- data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
- data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
- data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
- data/lib/active_record/connection_adapters/column.rb +28 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
- data/lib/active_record/connection_adapters/pool_config.rb +83 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- 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 +23 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
- data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
- 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 +102 -24
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
- 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 +176 -0
- data/lib/active_record/connection_handling.rb +243 -115
- data/lib/active_record/core.rb +481 -199
- data/lib/active_record/counter_cache.rb +69 -32
- data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
- data/lib/active_record/database_configurations/database_config.rb +77 -10
- data/lib/active_record/database_configurations/hash_config.rb +148 -26
- data/lib/active_record/database_configurations/url_config.rb +44 -45
- data/lib/active_record/database_configurations.rb +190 -114
- data/lib/active_record/delegated_type.rb +279 -0
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +38 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +5 -6
- 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 +171 -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 +58 -0
- data/lib/active_record/enum.rb +224 -73
- data/lib/active_record/errors.rb +254 -36
- data/lib/active_record/explain.rb +30 -17
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +22 -15
- data/lib/active_record/fixture_set/model_metadata.rb +15 -6
- data/lib/active_record/fixture_set/render_context.rb +3 -1
- data/lib/active_record/fixture_set/table_row.rb +88 -16
- data/lib/active_record/fixture_set/table_rows.rb +4 -5
- data/lib/active_record/fixtures.rb +229 -116
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +121 -48
- data/lib/active_record/insert_all.rb +178 -29
- data/lib/active_record/integration.rb +16 -14
- data/lib/active_record/internal_metadata.rb +132 -21
- data/lib/active_record/legacy_yaml_adapter.rb +3 -36
- data/lib/active_record/locking/optimistic.rb +64 -33
- data/lib/active_record/locking/pessimistic.rb +21 -8
- data/lib/active_record/log_subscriber.rb +61 -30
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
- data/lib/active_record/middleware/database_selector.rb +25 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +160 -55
- data/lib/active_record/migration/compatibility.rb +286 -43
- 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 -2
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +421 -193
- data/lib/active_record/model_schema.rb +217 -125
- data/lib/active_record/nested_attributes.rb +62 -27
- data/lib/active_record/no_touching.rb +4 -4
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +322 -319
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -15
- data/lib/active_record/query_logs.rb +193 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +54 -14
- data/lib/active_record/railtie.rb +250 -72
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +312 -197
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +45 -3
- data/lib/active_record/reflection.rb +389 -146
- data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
- data/lib/active_record/relation/batches.rb +214 -73
- data/lib/active_record/relation/calculations.rb +379 -124
- data/lib/active_record/relation/delegation.rb +36 -23
- data/lib/active_record/relation/finder_methods.rb +159 -49
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +41 -33
- data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +79 -53
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +1156 -279
- data/lib/active_record/relation/record_fetch_warning.rb +12 -11
- data/lib/active_record/relation/spawn_methods.rb +10 -9
- data/lib/active_record/relation/where_clause.rb +100 -66
- data/lib/active_record/relation.rb +829 -194
- data/lib/active_record/result.rb +76 -56
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +86 -47
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +140 -33
- data/lib/active_record/schema_migration.rb +74 -29
- data/lib/active_record/scoping/default.rb +73 -19
- data/lib/active_record/scoping/named.rb +10 -28
- data/lib/active_record/scoping.rb +65 -35
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +34 -8
- data/lib/active_record/serialization.rb +11 -4
- data/lib/active_record/signed_id.rb +138 -0
- data/lib/active_record/statement_cache.rb +26 -10
- data/lib/active_record/store.rb +19 -14
- data/lib/active_record/suppressor.rb +15 -17
- data/lib/active_record/table_metadata.rb +46 -36
- data/lib/active_record/tasks/database_tasks.rb +371 -205
- data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
- data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +189 -104
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +35 -25
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +31 -27
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +131 -99
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +33 -18
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +11 -6
- data/lib/active_record/type/time.rb +14 -0
- data/lib/active_record/type/type_map.rb +17 -21
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +7 -2
- data/lib/active_record/type_caster/connection.rb +4 -5
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -8
- data/lib/active_record/validations/numericality.rb +36 -0
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +88 -18
- data/lib/active_record/validations.rb +15 -8
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +446 -40
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +4 -8
- data/lib/arel/collectors/bind.rb +8 -1
- data/lib/arel/collectors/composite.rb +15 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/crud.rb +30 -22
- data/lib/arel/delete_manager.rb +23 -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 +82 -9
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +22 -10
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +14 -13
- data/lib/arel/nodes/equality.rb +6 -9
- 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/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +68 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/join_source.rb +1 -1
- 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 +122 -11
- data/lib/arel/nodes/ordering.rb +27 -0
- 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 +16 -0
- data/lib/arel/nodes/table_alias.rb +11 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes/update_statement.rb +11 -4
- data/lib/arel/nodes.rb +10 -3
- data/lib/arel/predications.rb +31 -28
- data/lib/arel/select_manager.rb +18 -9
- data/lib/arel/table.rb +21 -10
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +25 -5
- data/lib/arel/visitors/dot.rb +94 -90
- data/lib/arel/visitors/mysql.rb +34 -6
- data/lib/arel/visitors/postgresql.rb +5 -16
- data/lib/arel/visitors/sqlite.rb +25 -1
- data/lib/arel/visitors/to_sql.rb +227 -81
- data/lib/arel/visitors/visitor.rb +2 -3
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +37 -15
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
- data/lib/rails/generators/active_record/migration.rb +9 -3
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- 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 +117 -30
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/null_relation.rb +0 -68
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -204
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -157
- data/lib/arel/visitors/oracle.rb +0 -159
- data/lib/arel/visitors/oracle12.rb +0 -66
- data/lib/arel/visitors/where_sql.rb +0 -23
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "active_support/core_ext/array/wrap"
|
|
4
|
-
|
|
5
3
|
module ActiveRecord
|
|
6
4
|
module Associations
|
|
7
5
|
# = Active Record Associations
|
|
@@ -21,7 +19,7 @@ module ActiveRecord
|
|
|
21
19
|
# Associations in Active Record are middlemen between the object that
|
|
22
20
|
# holds the association, known as the <tt>owner</tt>, and the associated
|
|
23
21
|
# result set, known as the <tt>target</tt>. Association metadata is available in
|
|
24
|
-
# <tt>reflection</tt>, which is an instance of
|
|
22
|
+
# <tt>reflection</tt>, which is an instance of +ActiveRecord::Reflection::AssociationReflection+.
|
|
25
23
|
#
|
|
26
24
|
# For example, given
|
|
27
25
|
#
|
|
@@ -34,8 +32,9 @@ module ActiveRecord
|
|
|
34
32
|
# The association of <tt>blog.posts</tt> has the object +blog+ as its
|
|
35
33
|
# <tt>owner</tt>, the collection of its posts as <tt>target</tt>, and
|
|
36
34
|
# the <tt>reflection</tt> object represents a <tt>:has_many</tt> macro.
|
|
37
|
-
class Association
|
|
38
|
-
|
|
35
|
+
class Association # :nodoc:
|
|
36
|
+
attr_accessor :owner
|
|
37
|
+
attr_reader :target, :reflection, :disable_joins
|
|
39
38
|
|
|
40
39
|
delegate :options, to: :reflection
|
|
41
40
|
|
|
@@ -43,23 +42,28 @@ module ActiveRecord
|
|
|
43
42
|
reflection.check_validity!
|
|
44
43
|
|
|
45
44
|
@owner, @reflection = owner, reflection
|
|
45
|
+
@disable_joins = @reflection.options[:disable_joins] || false
|
|
46
46
|
|
|
47
47
|
reset
|
|
48
48
|
reset_scope
|
|
49
|
+
|
|
50
|
+
@skip_strict_loading = nil
|
|
49
51
|
end
|
|
50
52
|
|
|
51
53
|
# Resets the \loaded flag to +false+ and sets the \target to +nil+.
|
|
52
54
|
def reset
|
|
53
55
|
@loaded = false
|
|
54
|
-
@target = nil
|
|
55
56
|
@stale_state = nil
|
|
56
|
-
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def reset_negative_cache # :nodoc:
|
|
60
|
+
reset if loaded? && target.nil?
|
|
57
61
|
end
|
|
58
62
|
|
|
59
63
|
# Reloads the \target and returns +self+ on success.
|
|
60
64
|
# The QueryCache is cleared if +force+ is true.
|
|
61
65
|
def reload(force = false)
|
|
62
|
-
klass.
|
|
66
|
+
klass.connection_pool.clear_query_cache if force && klass
|
|
63
67
|
reset
|
|
64
68
|
reset_scope
|
|
65
69
|
load_target
|
|
@@ -75,7 +79,6 @@ module ActiveRecord
|
|
|
75
79
|
def loaded!
|
|
76
80
|
@loaded = true
|
|
77
81
|
@stale_state = stale_state
|
|
78
|
-
@inversed = false
|
|
79
82
|
end
|
|
80
83
|
|
|
81
84
|
# The target is stale if the target no longer points to the record(s) that the
|
|
@@ -85,7 +88,7 @@ module ActiveRecord
|
|
|
85
88
|
#
|
|
86
89
|
# Note that if the target has not been loaded, it is not considered stale.
|
|
87
90
|
def stale_target?
|
|
88
|
-
|
|
91
|
+
loaded? && @stale_state != stale_state
|
|
89
92
|
end
|
|
90
93
|
|
|
91
94
|
# Sets the target of this association to <tt>\target</tt>, and the \loaded flag to +true+.
|
|
@@ -95,7 +98,15 @@ module ActiveRecord
|
|
|
95
98
|
end
|
|
96
99
|
|
|
97
100
|
def scope
|
|
98
|
-
|
|
101
|
+
if disable_joins
|
|
102
|
+
DisableJoinsAssociationScope.create.scope(self)
|
|
103
|
+
elsif (scope = klass.current_scope) && scope.try(:proxy_association) == self
|
|
104
|
+
scope.spawn
|
|
105
|
+
elsif scope = klass.global_current_scope
|
|
106
|
+
target_scope.merge!(association_scope).merge!(scope)
|
|
107
|
+
else
|
|
108
|
+
target_scope.merge!(association_scope)
|
|
109
|
+
end
|
|
99
110
|
end
|
|
100
111
|
|
|
101
112
|
def reset_scope
|
|
@@ -126,9 +137,13 @@ module ActiveRecord
|
|
|
126
137
|
|
|
127
138
|
def inversed_from(record)
|
|
128
139
|
self.target = record
|
|
129
|
-
@inversed = !!record
|
|
130
140
|
end
|
|
131
|
-
|
|
141
|
+
|
|
142
|
+
def inversed_from_queries(record)
|
|
143
|
+
if inversable?(record)
|
|
144
|
+
self.target = record
|
|
145
|
+
end
|
|
146
|
+
end
|
|
132
147
|
|
|
133
148
|
# Returns the class of the target. belongs_to polymorphic overrides this to look at the
|
|
134
149
|
# polymorphic_type field on the owner.
|
|
@@ -177,7 +192,7 @@ module ActiveRecord
|
|
|
177
192
|
@reflection = @owner.class._reflect_on_association(reflection_name)
|
|
178
193
|
end
|
|
179
194
|
|
|
180
|
-
def initialize_attributes(record, except_from_scope_attributes = nil)
|
|
195
|
+
def initialize_attributes(record, except_from_scope_attributes = nil) # :nodoc:
|
|
181
196
|
except_from_scope_attributes ||= {}
|
|
182
197
|
skip_assign = [reflection.foreign_key, reflection.type].compact
|
|
183
198
|
assigned_keys = record.changed_attribute_names_to_save
|
|
@@ -187,27 +202,69 @@ module ActiveRecord
|
|
|
187
202
|
set_inverse_instance(record)
|
|
188
203
|
end
|
|
189
204
|
|
|
190
|
-
def create(attributes =
|
|
205
|
+
def create(attributes = nil, &block)
|
|
191
206
|
_create_record(attributes, &block)
|
|
192
207
|
end
|
|
193
208
|
|
|
194
|
-
def create!(attributes =
|
|
209
|
+
def create!(attributes = nil, &block)
|
|
195
210
|
_create_record(attributes, true, &block)
|
|
196
211
|
end
|
|
197
212
|
|
|
213
|
+
# Whether the association represent a single record
|
|
214
|
+
# or a collection of records.
|
|
215
|
+
def collection?
|
|
216
|
+
false
|
|
217
|
+
end
|
|
218
|
+
|
|
198
219
|
private
|
|
220
|
+
# Reader and writer methods call this so that consistent errors are presented
|
|
221
|
+
# when the association target class does not exist.
|
|
222
|
+
def ensure_klass_exists!
|
|
223
|
+
klass
|
|
224
|
+
end
|
|
225
|
+
|
|
199
226
|
def find_target
|
|
227
|
+
if violates_strict_loading?
|
|
228
|
+
Base.strict_loading_violation!(owner: owner.class, reflection: reflection)
|
|
229
|
+
end
|
|
230
|
+
|
|
200
231
|
scope = self.scope
|
|
201
232
|
return scope.to_a if skip_statement_cache?(scope)
|
|
202
233
|
|
|
203
|
-
|
|
204
|
-
sc = reflection.association_scope_cache(conn, owner) do |params|
|
|
234
|
+
sc = reflection.association_scope_cache(klass, owner) do |params|
|
|
205
235
|
as = AssociationScope.create { params.bind }
|
|
206
236
|
target_scope.merge!(as.scope(self))
|
|
207
237
|
end
|
|
208
238
|
|
|
209
239
|
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
|
210
|
-
|
|
240
|
+
klass.with_connection do |c|
|
|
241
|
+
sc.execute(binds, c) do |record|
|
|
242
|
+
set_inverse_instance(record)
|
|
243
|
+
if owner.strict_loading_n_plus_one_only? && reflection.macro == :has_many
|
|
244
|
+
record.strict_loading!
|
|
245
|
+
else
|
|
246
|
+
record.strict_loading!(false, mode: owner.strict_loading_mode)
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def skip_strict_loading(&block)
|
|
253
|
+
skip_strict_loading_was = @skip_strict_loading
|
|
254
|
+
@skip_strict_loading = true
|
|
255
|
+
yield
|
|
256
|
+
ensure
|
|
257
|
+
@skip_strict_loading = skip_strict_loading_was
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def violates_strict_loading?
|
|
261
|
+
return if @skip_strict_loading
|
|
262
|
+
|
|
263
|
+
return unless owner.validation_context.nil?
|
|
264
|
+
|
|
265
|
+
return reflection.strict_loading? if reflection.options.key?(:strict_loading)
|
|
266
|
+
|
|
267
|
+
owner.strict_loading? && !owner.strict_loading_n_plus_one_only?
|
|
211
268
|
end
|
|
212
269
|
|
|
213
270
|
# The scope for this association.
|
|
@@ -218,7 +275,11 @@ module ActiveRecord
|
|
|
218
275
|
# actually gets built.
|
|
219
276
|
def association_scope
|
|
220
277
|
if klass
|
|
221
|
-
@association_scope ||=
|
|
278
|
+
@association_scope ||= if disable_joins
|
|
279
|
+
DisableJoinsAssociationScope.scope(self)
|
|
280
|
+
else
|
|
281
|
+
AssociationScope.scope(self)
|
|
282
|
+
end
|
|
222
283
|
end
|
|
223
284
|
end
|
|
224
285
|
|
|
@@ -236,25 +297,6 @@ module ActiveRecord
|
|
|
236
297
|
!loaded? && (!owner.new_record? || foreign_key_present?) && klass
|
|
237
298
|
end
|
|
238
299
|
|
|
239
|
-
def creation_attributes
|
|
240
|
-
attributes = {}
|
|
241
|
-
|
|
242
|
-
if (reflection.has_one? || reflection.collection?) && !options[:through]
|
|
243
|
-
attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
|
|
244
|
-
|
|
245
|
-
if reflection.type
|
|
246
|
-
attributes[reflection.type] = owner.class.polymorphic_name
|
|
247
|
-
end
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
attributes
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
# Sets the owner attributes on the given record
|
|
254
|
-
def set_owner_attributes(record)
|
|
255
|
-
creation_attributes.each { |key, value| record[key] = value }
|
|
256
|
-
end
|
|
257
|
-
|
|
258
300
|
# Returns true if there is a foreign key present on the owner which
|
|
259
301
|
# references the target. This is used to determine whether we can load
|
|
260
302
|
# the target if the owner is currently a new record (and therefore
|
|
@@ -269,7 +311,7 @@ module ActiveRecord
|
|
|
269
311
|
|
|
270
312
|
# Raises ActiveRecord::AssociationTypeMismatch unless +record+ is of
|
|
271
313
|
# the kind of the class of the associated objects. Meant to be used as
|
|
272
|
-
# a
|
|
314
|
+
# a safety check when you are about to assign an associated record.
|
|
273
315
|
def raise_on_type_mismatch!(record)
|
|
274
316
|
unless record.is_a?(reflection.klass)
|
|
275
317
|
fresh_class = reflection.class_name.safe_constantize
|
|
@@ -302,7 +344,8 @@ module ActiveRecord
|
|
|
302
344
|
|
|
303
345
|
# Returns true if record contains the foreign_key
|
|
304
346
|
def foreign_key_for?(record)
|
|
305
|
-
|
|
347
|
+
foreign_key = Array(reflection.foreign_key)
|
|
348
|
+
foreign_key.all? { |key| record._has_attribute?(key) }
|
|
306
349
|
end
|
|
307
350
|
|
|
308
351
|
# This should be implemented to return the values of the relevant key(s) on the owner,
|
|
@@ -327,6 +370,28 @@ module ActiveRecord
|
|
|
327
370
|
klass.scope_attributes? ||
|
|
328
371
|
reflection.source_reflection.active_record.default_scopes.any?
|
|
329
372
|
end
|
|
373
|
+
|
|
374
|
+
def enqueue_destroy_association(options)
|
|
375
|
+
job_class = owner.class.destroy_association_async_job
|
|
376
|
+
|
|
377
|
+
if job_class
|
|
378
|
+
owner._after_commit_jobs.push([job_class, options])
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def inversable?(record)
|
|
383
|
+
record &&
|
|
384
|
+
((!record.persisted? || !owner.persisted?) || matches_foreign_key?(record))
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def matches_foreign_key?(record)
|
|
388
|
+
if foreign_key_for?(record)
|
|
389
|
+
record.read_attribute(reflection.foreign_key) == owner.id ||
|
|
390
|
+
(foreign_key_for?(owner) && owner.read_attribute(reflection.foreign_key) == record.id)
|
|
391
|
+
else
|
|
392
|
+
owner.read_attribute(reflection.foreign_key) == record.id
|
|
393
|
+
end
|
|
394
|
+
end
|
|
330
395
|
end
|
|
331
396
|
end
|
|
332
397
|
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module ActiveRecord
|
|
4
4
|
module Associations
|
|
5
|
-
class AssociationScope
|
|
5
|
+
class AssociationScope # :nodoc:
|
|
6
6
|
def self.scope(association)
|
|
7
7
|
INSTANCE.scope(association)
|
|
8
8
|
end
|
|
@@ -35,7 +35,7 @@ module ActiveRecord
|
|
|
35
35
|
binds = []
|
|
36
36
|
last_reflection = chain.last
|
|
37
37
|
|
|
38
|
-
binds
|
|
38
|
+
binds.push(*last_reflection.join_id_for(owner))
|
|
39
39
|
if last_reflection.type
|
|
40
40
|
binds << owner.class.polymorphic_name
|
|
41
41
|
end
|
|
@@ -52,17 +52,19 @@ module ActiveRecord
|
|
|
52
52
|
attr_reader :value_transformation
|
|
53
53
|
|
|
54
54
|
def join(table, constraint)
|
|
55
|
-
|
|
55
|
+
Arel::Nodes::LeadingJoin.new(table, Arel::Nodes::On.new(constraint))
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def last_chain_scope(scope, reflection, owner)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
foreign_key = join_keys.foreign_key
|
|
59
|
+
primary_key = Array(reflection.join_primary_key)
|
|
60
|
+
foreign_key = Array(reflection.join_foreign_key)
|
|
62
61
|
|
|
63
62
|
table = reflection.aliased_table
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
|
|
64
|
+
primary_key_foreign_key_pairs.each do |join_key, foreign_key|
|
|
65
|
+
value = transform_value(owner._read_attribute(foreign_key))
|
|
66
|
+
scope = apply_scope(scope, table, join_key, value)
|
|
67
|
+
end
|
|
66
68
|
|
|
67
69
|
if reflection.type
|
|
68
70
|
polymorphic_type = transform_value(owner.class.polymorphic_name)
|
|
@@ -77,20 +79,23 @@ module ActiveRecord
|
|
|
77
79
|
end
|
|
78
80
|
|
|
79
81
|
def next_chain_scope(scope, reflection, next_reflection)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
foreign_key = join_keys.foreign_key
|
|
82
|
+
primary_key = Array(reflection.join_primary_key)
|
|
83
|
+
foreign_key = Array(reflection.join_foreign_key)
|
|
83
84
|
|
|
84
85
|
table = reflection.aliased_table
|
|
85
86
|
foreign_table = next_reflection.aliased_table
|
|
86
|
-
|
|
87
|
+
|
|
88
|
+
primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
|
|
89
|
+
constraints = primary_key_foreign_key_pairs.map do |join_primary_key, foreign_key|
|
|
90
|
+
table[join_primary_key].eq(foreign_table[foreign_key])
|
|
91
|
+
end.inject(&:and)
|
|
87
92
|
|
|
88
93
|
if reflection.type
|
|
89
94
|
value = transform_value(next_reflection.klass.polymorphic_name)
|
|
90
95
|
scope = apply_scope(scope, table, reflection.type, value)
|
|
91
96
|
end
|
|
92
97
|
|
|
93
|
-
scope.joins!(join(foreign_table,
|
|
98
|
+
scope.joins!(join(foreign_table, constraints))
|
|
94
99
|
end
|
|
95
100
|
|
|
96
101
|
class ReflectionProxy < SimpleDelegator # :nodoc:
|
|
@@ -108,11 +113,9 @@ module ActiveRecord
|
|
|
108
113
|
name = reflection.name
|
|
109
114
|
chain = [Reflection::RuntimeReflection.new(reflection, association)]
|
|
110
115
|
reflection.chain.drop(1).each do |refl|
|
|
111
|
-
aliased_table = tracker.aliased_table_for(
|
|
112
|
-
refl.
|
|
113
|
-
|
|
114
|
-
refl.klass.type_caster
|
|
115
|
-
)
|
|
116
|
+
aliased_table = tracker.aliased_table_for(refl.klass.arel_table) do
|
|
117
|
+
refl.alias_candidate(name)
|
|
118
|
+
end
|
|
116
119
|
chain << ReflectionProxy.new(refl, aliased_table)
|
|
117
120
|
end
|
|
118
121
|
chain
|
|
@@ -127,17 +130,23 @@ module ActiveRecord
|
|
|
127
130
|
|
|
128
131
|
chain_head = chain.first
|
|
129
132
|
chain.reverse_each do |reflection|
|
|
130
|
-
# Exclude the scope of the association itself, because that
|
|
131
|
-
# was already merged in the #scope method.
|
|
132
133
|
reflection.constraints.each do |scope_chain_item|
|
|
133
134
|
item = eval_scope(reflection, scope_chain_item, owner)
|
|
134
135
|
|
|
135
136
|
if scope_chain_item == chain_head.scope
|
|
136
137
|
scope.merge! item.except(:where, :includes, :unscope, :order)
|
|
138
|
+
elsif !item.references_values.empty?
|
|
139
|
+
scope.merge! item.only(:joins, :left_outer_joins)
|
|
140
|
+
|
|
141
|
+
associations = item.eager_load_values | item.includes_values
|
|
142
|
+
|
|
143
|
+
unless associations.empty?
|
|
144
|
+
scope.joins! item.construct_join_dependency(associations, Arel::Nodes::OuterJoin)
|
|
145
|
+
end
|
|
137
146
|
end
|
|
138
147
|
|
|
139
148
|
reflection.all_includes do
|
|
140
|
-
scope.
|
|
149
|
+
scope.includes_values |= item.includes_values
|
|
141
150
|
end
|
|
142
151
|
|
|
143
152
|
scope.unscope!(*item.unscope_values)
|
|
@@ -3,16 +3,38 @@
|
|
|
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
|
|
|
10
10
|
case options[:dependent]
|
|
11
11
|
when :destroy
|
|
12
|
-
target.destroy
|
|
13
|
-
|
|
12
|
+
raise ActiveRecord::Rollback unless target.destroy
|
|
13
|
+
when :destroy_async
|
|
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
|
|
21
|
+
|
|
22
|
+
association_class = if reflection.polymorphic?
|
|
23
|
+
owner.public_send(reflection.foreign_type)
|
|
24
|
+
else
|
|
25
|
+
reflection.klass
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
enqueue_destroy_association(
|
|
29
|
+
owner_model_name: owner.class.to_s,
|
|
30
|
+
owner_id: owner.id,
|
|
31
|
+
association_class: association_class.to_s,
|
|
32
|
+
association_ids: [id],
|
|
33
|
+
association_primary_key_column: primary_key_column,
|
|
34
|
+
ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
|
|
35
|
+
)
|
|
14
36
|
else
|
|
15
|
-
target.
|
|
37
|
+
target.public_send(options[:dependent])
|
|
16
38
|
end
|
|
17
39
|
end
|
|
18
40
|
|
|
@@ -44,7 +66,8 @@ module ActiveRecord
|
|
|
44
66
|
|
|
45
67
|
def decrement_counters_before_last_save
|
|
46
68
|
if reflection.polymorphic?
|
|
47
|
-
|
|
69
|
+
model_type_was = owner.attribute_before_last_save(reflection.foreign_type)
|
|
70
|
+
model_was = owner.class.polymorphic_class_for(model_type_was) if model_type_was
|
|
48
71
|
else
|
|
49
72
|
model_was = klass
|
|
50
73
|
end
|
|
@@ -57,6 +80,14 @@ module ActiveRecord
|
|
|
57
80
|
end
|
|
58
81
|
|
|
59
82
|
def target_changed?
|
|
83
|
+
owner.attribute_changed?(reflection.foreign_key) || (!foreign_key_present? && target&.new_record?)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def target_previously_changed?
|
|
87
|
+
owner.attribute_previously_changed?(reflection.foreign_key)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def saved_change_to_target?
|
|
60
91
|
owner.saved_change_to_attribute?(reflection.foreign_key)
|
|
61
92
|
end
|
|
62
93
|
|
|
@@ -66,9 +97,11 @@ module ActiveRecord
|
|
|
66
97
|
raise_on_type_mismatch!(record)
|
|
67
98
|
set_inverse_instance(record)
|
|
68
99
|
@updated = true
|
|
100
|
+
elsif target
|
|
101
|
+
remove_inverse_instance(target)
|
|
69
102
|
end
|
|
70
103
|
|
|
71
|
-
replace_keys(record)
|
|
104
|
+
replace_keys(record, force: true)
|
|
72
105
|
|
|
73
106
|
self.target = record
|
|
74
107
|
end
|
|
@@ -96,8 +129,25 @@ module ActiveRecord
|
|
|
96
129
|
reflection.counter_cache_column && owner.persisted?
|
|
97
130
|
end
|
|
98
131
|
|
|
99
|
-
def replace_keys(record)
|
|
100
|
-
|
|
132
|
+
def replace_keys(record, force: false)
|
|
133
|
+
reflection_fk = reflection.foreign_key
|
|
134
|
+
if reflection_fk.is_a?(Array)
|
|
135
|
+
target_key_values = record ? Array(primary_key(record.class)).map { |key| record._read_attribute(key) } : []
|
|
136
|
+
|
|
137
|
+
if force || reflection_fk.map { |fk| owner._read_attribute(fk) } != target_key_values
|
|
138
|
+
owner_pk = Array(owner.class.primary_key)
|
|
139
|
+
reflection_fk.each_with_index do |key, index|
|
|
140
|
+
next if record.nil? && owner_pk.include?(key)
|
|
141
|
+
owner[key] = target_key_values[index]
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
else
|
|
145
|
+
target_key_value = record ? record._read_attribute(primary_key(record.class)) : nil
|
|
146
|
+
|
|
147
|
+
if force || owner._read_attribute(reflection_fk) != target_key_value
|
|
148
|
+
owner[reflection_fk] = target_key_value
|
|
149
|
+
end
|
|
150
|
+
end
|
|
101
151
|
end
|
|
102
152
|
|
|
103
153
|
def primary_key(klass)
|
|
@@ -105,19 +155,24 @@ module ActiveRecord
|
|
|
105
155
|
end
|
|
106
156
|
|
|
107
157
|
def foreign_key_present?
|
|
108
|
-
|
|
158
|
+
Array(reflection.foreign_key).all? { |fk| owner._read_attribute(fk) }
|
|
109
159
|
end
|
|
110
160
|
|
|
111
|
-
# NOTE - for now, we're only supporting inverse setting from belongs_to back onto
|
|
112
|
-
# has_one associations.
|
|
113
161
|
def invertible_for?(record)
|
|
114
162
|
inverse = inverse_reflection_for(record)
|
|
115
|
-
inverse && inverse.has_one?
|
|
163
|
+
inverse && (inverse.has_one? || inverse.klass.has_many_inversing)
|
|
116
164
|
end
|
|
117
165
|
|
|
118
166
|
def stale_state
|
|
119
|
-
|
|
120
|
-
|
|
167
|
+
foreign_key = reflection.foreign_key
|
|
168
|
+
if foreign_key.is_a?(Array)
|
|
169
|
+
attributes = foreign_key.map do |fk|
|
|
170
|
+
owner._read_attribute(fk) { |n| owner.send(:missing_attribute, n, caller) }
|
|
171
|
+
end
|
|
172
|
+
attributes if attributes.any?
|
|
173
|
+
else
|
|
174
|
+
owner._read_attribute(foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
|
|
175
|
+
end
|
|
121
176
|
end
|
|
122
177
|
end
|
|
123
178
|
end
|
|
@@ -3,20 +3,33 @@
|
|
|
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
|
-
type.presence && type
|
|
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
|
|
|
16
24
|
private
|
|
17
|
-
def replace_keys(record)
|
|
25
|
+
def replace_keys(record, force: false)
|
|
18
26
|
super
|
|
19
|
-
|
|
27
|
+
|
|
28
|
+
target_type = record ? record.class.polymorphic_name : nil
|
|
29
|
+
|
|
30
|
+
if force || owner._read_attribute(reflection.foreign_type) != target_type
|
|
31
|
+
owner[reflection.foreign_type] = target_type
|
|
32
|
+
end
|
|
20
33
|
end
|
|
21
34
|
|
|
22
35
|
def inverse_reflection_for(record)
|
|
@@ -28,8 +41,9 @@ module ActiveRecord
|
|
|
28
41
|
end
|
|
29
42
|
|
|
30
43
|
def stale_state
|
|
31
|
-
foreign_key = super
|
|
32
|
-
|
|
44
|
+
if foreign_key = super
|
|
45
|
+
[foreign_key, owner[reflection.foreign_type]]
|
|
46
|
+
end
|
|
33
47
|
end
|
|
34
48
|
end
|
|
35
49
|
end
|
|
@@ -12,13 +12,15 @@
|
|
|
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
|
-
VALID_OPTIONS = [
|
|
21
|
+
VALID_OPTIONS = [
|
|
22
|
+
:class_name, :anonymous_class, :primary_key, :foreign_key, :dependent, :validate, :inverse_of, :strict_loading, :query_constraints
|
|
23
|
+
].freeze # :nodoc:
|
|
22
24
|
|
|
23
25
|
def self.build(model, name, scope, options, &block)
|
|
24
26
|
if model.dangerous_attribute_method?(name)
|
|
@@ -31,6 +33,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
|
31
33
|
define_accessors model, reflection
|
|
32
34
|
define_callbacks model, reflection
|
|
33
35
|
define_validations model, reflection
|
|
36
|
+
define_change_tracking_methods model, reflection
|
|
34
37
|
reflection
|
|
35
38
|
end
|
|
36
39
|
|
|
@@ -72,8 +75,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
|
72
75
|
|
|
73
76
|
def self.define_callbacks(model, reflection)
|
|
74
77
|
if dependent = reflection.options[:dependent]
|
|
75
|
-
check_dependent_options(dependent)
|
|
78
|
+
check_dependent_options(dependent, model)
|
|
76
79
|
add_destroy_callbacks(model, reflection)
|
|
80
|
+
add_after_commit_jobs_callback(model, dependent)
|
|
77
81
|
end
|
|
78
82
|
|
|
79
83
|
Association.extensions.each do |extension|
|
|
@@ -114,11 +118,19 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
|
114
118
|
# noop
|
|
115
119
|
end
|
|
116
120
|
|
|
121
|
+
def self.define_change_tracking_methods(model, reflection)
|
|
122
|
+
# noop
|
|
123
|
+
end
|
|
124
|
+
|
|
117
125
|
def self.valid_dependent_options
|
|
118
126
|
raise NotImplementedError
|
|
119
127
|
end
|
|
120
128
|
|
|
121
|
-
def self.check_dependent_options(dependent)
|
|
129
|
+
def self.check_dependent_options(dependent, model)
|
|
130
|
+
if dependent == :destroy_async && !model.destroy_association_async_job
|
|
131
|
+
err_message = "A valid destroy_association_async_job is required to use `dependent: :destroy_async` on associations"
|
|
132
|
+
raise ActiveRecord::ConfigurationError, err_message
|
|
133
|
+
end
|
|
122
134
|
unless valid_dependent_options.include? dependent
|
|
123
135
|
raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
|
|
124
136
|
end
|
|
@@ -126,11 +138,32 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
|
126
138
|
|
|
127
139
|
def self.add_destroy_callbacks(model, reflection)
|
|
128
140
|
name = reflection.name
|
|
129
|
-
model.before_destroy
|
|
141
|
+
model.before_destroy(->(o) { o.association(name).handle_dependency })
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def self.add_after_commit_jobs_callback(model, dependent)
|
|
145
|
+
if dependent == :destroy_async
|
|
146
|
+
mixin = model.generated_association_methods
|
|
147
|
+
|
|
148
|
+
unless mixin.method_defined?(:_after_commit_jobs)
|
|
149
|
+
model.after_commit(-> do
|
|
150
|
+
_after_commit_jobs.each do |job_class, job_arguments|
|
|
151
|
+
job_class.perform_later(**job_arguments)
|
|
152
|
+
end
|
|
153
|
+
end)
|
|
154
|
+
|
|
155
|
+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
|
156
|
+
def _after_commit_jobs
|
|
157
|
+
@_after_commit_jobs ||= []
|
|
158
|
+
end
|
|
159
|
+
CODE
|
|
160
|
+
end
|
|
161
|
+
end
|
|
130
162
|
end
|
|
131
163
|
|
|
132
164
|
private_class_method :build_scope, :macro, :valid_options, :validate_options, :define_extensions,
|
|
133
165
|
:define_callbacks, :define_accessors, :define_readers, :define_writers, :define_validations,
|
|
134
|
-
:valid_dependent_options, :check_dependent_options,
|
|
166
|
+
:define_change_tracking_methods, :valid_dependent_options, :check_dependent_options,
|
|
167
|
+
:add_destroy_callbacks, :add_after_commit_jobs_callback
|
|
135
168
|
end
|
|
136
169
|
end
|