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
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module ActiveRecord
|
|
4
4
|
module Associations
|
|
5
|
+
# = Active Record Collection Proxy
|
|
6
|
+
#
|
|
5
7
|
# Collection proxies in Active Record are middlemen between an
|
|
6
8
|
# <tt>association</tt>, and its <tt>target</tt> result set.
|
|
7
9
|
#
|
|
@@ -27,7 +29,7 @@ module ActiveRecord
|
|
|
27
29
|
# is computed directly through SQL and does not trigger by itself the
|
|
28
30
|
# instantiation of the actual post records.
|
|
29
31
|
class CollectionProxy < Relation
|
|
30
|
-
def initialize(klass, association)
|
|
32
|
+
def initialize(klass, association, **) # :nodoc:
|
|
31
33
|
@association = association
|
|
32
34
|
super klass
|
|
33
35
|
|
|
@@ -46,11 +48,12 @@ module ActiveRecord
|
|
|
46
48
|
# Returns +true+ if the association has been loaded, otherwise +false+.
|
|
47
49
|
#
|
|
48
50
|
# person.pets.loaded? # => false
|
|
49
|
-
# person.pets
|
|
51
|
+
# person.pets.records
|
|
50
52
|
# person.pets.loaded? # => true
|
|
51
53
|
def loaded?
|
|
52
54
|
@association.loaded?
|
|
53
55
|
end
|
|
56
|
+
alias :loaded :loaded?
|
|
54
57
|
|
|
55
58
|
##
|
|
56
59
|
# :method: select
|
|
@@ -93,21 +96,21 @@ module ActiveRecord
|
|
|
93
96
|
# receive:
|
|
94
97
|
#
|
|
95
98
|
# person.pets.select(:name).first.person_id
|
|
96
|
-
# # => ActiveModel::MissingAttributeError: missing attribute
|
|
99
|
+
# # => ActiveModel::MissingAttributeError: missing attribute 'person_id' for Pet
|
|
97
100
|
#
|
|
98
|
-
# *Second:* You can pass a block so it can be used just like Array#select
|
|
101
|
+
# *Second:* You can pass a block so it can be used just like <tt>Array#select</tt>.
|
|
99
102
|
# This builds an array of objects from the database for the scope,
|
|
100
103
|
# converting them into an array and iterating through them using
|
|
101
|
-
# Array#select
|
|
104
|
+
# <tt>Array#select</tt>.
|
|
102
105
|
#
|
|
103
|
-
# person.pets.select { |pet| pet.name
|
|
106
|
+
# person.pets.select { |pet| /oo/.match?(pet.name) }
|
|
104
107
|
# # => [
|
|
105
108
|
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
|
106
109
|
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
|
107
110
|
# # ]
|
|
108
111
|
|
|
109
112
|
# Finds an object in the collection responding to the +id+. Uses the same
|
|
110
|
-
# rules as ActiveRecord::
|
|
113
|
+
# rules as ActiveRecord::FinderMethods.find. Returns ActiveRecord::RecordNotFound
|
|
111
114
|
# error if the object cannot be found.
|
|
112
115
|
#
|
|
113
116
|
# class Person < ActiveRecord::Base
|
|
@@ -217,7 +220,7 @@ module ActiveRecord
|
|
|
217
220
|
# :call-seq:
|
|
218
221
|
# third_to_last()
|
|
219
222
|
#
|
|
220
|
-
# Same as #
|
|
223
|
+
# Same as #last except returns only the third-to-last record.
|
|
221
224
|
|
|
222
225
|
##
|
|
223
226
|
# :method: second_to_last
|
|
@@ -225,7 +228,7 @@ module ActiveRecord
|
|
|
225
228
|
# :call-seq:
|
|
226
229
|
# second_to_last()
|
|
227
230
|
#
|
|
228
|
-
# Same as #
|
|
231
|
+
# Same as #last except returns only the second-to-last record.
|
|
229
232
|
|
|
230
233
|
# Returns the last record, or the last +n+ records, from the collection.
|
|
231
234
|
# If the collection is empty, the first form returns +nil+, and the second
|
|
@@ -259,7 +262,7 @@ module ActiveRecord
|
|
|
259
262
|
end
|
|
260
263
|
|
|
261
264
|
# Gives a record (or N records if a parameter is supplied) from the collection
|
|
262
|
-
# using the same rules as
|
|
265
|
+
# using the same rules as ActiveRecord::FinderMethods.take.
|
|
263
266
|
#
|
|
264
267
|
# class Person < ActiveRecord::Base
|
|
265
268
|
# has_many :pets
|
|
@@ -373,7 +376,7 @@ module ActiveRecord
|
|
|
373
376
|
# person.pets
|
|
374
377
|
# # => [#<Pet id: 1, name: "Gorby", group: "cats", person_id: 1>]
|
|
375
378
|
#
|
|
376
|
-
# other_pets = [Pet.new(name: 'Puff', group: 'celebrities']
|
|
379
|
+
# other_pets = [Pet.new(name: 'Puff', group: 'celebrities')]
|
|
377
380
|
#
|
|
378
381
|
# person.pets.replace(other_pets)
|
|
379
382
|
#
|
|
@@ -381,7 +384,7 @@ module ActiveRecord
|
|
|
381
384
|
# # => [#<Pet id: 2, name: "Puff", group: "celebrities", person_id: 1>]
|
|
382
385
|
#
|
|
383
386
|
# If the supplied array has an incorrect association type, it raises
|
|
384
|
-
# an
|
|
387
|
+
# an ActiveRecord::AssociationTypeMismatch error:
|
|
385
388
|
#
|
|
386
389
|
# person.pets.replace(["doo", "ggie", "gaga"])
|
|
387
390
|
# # => ActiveRecord::AssociationTypeMismatch: Pet expected, got String
|
|
@@ -474,7 +477,7 @@ module ActiveRecord
|
|
|
474
477
|
|
|
475
478
|
# Deletes the records of the collection directly from the database
|
|
476
479
|
# ignoring the +:dependent+ option. Records are instantiated and it
|
|
477
|
-
# invokes +before_remove+, +after_remove
|
|
480
|
+
# invokes +before_remove+, +after_remove+, +before_destroy+, and
|
|
478
481
|
# +after_destroy+ callbacks.
|
|
479
482
|
#
|
|
480
483
|
# class Person < ActiveRecord::Base
|
|
@@ -812,7 +815,7 @@ module ActiveRecord
|
|
|
812
815
|
# to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
|
|
813
816
|
# it is equivalent to <tt>!collection.exists?</tt>. If the collection has
|
|
814
817
|
# not already been loaded and you are going to fetch the records anyway it
|
|
815
|
-
# is better to check <tt>collection.
|
|
818
|
+
# is better to check <tt>collection.load.empty?</tt>.
|
|
816
819
|
#
|
|
817
820
|
# class Person < ActiveRecord::Base
|
|
818
821
|
# has_many :pets
|
|
@@ -848,6 +851,11 @@ module ActiveRecord
|
|
|
848
851
|
# person.pets.count # => 1
|
|
849
852
|
# person.pets.any? # => true
|
|
850
853
|
#
|
|
854
|
+
# Calling it without a block when the collection is not yet
|
|
855
|
+
# loaded is equivalent to <tt>collection.exists?</tt>.
|
|
856
|
+
# If you're going to load the collection anyway, it is better
|
|
857
|
+
# to call <tt>collection.load.any?</tt> to avoid an extra query.
|
|
858
|
+
#
|
|
851
859
|
# You can also pass a +block+ to define criteria. The behavior
|
|
852
860
|
# is the same, it returns true if the collection based on the
|
|
853
861
|
# criteria is not empty.
|
|
@@ -920,11 +928,24 @@ module ActiveRecord
|
|
|
920
928
|
!!@association.include?(record)
|
|
921
929
|
end
|
|
922
930
|
|
|
931
|
+
# Returns the association object for the collection.
|
|
932
|
+
#
|
|
933
|
+
# class Person < ActiveRecord::Base
|
|
934
|
+
# has_many :pets
|
|
935
|
+
# end
|
|
936
|
+
#
|
|
937
|
+
# person.pets.proxy_association
|
|
938
|
+
# # => #<ActiveRecord::Associations::HasManyAssociation owner="#<Person:0x00>">
|
|
939
|
+
#
|
|
940
|
+
# Returns the same object as <tt>person.association(:pets)</tt>,
|
|
941
|
+
# allowing you to make calls like <tt>person.pets.proxy_association.owner</tt>.
|
|
942
|
+
#
|
|
943
|
+
# See Associations::ClassMethods@Association+extensions for more.
|
|
923
944
|
def proxy_association
|
|
924
945
|
@association
|
|
925
946
|
end
|
|
926
947
|
|
|
927
|
-
# Returns a
|
|
948
|
+
# Returns a Relation object for the records in this association
|
|
928
949
|
def scope
|
|
929
950
|
@scope ||= @association.scope
|
|
930
951
|
end
|
|
@@ -949,10 +970,13 @@ module ActiveRecord
|
|
|
949
970
|
# person.pets == other
|
|
950
971
|
# # => true
|
|
951
972
|
#
|
|
973
|
+
#
|
|
974
|
+
# Note that unpersisted records can still be seen as equal:
|
|
975
|
+
#
|
|
952
976
|
# other = [Pet.new(id: 1), Pet.new(id: 2)]
|
|
953
977
|
#
|
|
954
978
|
# person.pets == other
|
|
955
|
-
# # =>
|
|
979
|
+
# # => true
|
|
956
980
|
def ==(other)
|
|
957
981
|
load_target == other
|
|
958
982
|
end
|
|
@@ -1086,22 +1110,33 @@ module ActiveRecord
|
|
|
1086
1110
|
end
|
|
1087
1111
|
|
|
1088
1112
|
def reset_scope # :nodoc:
|
|
1089
|
-
@offsets =
|
|
1113
|
+
@offsets = @take = nil
|
|
1090
1114
|
@scope = nil
|
|
1091
1115
|
self
|
|
1092
1116
|
end
|
|
1093
1117
|
|
|
1118
|
+
def inspect # :nodoc:
|
|
1119
|
+
load_target if find_from_target?
|
|
1120
|
+
super
|
|
1121
|
+
end
|
|
1122
|
+
|
|
1123
|
+
def pretty_print(pp) # :nodoc:
|
|
1124
|
+
load_target if find_from_target?
|
|
1125
|
+
super
|
|
1126
|
+
end
|
|
1127
|
+
|
|
1094
1128
|
delegate_methods = [
|
|
1095
1129
|
QueryMethods,
|
|
1096
1130
|
SpawnMethods,
|
|
1097
1131
|
].flat_map { |klass|
|
|
1098
1132
|
klass.public_instance_methods(false)
|
|
1099
|
-
} - self.public_instance_methods(false) - [:select] + [
|
|
1133
|
+
} - self.public_instance_methods(false) - [:select] + [
|
|
1134
|
+
:scoping, :values, :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all, :load_async
|
|
1135
|
+
]
|
|
1100
1136
|
|
|
1101
1137
|
delegate(*delegate_methods, to: :scope)
|
|
1102
1138
|
|
|
1103
1139
|
private
|
|
1104
|
-
|
|
1105
1140
|
def find_nth_with_limit(index, limit)
|
|
1106
1141
|
load_target if find_from_target?
|
|
1107
1142
|
super
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Associations
|
|
5
|
+
class DisableJoinsAssociationScope < AssociationScope # :nodoc:
|
|
6
|
+
def scope(association)
|
|
7
|
+
source_reflection = association.reflection
|
|
8
|
+
owner = association.owner
|
|
9
|
+
unscoped = association.klass.unscoped
|
|
10
|
+
reverse_chain = get_chain(source_reflection, association, unscoped.alias_tracker).reverse
|
|
11
|
+
|
|
12
|
+
last_reflection, last_ordered, last_join_ids = last_scope_chain(reverse_chain, owner)
|
|
13
|
+
|
|
14
|
+
add_constraints(last_reflection, last_reflection.join_primary_key, last_join_ids, owner, last_ordered)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
def last_scope_chain(reverse_chain, owner)
|
|
19
|
+
first_item = reverse_chain.shift
|
|
20
|
+
first_scope = [first_item, false, [owner._read_attribute(first_item.join_foreign_key)]]
|
|
21
|
+
|
|
22
|
+
reverse_chain.inject(first_scope) do |(reflection, ordered, join_ids), next_reflection|
|
|
23
|
+
key = reflection.join_primary_key
|
|
24
|
+
records = add_constraints(reflection, key, join_ids, owner, ordered)
|
|
25
|
+
foreign_key = next_reflection.join_foreign_key
|
|
26
|
+
record_ids = records.pluck(foreign_key)
|
|
27
|
+
records_ordered = records && records.order_values.any?
|
|
28
|
+
|
|
29
|
+
[next_reflection, records_ordered, record_ids]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def add_constraints(reflection, key, join_ids, owner, ordered)
|
|
34
|
+
scope = reflection.build_scope(reflection.aliased_table).where(key => join_ids)
|
|
35
|
+
|
|
36
|
+
relation = reflection.klass.scope_for_association
|
|
37
|
+
scope.merge!(
|
|
38
|
+
relation.except(:select, :create_with, :includes, :preload, :eager_load, :joins, :left_outer_joins)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
scope = reflection.constraints.inject(scope) do |memo, scope_chain_item|
|
|
42
|
+
item = eval_scope(reflection, scope_chain_item, owner)
|
|
43
|
+
scope.unscope!(*item.unscope_values)
|
|
44
|
+
scope.where_clause += item.where_clause
|
|
45
|
+
scope.order_values = item.order_values | scope.order_values
|
|
46
|
+
scope
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
if scope.order_values.empty? && ordered
|
|
50
|
+
split_scope = DisableJoinsAssociationRelation.create(scope.klass, key, join_ids)
|
|
51
|
+
split_scope.where_clause += scope.where_clause
|
|
52
|
+
split_scope
|
|
53
|
+
else
|
|
54
|
+
scope
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
class AssociationNotFoundError < ConfigurationError # :nodoc:
|
|
5
|
+
attr_reader :record, :association_name
|
|
6
|
+
|
|
7
|
+
def initialize(record = nil, association_name = nil)
|
|
8
|
+
@record = record
|
|
9
|
+
@association_name = association_name
|
|
10
|
+
if record && association_name
|
|
11
|
+
super("Association named '#{association_name}' was not found on #{record.class.name}; perhaps you misspelled it?")
|
|
12
|
+
else
|
|
13
|
+
super("Association was not found.")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
|
|
18
|
+
include DidYouMean::Correctable
|
|
19
|
+
|
|
20
|
+
def corrections
|
|
21
|
+
if record && association_name
|
|
22
|
+
@corrections ||= begin
|
|
23
|
+
maybe_these = record.class.reflections.keys
|
|
24
|
+
DidYouMean::SpellChecker.new(dictionary: maybe_these).correct(association_name)
|
|
25
|
+
end
|
|
26
|
+
else
|
|
27
|
+
[]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class InverseOfAssociationNotFoundError < ActiveRecordError # :nodoc:
|
|
34
|
+
attr_reader :reflection, :associated_class
|
|
35
|
+
|
|
36
|
+
def initialize(reflection = nil, associated_class = nil)
|
|
37
|
+
if reflection
|
|
38
|
+
@reflection = reflection
|
|
39
|
+
@associated_class = associated_class.nil? ? reflection.klass : associated_class
|
|
40
|
+
super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
|
|
41
|
+
else
|
|
42
|
+
super("Could not find the inverse association.")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
|
|
47
|
+
include DidYouMean::Correctable
|
|
48
|
+
|
|
49
|
+
def corrections
|
|
50
|
+
if reflection && associated_class
|
|
51
|
+
@corrections ||= begin
|
|
52
|
+
maybe_these = associated_class.reflections.keys
|
|
53
|
+
DidYouMean::SpellChecker.new(dictionary: maybe_these).correct(reflection.options[:inverse_of].to_s)
|
|
54
|
+
end
|
|
55
|
+
else
|
|
56
|
+
[]
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class InverseOfAssociationRecursiveError < ActiveRecordError # :nodoc:
|
|
63
|
+
attr_reader :reflection
|
|
64
|
+
def initialize(reflection = nil)
|
|
65
|
+
if reflection
|
|
66
|
+
@reflection = reflection
|
|
67
|
+
super("Inverse association #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{reflection.class_name}) is recursive.")
|
|
68
|
+
else
|
|
69
|
+
super("Inverse association is recursive.")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
class HasManyThroughAssociationNotFoundError < ActiveRecordError # :nodoc:
|
|
75
|
+
attr_reader :owner_class, :reflection
|
|
76
|
+
|
|
77
|
+
def initialize(owner_class = nil, reflection = nil)
|
|
78
|
+
if owner_class && reflection
|
|
79
|
+
@owner_class = owner_class
|
|
80
|
+
@reflection = reflection
|
|
81
|
+
super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class.name}")
|
|
82
|
+
else
|
|
83
|
+
super("Could not find the association.")
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
|
|
88
|
+
include DidYouMean::Correctable
|
|
89
|
+
|
|
90
|
+
def corrections
|
|
91
|
+
if owner_class && reflection
|
|
92
|
+
@corrections ||= begin
|
|
93
|
+
maybe_these = owner_class.reflections.keys
|
|
94
|
+
maybe_these -= [reflection.name.to_s] # remove failing reflection
|
|
95
|
+
DidYouMean::SpellChecker.new(dictionary: maybe_these).correct(reflection.options[:through].to_s)
|
|
96
|
+
end
|
|
97
|
+
else
|
|
98
|
+
[]
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class HasManyThroughAssociationPolymorphicSourceError < ActiveRecordError # :nodoc:
|
|
105
|
+
def initialize(owner_class_name = nil, reflection = nil, source_reflection = nil)
|
|
106
|
+
if owner_class_name && reflection && source_reflection
|
|
107
|
+
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' on the polymorphic object '#{source_reflection.class_name}##{source_reflection.name}' without 'source_type'. Try adding 'source_type: \"#{reflection.name.to_s.classify}\"' to 'has_many :through' definition.")
|
|
108
|
+
else
|
|
109
|
+
super("Cannot have a has_many :through association.")
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class HasManyThroughAssociationPolymorphicThroughError < ActiveRecordError # :nodoc:
|
|
115
|
+
def initialize(owner_class_name = nil, reflection = nil)
|
|
116
|
+
if owner_class_name && reflection
|
|
117
|
+
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
|
|
118
|
+
else
|
|
119
|
+
super("Cannot have a has_many :through association.")
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError # :nodoc:
|
|
125
|
+
def initialize(owner_class_name = nil, reflection = nil, source_reflection = nil)
|
|
126
|
+
if owner_class_name && reflection && source_reflection
|
|
127
|
+
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic. Try removing :source_type on your association.")
|
|
128
|
+
else
|
|
129
|
+
super("Cannot have a has_many :through association.")
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
class HasOneThroughCantAssociateThroughCollection < ActiveRecordError # :nodoc:
|
|
135
|
+
def initialize(owner_class_name = nil, reflection = nil, through_reflection = nil)
|
|
136
|
+
if owner_class_name && reflection && through_reflection
|
|
137
|
+
super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' where the :through association '#{owner_class_name}##{through_reflection.name}' is a collection. Specify a has_one or belongs_to association in the :through option instead.")
|
|
138
|
+
else
|
|
139
|
+
super("Cannot have a has_one :through association.")
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
class HasOneAssociationPolymorphicThroughError < ActiveRecordError # :nodoc:
|
|
145
|
+
def initialize(owner_class_name = nil, reflection = nil)
|
|
146
|
+
if owner_class_name && reflection
|
|
147
|
+
super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
|
|
148
|
+
else
|
|
149
|
+
super("Cannot have a has_one :through association.")
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError # :nodoc:
|
|
155
|
+
def initialize(reflection = nil)
|
|
156
|
+
if reflection
|
|
157
|
+
through_reflection = reflection.through_reflection
|
|
158
|
+
source_reflection_names = reflection.source_reflection_names
|
|
159
|
+
source_associations = reflection.through_reflection.klass._reflections.keys
|
|
160
|
+
super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')}?")
|
|
161
|
+
else
|
|
162
|
+
super("Could not find the source association(s).")
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
class HasManyThroughOrderError < ActiveRecordError # :nodoc:
|
|
168
|
+
def initialize(owner_class_name = nil, reflection = nil, through_reflection = nil)
|
|
169
|
+
if owner_class_name && reflection && through_reflection
|
|
170
|
+
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' which goes through '#{owner_class_name}##{through_reflection.name}' before the through association is defined.")
|
|
171
|
+
else
|
|
172
|
+
super("Cannot have a has_many :through association before the through association is defined.")
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
class ThroughCantAssociateThroughHasOneOrManyReflection < ActiveRecordError # :nodoc:
|
|
178
|
+
def initialize(owner = nil, reflection = nil)
|
|
179
|
+
if owner && reflection
|
|
180
|
+
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because the source reflection class '#{reflection.source_reflection.class_name}' is associated to '#{reflection.through_reflection.class_name}' via :#{reflection.source_reflection.macro}.")
|
|
181
|
+
else
|
|
182
|
+
super("Cannot modify association.")
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
class CompositePrimaryKeyMismatchError < ActiveRecordError # :nodoc:
|
|
188
|
+
attr_reader :reflection
|
|
189
|
+
|
|
190
|
+
def initialize(reflection = nil)
|
|
191
|
+
if reflection
|
|
192
|
+
if reflection.has_one? || reflection.collection?
|
|
193
|
+
super("Association #{reflection.active_record}##{reflection.name} primary key #{reflection.active_record_primary_key} doesn't match with foreign key #{reflection.foreign_key}. Please specify query_constraints, or primary_key and foreign_key values.")
|
|
194
|
+
else
|
|
195
|
+
super("Association #{reflection.active_record}##{reflection.name} primary key #{reflection.association_primary_key} doesn't match with foreign key #{reflection.foreign_key}. Please specify query_constraints, or primary_key and foreign_key values.")
|
|
196
|
+
end
|
|
197
|
+
else
|
|
198
|
+
super("Association primary key doesn't match with foreign key.")
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
class AmbiguousSourceReflectionForThroughAssociation < ActiveRecordError # :nodoc:
|
|
204
|
+
def initialize(klass, macro, association_name, options, possible_sources)
|
|
205
|
+
example_options = options.dup
|
|
206
|
+
example_options[:source] = possible_sources.first
|
|
207
|
+
|
|
208
|
+
super("Ambiguous source reflection for through association. Please " \
|
|
209
|
+
"specify a :source directive on your declaration like:\n" \
|
|
210
|
+
"\n" \
|
|
211
|
+
" class #{klass} < ActiveRecord::Base\n" \
|
|
212
|
+
" #{macro} :#{association_name}, #{example_options}\n" \
|
|
213
|
+
" end"
|
|
214
|
+
)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
class HasManyThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection # :nodoc:
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
class HasOneThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection # :nodoc:
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
class ThroughNestedAssociationsAreReadonly < ActiveRecordError # :nodoc:
|
|
225
|
+
def initialize(owner = nil, reflection = nil)
|
|
226
|
+
if owner && reflection
|
|
227
|
+
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because it goes through more than one other association.")
|
|
228
|
+
else
|
|
229
|
+
super("Through nested associations are read-only.")
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
class HasManyThroughNestedAssociationsAreReadonly < ThroughNestedAssociationsAreReadonly # :nodoc:
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
class HasOneThroughNestedAssociationsAreReadonly < ThroughNestedAssociationsAreReadonly # :nodoc:
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# This error is raised when trying to eager load a polymorphic association using a JOIN.
|
|
241
|
+
# Eager loading polymorphic associations is only possible with
|
|
242
|
+
# {ActiveRecord::Relation#preload}[rdoc-ref:QueryMethods#preload].
|
|
243
|
+
class EagerLoadPolymorphicError < ActiveRecordError
|
|
244
|
+
def initialize(reflection = nil)
|
|
245
|
+
if reflection
|
|
246
|
+
super("Cannot eagerly load the polymorphic association #{reflection.name.inspect}")
|
|
247
|
+
else
|
|
248
|
+
super("Eager load polymorphic error.")
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations
|
|
254
|
+
# (has_many, has_one) when there is at least 1 child associated instance.
|
|
255
|
+
# ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
|
|
256
|
+
class DeleteRestrictionError < ActiveRecordError # :nodoc:
|
|
257
|
+
def initialize(name = nil)
|
|
258
|
+
if name
|
|
259
|
+
super("Cannot delete record because of dependent #{name}")
|
|
260
|
+
else
|
|
261
|
+
super("Delete restriction error.")
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
@@ -12,9 +12,29 @@ module ActiveRecord::Associations
|
|
|
12
12
|
|
|
13
13
|
def nullified_owner_attributes
|
|
14
14
|
Hash.new.tap do |attrs|
|
|
15
|
-
|
|
15
|
+
Array(reflection.foreign_key).each { |foreign_key| attrs[foreign_key] = nil }
|
|
16
16
|
attrs[reflection.type] = nil if reflection.type.present?
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
# Sets the owner attributes on the given record
|
|
22
|
+
def set_owner_attributes(record)
|
|
23
|
+
return if options[:through]
|
|
24
|
+
|
|
25
|
+
primary_key_attribute_names = Array(reflection.join_primary_key)
|
|
26
|
+
foreign_key_attribute_names = Array(reflection.join_foreign_key)
|
|
27
|
+
|
|
28
|
+
primary_key_foreign_key_pairs = primary_key_attribute_names.zip(foreign_key_attribute_names)
|
|
29
|
+
|
|
30
|
+
primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
|
|
31
|
+
value = owner._read_attribute(foreign_key)
|
|
32
|
+
record._write_attribute(primary_key, value)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
if reflection.type
|
|
36
|
+
record._write_attribute(reflection.type, owner.class.polymorphic_name)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
19
39
|
end
|
|
20
40
|
end
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
module ActiveRecord
|
|
4
4
|
module Associations
|
|
5
5
|
# = Active Record Has Many Association
|
|
6
|
+
#
|
|
6
7
|
# This is the proxy that handles a has many association.
|
|
7
8
|
#
|
|
8
9
|
# If the association has a <tt>:through</tt> option further specialization
|
|
9
10
|
# is provided by its child HasManyThroughAssociation.
|
|
10
|
-
class HasManyAssociation < CollectionAssociation
|
|
11
|
+
class HasManyAssociation < CollectionAssociation # :nodoc:
|
|
11
12
|
include ForeignAssociation
|
|
12
13
|
|
|
13
14
|
def handle_dependency
|
|
@@ -26,6 +27,32 @@ module ActiveRecord
|
|
|
26
27
|
# No point in executing the counter update since we're going to destroy the parent anyway
|
|
27
28
|
load_target.each { |t| t.destroyed_by_association = reflection }
|
|
28
29
|
destroy_all
|
|
30
|
+
when :destroy_async
|
|
31
|
+
load_target.each do |t|
|
|
32
|
+
t.destroyed_by_association = reflection
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
unless target.empty?
|
|
36
|
+
association_class = target.first.class
|
|
37
|
+
if association_class.query_constraints_list
|
|
38
|
+
primary_key_column = association_class.query_constraints_list
|
|
39
|
+
ids = target.collect { |assoc| primary_key_column.map { |col| assoc.public_send(col) } }
|
|
40
|
+
else
|
|
41
|
+
primary_key_column = association_class.primary_key
|
|
42
|
+
ids = target.collect { |assoc| assoc.public_send(primary_key_column) }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
ids.each_slice(owner.class.destroy_association_async_batch_size || ids.size) do |ids_batch|
|
|
46
|
+
enqueue_destroy_association(
|
|
47
|
+
owner_model_name: owner.class.to_s,
|
|
48
|
+
owner_id: owner.id,
|
|
49
|
+
association_class: reflection.klass.to_s,
|
|
50
|
+
association_ids: ids_batch,
|
|
51
|
+
association_primary_key_column: primary_key_column,
|
|
52
|
+
ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
29
56
|
else
|
|
30
57
|
delete_all
|
|
31
58
|
end
|
|
@@ -37,7 +64,6 @@ module ActiveRecord
|
|
|
37
64
|
end
|
|
38
65
|
|
|
39
66
|
private
|
|
40
|
-
|
|
41
67
|
# Returns the number of records in this collection.
|
|
42
68
|
#
|
|
43
69
|
# If the association has a counter cache it gets that value. Otherwise
|
|
@@ -52,16 +78,19 @@ module ActiveRecord
|
|
|
52
78
|
# If the collection is empty the target is set to an empty array and
|
|
53
79
|
# the loaded flag is set to true as well.
|
|
54
80
|
def count_records
|
|
55
|
-
count = if reflection.
|
|
56
|
-
owner.
|
|
81
|
+
count = if reflection.has_active_cached_counter?
|
|
82
|
+
owner.read_attribute(reflection.counter_cache_column).to_i
|
|
57
83
|
else
|
|
58
84
|
scope.count(:all)
|
|
59
85
|
end
|
|
60
86
|
|
|
61
|
-
# If there's nothing in the database
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
|
|
87
|
+
# If there's nothing in the database, @target should only contain new
|
|
88
|
+
# records or be an empty array. This is a documented side-effect of
|
|
89
|
+
# the method that may avoid an extra SELECT.
|
|
90
|
+
if count == 0
|
|
91
|
+
target.select!(&:new_record?)
|
|
92
|
+
loaded!
|
|
93
|
+
end
|
|
65
94
|
|
|
66
95
|
[association_scope.limit_value, count].compact.min
|
|
67
96
|
end
|
|
@@ -76,7 +105,7 @@ module ActiveRecord
|
|
|
76
105
|
if reflection.counter_must_be_updated_by_has_many?
|
|
77
106
|
counter = reflection.counter_cache_column
|
|
78
107
|
owner.increment(counter, difference)
|
|
79
|
-
owner.send(:
|
|
108
|
+
owner.send(:"clear_#{counter}_change")
|
|
80
109
|
end
|
|
81
110
|
end
|
|
82
111
|
|
|
@@ -100,7 +129,9 @@ module ActiveRecord
|
|
|
100
129
|
records.each(&:destroy!)
|
|
101
130
|
update_counter(-records.length) unless reflection.inverse_updates_counter_cache?
|
|
102
131
|
else
|
|
103
|
-
|
|
132
|
+
query_constraints = reflection.klass.composite_query_constraints_list
|
|
133
|
+
values = records.map { |r| query_constraints.map { |col| r._read_attribute(col) } }
|
|
134
|
+
scope = self.scope.where(query_constraints => values)
|
|
104
135
|
update_counter(-delete_count(method, scope))
|
|
105
136
|
end
|
|
106
137
|
end
|