activerecord 5.2.8 → 7.0.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1393 -587
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +10 -9
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +122 -47
- data/lib/active_record/associations/association_scope.rb +24 -24
- data/lib/active_record/associations/belongs_to_association.rb +67 -49
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -7
- data/lib/active_record/associations/builder/association.rb +52 -23
- data/lib/active_record/associations/builder/belongs_to.rb +44 -61
- data/lib/active_record/associations/builder/collection_association.rb +17 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +10 -3
- data/lib/active_record/associations/builder/has_one.rb +35 -3
- data/lib/active_record/associations/builder/singular_association.rb +5 -3
- data/lib/active_record/associations/collection_association.rb +59 -50
- data/lib/active_record/associations/collection_proxy.rb +32 -23
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +27 -14
- data/lib/active_record/associations/has_many_through_association.rb +26 -19
- data/lib/active_record/associations/has_one_association.rb +52 -37
- data/lib/active_record/associations/has_one_through_association.rb +6 -6
- data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +97 -62
- data/lib/active_record/associations/preloader/association.rb +220 -60
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +85 -40
- data/lib/active_record/associations/preloader.rb +44 -105
- data/lib/active_record/associations/singular_association.rb +9 -17
- data/lib/active_record/associations/through_association.rb +4 -4
- data/lib/active_record/associations.rb +207 -66
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods/before_type_cast.rb +19 -8
- data/lib/active_record/attribute_methods/dirty.rb +141 -47
- data/lib/active_record/attribute_methods/primary_key.rb +22 -27
- data/lib/active_record/attribute_methods/query.rb +6 -10
- data/lib/active_record/attribute_methods/read.rb +15 -55
- data/lib/active_record/attribute_methods/serialization.rb +77 -18
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +16 -18
- data/lib/active_record/attribute_methods/write.rb +18 -37
- data/lib/active_record/attribute_methods.rb +90 -153
- data/lib/active_record/attributes.rb +38 -12
- data/lib/active_record/autosave_association.rb +50 -50
- data/lib/active_record/base.rb +23 -18
- data/lib/active_record/callbacks.rb +159 -44
- data/lib/active_record/coders/yaml_column.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +92 -464
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -51
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +209 -164
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +38 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +103 -82
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +140 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -94
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +16 -5
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +456 -159
- data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
- data/lib/active_record/connection_adapters/abstract_adapter.rb +367 -162
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +311 -327
- data/lib/active_record/connection_adapters/column.rb +33 -11
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +113 -45
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +71 -5
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +25 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +143 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +63 -22
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +53 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +56 -63
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -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 +54 -16
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
- 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 +26 -12
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -52
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +128 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +149 -113
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +386 -182
- data/lib/active_record/connection_adapters/schema_cache.rb +161 -22
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +65 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +92 -26
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +251 -204
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +53 -0
- data/lib/active_record/connection_handling.rb +292 -38
- data/lib/active_record/core.rb +385 -158
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations/connection_url_resolver.rb +100 -0
- data/lib/active_record/database_configurations/database_config.rb +83 -0
- data/lib/active_record/database_configurations/hash_config.rb +154 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +256 -0
- data/lib/active_record/delegated_type.rb +250 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +4 -5
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +61 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +208 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -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 +155 -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 +160 -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 +42 -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_serializer.rb +90 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +130 -51
- data/lib/active_record/errors.rb +129 -23
- data/lib/active_record/explain.rb +10 -6
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +22 -15
- data/lib/active_record/fixture_set/model_metadata.rb +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +187 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +206 -490
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +104 -37
- data/lib/active_record/insert_all.rb +278 -0
- data/lib/active_record/integration.rb +69 -18
- data/lib/active_record/internal_metadata.rb +24 -9
- data/lib/active_record/legacy_yaml_adapter.rb +3 -36
- data/lib/active_record/locking/optimistic.rb +41 -26
- data/lib/active_record/locking/pessimistic.rb +18 -8
- data/lib/active_record/log_subscriber.rb +46 -35
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector.rb +82 -0
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +246 -64
- data/lib/active_record/migration/join_table.rb +1 -2
- data/lib/active_record/migration.rb +266 -187
- data/lib/active_record/model_schema.rb +165 -52
- data/lib/active_record/nested_attributes.rb +17 -19
- data/lib/active_record/no_touching.rb +11 -4
- data/lib/active_record/null_relation.rb +2 -7
- data/lib/active_record/persistence.rb +467 -92
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +51 -24
- data/lib/active_record/railtie.rb +224 -57
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +31 -36
- data/lib/active_record/railties/databases.rake +369 -101
- data/lib/active_record/readonly_attributes.rb +15 -0
- data/lib/active_record/reflection.rb +170 -137
- data/lib/active_record/relation/batches/batch_enumerator.rb +44 -14
- data/lib/active_record/relation/batches.rb +46 -37
- data/lib/active_record/relation/calculations.rb +168 -96
- data/lib/active_record/relation/delegation.rb +37 -52
- data/lib/active_record/relation/finder_methods.rb +79 -58
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +50 -51
- data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +58 -46
- data/lib/active_record/relation/query_attribute.rb +9 -10
- data/lib/active_record/relation/query_methods.rb +685 -208
- data/lib/active_record/relation/record_fetch_warning.rb +9 -11
- data/lib/active_record/relation/spawn_methods.rb +10 -10
- data/lib/active_record/relation/where_clause.rb +108 -64
- data/lib/active_record/relation.rb +515 -151
- data/lib/active_record/result.rb +78 -42
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +29 -44
- data/lib/active_record/schema.rb +37 -31
- data/lib/active_record/schema_dumper.rb +74 -23
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping/default.rb +62 -17
- data/lib/active_record/scoping/named.rb +17 -32
- data/lib/active_record/scoping.rb +70 -41
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +6 -4
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +49 -6
- data/lib/active_record/store.rb +88 -9
- data/lib/active_record/suppressor.rb +13 -17
- data/lib/active_record/table_metadata.rb +42 -43
- data/lib/active_record/tasks/database_tasks.rb +352 -94
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +41 -39
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +287 -0
- data/lib/active_record/timestamp.rb +44 -34
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +67 -128
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +34 -19
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +7 -4
- data/lib/active_record/type/time.rb +10 -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 +9 -5
- data/lib/active_record/type_caster/connection.rb +15 -15
- data/lib/active_record/type_caster/map.rb +8 -8
- data/lib/active_record/validations/associated.rb +2 -3
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +39 -31
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record.rb +209 -32
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +33 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +48 -0
- data/lib/arel/delete_manager.rb +32 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +48 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +44 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +45 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +46 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +71 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +258 -0
- data/lib/arel/select_manager.rb +276 -0
- data/lib/arel/table.rb +117 -0
- data/lib/arel/tree_manager.rb +60 -0
- data/lib/arel/update_manager.rb +48 -0
- data/lib/arel/visitors/dot.rb +298 -0
- data/lib/arel/visitors/mysql.rb +99 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +955 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +55 -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 +3 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
- data/lib/rails/generators/active_record/migration.rb +19 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- 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 +10 -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 +162 -32
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
- data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -2,39 +2,6 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord::Associations::Builder # :nodoc:
|
4
4
|
class HasAndBelongsToMany # :nodoc:
|
5
|
-
class JoinTableResolver # :nodoc:
|
6
|
-
KnownTable = Struct.new :join_table
|
7
|
-
|
8
|
-
class KnownClass # :nodoc:
|
9
|
-
def initialize(lhs_class, rhs_class_name)
|
10
|
-
@lhs_class = lhs_class
|
11
|
-
@rhs_class_name = rhs_class_name
|
12
|
-
@join_table = nil
|
13
|
-
end
|
14
|
-
|
15
|
-
def join_table
|
16
|
-
@join_table ||= [@lhs_class.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def klass
|
22
|
-
@lhs_class.send(:compute_type, @rhs_class_name)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.build(lhs_class, name, options)
|
27
|
-
if options[:join_table]
|
28
|
-
KnownTable.new options[:join_table].to_s
|
29
|
-
else
|
30
|
-
class_name = options.fetch(:class_name) {
|
31
|
-
name.to_s.camelize.singularize
|
32
|
-
}
|
33
|
-
KnownClass.new lhs_class, class_name.to_s
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
5
|
attr_reader :lhs_model, :association_name, :options
|
39
6
|
|
40
7
|
def initialize(association_name, lhs_model, options)
|
@@ -44,8 +11,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
44
11
|
end
|
45
12
|
|
46
13
|
def through_model
|
47
|
-
habtm = JoinTableResolver.build lhs_model, association_name, options
|
48
|
-
|
49
14
|
join_model = Class.new(ActiveRecord::Base) {
|
50
15
|
class << self
|
51
16
|
attr_accessor :left_model
|
@@ -56,7 +21,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
56
21
|
end
|
57
22
|
|
58
23
|
def self.table_name
|
59
|
-
|
24
|
+
# Table name needs to be resolved lazily
|
25
|
+
# because RHS class might not have been loaded
|
26
|
+
@table_name ||= table_name_resolver.call
|
60
27
|
end
|
61
28
|
|
62
29
|
def self.compute_type(class_name)
|
@@ -79,14 +46,13 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
79
46
|
end
|
80
47
|
|
81
48
|
private
|
82
|
-
|
83
49
|
def self.suppress_composite_primary_key(pk)
|
84
50
|
pk unless pk.is_a?(Array)
|
85
51
|
end
|
86
52
|
}
|
87
53
|
|
88
54
|
join_model.name = "HABTM_#{association_name.to_s.camelize}"
|
89
|
-
join_model.table_name_resolver =
|
55
|
+
join_model.table_name_resolver = -> { table_name }
|
90
56
|
join_model.left_model = lhs_model
|
91
57
|
|
92
58
|
join_model.add_left_association :left_side, anonymous_class: lhs_model
|
@@ -96,7 +62,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
96
62
|
|
97
63
|
def middle_reflection(join_model)
|
98
64
|
middle_name = [lhs_model.name.downcase.pluralize,
|
99
|
-
association_name].join("_"
|
65
|
+
association_name.to_s].sort.join("_").gsub("::", "_").to_sym
|
100
66
|
middle_options = middle_options join_model
|
101
67
|
|
102
68
|
HasMany.create_reflection(lhs_model,
|
@@ -106,17 +72,27 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
106
72
|
end
|
107
73
|
|
108
74
|
private
|
109
|
-
|
110
75
|
def middle_options(join_model)
|
111
76
|
middle_options = {}
|
112
77
|
middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
|
113
|
-
middle_options[:source] = join_model.left_reflection.name
|
114
78
|
if options.key? :foreign_key
|
115
79
|
middle_options[:foreign_key] = options[:foreign_key]
|
116
80
|
end
|
117
81
|
middle_options
|
118
82
|
end
|
119
83
|
|
84
|
+
def table_name
|
85
|
+
if options[:join_table]
|
86
|
+
options[:join_table].to_s
|
87
|
+
else
|
88
|
+
class_name = options.fetch(:class_name) {
|
89
|
+
association_name.to_s.camelize.singularize
|
90
|
+
}
|
91
|
+
klass = lhs_model.send(:compute_type, class_name.to_s)
|
92
|
+
[lhs_model.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
120
96
|
def belongs_to_options(options)
|
121
97
|
rhs_options = {}
|
122
98
|
|
@@ -1,17 +1,24 @@
|
|
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]
|
11
|
+
valid += [:as, :foreign_type] if options[:as]
|
12
|
+
valid += [:through, :source, :source_type] if options[:through]
|
13
|
+
valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
|
14
|
+
valid += [:disable_joins] if options[:disable_joins] && options[:through]
|
15
|
+
valid
|
11
16
|
end
|
12
17
|
|
13
18
|
def self.valid_dependent_options
|
14
|
-
[:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception]
|
19
|
+
[:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception, :destroy_async]
|
15
20
|
end
|
21
|
+
|
22
|
+
private_class_method :macro, :valid_options, :valid_dependent_options
|
16
23
|
end
|
17
24
|
end
|
@@ -1,19 +1,27 @@
|
|
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
|
10
|
+
valid = super
|
11
|
+
valid += [:as, :foreign_type] if options[:as]
|
12
|
+
valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
|
11
13
|
valid += [:through, :source, :source_type] if options[:through]
|
14
|
+
valid += [:disable_joins] if options[:disable_joins] && options[:through]
|
12
15
|
valid
|
13
16
|
end
|
14
17
|
|
15
18
|
def self.valid_dependent_options
|
16
|
-
[:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
|
19
|
+
[:destroy, :destroy_async, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.define_callbacks(model, reflection)
|
23
|
+
super
|
24
|
+
add_touch_callbacks(model, reflection) if reflection.options[:touch]
|
17
25
|
end
|
18
26
|
|
19
27
|
def self.add_destroy_callbacks(model, reflection)
|
@@ -26,5 +34,29 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
26
34
|
model.validates_presence_of reflection.name, message: :required
|
27
35
|
end
|
28
36
|
end
|
37
|
+
|
38
|
+
def self.touch_record(record, name, touch)
|
39
|
+
instance = record.send(name)
|
40
|
+
|
41
|
+
if instance&.persisted?
|
42
|
+
touch != true ?
|
43
|
+
instance.touch(touch) : instance.touch
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.add_touch_callbacks(model, reflection)
|
48
|
+
name = reflection.name
|
49
|
+
touch = reflection.options[:touch]
|
50
|
+
|
51
|
+
callback = -> (record) { HasOne.touch_record(record, name, touch) }
|
52
|
+
model.after_create callback, if: :saved_changes?
|
53
|
+
model.after_create_commit { association(name).reset_negative_cache }
|
54
|
+
model.after_update callback, if: :saved_changes?
|
55
|
+
model.after_destroy callback
|
56
|
+
model.after_touch callback
|
57
|
+
end
|
58
|
+
|
59
|
+
private_class_method :macro, :valid_options, :valid_dependent_options, :add_destroy_callbacks,
|
60
|
+
:define_callbacks, :define_validations, :add_touch_callbacks
|
29
61
|
end
|
30
62
|
end
|
@@ -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,7 +13,7 @@ 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}
|
@@ -38,5 +38,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
38
38
|
end
|
39
39
|
CODE
|
40
40
|
end
|
41
|
+
|
42
|
+
private_class_method :valid_options, :define_accessors, :define_constructors
|
41
43
|
end
|
42
44
|
end
|
@@ -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
|
@@ -25,9 +27,11 @@ 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:
|
29
31
|
# Implements the reader method, e.g. foo.items for Foo.has_many :items
|
30
32
|
def reader
|
33
|
+
ensure_klass_exists!
|
34
|
+
|
31
35
|
if stale_target?
|
32
36
|
reload
|
33
37
|
end
|
@@ -56,7 +60,7 @@ module ActiveRecord
|
|
56
60
|
def ids_writer(ids)
|
57
61
|
primary_key = reflection.association_primary_key
|
58
62
|
pk_type = klass.type_for_attribute(primary_key)
|
59
|
-
ids = Array(ids).
|
63
|
+
ids = Array(ids).compact_blank
|
60
64
|
ids.map! { |i| pk_type.cast(i) }
|
61
65
|
|
62
66
|
records = klass.where(primary_key => ids).index_by do |r|
|
@@ -75,6 +79,7 @@ module ActiveRecord
|
|
75
79
|
def reset
|
76
80
|
super
|
77
81
|
@target = []
|
82
|
+
@replaced_or_added_targets = Set.new
|
78
83
|
@association_ids = nil
|
79
84
|
end
|
80
85
|
|
@@ -101,11 +106,11 @@ module ActiveRecord
|
|
101
106
|
end
|
102
107
|
end
|
103
108
|
|
104
|
-
def build(attributes =
|
109
|
+
def build(attributes = nil, &block)
|
105
110
|
if attributes.is_a?(Array)
|
106
111
|
attributes.collect { |attr| build(attr, &block) }
|
107
112
|
else
|
108
|
-
add_to_target(build_record(attributes, &block))
|
113
|
+
add_to_target(build_record(attributes, &block), replace: true)
|
109
114
|
end
|
110
115
|
end
|
111
116
|
|
@@ -121,21 +126,6 @@ module ActiveRecord
|
|
121
126
|
end
|
122
127
|
end
|
123
128
|
|
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
129
|
# Removes all records from the association without calling callbacks
|
140
130
|
# on the associated records. It honors the +:dependent+ option. However
|
141
131
|
# if the +:dependent+ value is +:destroy+ then in that case the +:delete_all+
|
@@ -211,9 +201,11 @@ module ActiveRecord
|
|
211
201
|
def size
|
212
202
|
if !find_target? || loaded?
|
213
203
|
target.size
|
204
|
+
elsif @association_ids
|
205
|
+
@association_ids.size
|
214
206
|
elsif !association_scope.group_values.empty?
|
215
207
|
load_target.size
|
216
|
-
elsif !association_scope.distinct_value && target.
|
208
|
+
elsif !association_scope.distinct_value && !target.empty?
|
217
209
|
unsaved_records = target.select(&:new_record?)
|
218
210
|
unsaved_records.size + count_records
|
219
211
|
else
|
@@ -226,14 +218,14 @@ module ActiveRecord
|
|
226
218
|
# If the collection has been loaded
|
227
219
|
# it is equivalent to <tt>collection.size.zero?</tt>. If the
|
228
220
|
# collection has not been loaded, it is equivalent to
|
229
|
-
# <tt
|
221
|
+
# <tt>!collection.exists?</tt>. If the collection has not already been
|
230
222
|
# loaded and you are going to fetch the records anyway it is better to
|
231
223
|
# check <tt>collection.length.zero?</tt>.
|
232
224
|
def empty?
|
233
|
-
if loaded?
|
225
|
+
if loaded? || @association_ids || reflection.has_cached_counter?
|
234
226
|
size.zero?
|
235
227
|
else
|
236
|
-
|
228
|
+
target.empty? && !scope.exists?
|
237
229
|
end
|
238
230
|
end
|
239
231
|
|
@@ -276,11 +268,21 @@ module ActiveRecord
|
|
276
268
|
target
|
277
269
|
end
|
278
270
|
|
279
|
-
def add_to_target(record, skip_callbacks
|
280
|
-
|
281
|
-
|
271
|
+
def add_to_target(record, skip_callbacks: false, replace: false, &block)
|
272
|
+
replace_on_target(record, skip_callbacks, replace: replace || association_scope.distinct_value, &block)
|
273
|
+
end
|
274
|
+
|
275
|
+
def target=(record)
|
276
|
+
return super unless reflection.klass.has_many_inversing
|
277
|
+
|
278
|
+
case record
|
279
|
+
when nil
|
280
|
+
# It's not possible to remove the record from the inverse association.
|
281
|
+
when Array
|
282
|
+
super
|
283
|
+
else
|
284
|
+
replace_on_target(record, true, replace: true, inversing: true)
|
282
285
|
end
|
283
|
-
replace_on_target(record, index, skip_callbacks, &block)
|
284
286
|
end
|
285
287
|
|
286
288
|
def scope
|
@@ -295,26 +297,15 @@ module ActiveRecord
|
|
295
297
|
|
296
298
|
def find_from_target?
|
297
299
|
loaded? ||
|
300
|
+
owner.strict_loading? ||
|
301
|
+
reflection.strict_loading? ||
|
298
302
|
owner.new_record? ||
|
299
303
|
target.any? { |record| record.new_record? || record.changed? }
|
300
304
|
end
|
301
305
|
|
302
306
|
private
|
303
|
-
|
304
|
-
|
305
|
-
scope = self.scope
|
306
|
-
return scope.to_a if skip_statement_cache?(scope)
|
307
|
-
|
308
|
-
conn = klass.connection
|
309
|
-
sc = reflection.association_scope_cache(conn, owner) do |params|
|
310
|
-
as = AssociationScope.create { params.bind }
|
311
|
-
target_scope.merge!(as.scope(self))
|
312
|
-
end
|
313
|
-
|
314
|
-
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
315
|
-
sc.execute(binds, conn) do |record|
|
316
|
-
set_inverse_instance(record)
|
317
|
-
end
|
307
|
+
def transaction(&block)
|
308
|
+
reflection.klass.transaction(&block)
|
318
309
|
end
|
319
310
|
|
320
311
|
# We have some records loaded from the database (persisted) and some that are
|
@@ -344,12 +335,12 @@ module ActiveRecord
|
|
344
335
|
end
|
345
336
|
end
|
346
337
|
|
347
|
-
persisted + memory
|
338
|
+
persisted + memory.reject(&:persisted?)
|
348
339
|
end
|
349
340
|
|
350
341
|
def _create_record(attributes, raise = false, &block)
|
351
342
|
unless owner.persisted?
|
352
|
-
raise ActiveRecord::RecordNotSaved
|
343
|
+
raise ActiveRecord::RecordNotSaved.new("You cannot call create unless the parent is saved", owner)
|
353
344
|
end
|
354
345
|
|
355
346
|
if attributes.is_a?(Array)
|
@@ -393,10 +384,12 @@ module ActiveRecord
|
|
393
384
|
end
|
394
385
|
|
395
386
|
def remove_records(existing_records, records, method)
|
396
|
-
|
387
|
+
catch(:abort) do
|
388
|
+
records.each { |record| callback(:before_remove, record) }
|
389
|
+
end || return
|
397
390
|
|
398
391
|
delete_records(existing_records, method) if existing_records.any?
|
399
|
-
|
392
|
+
@target -= records
|
400
393
|
@association_ids = nil
|
401
394
|
|
402
395
|
records.each { |record| callback(:after_remove, record) }
|
@@ -425,7 +418,7 @@ module ActiveRecord
|
|
425
418
|
common_records = intersection(new_target, original_target)
|
426
419
|
common_records.each do |record|
|
427
420
|
skip_callbacks = true
|
428
|
-
replace_on_target(record,
|
421
|
+
replace_on_target(record, skip_callbacks, replace: true)
|
429
422
|
end
|
430
423
|
end
|
431
424
|
|
@@ -448,8 +441,14 @@ module ActiveRecord
|
|
448
441
|
records
|
449
442
|
end
|
450
443
|
|
451
|
-
def replace_on_target(record,
|
452
|
-
|
444
|
+
def replace_on_target(record, skip_callbacks, replace:, inversing: false)
|
445
|
+
if replace && (!record.new_record? || @replaced_or_added_targets.include?(record))
|
446
|
+
index = @target.index(record)
|
447
|
+
end
|
448
|
+
|
449
|
+
catch(:abort) do
|
450
|
+
callback(:before_add, record)
|
451
|
+
end || return unless skip_callbacks
|
453
452
|
|
454
453
|
set_inverse_instance(record)
|
455
454
|
|
@@ -457,6 +456,12 @@ module ActiveRecord
|
|
457
456
|
|
458
457
|
yield(record) if block_given?
|
459
458
|
|
459
|
+
if !index && @replaced_or_added_targets.include?(record)
|
460
|
+
index = @target.index(record)
|
461
|
+
end
|
462
|
+
|
463
|
+
@replaced_or_added_targets << record if inversing || index || record.new_record?
|
464
|
+
|
460
465
|
if index
|
461
466
|
target[index] = record
|
462
467
|
elsif @_was_loaded || !loaded?
|
@@ -479,7 +484,11 @@ module ActiveRecord
|
|
479
484
|
|
480
485
|
def callbacks_for(callback_name)
|
481
486
|
full_callback_name = "#{callback_name}_for_#{reflection.name}"
|
482
|
-
owner.class.
|
487
|
+
if owner.class.respond_to?(full_callback_name)
|
488
|
+
owner.class.send(full_callback_name)
|
489
|
+
else
|
490
|
+
[]
|
491
|
+
end
|
483
492
|
end
|
484
493
|
|
485
494
|
def include_in_memory?(record)
|
@@ -2,11 +2,8 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# object, known as the <tt>@target</tt>. The kind of association any proxy is
|
8
|
-
# about is available in <tt>@reflection</tt>. That's an instance of the class
|
9
|
-
# ActiveRecord::Reflection::AssociationReflection.
|
5
|
+
# Collection proxies in Active Record are middlemen between an
|
6
|
+
# <tt>association</tt>, and its <tt>target</tt> result set.
|
10
7
|
#
|
11
8
|
# For example, given
|
12
9
|
#
|
@@ -16,21 +13,21 @@ module ActiveRecord
|
|
16
13
|
#
|
17
14
|
# blog = Blog.first
|
18
15
|
#
|
19
|
-
#
|
20
|
-
# <tt
|
21
|
-
#
|
16
|
+
# The collection proxy returned by <tt>blog.posts</tt> is built from a
|
17
|
+
# <tt>:has_many</tt> <tt>association</tt>, and delegates to a collection
|
18
|
+
# of posts as the <tt>target</tt>.
|
22
19
|
#
|
23
|
-
# This class delegates unknown methods to <tt
|
24
|
-
#
|
20
|
+
# This class delegates unknown methods to the <tt>association</tt>'s
|
21
|
+
# relation class via a delegate cache.
|
25
22
|
#
|
26
|
-
# The <tt
|
23
|
+
# The <tt>target</tt> result set is not loaded until needed. For example,
|
27
24
|
#
|
28
25
|
# blog.posts.count
|
29
26
|
#
|
30
27
|
# is computed directly through SQL and does not trigger by itself the
|
31
28
|
# instantiation of the actual post records.
|
32
29
|
class CollectionProxy < Relation
|
33
|
-
def initialize(klass, association)
|
30
|
+
def initialize(klass, association, **) # :nodoc:
|
34
31
|
@association = association
|
35
32
|
super klass
|
36
33
|
|
@@ -49,11 +46,12 @@ module ActiveRecord
|
|
49
46
|
# Returns +true+ if the association has been loaded, otherwise +false+.
|
50
47
|
#
|
51
48
|
# person.pets.loaded? # => false
|
52
|
-
# person.pets
|
49
|
+
# person.pets.records
|
53
50
|
# person.pets.loaded? # => true
|
54
51
|
def loaded?
|
55
52
|
@association.loaded?
|
56
53
|
end
|
54
|
+
alias :loaded :loaded?
|
57
55
|
|
58
56
|
##
|
59
57
|
# :method: select
|
@@ -103,7 +101,7 @@ module ActiveRecord
|
|
103
101
|
# converting them into an array and iterating through them using
|
104
102
|
# Array#select.
|
105
103
|
#
|
106
|
-
# person.pets.select { |pet| pet.name
|
104
|
+
# person.pets.select { |pet| /oo/.match?(pet.name) }
|
107
105
|
# # => [
|
108
106
|
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
109
107
|
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
@@ -376,7 +374,7 @@ module ActiveRecord
|
|
376
374
|
# person.pets
|
377
375
|
# # => [#<Pet id: 1, name: "Gorby", group: "cats", person_id: 1>]
|
378
376
|
#
|
379
|
-
# other_pets = [Pet.new(name: 'Puff', group: 'celebrities']
|
377
|
+
# other_pets = [Pet.new(name: 'Puff', group: 'celebrities')]
|
380
378
|
#
|
381
379
|
# person.pets.replace(other_pets)
|
382
380
|
#
|
@@ -815,7 +813,7 @@ module ActiveRecord
|
|
815
813
|
# to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
|
816
814
|
# it is equivalent to <tt>!collection.exists?</tt>. If the collection has
|
817
815
|
# not already been loaded and you are going to fetch the records anyway it
|
818
|
-
# is better to check <tt>collection.
|
816
|
+
# is better to check <tt>collection.load.empty?</tt>.
|
819
817
|
#
|
820
818
|
# class Person < ActiveRecord::Base
|
821
819
|
# has_many :pets
|
@@ -851,6 +849,11 @@ module ActiveRecord
|
|
851
849
|
# person.pets.count # => 1
|
852
850
|
# person.pets.any? # => true
|
853
851
|
#
|
852
|
+
# Calling it without a block when the collection is not yet
|
853
|
+
# loaded is equivalent to <tt>collection.exists?</tt>.
|
854
|
+
# If you're going to load the collection anyway, it is better
|
855
|
+
# to call <tt>collection.load.any?</tt> to avoid an extra query.
|
856
|
+
#
|
854
857
|
# You can also pass a +block+ to define criteria. The behavior
|
855
858
|
# is the same, it returns true if the collection based on the
|
856
859
|
# criteria is not empty.
|
@@ -923,7 +926,7 @@ module ActiveRecord
|
|
923
926
|
!!@association.include?(record)
|
924
927
|
end
|
925
928
|
|
926
|
-
def proxy_association
|
929
|
+
def proxy_association # :nodoc:
|
927
930
|
@association
|
928
931
|
end
|
929
932
|
|
@@ -1005,7 +1008,7 @@ module ActiveRecord
|
|
1005
1008
|
end
|
1006
1009
|
|
1007
1010
|
# Adds one or more +records+ to the collection by setting their foreign keys
|
1008
|
-
# to the association's primary key. Since
|
1011
|
+
# to the association's primary key. Since <tt><<</tt> flattens its argument list and
|
1009
1012
|
# inserts each record, +push+ and +concat+ behave identically. Returns +self+
|
1010
1013
|
# so several appends may be chained together.
|
1011
1014
|
#
|
@@ -1032,7 +1035,7 @@ module ActiveRecord
|
|
1032
1035
|
alias_method :append, :<<
|
1033
1036
|
alias_method :concat, :<<
|
1034
1037
|
|
1035
|
-
def prepend(*args)
|
1038
|
+
def prepend(*args) # :nodoc:
|
1036
1039
|
raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
|
1037
1040
|
end
|
1038
1041
|
|
@@ -1062,7 +1065,7 @@ module ActiveRecord
|
|
1062
1065
|
# person.pets.reload # fetches pets from the database
|
1063
1066
|
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
|
1064
1067
|
def reload
|
1065
|
-
proxy_association.reload
|
1068
|
+
proxy_association.reload(true)
|
1066
1069
|
reset_scope
|
1067
1070
|
end
|
1068
1071
|
|
@@ -1089,22 +1092,28 @@ module ActiveRecord
|
|
1089
1092
|
end
|
1090
1093
|
|
1091
1094
|
def reset_scope # :nodoc:
|
1092
|
-
@offsets =
|
1095
|
+
@offsets = @take = nil
|
1093
1096
|
@scope = nil
|
1094
1097
|
self
|
1095
1098
|
end
|
1096
1099
|
|
1100
|
+
def inspect # :nodoc:
|
1101
|
+
load_target if find_from_target?
|
1102
|
+
super
|
1103
|
+
end
|
1104
|
+
|
1097
1105
|
delegate_methods = [
|
1098
1106
|
QueryMethods,
|
1099
1107
|
SpawnMethods,
|
1100
1108
|
].flat_map { |klass|
|
1101
1109
|
klass.public_instance_methods(false)
|
1102
|
-
} - self.public_instance_methods(false) - [:select] + [
|
1110
|
+
} - self.public_instance_methods(false) - [:select] + [
|
1111
|
+
:scoping, :values, :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all
|
1112
|
+
]
|
1103
1113
|
|
1104
1114
|
delegate(*delegate_methods, to: :scope)
|
1105
1115
|
|
1106
1116
|
private
|
1107
|
-
|
1108
1117
|
def find_nth_with_limit(index, limit)
|
1109
1118
|
load_target if find_from_target?
|
1110
1119
|
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
|