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,17 +1,20 @@
|
|
|
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
|
|
8
8
|
|
|
9
9
|
def self.valid_options(options)
|
|
10
|
-
super + [:polymorphic, :
|
|
10
|
+
valid = super + [:polymorphic, :counter_cache, :optional, :default]
|
|
11
|
+
valid += [:foreign_type] if options[:polymorphic]
|
|
12
|
+
valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
|
|
13
|
+
valid
|
|
11
14
|
end
|
|
12
15
|
|
|
13
16
|
def self.valid_dependent_options
|
|
14
|
-
[:destroy, :delete]
|
|
17
|
+
[:destroy, :delete, :destroy_async]
|
|
15
18
|
end
|
|
16
19
|
|
|
17
20
|
def self.define_callbacks(model, reflection)
|
|
@@ -27,17 +30,18 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
|
27
30
|
model.after_update lambda { |record|
|
|
28
31
|
association = association(reflection.name)
|
|
29
32
|
|
|
30
|
-
if association.
|
|
33
|
+
if association.saved_change_to_target?
|
|
31
34
|
association.increment_counters
|
|
32
35
|
association.decrement_counters_before_last_save
|
|
33
36
|
end
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
klass = reflection.class_name.safe_constantize
|
|
37
|
-
klass.
|
|
40
|
+
klass._counter_cache_columns |= [cache_column] if klass && klass.respond_to?(:_counter_cache_columns)
|
|
41
|
+
model.counter_cached_association_names |= [reflection.name]
|
|
38
42
|
end
|
|
39
43
|
|
|
40
|
-
def self.touch_record(o, changes, foreign_key, name, touch
|
|
44
|
+
def self.touch_record(o, changes, foreign_key, name, touch) # :nodoc:
|
|
41
45
|
old_foreign_id = changes[foreign_key] && changes[foreign_key].first
|
|
42
46
|
|
|
43
47
|
if old_foreign_id
|
|
@@ -46,7 +50,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
|
46
50
|
if reflection.polymorphic?
|
|
47
51
|
foreign_type = reflection.foreign_type
|
|
48
52
|
klass = changes[foreign_type] && changes[foreign_type].first || o.public_send(foreign_type)
|
|
49
|
-
klass = klass
|
|
53
|
+
klass = o.class.polymorphic_class_for(klass)
|
|
50
54
|
else
|
|
51
55
|
klass = association.klass
|
|
52
56
|
end
|
|
@@ -55,19 +59,19 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
|
55
59
|
|
|
56
60
|
if old_record
|
|
57
61
|
if touch != true
|
|
58
|
-
old_record.
|
|
62
|
+
old_record.touch_later(touch)
|
|
59
63
|
else
|
|
60
|
-
old_record.
|
|
64
|
+
old_record.touch_later
|
|
61
65
|
end
|
|
62
66
|
end
|
|
63
67
|
end
|
|
64
68
|
|
|
65
|
-
record = o.
|
|
69
|
+
record = o.public_send name
|
|
66
70
|
if record && record.persisted?
|
|
67
71
|
if touch != true
|
|
68
|
-
record.
|
|
72
|
+
record.touch_later(touch)
|
|
69
73
|
else
|
|
70
|
-
record.
|
|
74
|
+
record.touch_later
|
|
71
75
|
end
|
|
72
76
|
end
|
|
73
77
|
end
|
|
@@ -78,13 +82,13 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
|
78
82
|
touch = reflection.options[:touch]
|
|
79
83
|
|
|
80
84
|
callback = lambda { |changes_method| lambda { |record|
|
|
81
|
-
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)
|
|
82
86
|
}}
|
|
83
87
|
|
|
84
88
|
if reflection.counter_cache_column
|
|
85
89
|
touch_callback = callback.(:saved_changes)
|
|
86
90
|
update_callback = lambda { |record|
|
|
87
|
-
instance_exec(record, &touch_callback) unless association(reflection.name).
|
|
91
|
+
instance_exec(record, &touch_callback) unless association(reflection.name).saved_change_to_target?
|
|
88
92
|
}
|
|
89
93
|
model.after_update update_callback, if: :saved_changes?
|
|
90
94
|
else
|
|
@@ -120,11 +124,37 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
|
120
124
|
super
|
|
121
125
|
|
|
122
126
|
if required
|
|
123
|
-
|
|
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
|
|
124
141
|
end
|
|
125
142
|
end
|
|
126
143
|
|
|
127
|
-
|
|
128
|
-
|
|
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
|
|
129
159
|
end
|
|
130
160
|
end
|
|
@@ -3,12 +3,11 @@
|
|
|
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)
|
|
10
|
-
super + [:
|
|
11
|
-
:after_add, :before_remove, :after_remove, :extend]
|
|
10
|
+
super + [:before_add, :after_add, :before_remove, :after_remove, :extend]
|
|
12
11
|
end
|
|
13
12
|
|
|
14
13
|
def self.define_callbacks(model, reflection)
|
|
@@ -31,9 +30,18 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
|
31
30
|
def self.define_callback(model, callback_name, name, options)
|
|
32
31
|
full_callback_name = "#{callback_name}_for_#{name}"
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
41
|
+
model.class_attribute(full_callback_name, instance_accessor: false, instance_predicate: false)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
callbacks = callback_values.map do |callback|
|
|
37
45
|
case callback
|
|
38
46
|
when Symbol
|
|
39
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,15 +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
|
-
|
|
50
|
-
def self.suppress_composite_primary_key(pk)
|
|
51
|
-
pk unless pk.is_a?(Array)
|
|
52
|
-
end
|
|
53
48
|
}
|
|
54
49
|
|
|
55
50
|
join_model.name = "HABTM_#{association_name.to_s.camelize}"
|
|
@@ -73,11 +68,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
|
73
68
|
end
|
|
74
69
|
|
|
75
70
|
private
|
|
76
|
-
|
|
77
71
|
def middle_options(join_model)
|
|
78
72
|
middle_options = {}
|
|
79
73
|
middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
|
|
80
|
-
middle_options[:source] = join_model.left_reflection.name
|
|
81
74
|
if options.key? :foreign_key
|
|
82
75
|
middle_options[:foreign_key] = options[:foreign_key]
|
|
83
76
|
end
|
|
@@ -1,17 +1,21 @@
|
|
|
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
|
-
super + [:
|
|
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
|
+
valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
|
|
14
|
+
valid
|
|
11
15
|
end
|
|
12
16
|
|
|
13
17
|
def self.valid_dependent_options
|
|
14
|
-
[:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception]
|
|
18
|
+
[:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception, :destroy_async]
|
|
15
19
|
end
|
|
16
20
|
|
|
17
21
|
private_class_method :macro, :valid_options, :valid_dependent_options
|
|
@@ -1,19 +1,21 @@
|
|
|
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 + [:as, :
|
|
11
|
-
valid += [:
|
|
10
|
+
valid = super + [:as, :through]
|
|
11
|
+
valid += [:foreign_type] if options[:as]
|
|
12
|
+
valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
|
|
13
|
+
valid += [:source, :source_type, :disable_joins] if options[:through]
|
|
12
14
|
valid
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def self.valid_dependent_options
|
|
16
|
-
[:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
|
|
18
|
+
[:destroy, :destroy_async, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
def self.define_callbacks(model, reflection)
|
|
@@ -32,15 +34,12 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
|
32
34
|
end
|
|
33
35
|
end
|
|
34
36
|
|
|
35
|
-
def self.touch_record(
|
|
36
|
-
|
|
37
|
+
def self.touch_record(record, name, touch)
|
|
38
|
+
instance = record.send(name)
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
record.touch(touch)
|
|
42
|
-
else
|
|
43
|
-
record.touch
|
|
40
|
+
if instance&.persisted?
|
|
41
|
+
touch != true ?
|
|
42
|
+
instance.touch(touch) : instance.touch
|
|
44
43
|
end
|
|
45
44
|
end
|
|
46
45
|
|
|
@@ -48,11 +47,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
|
48
47
|
name = reflection.name
|
|
49
48
|
touch = reflection.options[:touch]
|
|
50
49
|
|
|
51
|
-
callback =
|
|
52
|
-
HasOne.touch_record(record, name, touch)
|
|
53
|
-
}
|
|
54
|
-
|
|
50
|
+
callback = -> (record) { HasOne.touch_record(record, name, touch) }
|
|
55
51
|
model.after_create callback, if: :saved_changes?
|
|
52
|
+
model.after_create_commit { association(name).reset_negative_cache }
|
|
56
53
|
model.after_update callback, if: :saved_changes?
|
|
57
54
|
model.after_destroy callback
|
|
58
55
|
model.after_touch callback
|
|
@@ -3,9 +3,9 @@
|
|
|
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
|
-
super + [:
|
|
8
|
+
super + [:required, :touch]
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def self.define_accessors(model, reflection)
|
|
@@ -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
|
|
|
@@ -56,15 +62,21 @@ module ActiveRecord
|
|
|
56
62
|
def ids_writer(ids)
|
|
57
63
|
primary_key = reflection.association_primary_key
|
|
58
64
|
pk_type = klass.type_for_attribute(primary_key)
|
|
59
|
-
ids = Array(ids).
|
|
60
|
-
ids.map! { |
|
|
65
|
+
ids = Array(ids).compact_blank
|
|
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,6 +87,7 @@ module ActiveRecord
|
|
|
75
87
|
def reset
|
|
76
88
|
super
|
|
77
89
|
@target = []
|
|
90
|
+
@replaced_or_added_targets = Set.new.compare_by_identity
|
|
78
91
|
@association_ids = nil
|
|
79
92
|
end
|
|
80
93
|
|
|
@@ -101,11 +114,11 @@ module ActiveRecord
|
|
|
101
114
|
end
|
|
102
115
|
end
|
|
103
116
|
|
|
104
|
-
def build(attributes =
|
|
117
|
+
def build(attributes = nil, &block)
|
|
105
118
|
if attributes.is_a?(Array)
|
|
106
119
|
attributes.collect { |attr| build(attr, &block) }
|
|
107
120
|
else
|
|
108
|
-
add_to_target(build_record(attributes, &block))
|
|
121
|
+
add_to_target(build_record(attributes, &block), replace: true)
|
|
109
122
|
end
|
|
110
123
|
end
|
|
111
124
|
|
|
@@ -114,28 +127,13 @@ module ActiveRecord
|
|
|
114
127
|
def concat(*records)
|
|
115
128
|
records = records.flatten
|
|
116
129
|
if owner.new_record?
|
|
117
|
-
load_target
|
|
130
|
+
skip_strict_loading { load_target }
|
|
118
131
|
concat_records(records)
|
|
119
132
|
else
|
|
120
133
|
transaction { concat_records(records) }
|
|
121
134
|
end
|
|
122
135
|
end
|
|
123
136
|
|
|
124
|
-
# Starts a transaction in the association class's database connection.
|
|
125
|
-
#
|
|
126
|
-
# class Author < ActiveRecord::Base
|
|
127
|
-
# has_many :books
|
|
128
|
-
# end
|
|
129
|
-
#
|
|
130
|
-
# Author.first.books.transaction do
|
|
131
|
-
# # same effect as calling Book.transaction
|
|
132
|
-
# end
|
|
133
|
-
def transaction(*args)
|
|
134
|
-
reflection.klass.transaction(*args) do
|
|
135
|
-
yield
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
137
|
# Removes all records from the association without calling callbacks
|
|
140
138
|
# on the associated records. It honors the +:dependent+ option. However
|
|
141
139
|
# if the +:dependent+ value is +:destroy+ then in that case the +:delete_all+
|
|
@@ -190,7 +188,7 @@ module ActiveRecord
|
|
|
190
188
|
end
|
|
191
189
|
|
|
192
190
|
# Deletes the +records+ and removes them from this association calling
|
|
193
|
-
# +before_remove
|
|
191
|
+
# +before_remove+, +after_remove+, +before_destroy+ and +after_destroy+ callbacks.
|
|
194
192
|
#
|
|
195
193
|
# Note that this method removes records from the database ignoring the
|
|
196
194
|
# +:dependent+ option.
|
|
@@ -228,11 +226,11 @@ module ActiveRecord
|
|
|
228
226
|
# If the collection has been loaded
|
|
229
227
|
# it is equivalent to <tt>collection.size.zero?</tt>. If the
|
|
230
228
|
# collection has not been loaded, it is equivalent to
|
|
231
|
-
# <tt
|
|
229
|
+
# <tt>!collection.exists?</tt>. If the collection has not already been
|
|
232
230
|
# loaded and you are going to fetch the records anyway it is better to
|
|
233
231
|
# check <tt>collection.length.zero?</tt>.
|
|
234
232
|
def empty?
|
|
235
|
-
if loaded? || @association_ids || reflection.
|
|
233
|
+
if loaded? || @association_ids || reflection.has_active_cached_counter?
|
|
236
234
|
size.zero?
|
|
237
235
|
else
|
|
238
236
|
target.empty? && !scope.exists?
|
|
@@ -243,7 +241,7 @@ module ActiveRecord
|
|
|
243
241
|
# and delete/add only records that have changed.
|
|
244
242
|
def replace(other_array)
|
|
245
243
|
other_array.each { |val| raise_on_type_mismatch!(val) }
|
|
246
|
-
original_target = load_target.dup
|
|
244
|
+
original_target = skip_strict_loading { load_target }.dup
|
|
247
245
|
|
|
248
246
|
if owner.new_record?
|
|
249
247
|
replace_records(other_array, original_target)
|
|
@@ -258,14 +256,16 @@ module ActiveRecord
|
|
|
258
256
|
end
|
|
259
257
|
|
|
260
258
|
def include?(record)
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
259
|
+
klass = reflection.klass
|
|
260
|
+
return false unless record.is_a?(klass)
|
|
261
|
+
|
|
262
|
+
if loaded?
|
|
263
|
+
target.include?(record)
|
|
264
|
+
elsif record.new_record?
|
|
265
|
+
include_in_memory?(record)
|
|
267
266
|
else
|
|
268
|
-
|
|
267
|
+
record_id = klass.composite_primary_key? ? klass.primary_key.zip(record.id).to_h : record.id
|
|
268
|
+
scope.exists?(record_id)
|
|
269
269
|
end
|
|
270
270
|
end
|
|
271
271
|
|
|
@@ -278,11 +278,21 @@ module ActiveRecord
|
|
|
278
278
|
target
|
|
279
279
|
end
|
|
280
280
|
|
|
281
|
-
def add_to_target(record, skip_callbacks
|
|
282
|
-
|
|
283
|
-
|
|
281
|
+
def add_to_target(record, skip_callbacks: false, replace: false, &block)
|
|
282
|
+
replace_on_target(record, skip_callbacks, replace: replace || association_scope.distinct_value, &block)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def target=(record)
|
|
286
|
+
return super unless reflection.klass.has_many_inversing
|
|
287
|
+
|
|
288
|
+
case record
|
|
289
|
+
when nil
|
|
290
|
+
# It's not possible to remove the record from the inverse association.
|
|
291
|
+
when Array
|
|
292
|
+
super
|
|
293
|
+
else
|
|
294
|
+
replace_on_target(record, true, replace: true, inversing: true)
|
|
284
295
|
end
|
|
285
|
-
replace_on_target(record, index, skip_callbacks, &block)
|
|
286
296
|
end
|
|
287
297
|
|
|
288
298
|
def scope
|
|
@@ -297,11 +307,21 @@ module ActiveRecord
|
|
|
297
307
|
|
|
298
308
|
def find_from_target?
|
|
299
309
|
loaded? ||
|
|
310
|
+
(owner.strict_loading? && owner.strict_loading_all?) ||
|
|
311
|
+
reflection.strict_loading? ||
|
|
300
312
|
owner.new_record? ||
|
|
301
313
|
target.any? { |record| record.new_record? || record.changed? }
|
|
302
314
|
end
|
|
303
315
|
|
|
316
|
+
def collection?
|
|
317
|
+
true
|
|
318
|
+
end
|
|
319
|
+
|
|
304
320
|
private
|
|
321
|
+
def transaction(&block)
|
|
322
|
+
reflection.klass.transaction(&block)
|
|
323
|
+
end
|
|
324
|
+
|
|
305
325
|
# We have some records loaded from the database (persisted) and some that are
|
|
306
326
|
# in-memory (memory). The same record may be represented in the persisted array
|
|
307
327
|
# and in the memory array.
|
|
@@ -314,13 +334,12 @@ module ActiveRecord
|
|
|
314
334
|
# * Otherwise, attributes should have the value found in the database
|
|
315
335
|
def merge_target_lists(persisted, memory)
|
|
316
336
|
return persisted if memory.empty?
|
|
317
|
-
return memory if persisted.empty?
|
|
318
337
|
|
|
319
338
|
persisted.map! do |record|
|
|
320
339
|
if mem_record = memory.delete(record)
|
|
321
340
|
|
|
322
|
-
((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save).each do |name|
|
|
323
|
-
mem_record
|
|
341
|
+
((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save - mem_record.class._attr_readonly).each do |name|
|
|
342
|
+
mem_record._write_attribute(name, record[name])
|
|
324
343
|
end
|
|
325
344
|
|
|
326
345
|
mem_record
|
|
@@ -329,12 +348,12 @@ module ActiveRecord
|
|
|
329
348
|
end
|
|
330
349
|
end
|
|
331
350
|
|
|
332
|
-
persisted + memory
|
|
351
|
+
persisted + memory.reject(&:persisted?)
|
|
333
352
|
end
|
|
334
353
|
|
|
335
354
|
def _create_record(attributes, raise = false, &block)
|
|
336
355
|
unless owner.persisted?
|
|
337
|
-
raise ActiveRecord::RecordNotSaved
|
|
356
|
+
raise ActiveRecord::RecordNotSaved.new("You cannot call create unless the parent is saved", owner)
|
|
338
357
|
end
|
|
339
358
|
|
|
340
359
|
if attributes.is_a?(Array)
|
|
@@ -378,7 +397,9 @@ module ActiveRecord
|
|
|
378
397
|
end
|
|
379
398
|
|
|
380
399
|
def remove_records(existing_records, records, method)
|
|
381
|
-
|
|
400
|
+
catch(:abort) do
|
|
401
|
+
records.each { |record| callback(:before_remove, record) }
|
|
402
|
+
end || return
|
|
382
403
|
|
|
383
404
|
delete_records(existing_records, method) if existing_records.any?
|
|
384
405
|
@target -= records
|
|
@@ -410,7 +431,7 @@ module ActiveRecord
|
|
|
410
431
|
common_records = intersection(new_target, original_target)
|
|
411
432
|
common_records.each do |record|
|
|
412
433
|
skip_callbacks = true
|
|
413
|
-
replace_on_target(record,
|
|
434
|
+
replace_on_target(record, skip_callbacks, replace: true)
|
|
414
435
|
end
|
|
415
436
|
end
|
|
416
437
|
|
|
@@ -433,8 +454,14 @@ module ActiveRecord
|
|
|
433
454
|
records
|
|
434
455
|
end
|
|
435
456
|
|
|
436
|
-
def replace_on_target(record,
|
|
437
|
-
|
|
457
|
+
def replace_on_target(record, skip_callbacks, replace:, inversing: false)
|
|
458
|
+
if replace && (!record.new_record? || @replaced_or_added_targets.include?(record))
|
|
459
|
+
index = @target.index(record)
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
catch(:abort) do
|
|
463
|
+
callback(:before_add, record)
|
|
464
|
+
end || return unless skip_callbacks
|
|
438
465
|
|
|
439
466
|
set_inverse_instance(record)
|
|
440
467
|
|
|
@@ -442,6 +469,12 @@ module ActiveRecord
|
|
|
442
469
|
|
|
443
470
|
yield(record) if block_given?
|
|
444
471
|
|
|
472
|
+
if !index && @replaced_or_added_targets.include?(record)
|
|
473
|
+
index = @target.index(record)
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
@replaced_or_added_targets << record if inversing || index || record.new_record?
|
|
477
|
+
|
|
445
478
|
if index
|
|
446
479
|
target[index] = record
|
|
447
480
|
elsif @_was_loaded || !loaded?
|
|
@@ -464,7 +497,11 @@ module ActiveRecord
|
|
|
464
497
|
|
|
465
498
|
def callbacks_for(callback_name)
|
|
466
499
|
full_callback_name = "#{callback_name}_for_#{reflection.name}"
|
|
467
|
-
owner.class.
|
|
500
|
+
if owner.class.respond_to?(full_callback_name)
|
|
501
|
+
owner.class.send(full_callback_name)
|
|
502
|
+
else
|
|
503
|
+
[]
|
|
504
|
+
end
|
|
468
505
|
end
|
|
469
506
|
|
|
470
507
|
def include_in_memory?(record)
|