activerecord 5.2.3 → 6.1.0
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 +898 -532
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +5 -4
- 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 +95 -42
- data/lib/active_record/associations/association_scope.rb +21 -21
- data/lib/active_record/associations/belongs_to_association.rb +50 -46
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -5
- data/lib/active_record/associations/builder/association.rb +23 -21
- data/lib/active_record/associations/builder/belongs_to.rb +29 -59
- data/lib/active_record/associations/builder/collection_association.rb +10 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -2
- data/lib/active_record/associations/builder/has_one.rb +33 -2
- data/lib/active_record/associations/builder/singular_association.rb +3 -1
- data/lib/active_record/associations/collection_association.rb +31 -29
- data/lib/active_record/associations/collection_proxy.rb +25 -21
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +26 -13
- data/lib/active_record/associations/has_many_through_association.rb +27 -28
- data/lib/active_record/associations/has_one_association.rb +43 -31
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +54 -12
- data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +91 -60
- data/lib/active_record/associations/preloader/association.rb +71 -43
- data/lib/active_record/associations/preloader/through_association.rb +49 -40
- data/lib/active_record/associations/preloader.rb +48 -35
- data/lib/active_record/associations/singular_association.rb +3 -17
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +133 -25
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
- data/lib/active_record/attribute_methods/dirty.rb +101 -40
- data/lib/active_record/attribute_methods/primary_key.rb +20 -25
- data/lib/active_record/attribute_methods/query.rb +4 -8
- data/lib/active_record/attribute_methods/read.rb +14 -56
- data/lib/active_record/attribute_methods/serialization.rb +12 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
- data/lib/active_record/attribute_methods/write.rb +18 -34
- data/lib/active_record/attribute_methods.rb +81 -143
- data/lib/active_record/attributes.rb +45 -8
- data/lib/active_record/autosave_association.rb +76 -47
- data/lib/active_record/base.rb +4 -17
- data/lib/active_record/callbacks.rb +158 -43
- data/lib/active_record/coders/yaml_column.rb +1 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +293 -132
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +21 -17
- data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +203 -90
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +381 -146
- data/lib/active_record/connection_adapters/abstract/transaction.rb +155 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +229 -98
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
- data/lib/active_record/connection_adapters/column.rb +30 -12
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +86 -32
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- 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 +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +139 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +38 -54
- 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 +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
- 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/money.rb +2 -2
- 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/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
- 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 +222 -112
- data/lib/active_record/connection_adapters/schema_cache.rb +127 -21
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +19 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +175 -187
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_handling.rb +285 -33
- data/lib/active_record/core.rb +308 -100
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +272 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +3 -4
- data/lib/active_record/enum.rb +71 -17
- data/lib/active_record/errors.rb +62 -19
- data/lib/active_record/explain.rb +10 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- 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 +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +197 -481
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +53 -24
- data/lib/active_record/insert_all.rb +208 -0
- data/lib/active_record/integration.rb +67 -17
- data/lib/active_record/internal_metadata.rb +26 -9
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +26 -22
- data/lib/active_record/locking/pessimistic.rb +9 -5
- data/lib/active_record/log_subscriber.rb +34 -35
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +141 -64
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/migration.rb +205 -156
- data/lib/active_record/model_schema.rb +148 -22
- data/lib/active_record/nested_attributes.rb +4 -7
- data/lib/active_record/no_touching.rb +8 -1
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +267 -59
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/querying.rb +40 -23
- data/lib/active_record/railtie.rb +115 -58
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +402 -78
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +113 -101
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +44 -35
- data/lib/active_record/relation/calculations.rb +157 -93
- data/lib/active_record/relation/delegation.rb +35 -50
- data/lib/active_record/relation/finder_methods.rb +65 -40
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +32 -40
- 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 +4 -7
- 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 -40
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +487 -199
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +9 -9
- data/lib/active_record/relation/where_clause.rb +108 -58
- data/lib/active_record/relation.rb +375 -104
- data/lib/active_record/result.rb +64 -38
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +22 -41
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +54 -9
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping/default.rb +6 -8
- data/lib/active_record/scoping/named.rb +17 -24
- data/lib/active_record/scoping.rb +8 -9
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +51 -8
- data/lib/active_record/store.rb +88 -9
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +39 -43
- data/lib/active_record/tasks/database_tasks.rb +276 -81
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
- 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 +246 -0
- data/lib/active_record/timestamp.rb +43 -32
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +59 -117
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +3 -13
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +6 -3
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +10 -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 +1 -2
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +38 -30
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record.rb +13 -12
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -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 +42 -0
- data/lib/arel/delete_manager.rb +18 -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/insert_manager.rb +49 -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 +45 -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/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -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 +41 -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 +70 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -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 +54 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -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
- metadata +117 -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
| @@ -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 | 
             
                # Implements the details of eager loading of Active Record associations.
         | 
| @@ -58,7 +60,7 @@ module ActiveRecord | |
| 58 60 | 
             
                  # == Parameters
         | 
| 59 61 | 
             
                  # +records+ is an array of ActiveRecord::Base. This array needs not be flat,
         | 
| 60 62 | 
             
                  # i.e. +records+ itself may also contain arrays of records. In any case,
         | 
| 61 | 
            -
                  # +preload_associations+ will preload  | 
| 63 | 
            +
                  # +preload_associations+ will preload all associations records by
         | 
| 62 64 | 
             
                  # flattening +records+.
         | 
| 63 65 | 
             
                  #
         | 
| 64 66 | 
             
                  # +associations+ specifies one or more associations that you want to
         | 
| @@ -88,44 +90,46 @@ module ActiveRecord | |
| 88 90 | 
             
                    if records.empty?
         | 
| 89 91 | 
             
                      []
         | 
| 90 92 | 
             
                    else
         | 
| 91 | 
            -
                      records.uniq!
         | 
| 92 93 | 
             
                      Array.wrap(associations).flat_map { |association|
         | 
| 93 94 | 
             
                        preloaders_on association, records, preload_scope
         | 
| 94 95 | 
             
                      }
         | 
| 95 96 | 
             
                    end
         | 
| 96 97 | 
             
                  end
         | 
| 97 98 |  | 
| 98 | 
            -
                   | 
| 99 | 
            +
                  def initialize(associate_by_default: true)
         | 
| 100 | 
            +
                    @associate_by_default = associate_by_default
         | 
| 101 | 
            +
                  end
         | 
| 99 102 |  | 
| 103 | 
            +
                  private
         | 
| 100 104 | 
             
                    # Loads all the given data into +records+ for the +association+.
         | 
| 101 | 
            -
                    def preloaders_on(association, records, scope)
         | 
| 105 | 
            +
                    def preloaders_on(association, records, scope, polymorphic_parent = false)
         | 
| 102 106 | 
             
                      case association
         | 
| 103 107 | 
             
                      when Hash
         | 
| 104 | 
            -
                        preloaders_for_hash(association, records, scope)
         | 
| 105 | 
            -
                      when Symbol
         | 
| 106 | 
            -
                        preloaders_for_one(association, records, scope)
         | 
| 107 | 
            -
                      when String
         | 
| 108 | 
            -
                        preloaders_for_one(association.to_sym, records, scope)
         | 
| 108 | 
            +
                        preloaders_for_hash(association, records, scope, polymorphic_parent)
         | 
| 109 | 
            +
                      when Symbol, String
         | 
| 110 | 
            +
                        preloaders_for_one(association, records, scope, polymorphic_parent)
         | 
| 109 111 | 
             
                      else
         | 
| 110 112 | 
             
                        raise ArgumentError, "#{association.inspect} was not recognized for preload"
         | 
| 111 113 | 
             
                      end
         | 
| 112 114 | 
             
                    end
         | 
| 113 115 |  | 
| 114 | 
            -
                    def preloaders_for_hash(association, records, scope)
         | 
| 116 | 
            +
                    def preloaders_for_hash(association, records, scope, polymorphic_parent)
         | 
| 115 117 | 
             
                      association.flat_map { |parent, child|
         | 
| 116 | 
            -
                         | 
| 117 | 
            -
             | 
| 118 | 
            -
             | 
| 119 | 
            -
             | 
| 120 | 
            -
                           | 
| 121 | 
            -
             | 
| 122 | 
            -
             | 
| 118 | 
            +
                        grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
         | 
| 119 | 
            +
                          loaders = preloaders_for_reflection(reflection, reflection_records, scope)
         | 
| 120 | 
            +
                          recs = loaders.flat_map(&:preloaded_records).uniq
         | 
| 121 | 
            +
                          child_polymorphic_parent = reflection && reflection.options[:polymorphic]
         | 
| 122 | 
            +
                          loaders.concat Array.wrap(child).flat_map { |assoc|
         | 
| 123 | 
            +
                            preloaders_on assoc, recs, scope, child_polymorphic_parent
         | 
| 124 | 
            +
                          }
         | 
| 125 | 
            +
                          loaders
         | 
| 126 | 
            +
                        end
         | 
| 123 127 | 
             
                      }
         | 
| 124 128 | 
             
                    end
         | 
| 125 129 |  | 
| 126 130 | 
             
                    # Loads all the given data into +records+ for a singular +association+.
         | 
| 127 131 | 
             
                    #
         | 
| 128 | 
            -
                    # Functions by instantiating a preloader class such as Preloader:: | 
| 132 | 
            +
                    # Functions by instantiating a preloader class such as Preloader::Association and
         | 
| 129 133 | 
             
                    # call the +run+ method for each passed in class in the +records+ argument.
         | 
| 130 134 | 
             
                    #
         | 
| 131 135 | 
             
                    # Not all records have the same class, so group then preload group on the reflection
         | 
| @@ -135,41 +139,50 @@ module ActiveRecord | |
| 135 139 | 
             
                    # Additionally, polymorphic belongs_to associations can have multiple associated
         | 
| 136 140 | 
             
                    # classes, depending on the polymorphic_type field. So we group by the classes as
         | 
| 137 141 | 
             
                    # well.
         | 
| 138 | 
            -
                    def preloaders_for_one(association, records, scope)
         | 
| 139 | 
            -
                      grouped_records(association, records | 
| 140 | 
            -
                         | 
| 141 | 
            -
                           | 
| 142 | 
            -
                          loader.run self
         | 
| 143 | 
            -
                          loader
         | 
| 142 | 
            +
                    def preloaders_for_one(association, records, scope, polymorphic_parent)
         | 
| 143 | 
            +
                      grouped_records(association, records, polymorphic_parent)
         | 
| 144 | 
            +
                        .flat_map do |reflection, reflection_records|
         | 
| 145 | 
            +
                          preloaders_for_reflection reflection, reflection_records, scope
         | 
| 144 146 | 
             
                        end
         | 
| 147 | 
            +
                    end
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                    def preloaders_for_reflection(reflection, records, scope)
         | 
| 150 | 
            +
                      records.group_by { |record| record.association(reflection.name).klass }.map do |rhs_klass, rs|
         | 
| 151 | 
            +
                        preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope, @associate_by_default).run
         | 
| 145 152 | 
             
                      end
         | 
| 146 153 | 
             
                    end
         | 
| 147 154 |  | 
| 148 | 
            -
                    def grouped_records(association, records)
         | 
| 155 | 
            +
                    def grouped_records(association, records, polymorphic_parent)
         | 
| 149 156 | 
             
                      h = {}
         | 
| 150 157 | 
             
                      records.each do |record|
         | 
| 151 | 
            -
                         | 
| 152 | 
            -
                         | 
| 153 | 
            -
                         | 
| 154 | 
            -
                        klasses = h[assoc.reflection] ||= {}
         | 
| 155 | 
            -
                        (klasses[assoc.klass] ||= []) << record
         | 
| 158 | 
            +
                        reflection = record.class._reflect_on_association(association)
         | 
| 159 | 
            +
                        next if polymorphic_parent && !reflection || !record.association(association).klass
         | 
| 160 | 
            +
                        (h[reflection] ||= []) << record
         | 
| 156 161 | 
             
                      end
         | 
| 157 162 | 
             
                      h
         | 
| 158 163 | 
             
                    end
         | 
| 159 164 |  | 
| 160 165 | 
             
                    class AlreadyLoaded # :nodoc:
         | 
| 161 | 
            -
                      def initialize(klass, owners, reflection, preload_scope)
         | 
| 166 | 
            +
                      def initialize(klass, owners, reflection, preload_scope, associate_by_default = true)
         | 
| 162 167 | 
             
                        @owners = owners
         | 
| 163 168 | 
             
                        @reflection = reflection
         | 
| 164 169 | 
             
                      end
         | 
| 165 170 |  | 
| 166 | 
            -
                      def run | 
| 171 | 
            +
                      def run
         | 
| 172 | 
            +
                        self
         | 
| 173 | 
            +
                      end
         | 
| 167 174 |  | 
| 168 175 | 
             
                      def preloaded_records
         | 
| 169 | 
            -
                         | 
| 176 | 
            +
                        @preloaded_records ||= records_by_owner.flat_map(&:last)
         | 
| 177 | 
            +
                      end
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                      def records_by_owner
         | 
| 180 | 
            +
                        @records_by_owner ||= owners.index_with do |owner|
         | 
| 181 | 
            +
                          Array(owner.association(reflection.name).target)
         | 
| 182 | 
            +
                        end
         | 
| 170 183 | 
             
                      end
         | 
| 171 184 |  | 
| 172 | 
            -
                       | 
| 185 | 
            +
                      private
         | 
| 173 186 | 
             
                        attr_reader :owners, :reflection
         | 
| 174 187 | 
             
                    end
         | 
| 175 188 |  | 
| @@ -177,7 +190,7 @@ module ActiveRecord | |
| 177 190 | 
             
                    # and attach it to a relation. The class returned implements a `run` method
         | 
| 178 191 | 
             
                    # that accepts a preloader.
         | 
| 179 192 | 
             
                    def preloader_for(reflection, owners)
         | 
| 180 | 
            -
                      if owners. | 
| 193 | 
            +
                      if owners.all? { |o| o.association(reflection.name).loaded? }
         | 
| 181 194 | 
             
                        return AlreadyLoaded
         | 
| 182 195 | 
             
                      end
         | 
| 183 196 | 
             
                      reflection.check_preloadable!
         | 
| @@ -17,7 +17,7 @@ module ActiveRecord | |
| 17 17 | 
             
                    replace(record)
         | 
| 18 18 | 
             
                  end
         | 
| 19 19 |  | 
| 20 | 
            -
                  def build(attributes =  | 
| 20 | 
            +
                  def build(attributes = nil, &block)
         | 
| 21 21 | 
             
                    record = build_record(attributes, &block)
         | 
| 22 22 | 
             
                    set_new_record(record)
         | 
| 23 23 | 
             
                    record
         | 
| @@ -26,7 +26,7 @@ module ActiveRecord | |
| 26 26 | 
             
                  # Implements the reload reader method, e.g. foo.reload_bar for
         | 
| 27 27 | 
             
                  # Foo.has_one :bar
         | 
| 28 28 | 
             
                  def force_reload_reader
         | 
| 29 | 
            -
                     | 
| 29 | 
            +
                    reload(true)
         | 
| 30 30 | 
             
                    target
         | 
| 31 31 | 
             
                  end
         | 
| 32 32 |  | 
| @@ -36,21 +36,7 @@ module ActiveRecord | |
| 36 36 | 
             
                    end
         | 
| 37 37 |  | 
| 38 38 | 
             
                    def find_target
         | 
| 39 | 
            -
                       | 
| 40 | 
            -
                      return scope.take if skip_statement_cache?(scope)
         | 
| 41 | 
            -
             | 
| 42 | 
            -
                      conn = klass.connection
         | 
| 43 | 
            -
                      sc = reflection.association_scope_cache(conn, owner) do |params|
         | 
| 44 | 
            -
                        as = AssociationScope.create { params.bind }
         | 
| 45 | 
            -
                        target_scope.merge!(as.scope(self)).limit(1)
         | 
| 46 | 
            -
                      end
         | 
| 47 | 
            -
             | 
| 48 | 
            -
                      binds = AssociationScope.get_bind_values(owner, reflection.chain)
         | 
| 49 | 
            -
                      sc.execute(binds, conn) do |record|
         | 
| 50 | 
            -
                        set_inverse_instance record
         | 
| 51 | 
            -
                      end.first
         | 
| 52 | 
            -
                    rescue ::RangeError
         | 
| 53 | 
            -
                      nil
         | 
| 39 | 
            +
                      super.first
         | 
| 54 40 | 
             
                    end
         | 
| 55 41 |  | 
| 56 42 | 
             
                    def replace(record)
         | 
| @@ -32,7 +32,7 @@ module ActiveRecord | |
| 32 32 | 
             
                      reflection.chain.drop(1).each do |reflection|
         | 
| 33 33 | 
             
                        relation = reflection.klass.scope_for_association
         | 
| 34 34 | 
             
                        scope.merge!(
         | 
| 35 | 
            -
                          relation.except(:select, :create_with, :includes, :preload, :joins, : | 
| 35 | 
            +
                          relation.except(:select, :create_with, :includes, :preload, :eager_load, :joins, :left_outer_joins)
         | 
| 36 36 | 
             
                        )
         | 
| 37 37 | 
             
                      end
         | 
| 38 38 | 
             
                      scope
         | 
| @@ -2,38 +2,116 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            require "active_support/core_ext/enumerable"
         | 
| 4 4 | 
             
            require "active_support/core_ext/string/conversions"
         | 
| 5 | 
            -
            require "active_support/core_ext/module/remove_method"
         | 
| 6 | 
            -
            require "active_record/errors"
         | 
| 7 5 |  | 
| 8 6 | 
             
            module ActiveRecord
         | 
| 9 7 | 
             
              class AssociationNotFoundError < ConfigurationError #:nodoc:
         | 
| 8 | 
            +
                attr_reader :record, :association_name
         | 
| 10 9 | 
             
                def initialize(record = nil, association_name = nil)
         | 
| 10 | 
            +
                  @record           = record
         | 
| 11 | 
            +
                  @association_name = association_name
         | 
| 11 12 | 
             
                  if record && association_name
         | 
| 12 13 | 
             
                    super("Association named '#{association_name}' was not found on #{record.class.name}; perhaps you misspelled it?")
         | 
| 13 14 | 
             
                  else
         | 
| 14 15 | 
             
                    super("Association was not found.")
         | 
| 15 16 | 
             
                  end
         | 
| 16 17 | 
             
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                class Correction
         | 
| 20 | 
            +
                  def initialize(error)
         | 
| 21 | 
            +
                    @error = error
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  def corrections
         | 
| 25 | 
            +
                    if @error.association_name
         | 
| 26 | 
            +
                      maybe_these = @error.record.class.reflections.keys
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                      maybe_these.sort_by { |n|
         | 
| 29 | 
            +
                        DidYouMean::Jaro.distance(@error.association_name.to_s, n)
         | 
| 30 | 
            +
                      }.reverse.first(4)
         | 
| 31 | 
            +
                    else
         | 
| 32 | 
            +
                      []
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                # We may not have DYM, and DYM might not let us register error handlers
         | 
| 38 | 
            +
                if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
         | 
| 39 | 
            +
                  DidYouMean.correct_error(self, Correction)
         | 
| 40 | 
            +
                end
         | 
| 17 41 | 
             
              end
         | 
| 18 42 |  | 
| 19 43 | 
             
              class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
         | 
| 44 | 
            +
                attr_reader :reflection, :associated_class
         | 
| 20 45 | 
             
                def initialize(reflection = nil, associated_class = nil)
         | 
| 21 46 | 
             
                  if reflection
         | 
| 47 | 
            +
                    @reflection = reflection
         | 
| 48 | 
            +
                    @associated_class = associated_class.nil? ? reflection.klass : associated_class
         | 
| 22 49 | 
             
                    super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
         | 
| 23 50 | 
             
                  else
         | 
| 24 51 | 
             
                    super("Could not find the inverse association.")
         | 
| 25 52 | 
             
                  end
         | 
| 26 53 | 
             
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                class Correction
         | 
| 56 | 
            +
                  def initialize(error)
         | 
| 57 | 
            +
                    @error = error
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  def corrections
         | 
| 61 | 
            +
                    if @error.reflection && @error.associated_class
         | 
| 62 | 
            +
                      maybe_these = @error.associated_class.reflections.keys
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                      maybe_these.sort_by { |n|
         | 
| 65 | 
            +
                        DidYouMean::Jaro.distance(@error.reflection.options[:inverse_of].to_s, n)
         | 
| 66 | 
            +
                      }.reverse.first(4)
         | 
| 67 | 
            +
                    else
         | 
| 68 | 
            +
                      []
         | 
| 69 | 
            +
                    end
         | 
| 70 | 
            +
                  end
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                # We may not have DYM, and DYM might not let us register error handlers
         | 
| 74 | 
            +
                if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
         | 
| 75 | 
            +
                  DidYouMean.correct_error(self, Correction)
         | 
| 76 | 
            +
                end
         | 
| 27 77 | 
             
              end
         | 
| 28 78 |  | 
| 29 79 | 
             
              class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc:
         | 
| 30 | 
            -
                 | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 80 | 
            +
                attr_reader :owner_class, :reflection
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                def initialize(owner_class = nil, reflection = nil)
         | 
| 83 | 
            +
                  if owner_class && reflection
         | 
| 84 | 
            +
                    @owner_class = owner_class
         | 
| 85 | 
            +
                    @reflection = reflection
         | 
| 86 | 
            +
                    super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class.name}")
         | 
| 33 87 | 
             
                  else
         | 
| 34 88 | 
             
                    super("Could not find the association.")
         | 
| 35 89 | 
             
                  end
         | 
| 36 90 | 
             
                end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                class Correction
         | 
| 93 | 
            +
                  def initialize(error)
         | 
| 94 | 
            +
                    @error = error
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                  def corrections
         | 
| 98 | 
            +
                    if @error.reflection && @error.owner_class
         | 
| 99 | 
            +
                      maybe_these = @error.owner_class.reflections.keys
         | 
| 100 | 
            +
                      maybe_these -= [@error.reflection.name.to_s] # remove failing reflection
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                      maybe_these.sort_by { |n|
         | 
| 103 | 
            +
                        DidYouMean::Jaro.distance(@error.reflection.options[:through].to_s, n)
         | 
| 104 | 
            +
                      }.reverse.first(4)
         | 
| 105 | 
            +
                    else
         | 
| 106 | 
            +
                      []
         | 
| 107 | 
            +
                    end
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                # We may not have DYM, and DYM might not let us register error handlers
         | 
| 112 | 
            +
                if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
         | 
| 113 | 
            +
                  DidYouMean.correct_error(self, Correction)
         | 
| 114 | 
            +
                end
         | 
| 37 115 | 
             
              end
         | 
| 38 116 |  | 
| 39 117 | 
             
              class HasManyThroughAssociationPolymorphicSourceError < ActiveRecordError #:nodoc:
         | 
| @@ -92,7 +170,7 @@ module ActiveRecord | |
| 92 170 | 
             
                    through_reflection      = reflection.through_reflection
         | 
| 93 171 | 
             
                    source_reflection_names = reflection.source_reflection_names
         | 
| 94 172 | 
             
                    source_associations     = reflection.through_reflection.klass._reflections.keys
         | 
| 95 | 
            -
                    super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(two_words_connector: ' or ', last_word_connector: ', or ' | 
| 173 | 
            +
                    super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')}?")
         | 
| 96 174 | 
             
                  else
         | 
| 97 175 | 
             
                    super("Could not find the source association(s).")
         | 
| 98 176 | 
             
                  end
         | 
| @@ -292,13 +370,13 @@ module ActiveRecord | |
| 292 370 | 
             
                  #
         | 
| 293 371 | 
             
                  # The project class now has the following methods (and more) to ease the traversal and
         | 
| 294 372 | 
             
                  # manipulation of its relationships:
         | 
| 295 | 
            -
                  # * <tt>Project#portfolio | 
| 296 | 
            -
                  # * <tt>Project#project_manager | 
| 297 | 
            -
                  # * <tt>Project#milestones.empty | 
| 298 | 
            -
                  #   <tt>Project#milestones.delete(milestone) | 
| 299 | 
            -
                  #   <tt>Project#milestones.build | 
| 300 | 
            -
                  # * <tt>Project#categories.empty | 
| 301 | 
            -
                  #   <tt>Project#categories.delete(category1) | 
| 373 | 
            +
                  # * <tt>Project#portfolio</tt>, <tt>Project#portfolio=(portfolio)</tt>, <tt>Project#reload_portfolio</tt>
         | 
| 374 | 
            +
                  # * <tt>Project#project_manager</tt>, <tt>Project#project_manager=(project_manager)</tt>, <tt>Project#reload_project_manager</tt>
         | 
| 375 | 
            +
                  # * <tt>Project#milestones.empty?</tt>, <tt>Project#milestones.size</tt>, <tt>Project#milestones</tt>, <tt>Project#milestones<<(milestone)</tt>,
         | 
| 376 | 
            +
                  #   <tt>Project#milestones.delete(milestone)</tt>, <tt>Project#milestones.destroy(milestone)</tt>, <tt>Project#milestones.find(milestone_id)</tt>,
         | 
| 377 | 
            +
                  #   <tt>Project#milestones.build</tt>, <tt>Project#milestones.create</tt>
         | 
| 378 | 
            +
                  # * <tt>Project#categories.empty?</tt>, <tt>Project#categories.size</tt>, <tt>Project#categories</tt>, <tt>Project#categories<<(category1)</tt>,
         | 
| 379 | 
            +
                  #   <tt>Project#categories.delete(category1)</tt>, <tt>Project#categories.destroy(category1)</tt>
         | 
| 302 380 | 
             
                  #
         | 
| 303 381 | 
             
                  # === A word of warning
         | 
| 304 382 | 
             
                  #
         | 
| @@ -702,9 +780,9 @@ module ActiveRecord | |
| 702 780 | 
             
                  # inverse detection only works on #has_many, #has_one, and
         | 
| 703 781 | 
             
                  # #belongs_to associations.
         | 
| 704 782 | 
             
                  #
         | 
| 705 | 
            -
                  #  | 
| 706 | 
            -
                  #  | 
| 707 | 
            -
                  #  | 
| 783 | 
            +
                  # <tt>:foreign_key</tt> and <tt>:through</tt> options on the associations,
         | 
| 784 | 
            +
                  # or a custom scope, will also prevent the association's inverse
         | 
| 785 | 
            +
                  # from being found automatically.
         | 
| 708 786 | 
             
                  #
         | 
| 709 787 | 
             
                  # The automatic guessing of the inverse association uses a heuristic based
         | 
| 710 788 | 
             
                  # on the name of the class, so it may not work for all associations,
         | 
| @@ -1291,10 +1369,13 @@ module ActiveRecord | |
| 1291 1369 | 
             
                    #   similar callbacks may affect the <tt>:dependent</tt> behavior, and the
         | 
| 1292 1370 | 
             
                    #   <tt>:dependent</tt> behavior may affect other callbacks.
         | 
| 1293 1371 | 
             
                    #
         | 
| 1372 | 
            +
                    #   * <tt>nil</tt> do nothing (default).
         | 
| 1294 1373 | 
             
                    #   * <tt>:destroy</tt> causes all the associated objects to also be destroyed.
         | 
| 1374 | 
            +
                    #   * <tt>:destroy_async</tt> destroys all the associated objects in a background job.
         | 
| 1295 1375 | 
             
                    #   * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).
         | 
| 1296 | 
            -
                    #   * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+.  | 
| 1297 | 
            -
                    # | 
| 1376 | 
            +
                    #   * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Polymorphic type will also be nullified
         | 
| 1377 | 
            +
                    #     on polymorphic associations. Callbacks are not executed.
         | 
| 1378 | 
            +
                    #   * <tt>:restrict_with_exception</tt> causes an <tt>ActiveRecord::DeleteRestrictionError</tt> exception to be raised if there are any associated records.
         | 
| 1298 1379 | 
             
                    #   * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there are any associated objects.
         | 
| 1299 1380 | 
             
                    #
         | 
| 1300 1381 | 
             
                    #   If using with the <tt>:through</tt> option, the association on the join model must be
         | 
| @@ -1355,6 +1436,11 @@ module ActiveRecord | |
| 1355 1436 | 
             
                    #   Specifies a module or array of modules that will be extended into the association object returned.
         | 
| 1356 1437 | 
             
                    #   Useful for defining methods on associations, especially when they should be shared between multiple
         | 
| 1357 1438 | 
             
                    #   association objects.
         | 
| 1439 | 
            +
                    # [:strict_loading]
         | 
| 1440 | 
            +
                    #   Enforces strict loading every time the associated record is loaded through this association.
         | 
| 1441 | 
            +
                    # [:ensuring_owner_was]
         | 
| 1442 | 
            +
                    #   Specifies an instance method to be called on the owner. The method must return true in order for the
         | 
| 1443 | 
            +
                    #   associated records to be deleted in a background job.
         | 
| 1358 1444 | 
             
                    #
         | 
| 1359 1445 | 
             
                    # Option examples:
         | 
| 1360 1446 | 
             
                    #   has_many :comments, -> { order("posted_on") }
         | 
| @@ -1365,6 +1451,7 @@ module ActiveRecord | |
| 1365 1451 | 
             
                    #   has_many :tags, as: :taggable
         | 
| 1366 1452 | 
             
                    #   has_many :reports, -> { readonly }
         | 
| 1367 1453 | 
             
                    #   has_many :subscribers, through: :subscriptions, source: :user
         | 
| 1454 | 
            +
                    #   has_many :comments, strict_loading: true
         | 
| 1368 1455 | 
             
                    def has_many(name, scope = nil, **options, &extension)
         | 
| 1369 1456 | 
             
                      reflection = Builder::HasMany.build(self, name, scope, options, &extension)
         | 
| 1370 1457 | 
             
                      Reflection.add_reflection self, name, reflection
         | 
| @@ -1434,10 +1521,13 @@ module ActiveRecord | |
| 1434 1521 | 
             
                    #   Controls what happens to the associated object when
         | 
| 1435 1522 | 
             
                    #   its owner is destroyed:
         | 
| 1436 1523 | 
             
                    #
         | 
| 1524 | 
            +
                    #   * <tt>nil</tt> do nothing (default).
         | 
| 1437 1525 | 
             
                    #   * <tt>:destroy</tt> causes the associated object to also be destroyed
         | 
| 1526 | 
            +
                    #   * <tt>:destroy_async</tt> causes all the associated object to be destroyed in a background job.
         | 
| 1438 1527 | 
             
                    #   * <tt>:delete</tt> causes the associated object to be deleted directly from the database (so callbacks will not execute)
         | 
| 1439 | 
            -
                    #   * <tt>:nullify</tt> causes the foreign key to be set to +NULL+.  | 
| 1440 | 
            -
                    # | 
| 1528 | 
            +
                    #   * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Polymorphic type column is also nullified
         | 
| 1529 | 
            +
                    #     on polymorphic associations. Callbacks are not executed.
         | 
| 1530 | 
            +
                    #   * <tt>:restrict_with_exception</tt> causes an <tt>ActiveRecord::DeleteRestrictionError</tt> exception to be raised if there is an associated record
         | 
| 1441 1531 | 
             
                    #   * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there is an associated object
         | 
| 1442 1532 | 
             
                    #
         | 
| 1443 1533 | 
             
                    #   Note that <tt>:dependent</tt> option is ignored when using <tt>:through</tt> option.
         | 
| @@ -1492,6 +1582,11 @@ module ActiveRecord | |
| 1492 1582 | 
             
                    #   When set to +true+, the association will also have its presence validated.
         | 
| 1493 1583 | 
             
                    #   This will validate the association itself, not the id. You can use
         | 
| 1494 1584 | 
             
                    #   +:inverse_of+ to avoid an extra query during validation.
         | 
| 1585 | 
            +
                    # [:strict_loading]
         | 
| 1586 | 
            +
                    #   Enforces strict loading every time the associated record is loaded through this association.
         | 
| 1587 | 
            +
                    # [:ensuring_owner_was]
         | 
| 1588 | 
            +
                    #   Specifies an instance method to be called on the owner. The method must return true in order for the
         | 
| 1589 | 
            +
                    #   associated records to be deleted in a background job.
         | 
| 1495 1590 | 
             
                    #
         | 
| 1496 1591 | 
             
                    # Option examples:
         | 
| 1497 1592 | 
             
                    #   has_one :credit_card, dependent: :destroy  # destroys the associated credit card
         | 
| @@ -1504,6 +1599,7 @@ module ActiveRecord | |
| 1504 1599 | 
             
                    #   has_one :club, through: :membership
         | 
| 1505 1600 | 
             
                    #   has_one :primary_address, -> { where(primary: true) }, through: :addressables, source: :addressable
         | 
| 1506 1601 | 
             
                    #   has_one :credit_card, required: true
         | 
| 1602 | 
            +
                    #   has_one :credit_card, strict_loading: true
         | 
| 1507 1603 | 
             
                    def has_one(name, scope = nil, **options)
         | 
| 1508 1604 | 
             
                      reflection = Builder::HasOne.build(self, name, scope, options)
         | 
| 1509 1605 | 
             
                      Reflection.add_reflection self, name, reflection
         | 
| @@ -1524,6 +1620,7 @@ module ActiveRecord | |
| 1524 1620 | 
             
                    #   Returns the associated object. +nil+ is returned if none is found.
         | 
| 1525 1621 | 
             
                    # [association=(associate)]
         | 
| 1526 1622 | 
             
                    #   Assigns the associate object, extracts the primary key, and sets it as the foreign key.
         | 
| 1623 | 
            +
                    #   No modification or deletion of existing records takes place.
         | 
| 1527 1624 | 
             
                    # [build_association(attributes = {})]
         | 
| 1528 1625 | 
             
                    #   Returns a new object of the associated type that has been instantiated
         | 
| 1529 1626 | 
             
                    #   with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
         | 
| @@ -1581,10 +1678,11 @@ module ActiveRecord | |
| 1581 1678 | 
             
                    #   association will use "taggable_type" as the default <tt>:foreign_type</tt>.
         | 
| 1582 1679 | 
             
                    # [:primary_key]
         | 
| 1583 1680 | 
             
                    #   Specify the method that returns the primary key of associated object used for the association.
         | 
| 1584 | 
            -
                    #   By default this is id | 
| 1681 | 
            +
                    #   By default this is +id+.
         | 
| 1585 1682 | 
             
                    # [:dependent]
         | 
| 1586 1683 | 
             
                    #   If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
         | 
| 1587 | 
            -
                    #   <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
         | 
| 1684 | 
            +
                    #   <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. If set to
         | 
| 1685 | 
            +
                    #   <tt>:destroy_async</tt>, the associated object is scheduled to be destroyed in a background job.
         | 
| 1588 1686 | 
             
                    #   This option should not be specified when #belongs_to is used in conjunction with
         | 
| 1589 1687 | 
             
                    #   a #has_many relationship on another class because of the potential to leave
         | 
| 1590 1688 | 
             
                    #   orphaned records behind.
         | 
| @@ -1636,6 +1734,11 @@ module ActiveRecord | |
| 1636 1734 | 
             
                    # [:default]
         | 
| 1637 1735 | 
             
                    #   Provide a callable (i.e. proc or lambda) to specify that the association should
         | 
| 1638 1736 | 
             
                    #   be initialized with a particular record before validation.
         | 
| 1737 | 
            +
                    # [:strict_loading]
         | 
| 1738 | 
            +
                    #   Enforces strict loading every time the associated record is loaded through this association.
         | 
| 1739 | 
            +
                    # [:ensuring_owner_was]
         | 
| 1740 | 
            +
                    #   Specifies an instance method to be called on the owner. The method must return true in order for the
         | 
| 1741 | 
            +
                    #   associated records to be deleted in a background job.
         | 
| 1639 1742 | 
             
                    #
         | 
| 1640 1743 | 
             
                    # Option examples:
         | 
| 1641 1744 | 
             
                    #   belongs_to :firm, foreign_key: "client_of"
         | 
| @@ -1650,6 +1753,7 @@ module ActiveRecord | |
| 1650 1753 | 
             
                    #   belongs_to :company, touch: :employees_last_updated_at
         | 
| 1651 1754 | 
             
                    #   belongs_to :user, optional: true
         | 
| 1652 1755 | 
             
                    #   belongs_to :account, default: -> { company.account }
         | 
| 1756 | 
            +
                    #   belongs_to :account, strict_loading: true
         | 
| 1653 1757 | 
             
                    def belongs_to(name, scope = nil, **options)
         | 
| 1654 1758 | 
             
                      reflection = Builder::BelongsTo.build(self, name, scope, options)
         | 
| 1655 1759 | 
             
                      Reflection.add_reflection self, name, reflection
         | 
| @@ -1672,7 +1776,7 @@ module ActiveRecord | |
| 1672 1776 | 
             
                    # The join table should not have a primary key or a model associated with it. You must manually generate the
         | 
| 1673 1777 | 
             
                    # join table with a migration such as this:
         | 
| 1674 1778 | 
             
                    #
         | 
| 1675 | 
            -
                    #   class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[ | 
| 1779 | 
            +
                    #   class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[6.0]
         | 
| 1676 1780 | 
             
                    #     def change
         | 
| 1677 1781 | 
             
                    #       create_join_table :developers, :projects
         | 
| 1678 1782 | 
             
                    #     end
         | 
| @@ -1761,6 +1865,7 @@ module ActiveRecord | |
| 1761 1865 | 
             
                    #   has_and_belongs_to_many :projects, -> { includes(:milestones, :manager) }
         | 
| 1762 1866 | 
             
                    #   has_and_belongs_to_many :categories, ->(post) {
         | 
| 1763 1867 | 
             
                    #     where("default_category = ?", post.default_category)
         | 
| 1868 | 
            +
                    #   }
         | 
| 1764 1869 | 
             
                    #
         | 
| 1765 1870 | 
             
                    # === Extensions
         | 
| 1766 1871 | 
             
                    #
         | 
| @@ -1811,6 +1916,8 @@ module ActiveRecord | |
| 1811 1916 | 
             
                    #
         | 
| 1812 1917 | 
             
                    #   Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for sets
         | 
| 1813 1918 | 
             
                    #   <tt>:autosave</tt> to <tt>true</tt>.
         | 
| 1919 | 
            +
                    # [:strict_loading]
         | 
| 1920 | 
            +
                    #   Enforces strict loading every time an associated record is loaded through this association.
         | 
| 1814 1921 | 
             
                    #
         | 
| 1815 1922 | 
             
                    # Option examples:
         | 
| 1816 1923 | 
             
                    #   has_and_belongs_to_many :projects
         | 
| @@ -1818,6 +1925,7 @@ module ActiveRecord | |
| 1818 1925 | 
             
                    #   has_and_belongs_to_many :nations, class_name: "Country"
         | 
| 1819 1926 | 
             
                    #   has_and_belongs_to_many :categories, join_table: "prods_cats"
         | 
| 1820 1927 | 
             
                    #   has_and_belongs_to_many :categories, -> { readonly }
         | 
| 1928 | 
            +
                    #   has_and_belongs_to_many :categories, strict_loading: true
         | 
| 1821 1929 | 
             
                    def has_and_belongs_to_many(name, scope = nil, **options, &extension)
         | 
| 1822 1930 | 
             
                      habtm_reflection = ActiveRecord::Reflection::HasAndBelongsToManyReflection.new(name, scope, options, self)
         | 
| 1823 1931 |  | 
| @@ -1848,11 +1956,11 @@ module ActiveRecord | |
| 1848 1956 | 
             
                      hm_options[:through] = middle_reflection.name
         | 
| 1849 1957 | 
             
                      hm_options[:source] = join_model.right_reflection.name
         | 
| 1850 1958 |  | 
| 1851 | 
            -
                      [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend].each do |k|
         | 
| 1959 | 
            +
                      [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend, :strict_loading].each do |k|
         | 
| 1852 1960 | 
             
                        hm_options[k] = options[k] if options.key? k
         | 
| 1853 1961 | 
             
                      end
         | 
| 1854 1962 |  | 
| 1855 | 
            -
                      has_many name, scope, hm_options, &extension
         | 
| 1963 | 
            +
                      has_many name, scope, **hm_options, &extension
         | 
| 1856 1964 | 
             
                      _reflections[name.to_s].parent_reflection = habtm_reflection
         | 
| 1857 1965 | 
             
                    end
         | 
| 1858 1966 | 
             
                  end
         | 
| @@ -4,26 +4,26 @@ require "active_model/forbidden_attributes_protection" | |
| 4 4 |  | 
| 5 5 | 
             
            module ActiveRecord
         | 
| 6 6 | 
             
              module AttributeAssignment
         | 
| 7 | 
            -
                extend ActiveSupport::Concern
         | 
| 8 7 | 
             
                include ActiveModel::AttributeAssignment
         | 
| 9 8 |  | 
| 10 9 | 
             
                private
         | 
| 11 | 
            -
             | 
| 12 10 | 
             
                  def _assign_attributes(attributes)
         | 
| 13 | 
            -
                    multi_parameter_attributes | 
| 14 | 
            -
                    nested_parameter_attributes = {}
         | 
| 11 | 
            +
                    multi_parameter_attributes = nested_parameter_attributes = nil
         | 
| 15 12 |  | 
| 16 13 | 
             
                    attributes.each do |k, v|
         | 
| 17 | 
            -
                       | 
| 18 | 
            -
             | 
| 14 | 
            +
                      key = k.to_s
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                      if key.include?("(")
         | 
| 17 | 
            +
                        (multi_parameter_attributes ||= {})[key] = v
         | 
| 19 18 | 
             
                      elsif v.is_a?(Hash)
         | 
| 20 | 
            -
                        nested_parameter_attributes[ | 
| 19 | 
            +
                        (nested_parameter_attributes ||= {})[key] = v
         | 
| 20 | 
            +
                      else
         | 
| 21 | 
            +
                        _assign_attribute(key, v)
         | 
| 21 22 | 
             
                      end
         | 
| 22 23 | 
             
                    end
         | 
| 23 | 
            -
                    super(attributes)
         | 
| 24 24 |  | 
| 25 | 
            -
                    assign_nested_parameter_attributes(nested_parameter_attributes)  | 
| 26 | 
            -
                    assign_multiparameter_attributes(multi_parameter_attributes)  | 
| 25 | 
            +
                    assign_nested_parameter_attributes(nested_parameter_attributes) if nested_parameter_attributes
         | 
| 26 | 
            +
                    assign_multiparameter_attributes(multi_parameter_attributes) if multi_parameter_attributes
         | 
| 27 27 | 
             
                  end
         | 
| 28 28 |  | 
| 29 29 | 
             
                  # Assign any deferred nested attributes after the base attributes have been set.
         | 
| @@ -46,16 +46,14 @@ module ActiveRecord | |
| 46 46 | 
             
                  def execute_callstack_for_multiparameter_attributes(callstack)
         | 
| 47 47 | 
             
                    errors = []
         | 
| 48 48 | 
             
                    callstack.each do |name, values_with_empty_parameters|
         | 
| 49 | 
            -
                       | 
| 50 | 
            -
                         | 
| 51 | 
            -
             | 
| 52 | 
            -
                         | 
| 53 | 
            -
                          values = values_with_empty_parameters
         | 
| 54 | 
            -
                        end
         | 
| 55 | 
            -
                        send("#{name}=", values)
         | 
| 56 | 
            -
                      rescue => ex
         | 
| 57 | 
            -
                        errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
         | 
| 49 | 
            +
                      if values_with_empty_parameters.each_value.all?(&:nil?)
         | 
| 50 | 
            +
                        values = nil
         | 
| 51 | 
            +
                      else
         | 
| 52 | 
            +
                        values = values_with_empty_parameters
         | 
| 58 53 | 
             
                      end
         | 
| 54 | 
            +
                      send("#{name}=", values)
         | 
| 55 | 
            +
                    rescue => ex
         | 
| 56 | 
            +
                      errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
         | 
| 59 57 | 
             
                    end
         | 
| 60 58 | 
             
                    unless errors.empty?
         | 
| 61 59 | 
             
                      error_descriptions = errors.map(&:message).join(",")
         | 
| @@ -29,7 +29,7 @@ module ActiveRecord | |
| 29 29 | 
             
                  extend ActiveSupport::Concern
         | 
| 30 30 |  | 
| 31 31 | 
             
                  included do
         | 
| 32 | 
            -
                    attribute_method_suffix "_before_type_cast"
         | 
| 32 | 
            +
                    attribute_method_suffix "_before_type_cast", "_for_database"
         | 
| 33 33 | 
             
                    attribute_method_suffix "_came_from_user?"
         | 
| 34 34 | 
             
                  end
         | 
| 35 35 |  | 
| @@ -46,7 +46,10 @@ module ActiveRecord | |
| 46 46 | 
             
                  #   task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
         | 
| 47 47 | 
             
                  #   task.read_attribute_before_type_cast(:completed_on)  # => "2012-10-21"
         | 
| 48 48 | 
             
                  def read_attribute_before_type_cast(attr_name)
         | 
| 49 | 
            -
                     | 
| 49 | 
            +
                    name = attr_name.to_s
         | 
| 50 | 
            +
                    name = self.class.attribute_aliases[name] || name
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    attribute_before_type_cast(name)
         | 
| 50 53 | 
             
                  end
         | 
| 51 54 |  | 
| 52 55 | 
             
                  # Returns a hash of attributes before typecasting and deserialization.
         | 
| @@ -64,14 +67,17 @@ module ActiveRecord | |
| 64 67 | 
             
                  end
         | 
| 65 68 |  | 
| 66 69 | 
             
                  private
         | 
| 70 | 
            +
                    # Dispatch target for <tt>*_before_type_cast</tt> attribute methods.
         | 
| 71 | 
            +
                    def attribute_before_type_cast(attr_name)
         | 
| 72 | 
            +
                      @attributes[attr_name].value_before_type_cast
         | 
| 73 | 
            +
                    end
         | 
| 67 74 |  | 
| 68 | 
            -
                     | 
| 69 | 
            -
             | 
| 70 | 
            -
                      read_attribute_before_type_cast(attribute_name)
         | 
| 75 | 
            +
                    def attribute_for_database(attr_name)
         | 
| 76 | 
            +
                      @attributes[attr_name].value_for_database
         | 
| 71 77 | 
             
                    end
         | 
| 72 78 |  | 
| 73 | 
            -
                    def attribute_came_from_user?( | 
| 74 | 
            -
                      @attributes[ | 
| 79 | 
            +
                    def attribute_came_from_user?(attr_name)
         | 
| 80 | 
            +
                      @attributes[attr_name].came_from_user?
         | 
| 75 81 | 
             
                    end
         | 
| 76 82 | 
             
                end
         | 
| 77 83 | 
             
              end
         |