activerecord 6.1.6 → 7.1.2
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.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1627 -983
- data/MIT-LICENSE +1 -1
- data/README.rdoc +18 -18
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +1 -11
- data/lib/active_record/associations/association.rb +50 -19
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +28 -9
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +40 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +35 -31
- data/lib/active_record/associations/collection_proxy.rb +30 -15
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +28 -18
- data/lib/active_record/associations/has_many_through_association.rb +12 -7
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +26 -16
- data/lib/active_record/associations/preloader/association.rb +207 -52
- 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 +50 -14
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +9 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +439 -305
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -3
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +78 -26
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +25 -10
- data/lib/active_record/attribute_methods/serialization.rb +194 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +10 -13
- data/lib/active_record/attribute_methods.rb +121 -40
- data/lib/active_record/attributes.rb +27 -38
- data/lib/active_record/autosave_association.rb +61 -30
- data/lib/active_record/base.rb +25 -2
- data/lib/active_record/callbacks.rb +18 -34
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -34
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -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 +78 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +96 -590
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +77 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +360 -138
- data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
- data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -149
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +285 -156
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
- data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +104 -53
- data/lib/active_record/connection_adapters/pool_config.rb +20 -11
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +394 -74
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +509 -247
- data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -102
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +9 -6
- data/lib/active_record/connection_handling.rb +107 -136
- data/lib/active_record/core.rb +202 -223
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +21 -12
- data/lib/active_record/database_configurations/hash_config.rb +84 -16
- data/lib/active_record/database_configurations/url_config.rb +18 -12
- data/lib/active_record/database_configurations.rb +95 -59
- data/lib/active_record/delegated_type.rb +61 -15
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- 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 +224 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +151 -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 +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_serializer.rb +92 -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 +96 -0
- data/lib/active_record/encryption.rb +56 -0
- data/lib/active_record/enum.rb +154 -63
- data/lib/active_record/errors.rb +171 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +131 -86
- data/lib/active_record/future_result.rb +164 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +81 -29
- data/lib/active_record/insert_all.rb +135 -22
- data/lib/active_record/integration.rb +11 -10
- data/lib/active_record/internal_metadata.rb +119 -33
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +36 -21
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +52 -19
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +112 -14
- data/lib/active_record/migration/compatibility.rb +221 -48
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +358 -171
- data/lib/active_record/model_schema.rb +120 -101
- data/lib/active_record/nested_attributes.rb +37 -18
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +405 -85
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +174 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +29 -6
- data/lib/active_record/railtie.rb +219 -43
- data/lib/active_record/railties/controller_runtime.rb +13 -9
- data/lib/active_record/railties/databases.rake +188 -252
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +241 -80
- data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
- data/lib/active_record/relation/batches.rb +192 -63
- data/lib/active_record/relation/calculations.rb +219 -90
- data/lib/active_record/relation/delegation.rb +27 -13
- data/lib/active_record/relation/finder_methods.rb +108 -51
- data/lib/active_record/relation/merger.rb +22 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +27 -20
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +654 -127
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +20 -3
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +262 -120
- data/lib/active_record/result.rb +37 -11
- data/lib/active_record/runtime_registry.rb +18 -13
- data/lib/active_record/sanitization.rb +65 -20
- data/lib/active_record/schema.rb +36 -22
- data/lib/active_record/schema_dumper.rb +73 -24
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +10 -8
- data/lib/active_record/store.rb +16 -11
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +225 -136
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +123 -99
- data/lib/active_record/timestamp.rb +29 -18
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +48 -27
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -5
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +4 -4
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +51 -6
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +335 -32
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/and.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- 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 +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +5 -0
- data/lib/arel/predications.rb +13 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +16 -3
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +139 -19
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +18 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +93 -13
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
| @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            module ActiveRecord::Associations::Builder # :nodoc:
         | 
| 4 | 
            -
              class BelongsTo < SingularAssociation  | 
| 4 | 
            +
              class BelongsTo < SingularAssociation # :nodoc:
         | 
| 5 5 | 
             
                def self.macro
         | 
| 6 6 | 
             
                  :belongs_to
         | 
| 7 7 | 
             
                end
         | 
| @@ -30,17 +30,17 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 30 30 | 
             
                  model.after_update lambda { |record|
         | 
| 31 31 | 
             
                    association = association(reflection.name)
         | 
| 32 32 |  | 
| 33 | 
            -
                    if association. | 
| 33 | 
            +
                    if association.saved_change_to_target?
         | 
| 34 34 | 
             
                      association.increment_counters
         | 
| 35 35 | 
             
                      association.decrement_counters_before_last_save
         | 
| 36 36 | 
             
                    end
         | 
| 37 37 | 
             
                  }
         | 
| 38 38 |  | 
| 39 39 | 
             
                  klass = reflection.class_name.safe_constantize
         | 
| 40 | 
            -
                  klass. | 
| 40 | 
            +
                  klass._counter_cache_columns |= [cache_column] if klass && klass.respond_to?(:_counter_cache_columns)
         | 
| 41 41 | 
             
                end
         | 
| 42 42 |  | 
| 43 | 
            -
                def self.touch_record(o, changes, foreign_key, name, touch | 
| 43 | 
            +
                def self.touch_record(o, changes, foreign_key, name, touch) # :nodoc:
         | 
| 44 44 | 
             
                  old_foreign_id = changes[foreign_key] && changes[foreign_key].first
         | 
| 45 45 |  | 
| 46 46 | 
             
                  if old_foreign_id
         | 
| @@ -49,7 +49,7 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 49 49 | 
             
                    if reflection.polymorphic?
         | 
| 50 50 | 
             
                      foreign_type = reflection.foreign_type
         | 
| 51 51 | 
             
                      klass = changes[foreign_type] && changes[foreign_type].first || o.public_send(foreign_type)
         | 
| 52 | 
            -
                      klass = klass | 
| 52 | 
            +
                      klass = o.class.polymorphic_class_for(klass)
         | 
| 53 53 | 
             
                    else
         | 
| 54 54 | 
             
                      klass = association.klass
         | 
| 55 55 | 
             
                    end
         | 
| @@ -58,9 +58,9 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 58 58 |  | 
| 59 59 | 
             
                    if old_record
         | 
| 60 60 | 
             
                      if touch != true
         | 
| 61 | 
            -
                        old_record. | 
| 61 | 
            +
                        old_record.touch_later(touch)
         | 
| 62 62 | 
             
                      else
         | 
| 63 | 
            -
                        old_record. | 
| 63 | 
            +
                        old_record.touch_later
         | 
| 64 64 | 
             
                      end
         | 
| 65 65 | 
             
                    end
         | 
| 66 66 | 
             
                  end
         | 
| @@ -68,9 +68,9 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 68 68 | 
             
                  record = o.public_send name
         | 
| 69 69 | 
             
                  if record && record.persisted?
         | 
| 70 70 | 
             
                    if touch != true
         | 
| 71 | 
            -
                      record. | 
| 71 | 
            +
                      record.touch_later(touch)
         | 
| 72 72 | 
             
                    else
         | 
| 73 | 
            -
                      record. | 
| 73 | 
            +
                      record.touch_later
         | 
| 74 74 | 
             
                    end
         | 
| 75 75 | 
             
                  end
         | 
| 76 76 | 
             
                end
         | 
| @@ -81,13 +81,13 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 81 81 | 
             
                  touch       = reflection.options[:touch]
         | 
| 82 82 |  | 
| 83 83 | 
             
                  callback = lambda { |changes_method| lambda { |record|
         | 
| 84 | 
            -
                    BelongsTo.touch_record(record, record.send(changes_method), foreign_key, name, touch | 
| 84 | 
            +
                    BelongsTo.touch_record(record, record.send(changes_method), foreign_key, name, touch)
         | 
| 85 85 | 
             
                  }}
         | 
| 86 86 |  | 
| 87 87 | 
             
                  if reflection.counter_cache_column
         | 
| 88 88 | 
             
                    touch_callback = callback.(:saved_changes)
         | 
| 89 89 | 
             
                    update_callback = lambda { |record|
         | 
| 90 | 
            -
                      instance_exec(record, &touch_callback) unless association(reflection.name). | 
| 90 | 
            +
                      instance_exec(record, &touch_callback) unless association(reflection.name).saved_change_to_target?
         | 
| 91 91 | 
             
                    }
         | 
| 92 92 | 
             
                    model.after_update update_callback, if: :saved_changes?
         | 
| 93 93 | 
             
                  else
         | 
| @@ -123,11 +123,37 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 123 123 | 
             
                  super
         | 
| 124 124 |  | 
| 125 125 | 
             
                  if required
         | 
| 126 | 
            -
                     | 
| 126 | 
            +
                    if ActiveRecord.belongs_to_required_validates_foreign_key
         | 
| 127 | 
            +
                      model.validates_presence_of reflection.name, message: :required
         | 
| 128 | 
            +
                    else
         | 
| 129 | 
            +
                      condition = lambda { |record|
         | 
| 130 | 
            +
                        foreign_key = reflection.foreign_key
         | 
| 131 | 
            +
                        foreign_type = reflection.foreign_type
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                        record.read_attribute(foreign_key).nil? ||
         | 
| 134 | 
            +
                          record.attribute_changed?(foreign_key) ||
         | 
| 135 | 
            +
                          (reflection.polymorphic? && (record.read_attribute(foreign_type).nil? || record.attribute_changed?(foreign_type)))
         | 
| 136 | 
            +
                      }
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                      model.validates_presence_of reflection.name, message: :required, if: condition
         | 
| 139 | 
            +
                    end
         | 
| 127 140 | 
             
                  end
         | 
| 128 141 | 
             
                end
         | 
| 129 142 |  | 
| 130 | 
            -
                 | 
| 131 | 
            -
                   | 
| 143 | 
            +
                def self.define_change_tracking_methods(model, reflection)
         | 
| 144 | 
            +
                  model.generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
         | 
| 145 | 
            +
                    def #{reflection.name}_changed?
         | 
| 146 | 
            +
                      association(:#{reflection.name}).target_changed?
         | 
| 147 | 
            +
                    end
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                    def #{reflection.name}_previously_changed?
         | 
| 150 | 
            +
                      association(:#{reflection.name}).target_previously_changed?
         | 
| 151 | 
            +
                    end
         | 
| 152 | 
            +
                  CODE
         | 
| 153 | 
            +
                end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                private_class_method :macro, :valid_options, :valid_dependent_options, :define_callbacks,
         | 
| 156 | 
            +
                  :define_validations, :define_change_tracking_methods, :add_counter_cache_callbacks,
         | 
| 157 | 
            +
                  :add_touch_callbacks, :add_default_callbacks, :add_destroy_callbacks
         | 
| 132 158 | 
             
              end
         | 
| 133 159 | 
             
            end
         | 
| @@ -3,7 +3,7 @@ | |
| 3 3 | 
             
            require "active_record/associations"
         | 
| 4 4 |  | 
| 5 5 | 
             
            module ActiveRecord::Associations::Builder # :nodoc:
         | 
| 6 | 
            -
              class CollectionAssociation < Association  | 
| 6 | 
            +
              class CollectionAssociation < Association # :nodoc:
         | 
| 7 7 | 
             
                CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
         | 
| 8 8 |  | 
| 9 9 | 
             
                def self.valid_options(options)
         | 
| @@ -30,11 +30,18 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 30 30 | 
             
                def self.define_callback(model, callback_name, name, options)
         | 
| 31 31 | 
             
                  full_callback_name = "#{callback_name}_for_#{name}"
         | 
| 32 32 |  | 
| 33 | 
            -
                   | 
| 33 | 
            +
                  callback_values = Array(options[callback_name.to_sym])
         | 
| 34 | 
            +
                  method_defined = model.respond_to?(full_callback_name)
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  # If there are no callbacks, we must also check if a superclass had
         | 
| 37 | 
            +
                  # previously defined this association
         | 
| 38 | 
            +
                  return if callback_values.empty? && !method_defined
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  unless method_defined
         | 
| 34 41 | 
             
                    model.class_attribute(full_callback_name, instance_accessor: false, instance_predicate: false)
         | 
| 35 42 | 
             
                  end
         | 
| 36 43 |  | 
| 37 | 
            -
                  callbacks =  | 
| 44 | 
            +
                  callbacks = callback_values.map do |callback|
         | 
| 38 45 | 
             
                    case callback
         | 
| 39 46 | 
             
                    when Symbol
         | 
| 40 47 | 
             
                      ->(method, owner, record) { owner.send(callback, record) }
         | 
| @@ -20,6 +20,7 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 20 20 | 
             
                      attr_accessor :right_reflection
         | 
| 21 21 | 
             
                    end
         | 
| 22 22 |  | 
| 23 | 
            +
                    @table_name = nil
         | 
| 23 24 | 
             
                    def self.table_name
         | 
| 24 25 | 
             
                      # Table name needs to be resolved lazily
         | 
| 25 26 | 
             
                      # because RHS class might not have been loaded
         | 
| @@ -44,11 +45,6 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 44 45 | 
             
                    def self.retrieve_connection
         | 
| 45 46 | 
             
                      left_model.retrieve_connection
         | 
| 46 47 | 
             
                    end
         | 
| 47 | 
            -
             | 
| 48 | 
            -
                    private
         | 
| 49 | 
            -
                      def self.suppress_composite_primary_key(pk)
         | 
| 50 | 
            -
                        pk unless pk.is_a?(Array)
         | 
| 51 | 
            -
                      end
         | 
| 52 48 | 
             
                  }
         | 
| 53 49 |  | 
| 54 50 | 
             
                  join_model.name                = "HABTM_#{association_name.to_s.camelize}"
         | 
| @@ -1,16 +1,17 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            module ActiveRecord::Associations::Builder # :nodoc:
         | 
| 4 | 
            -
              class HasMany < CollectionAssociation  | 
| 4 | 
            +
              class HasMany < CollectionAssociation # :nodoc:
         | 
| 5 5 | 
             
                def self.macro
         | 
| 6 6 | 
             
                  :has_many
         | 
| 7 7 | 
             
                end
         | 
| 8 8 |  | 
| 9 9 | 
             
                def self.valid_options(options)
         | 
| 10 | 
            -
                  valid = super + [:counter_cache, :join_table, :index_errors | 
| 10 | 
            +
                  valid = super + [:counter_cache, :join_table, :index_errors]
         | 
| 11 11 | 
             
                  valid += [:as, :foreign_type] if options[:as]
         | 
| 12 12 | 
             
                  valid += [:through, :source, :source_type] if options[:through]
         | 
| 13 13 | 
             
                  valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
         | 
| 14 | 
            +
                  valid += [:disable_joins] if options[:disable_joins] && options[:through]
         | 
| 14 15 | 
             
                  valid
         | 
| 15 16 | 
             
                end
         | 
| 16 17 |  | 
| @@ -1,7 +1,7 @@ | |
| 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
         | 
| @@ -11,6 +11,7 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 11 11 | 
             
                  valid += [:as, :foreign_type] if options[:as]
         | 
| 12 12 | 
             
                  valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
         | 
| 13 13 | 
             
                  valid += [:through, :source, :source_type] if options[:through]
         | 
| 14 | 
            +
                  valid += [:disable_joins] if options[:disable_joins] && options[:through]
         | 
| 14 15 | 
             
                  valid
         | 
| 15 16 | 
             
                end
         | 
| 16 17 |  | 
| @@ -3,7 +3,7 @@ | |
| 3 3 | 
             
            # This class is inherited by the has_one and belongs_to association classes
         | 
| 4 4 |  | 
| 5 5 | 
             
            module ActiveRecord::Associations::Builder # :nodoc:
         | 
| 6 | 
            -
              class SingularAssociation < Association  | 
| 6 | 
            +
              class SingularAssociation < Association # :nodoc:
         | 
| 7 7 | 
             
                def self.valid_options(options)
         | 
| 8 8 | 
             
                  super + [:required, :touch]
         | 
| 9 9 | 
             
                end
         | 
| @@ -13,12 +13,16 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 13 13 | 
             
                  mixin = model.generated_association_methods
         | 
| 14 14 | 
             
                  name = reflection.name
         | 
| 15 15 |  | 
| 16 | 
            -
                  define_constructors(mixin, name)  | 
| 16 | 
            +
                  define_constructors(mixin, name) unless reflection.polymorphic?
         | 
| 17 17 |  | 
| 18 18 | 
             
                  mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
         | 
| 19 19 | 
             
                    def reload_#{name}
         | 
| 20 20 | 
             
                      association(:#{name}).force_reload_reader
         | 
| 21 21 | 
             
                    end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    def reset_#{name}
         | 
| 24 | 
            +
                      association(:#{name}).reset
         | 
| 25 | 
            +
                    end
         | 
| 22 26 | 
             
                  CODE
         | 
| 23 27 | 
             
                end
         | 
| 24 28 |  | 
| @@ -1,5 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require "active_support/core_ext/enumerable"
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            module ActiveRecord
         | 
| 4 6 | 
             
              module Associations
         | 
| 5 7 | 
             
                # = Active Record Association Collection
         | 
| @@ -14,7 +16,7 @@ module ActiveRecord | |
| 14 16 | 
             
                #
         | 
| 15 17 | 
             
                # The CollectionAssociation class provides common methods to the collections
         | 
| 16 18 | 
             
                # defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
         | 
| 17 | 
            -
                # the  | 
| 19 | 
            +
                # the <tt>:through association</tt> option.
         | 
| 18 20 | 
             
                #
         | 
| 19 21 | 
             
                # You need to be careful with assumptions regarding the target: The proxy
         | 
| 20 22 | 
             
                # does not fetch records from the database until it needs them, but new
         | 
| @@ -25,9 +27,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
         | 
| @@ -57,14 +61,20 @@ module ActiveRecord | |
| 57 61 | 
             
                    primary_key = reflection.association_primary_key
         | 
| 58 62 | 
             
                    pk_type = klass.type_for_attribute(primary_key)
         | 
| 59 63 | 
             
                    ids = Array(ids).compact_blank
         | 
| 60 | 
            -
                    ids.map! { | | 
| 64 | 
            +
                    ids.map! { |id| pk_type.cast(id) }
         | 
| 61 65 |  | 
| 62 | 
            -
                    records = klass. | 
| 63 | 
            -
                       | 
| 66 | 
            +
                    records = if klass.composite_primary_key?
         | 
| 67 | 
            +
                      klass.where(primary_key => ids).index_by do |record|
         | 
| 68 | 
            +
                        primary_key.map { |primary_key| record._read_attribute(primary_key) }
         | 
| 69 | 
            +
                      end
         | 
| 70 | 
            +
                    else
         | 
| 71 | 
            +
                      klass.where(primary_key => ids).index_by do |record|
         | 
| 72 | 
            +
                        record._read_attribute(primary_key)
         | 
| 73 | 
            +
                      end
         | 
| 64 74 | 
             
                    end.values_at(*ids).compact
         | 
| 65 75 |  | 
| 66 76 | 
             
                    if records.size != ids.size
         | 
| 67 | 
            -
                      found_ids = records.map { |record| record. | 
| 77 | 
            +
                      found_ids = records.map { |record| record._read_attribute(primary_key) }
         | 
| 68 78 | 
             
                      not_found_ids = ids - found_ids
         | 
| 69 79 | 
             
                      klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids)
         | 
| 70 80 | 
             
                    else
         | 
| @@ -75,7 +85,7 @@ module ActiveRecord | |
| 75 85 | 
             
                  def reset
         | 
| 76 86 | 
             
                    super
         | 
| 77 87 | 
             
                    @target = []
         | 
| 78 | 
            -
                    @replaced_or_added_targets = Set.new
         | 
| 88 | 
            +
                    @replaced_or_added_targets = Set.new.compare_by_identity
         | 
| 79 89 | 
             
                    @association_ids = nil
         | 
| 80 90 | 
             
                  end
         | 
| 81 91 |  | 
| @@ -115,28 +125,13 @@ module ActiveRecord | |
| 115 125 | 
             
                  def concat(*records)
         | 
| 116 126 | 
             
                    records = records.flatten
         | 
| 117 127 | 
             
                    if owner.new_record?
         | 
| 118 | 
            -
                      load_target
         | 
| 128 | 
            +
                      skip_strict_loading { load_target }
         | 
| 119 129 | 
             
                      concat_records(records)
         | 
| 120 130 | 
             
                    else
         | 
| 121 131 | 
             
                      transaction { concat_records(records) }
         | 
| 122 132 | 
             
                    end
         | 
| 123 133 | 
             
                  end
         | 
| 124 134 |  | 
| 125 | 
            -
                  # Starts a transaction in the association class's database connection.
         | 
| 126 | 
            -
                  #
         | 
| 127 | 
            -
                  #   class Author < ActiveRecord::Base
         | 
| 128 | 
            -
                  #     has_many :books
         | 
| 129 | 
            -
                  #   end
         | 
| 130 | 
            -
                  #
         | 
| 131 | 
            -
                  #   Author.first.books.transaction do
         | 
| 132 | 
            -
                  #     # same effect as calling Book.transaction
         | 
| 133 | 
            -
                  #   end
         | 
| 134 | 
            -
                  def transaction(*args)
         | 
| 135 | 
            -
                    reflection.klass.transaction(*args) do
         | 
| 136 | 
            -
                      yield
         | 
| 137 | 
            -
                    end
         | 
| 138 | 
            -
                  end
         | 
| 139 | 
            -
             | 
| 140 135 | 
             
                  # Removes all records from the association without calling callbacks
         | 
| 141 136 | 
             
                  # on the associated records. It honors the +:dependent+ option. However
         | 
| 142 137 | 
             
                  # if the +:dependent+ value is +:destroy+ then in that case the +:delete_all+
         | 
| @@ -191,7 +186,7 @@ module ActiveRecord | |
| 191 186 | 
             
                  end
         | 
| 192 187 |  | 
| 193 188 | 
             
                  # Deletes the +records+ and removes them from this association calling
         | 
| 194 | 
            -
                  # +before_remove | 
| 189 | 
            +
                  # +before_remove+, +after_remove+, +before_destroy+ and +after_destroy+ callbacks.
         | 
| 195 190 | 
             
                  #
         | 
| 196 191 | 
             
                  # Note that this method removes records from the database ignoring the
         | 
| 197 192 | 
             
                  # +:dependent+ option.
         | 
| @@ -244,7 +239,7 @@ module ActiveRecord | |
| 244 239 | 
             
                  # and delete/add only records that have changed.
         | 
| 245 240 | 
             
                  def replace(other_array)
         | 
| 246 241 | 
             
                    other_array.each { |val| raise_on_type_mismatch!(val) }
         | 
| 247 | 
            -
                    original_target = load_target.dup
         | 
| 242 | 
            +
                    original_target = skip_strict_loading { load_target }.dup
         | 
| 248 243 |  | 
| 249 244 | 
             
                    if owner.new_record?
         | 
| 250 245 | 
             
                      replace_records(other_array, original_target)
         | 
| @@ -284,9 +279,11 @@ module ActiveRecord | |
| 284 279 | 
             
                  end
         | 
| 285 280 |  | 
| 286 281 | 
             
                  def target=(record)
         | 
| 287 | 
            -
                    return super unless  | 
| 282 | 
            +
                    return super unless reflection.klass.has_many_inversing
         | 
| 288 283 |  | 
| 289 284 | 
             
                    case record
         | 
| 285 | 
            +
                    when nil
         | 
| 286 | 
            +
                      # It's not possible to remove the record from the inverse association.
         | 
| 290 287 | 
             
                    when Array
         | 
| 291 288 | 
             
                      super
         | 
| 292 289 | 
             
                    else
         | 
| @@ -313,6 +310,10 @@ module ActiveRecord | |
| 313 310 | 
             
                  end
         | 
| 314 311 |  | 
| 315 312 | 
             
                  private
         | 
| 313 | 
            +
                    def transaction(&block)
         | 
| 314 | 
            +
                      reflection.klass.transaction(&block)
         | 
| 315 | 
            +
                    end
         | 
| 316 | 
            +
             | 
| 316 317 | 
             
                    # We have some records loaded from the database (persisted) and some that are
         | 
| 317 318 | 
             
                    # in-memory (memory). The same record may be represented in the persisted array
         | 
| 318 319 | 
             
                    # and in the memory array.
         | 
| @@ -325,13 +326,12 @@ module ActiveRecord | |
| 325 326 | 
             
                    #   * Otherwise, attributes should have the value found in the database
         | 
| 326 327 | 
             
                    def merge_target_lists(persisted, memory)
         | 
| 327 328 | 
             
                      return persisted if memory.empty?
         | 
| 328 | 
            -
                      return memory    if persisted.empty?
         | 
| 329 329 |  | 
| 330 330 | 
             
                      persisted.map! do |record|
         | 
| 331 331 | 
             
                        if mem_record = memory.delete(record)
         | 
| 332 332 |  | 
| 333 | 
            -
                          ((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save).each do |name|
         | 
| 334 | 
            -
                            mem_record | 
| 333 | 
            +
                          ((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save - mem_record.class._attr_readonly).each do |name|
         | 
| 334 | 
            +
                            mem_record._write_attribute(name, record[name])
         | 
| 335 335 | 
             
                          end
         | 
| 336 336 |  | 
| 337 337 | 
             
                          mem_record
         | 
| @@ -345,7 +345,7 @@ module ActiveRecord | |
| 345 345 |  | 
| 346 346 | 
             
                    def _create_record(attributes, raise = false, &block)
         | 
| 347 347 | 
             
                      unless owner.persisted?
         | 
| 348 | 
            -
                        raise ActiveRecord::RecordNotSaved | 
| 348 | 
            +
                        raise ActiveRecord::RecordNotSaved.new("You cannot call create unless the parent is saved", owner)
         | 
| 349 349 | 
             
                      end
         | 
| 350 350 |  | 
| 351 351 | 
             
                      if attributes.is_a?(Array)
         | 
| @@ -489,7 +489,11 @@ module ActiveRecord | |
| 489 489 |  | 
| 490 490 | 
             
                    def callbacks_for(callback_name)
         | 
| 491 491 | 
             
                      full_callback_name = "#{callback_name}_for_#{reflection.name}"
         | 
| 492 | 
            -
                      owner.class. | 
| 492 | 
            +
                      if owner.class.respond_to?(full_callback_name)
         | 
| 493 | 
            +
                        owner.class.send(full_callback_name)
         | 
| 494 | 
            +
                      else
         | 
| 495 | 
            +
                        []
         | 
| 496 | 
            +
                      end
         | 
| 493 497 | 
             
                    end
         | 
| 494 498 |  | 
| 495 499 | 
             
                    def include_in_memory?(record)
         | 
| @@ -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,7 +48,7 @@ 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?
         | 
| @@ -94,12 +96,12 @@ module ActiveRecord | |
| 94 96 | 
             
                  # receive:
         | 
| 95 97 | 
             
                  #
         | 
| 96 98 | 
             
                  #   person.pets.select(:name).first.person_id
         | 
| 97 | 
            -
                  #   # => ActiveModel::MissingAttributeError: missing attribute | 
| 99 | 
            +
                  #   # => ActiveModel::MissingAttributeError: missing attribute 'person_id' for Pet
         | 
| 98 100 | 
             
                  #
         | 
| 99 | 
            -
                  # *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>.
         | 
| 100 102 | 
             
                  # This builds an array of objects from the database for the scope,
         | 
| 101 103 | 
             
                  # converting them into an array and iterating through them using
         | 
| 102 | 
            -
                  # Array#select | 
| 104 | 
            +
                  # <tt>Array#select</tt>.
         | 
| 103 105 | 
             
                  #
         | 
| 104 106 | 
             
                  #   person.pets.select { |pet| /oo/.match?(pet.name) }
         | 
| 105 107 | 
             
                  #   # => [
         | 
| @@ -108,7 +110,7 @@ module ActiveRecord | |
| 108 110 | 
             
                  #   #    ]
         | 
| 109 111 |  | 
| 110 112 | 
             
                  # Finds an object in the collection responding to the +id+. Uses the same
         | 
| 111 | 
            -
                  # rules as ActiveRecord:: | 
| 113 | 
            +
                  # rules as ActiveRecord::FinderMethods.find. Returns ActiveRecord::RecordNotFound
         | 
| 112 114 | 
             
                  # error if the object cannot be found.
         | 
| 113 115 | 
             
                  #
         | 
| 114 116 | 
             
                  #   class Person < ActiveRecord::Base
         | 
| @@ -218,7 +220,7 @@ module ActiveRecord | |
| 218 220 | 
             
                  # :call-seq:
         | 
| 219 221 | 
             
                  #   third_to_last()
         | 
| 220 222 | 
             
                  #
         | 
| 221 | 
            -
                  # Same as # | 
| 223 | 
            +
                  # Same as #last except returns only the third-to-last record.
         | 
| 222 224 |  | 
| 223 225 | 
             
                  ##
         | 
| 224 226 | 
             
                  # :method: second_to_last
         | 
| @@ -226,7 +228,7 @@ module ActiveRecord | |
| 226 228 | 
             
                  # :call-seq:
         | 
| 227 229 | 
             
                  #   second_to_last()
         | 
| 228 230 | 
             
                  #
         | 
| 229 | 
            -
                  # Same as # | 
| 231 | 
            +
                  # Same as #last except returns only the second-to-last record.
         | 
| 230 232 |  | 
| 231 233 | 
             
                  # Returns the last record, or the last +n+ records, from the collection.
         | 
| 232 234 | 
             
                  # If the collection is empty, the first form returns +nil+, and the second
         | 
| @@ -260,7 +262,7 @@ module ActiveRecord | |
| 260 262 | 
             
                  end
         | 
| 261 263 |  | 
| 262 264 | 
             
                  # Gives a record (or N records if a parameter is supplied) from the collection
         | 
| 263 | 
            -
                  # using the same rules as  | 
| 265 | 
            +
                  # using the same rules as ActiveRecord::FinderMethods.take.
         | 
| 264 266 | 
             
                  #
         | 
| 265 267 | 
             
                  #   class Person < ActiveRecord::Base
         | 
| 266 268 | 
             
                  #     has_many :pets
         | 
| @@ -382,7 +384,7 @@ module ActiveRecord | |
| 382 384 | 
             
                  #   # => [#<Pet id: 2, name: "Puff", group: "celebrities", person_id: 1>]
         | 
| 383 385 | 
             
                  #
         | 
| 384 386 | 
             
                  # If the supplied array has an incorrect association type, it raises
         | 
| 385 | 
            -
                  # an  | 
| 387 | 
            +
                  # an ActiveRecord::AssociationTypeMismatch error:
         | 
| 386 388 | 
             
                  #
         | 
| 387 389 | 
             
                  #   person.pets.replace(["doo", "ggie", "gaga"])
         | 
| 388 390 | 
             
                  #   # => ActiveRecord::AssociationTypeMismatch: Pet expected, got String
         | 
| @@ -475,7 +477,7 @@ module ActiveRecord | |
| 475 477 |  | 
| 476 478 | 
             
                  # Deletes the records of the collection directly from the database
         | 
| 477 479 | 
             
                  # ignoring the +:dependent+ option. Records are instantiated and it
         | 
| 478 | 
            -
                  # invokes +before_remove+, +after_remove | 
| 480 | 
            +
                  # invokes +before_remove+, +after_remove+, +before_destroy+, and
         | 
| 479 481 | 
             
                  # +after_destroy+ callbacks.
         | 
| 480 482 | 
             
                  #
         | 
| 481 483 | 
             
                  #   class Person < ActiveRecord::Base
         | 
| @@ -813,7 +815,7 @@ module ActiveRecord | |
| 813 815 | 
             
                  # to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
         | 
| 814 816 | 
             
                  # it is equivalent to <tt>!collection.exists?</tt>. If the collection has
         | 
| 815 817 | 
             
                  # not already been loaded and you are going to fetch the records anyway it
         | 
| 816 | 
            -
                  # is better to check <tt>collection. | 
| 818 | 
            +
                  # is better to check <tt>collection.load.empty?</tt>.
         | 
| 817 819 | 
             
                  #
         | 
| 818 820 | 
             
                  #   class Person < ActiveRecord::Base
         | 
| 819 821 | 
             
                  #     has_many :pets
         | 
| @@ -849,6 +851,11 @@ module ActiveRecord | |
| 849 851 | 
             
                  #   person.pets.count # => 1
         | 
| 850 852 | 
             
                  #   person.pets.any?  # => true
         | 
| 851 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 | 
            +
                  #
         | 
| 852 859 | 
             
                  # You can also pass a +block+ to define criteria. The behavior
         | 
| 853 860 | 
             
                  # is the same, it returns true if the collection based on the
         | 
| 854 861 | 
             
                  # criteria is not empty.
         | 
| @@ -925,7 +932,7 @@ module ActiveRecord | |
| 925 932 | 
             
                    @association
         | 
| 926 933 | 
             
                  end
         | 
| 927 934 |  | 
| 928 | 
            -
                  # Returns a  | 
| 935 | 
            +
                  # Returns a Relation object for the records in this association
         | 
| 929 936 | 
             
                  def scope
         | 
| 930 937 | 
             
                    @scope ||= @association.scope
         | 
| 931 938 | 
             
                  end
         | 
| @@ -950,10 +957,13 @@ module ActiveRecord | |
| 950 957 | 
             
                  #   person.pets == other
         | 
| 951 958 | 
             
                  #   # => true
         | 
| 952 959 | 
             
                  #
         | 
| 960 | 
            +
                  #
         | 
| 961 | 
            +
                  # Note that unpersisted records can still be seen as equal:
         | 
| 962 | 
            +
                  #
         | 
| 953 963 | 
             
                  #   other = [Pet.new(id: 1), Pet.new(id: 2)]
         | 
| 954 964 | 
             
                  #
         | 
| 955 965 | 
             
                  #   person.pets == other
         | 
| 956 | 
            -
                  #   # =>  | 
| 966 | 
            +
                  #   # => true
         | 
| 957 967 | 
             
                  def ==(other)
         | 
| 958 968 | 
             
                    load_target == other
         | 
| 959 969 | 
             
                  end
         | 
| @@ -1097,13 +1107,18 @@ module ActiveRecord | |
| 1097 1107 | 
             
                    super
         | 
| 1098 1108 | 
             
                  end
         | 
| 1099 1109 |  | 
| 1110 | 
            +
                  def pretty_print(pp) # :nodoc:
         | 
| 1111 | 
            +
                    load_target if find_from_target?
         | 
| 1112 | 
            +
                    super
         | 
| 1113 | 
            +
                  end
         | 
| 1114 | 
            +
             | 
| 1100 1115 | 
             
                  delegate_methods = [
         | 
| 1101 1116 | 
             
                    QueryMethods,
         | 
| 1102 1117 | 
             
                    SpawnMethods,
         | 
| 1103 1118 | 
             
                  ].flat_map { |klass|
         | 
| 1104 1119 | 
             
                    klass.public_instance_methods(false)
         | 
| 1105 1120 | 
             
                  } - self.public_instance_methods(false) - [:select] + [
         | 
| 1106 | 
            -
                    :scoping, :values, :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all
         | 
| 1121 | 
            +
                    :scoping, :values, :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all, :load_async
         | 
| 1107 1122 | 
             
                  ]
         | 
| 1108 1123 |  | 
| 1109 1124 | 
             
                  delegate(*delegate_methods, to: :scope)
         | 
| @@ -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
         | 
| @@ -12,7 +12,7 @@ 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
         | 
| @@ -22,8 +22,15 @@ module ActiveRecord::Associations | |
| 22 22 | 
             
                  def set_owner_attributes(record)
         | 
| 23 23 | 
             
                    return if options[:through]
         | 
| 24 24 |  | 
| 25 | 
            -
                     | 
| 26 | 
            -
                     | 
| 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
         | 
| 27 34 |  | 
| 28 35 | 
             
                    if reflection.type
         | 
| 29 36 | 
             
                      record._write_attribute(reflection.type, owner.class.polymorphic_name)
         |