activerecord 7.1.3.4 → 7.2.0.beta1
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 +507 -2133
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +9 -8
- data/lib/active_record/associations/belongs_to_association.rb +18 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/collection_association.rb +4 -2
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/has_many_association.rb +3 -3
- data/lib/active_record/associations/has_one_association.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +5 -7
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +2 -1
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +34 -11
- data/lib/active_record/attribute_assignment.rb +1 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +1 -1
- data/lib/active_record/attribute_methods/primary_key.rb +23 -55
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
- data/lib/active_record/attribute_methods.rb +87 -58
- data/lib/active_record/attributes.rb +55 -42
- data/lib/active_record/autosave_association.rb +14 -30
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +248 -58
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +160 -75
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +22 -9
- data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
- data/lib/active_record/connection_adapters/abstract_adapter.rb +32 -61
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +69 -19
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -32
- data/lib/active_record/connection_adapters/pool_config.rb +7 -6
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +16 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
- data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +109 -77
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +32 -65
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +56 -41
- data/lib/active_record/core.rb +59 -38
- data/lib/active_record/counter_cache.rb +23 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
- data/lib/active_record/database_configurations/database_config.rb +15 -4
- data/lib/active_record/database_configurations/hash_config.rb +44 -36
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +30 -6
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +2 -2
- data/lib/active_record/encryption/encrypted_attribute_type.rb +24 -4
- data/lib/active_record/encryption/encryptor.rb +17 -2
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +8 -4
- data/lib/active_record/enum.rb +11 -2
- data/lib/active_record/errors.rb +16 -11
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +17 -4
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +18 -15
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/marshalling.rb +1 -1
- data/lib/active_record/message_pack.rb +2 -2
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +11 -3
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +85 -76
- data/lib/active_record/model_schema.rb +34 -69
- data/lib/active_record/nested_attributes.rb +11 -3
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +32 -354
- data/lib/active_record/query_cache.rb +18 -6
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/query_logs_formatter.rb +1 -1
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +52 -64
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +41 -44
- data/lib/active_record/reflection.rb +98 -37
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +94 -61
- data/lib/active_record/relation/delegation.rb +8 -11
- data/lib/active_record/relation/finder_methods.rb +16 -2
- data/lib/active_record/relation/merger.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
- data/lib/active_record/relation/predicate_builder.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +196 -43
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +2 -18
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +500 -66
- data/lib/active_record/result.rb +32 -45
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +24 -19
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +19 -9
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/signed_id.rb +11 -1
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/table_metadata.rb +1 -10
- data/lib/active_record/tasks/database_tasks.rb +70 -42
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
- data/lib/active_record/test_fixtures.rb +82 -91
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +68 -0
- data/lib/active_record/transactions.rb +43 -14
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +14 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +149 -40
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/nodes/binary.rb +0 -6
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +4 -3
- data/lib/arel/nodes/sql_literal.rb +7 -0
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/to_sql.rb +31 -17
- data/lib/arel.rb +7 -3
- metadata +17 -12
| @@ -1,6 +1,5 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require "mutex_m"
         | 
| 4 3 | 
             
            require "active_support/core_ext/enumerable"
         | 
| 5 4 |  | 
| 6 5 | 
             
            module ActiveRecord
         | 
| @@ -21,10 +20,10 @@ module ActiveRecord | |
| 21 20 | 
             
                  include Serialization
         | 
| 22 21 | 
             
                end
         | 
| 23 22 |  | 
| 24 | 
            -
                RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name  | 
| 23 | 
            +
                RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name superclass)
         | 
| 25 24 |  | 
| 26 25 | 
             
                class GeneratedAttributeMethods < Module # :nodoc:
         | 
| 27 | 
            -
                   | 
| 26 | 
            +
                  LOCK = Monitor.new
         | 
| 28 27 | 
             
                end
         | 
| 29 28 |  | 
| 30 29 | 
             
                class << self
         | 
| @@ -50,6 +49,20 @@ module ActiveRecord | |
| 50 49 | 
             
                    super
         | 
| 51 50 | 
             
                  end
         | 
| 52 51 |  | 
| 52 | 
            +
                  # Allows you to make aliases for attributes.
         | 
| 53 | 
            +
                  #
         | 
| 54 | 
            +
                  #   class Person < ActiveRecord::Base
         | 
| 55 | 
            +
                  #     alias_attribute :nickname, :name
         | 
| 56 | 
            +
                  #   end
         | 
| 57 | 
            +
                  #
         | 
| 58 | 
            +
                  #   person = Person.create(name: 'Bob')
         | 
| 59 | 
            +
                  #   person.name     # => "Bob"
         | 
| 60 | 
            +
                  #   person.nickname # => "Bob"
         | 
| 61 | 
            +
                  #
         | 
| 62 | 
            +
                  # The alias can also be used for querying:
         | 
| 63 | 
            +
                  #
         | 
| 64 | 
            +
                  #   Person.where(nickname: "Bob")
         | 
| 65 | 
            +
                  #   # SELECT "people".* FROM "people" WHERE "people"."name" = "Bob"
         | 
| 53 66 | 
             
                  def alias_attribute(new_name, old_name)
         | 
| 54 67 | 
             
                    super
         | 
| 55 68 |  | 
| @@ -64,80 +77,69 @@ module ActiveRecord | |
| 64 77 | 
             
                    # alias attributes in Active Record are lazily generated
         | 
| 65 78 | 
             
                  end
         | 
| 66 79 |  | 
| 67 | 
            -
                  def generate_alias_attributes # :nodoc:
         | 
| 68 | 
            -
                    superclass.generate_alias_attributes unless superclass == Base
         | 
| 69 | 
            -
                    return if @alias_attributes_mass_generated
         | 
| 70 | 
            -
             | 
| 71 | 
            -
                    generated_attribute_methods.synchronize do
         | 
| 72 | 
            -
                      return if @alias_attributes_mass_generated
         | 
| 73 | 
            -
                      ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
         | 
| 74 | 
            -
                        aliases_by_attribute_name.each do |old_name, new_names|
         | 
| 75 | 
            -
                          new_names.each do |new_name|
         | 
| 76 | 
            -
                            generate_alias_attribute_methods(code_generator, new_name, old_name)
         | 
| 77 | 
            -
                          end
         | 
| 78 | 
            -
                        end
         | 
| 79 | 
            -
                      end
         | 
| 80 | 
            -
             | 
| 81 | 
            -
                      @alias_attributes_mass_generated = true
         | 
| 82 | 
            -
                    end
         | 
| 83 | 
            -
                  end
         | 
| 84 | 
            -
             | 
| 85 80 | 
             
                  def alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
         | 
| 86 | 
            -
                    method_name = pattern.method_name(new_name).to_s
         | 
| 87 | 
            -
                    target_name = pattern.method_name(old_name).to_s
         | 
| 88 | 
            -
                    parameters = pattern.parameters
         | 
| 89 81 | 
             
                    old_name = old_name.to_s
         | 
| 90 82 |  | 
| 91 | 
            -
                    method_defined = method_defined?(target_name) || private_method_defined?(target_name)
         | 
| 92 | 
            -
                    manually_defined = method_defined &&
         | 
| 93 | 
            -
                      !self.instance_method(target_name).owner.is_a?(GeneratedAttributeMethods)
         | 
| 94 | 
            -
                    reserved_method_name = ::ActiveRecord::AttributeMethods.dangerous_attribute_methods.include?(target_name)
         | 
| 95 | 
            -
             | 
| 96 83 | 
             
                    if !abstract_class? && !has_attribute?(old_name)
         | 
| 97 | 
            -
                       | 
| 98 | 
            -
             | 
| 99 | 
            -
                      if should_warn
         | 
| 100 | 
            -
                        ActiveRecord.deprecator.warn(
         | 
| 101 | 
            -
                          "#{self} model aliases `#{old_name}`, but `#{old_name}` is not an attribute. " \
         | 
| 102 | 
            -
                          "Starting in Rails 7.2, alias_attribute with non-attribute targets will raise. " \
         | 
| 103 | 
            -
                          "Use `alias_method :#{new_name}, :#{old_name}` or define the method manually."
         | 
| 104 | 
            -
                        )
         | 
| 105 | 
            -
                      end
         | 
| 106 | 
            -
                      super
         | 
| 107 | 
            -
                    elsif manually_defined && !reserved_method_name
         | 
| 108 | 
            -
                      aliased_method_redefined_as_well = method_defined_within?(method_name, self)
         | 
| 109 | 
            -
                      return if aliased_method_redefined_as_well
         | 
| 110 | 
            -
             | 
| 111 | 
            -
                      ActiveRecord.deprecator.warn(
         | 
| 112 | 
            -
                        "#{self} model aliases `#{old_name}` and has a method called `#{target_name}` defined. " \
         | 
| 113 | 
            -
                        "Starting in Rails 7.2 `#{method_name}` will not be calling `#{target_name}` anymore. " \
         | 
| 114 | 
            -
                        "You may want to additionally define `#{method_name}` to preserve the current behavior."
         | 
| 115 | 
            -
                      )
         | 
| 116 | 
            -
                      super
         | 
| 84 | 
            +
                      raise ArgumentError, "#{self.name} model aliases `#{old_name}`, but `#{old_name}` is not an attribute. " \
         | 
| 85 | 
            +
                        "Use `alias_method :#{new_name}, :#{old_name}` or define the method manually."
         | 
| 117 86 | 
             
                    else
         | 
| 87 | 
            +
                      method_name = pattern.method_name(new_name).to_s
         | 
| 88 | 
            +
                      parameters = pattern.parameters
         | 
| 89 | 
            +
             | 
| 118 90 | 
             
                      define_proxy_call(code_generator, method_name, pattern.proxy_target, parameters, old_name,
         | 
| 119 91 | 
             
                        namespace: :proxy_alias_attribute
         | 
| 120 92 | 
             
                      )
         | 
| 121 93 | 
             
                    end
         | 
| 122 94 | 
             
                  end
         | 
| 123 95 |  | 
| 96 | 
            +
                  def attribute_methods_generated? # :nodoc:
         | 
| 97 | 
            +
                    @attribute_methods_generated
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
             | 
| 124 100 | 
             
                  # Generates all the attribute related methods for columns in the database
         | 
| 125 101 | 
             
                  # accessors, mutators and query methods.
         | 
| 126 102 | 
             
                  def define_attribute_methods # :nodoc:
         | 
| 127 103 | 
             
                    return false if @attribute_methods_generated
         | 
| 128 104 | 
             
                    # Use a mutex; we don't want two threads simultaneously trying to define
         | 
| 129 105 | 
             
                    # attribute methods.
         | 
| 130 | 
            -
                     | 
| 106 | 
            +
                    GeneratedAttributeMethods::LOCK.synchronize do
         | 
| 131 107 | 
             
                      return false if @attribute_methods_generated
         | 
| 108 | 
            +
             | 
| 132 109 | 
             
                      superclass.define_attribute_methods unless base_class?
         | 
| 133 | 
            -
             | 
| 110 | 
            +
             | 
| 111 | 
            +
                      unless abstract_class?
         | 
| 112 | 
            +
                        load_schema
         | 
| 113 | 
            +
                        super(attribute_names)
         | 
| 114 | 
            +
                        alias_attribute :id_value, :id if _has_attribute?("id")
         | 
| 115 | 
            +
                      end
         | 
| 116 | 
            +
             | 
| 134 117 | 
             
                      @attribute_methods_generated = true
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                      generate_alias_attributes
         | 
| 120 | 
            +
                    end
         | 
| 121 | 
            +
                    true
         | 
| 122 | 
            +
                  end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                  def generate_alias_attributes # :nodoc:
         | 
| 125 | 
            +
                    superclass.generate_alias_attributes unless superclass == Base
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                    return if @alias_attributes_mass_generated
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                    ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
         | 
| 130 | 
            +
                      aliases_by_attribute_name.each do |old_name, new_names|
         | 
| 131 | 
            +
                        new_names.each do |new_name|
         | 
| 132 | 
            +
                          generate_alias_attribute_methods(code_generator, new_name, old_name)
         | 
| 133 | 
            +
                        end
         | 
| 134 | 
            +
                      end
         | 
| 135 135 | 
             
                    end
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                    @alias_attributes_mass_generated = true
         | 
| 136 138 | 
             
                  end
         | 
| 137 139 |  | 
| 138 140 | 
             
                  def undefine_attribute_methods # :nodoc:
         | 
| 139 | 
            -
                     | 
| 140 | 
            -
                      super if  | 
| 141 | 
            +
                    GeneratedAttributeMethods::LOCK.synchronize do
         | 
| 142 | 
            +
                      super if @attribute_methods_generated
         | 
| 141 143 | 
             
                      @attribute_methods_generated = false
         | 
| 142 144 | 
             
                      @alias_attributes_mass_generated = false
         | 
| 143 145 | 
             
                    end
         | 
| @@ -288,9 +290,7 @@ module ActiveRecord | |
| 288 290 |  | 
| 289 291 | 
             
                  # If the result is true then check for the select case.
         | 
| 290 292 | 
             
                  # For queries selecting a subset of columns, return false for unselected columns.
         | 
| 291 | 
            -
                   | 
| 292 | 
            -
                  # have been allocated but not yet initialized.
         | 
| 293 | 
            -
                  if defined?(@attributes)
         | 
| 293 | 
            +
                  if @attributes
         | 
| 294 294 | 
             
                    if name = self.class.symbol_column_to_string(name.to_sym)
         | 
| 295 295 | 
             
                      return _has_attribute?(name)
         | 
| 296 296 | 
             
                    end
         | 
| @@ -459,9 +459,38 @@ module ActiveRecord | |
| 459 459 | 
             
                end
         | 
| 460 460 |  | 
| 461 461 | 
             
                private
         | 
| 462 | 
            +
                  def respond_to_missing?(name, include_private = false)
         | 
| 463 | 
            +
                    if self.class.define_attribute_methods
         | 
| 464 | 
            +
                      # Some methods weren't defined yet.
         | 
| 465 | 
            +
                      return true if self.class.method_defined?(name)
         | 
| 466 | 
            +
                      return true if include_private && self.class.private_method_defined?(name)
         | 
| 467 | 
            +
                    end
         | 
| 468 | 
            +
             | 
| 469 | 
            +
                    super
         | 
| 470 | 
            +
                  end
         | 
| 471 | 
            +
             | 
| 472 | 
            +
                  def method_missing(name, ...)
         | 
| 473 | 
            +
                    unless self.class.attribute_methods_generated?
         | 
| 474 | 
            +
                      if self.class.method_defined?(name)
         | 
| 475 | 
            +
                        # The method is explicitly defined in the model, but calls a generated
         | 
| 476 | 
            +
                        # method with super. So we must resume the call chain at the right step.
         | 
| 477 | 
            +
                        last_method = method(name)
         | 
| 478 | 
            +
                        last_method = last_method.super_method while last_method.super_method
         | 
| 479 | 
            +
                        self.class.define_attribute_methods
         | 
| 480 | 
            +
                        if last_method.super_method
         | 
| 481 | 
            +
                          return last_method.super_method.call(...)
         | 
| 482 | 
            +
                        end
         | 
| 483 | 
            +
                      elsif self.class.define_attribute_methods
         | 
| 484 | 
            +
                        # Some attribute methods weren't generated yet, we retry the call
         | 
| 485 | 
            +
                        return public_send(name, ...)
         | 
| 486 | 
            +
                      end
         | 
| 487 | 
            +
                    end
         | 
| 488 | 
            +
             | 
| 489 | 
            +
                    super
         | 
| 490 | 
            +
                  end
         | 
| 491 | 
            +
             | 
| 462 492 | 
             
                  def attribute_method?(attr_name)
         | 
| 463 | 
            -
                     | 
| 464 | 
            -
                    defined?(@attributes) && @attributes.key?(attr_name)
         | 
| 493 | 
            +
                    @attributes&.key?(attr_name)
         | 
| 465 494 | 
             
                  end
         | 
| 466 495 |  | 
| 467 496 | 
             
                  def attributes_with_values(attribute_names)
         | 
| @@ -6,12 +6,13 @@ module ActiveRecord | |
| 6 6 | 
             
              # See ActiveRecord::Attributes::ClassMethods for documentation
         | 
| 7 7 | 
             
              module Attributes
         | 
| 8 8 | 
             
                extend ActiveSupport::Concern
         | 
| 9 | 
            +
                include ActiveModel::AttributeRegistration
         | 
| 9 10 |  | 
| 10 | 
            -
                included do
         | 
| 11 | 
            -
                  class_attribute :attributes_to_define_after_schema_loads, instance_accessor: false, default: {} # :internal:
         | 
| 12 | 
            -
                end
         | 
| 13 11 | 
             
                # = Active Record \Attributes
         | 
| 14 12 | 
             
                module ClassMethods
         | 
| 13 | 
            +
                  # :method: attribute
         | 
| 14 | 
            +
                  # :call-seq: attribute(name, cast_type = nil, **options)
         | 
| 15 | 
            +
                  #
         | 
| 15 16 | 
             
                  # Defines an attribute with a type on this model. It will override the
         | 
| 16 17 | 
             
                  # type of existing attributes if needed. This allows control over how
         | 
| 17 18 | 
             
                  # values are converted to and from SQL when assigned to a model. It also
         | 
| @@ -134,7 +135,7 @@ module ActiveRecord | |
| 134 135 | 
             
                  # expected API. It is recommended that your type objects inherit from an
         | 
| 135 136 | 
             
                  # existing type, or from ActiveRecord::Type::Value
         | 
| 136 137 | 
             
                  #
         | 
| 137 | 
            -
                  #   class  | 
| 138 | 
            +
                  #   class PriceType < ActiveRecord::Type::Integer
         | 
| 138 139 | 
             
                  #     def cast(value)
         | 
| 139 140 | 
             
                  #       if !value.kind_of?(Numeric) && value.include?('$')
         | 
| 140 141 | 
             
                  #         price_in_dollars = value.gsub(/\$/, '').to_f
         | 
| @@ -146,11 +147,11 @@ module ActiveRecord | |
| 146 147 | 
             
                  #   end
         | 
| 147 148 | 
             
                  #
         | 
| 148 149 | 
             
                  #   # config/initializers/types.rb
         | 
| 149 | 
            -
                  #   ActiveRecord::Type.register(: | 
| 150 | 
            +
                  #   ActiveRecord::Type.register(:price, PriceType)
         | 
| 150 151 | 
             
                  #
         | 
| 151 152 | 
             
                  #   # app/models/store_listing.rb
         | 
| 152 153 | 
             
                  #   class StoreListing < ActiveRecord::Base
         | 
| 153 | 
            -
                  #     attribute :price_in_cents, : | 
| 154 | 
            +
                  #     attribute :price_in_cents, :price
         | 
| 154 155 | 
             
                  #   end
         | 
| 155 156 | 
             
                  #
         | 
| 156 157 | 
             
                  #   store_listing = StoreListing.new(price_in_cents: '$10.00')
         | 
| @@ -170,7 +171,7 @@ module ActiveRecord | |
| 170 171 | 
             
                  #   class Money < Struct.new(:amount, :currency)
         | 
| 171 172 | 
             
                  #   end
         | 
| 172 173 | 
             
                  #
         | 
| 173 | 
            -
                  #   class  | 
| 174 | 
            +
                  #   class PriceType < ActiveRecord::Type::Value
         | 
| 174 175 | 
             
                  #     def initialize(currency_converter:)
         | 
| 175 176 | 
             
                  #       @currency_converter = currency_converter
         | 
| 176 177 | 
             
                  #     end
         | 
| @@ -185,12 +186,12 @@ module ActiveRecord | |
| 185 186 | 
             
                  #   end
         | 
| 186 187 | 
             
                  #
         | 
| 187 188 | 
             
                  #   # config/initializers/types.rb
         | 
| 188 | 
            -
                  #   ActiveRecord::Type.register(: | 
| 189 | 
            +
                  #   ActiveRecord::Type.register(:price, PriceType)
         | 
| 189 190 | 
             
                  #
         | 
| 190 191 | 
             
                  #   # app/models/product.rb
         | 
| 191 192 | 
             
                  #   class Product < ActiveRecord::Base
         | 
| 192 193 | 
             
                  #     currency_converter = ConversionRatesFromTheInternet.new
         | 
| 193 | 
            -
                  #     attribute :price_in_bitcoins, : | 
| 194 | 
            +
                  #     attribute :price_in_bitcoins, :price, currency_converter: currency_converter
         | 
| 194 195 | 
             
                  #   end
         | 
| 195 196 | 
             
                  #
         | 
| 196 197 | 
             
                  #   Product.where(price_in_bitcoins: Money.new(5, "USD"))
         | 
| @@ -205,37 +206,13 @@ module ActiveRecord | |
| 205 206 | 
             
                  # tracking is performed. The methods +changed?+ and +changed_in_place?+
         | 
| 206 207 | 
             
                  # will be called from ActiveModel::Dirty. See the documentation for those
         | 
| 207 208 | 
             
                  # methods in ActiveModel::Type::Value for more details.
         | 
| 208 | 
            -
                   | 
| 209 | 
            -
             | 
| 210 | 
            -
             | 
| 211 | 
            -
             | 
| 212 | 
            -
                    reload_schema_from_cache
         | 
| 213 | 
            -
             | 
| 214 | 
            -
                    case cast_type
         | 
| 215 | 
            -
                    when Symbol
         | 
| 216 | 
            -
                      cast_type = Type.lookup(cast_type, **options, adapter: Type.adapter_name_from(self))
         | 
| 217 | 
            -
                    when nil
         | 
| 218 | 
            -
                      if (prev_cast_type, prev_default = attributes_to_define_after_schema_loads[name])
         | 
| 219 | 
            -
                        default = prev_default if default == NO_DEFAULT_PROVIDED
         | 
| 220 | 
            -
                      else
         | 
| 221 | 
            -
                        prev_cast_type = -> subtype { subtype }
         | 
| 222 | 
            -
                      end
         | 
| 223 | 
            -
             | 
| 224 | 
            -
                      cast_type = if block_given?
         | 
| 225 | 
            -
                        -> subtype { yield Proc === prev_cast_type ? prev_cast_type[subtype] : prev_cast_type }
         | 
| 226 | 
            -
                      else
         | 
| 227 | 
            -
                        prev_cast_type
         | 
| 228 | 
            -
                      end
         | 
| 229 | 
            -
                    end
         | 
| 230 | 
            -
             | 
| 231 | 
            -
                    self.attributes_to_define_after_schema_loads =
         | 
| 232 | 
            -
                      attributes_to_define_after_schema_loads.merge(name => [cast_type, default])
         | 
| 233 | 
            -
                  end
         | 
| 209 | 
            +
                  #
         | 
| 210 | 
            +
                  #--
         | 
| 211 | 
            +
                  # Implemented by ActiveModel::AttributeRegistration#attribute.
         | 
| 234 212 |  | 
| 235 213 | 
             
                  # This is the low level API which sits beneath +attribute+. It only
         | 
| 236 214 | 
             
                  # accepts type objects, and will do its work immediately instead of
         | 
| 237 | 
            -
                  # waiting for the schema to load.  | 
| 238 | 
            -
                  # ClassMethods#attribute both call this under the hood. While this method
         | 
| 215 | 
            +
                  # waiting for the schema to load. While this method
         | 
| 239 216 | 
             
                  # is provided so it can be used by plugin authors, application code
         | 
| 240 217 | 
             
                  # should probably use ClassMethods#attribute.
         | 
| 241 218 | 
             
                  #
         | 
| @@ -260,14 +237,38 @@ module ActiveRecord | |
| 260 237 | 
             
                    define_default_attribute(name, default, cast_type, from_user: user_provided_default)
         | 
| 261 238 | 
             
                  end
         | 
| 262 239 |  | 
| 263 | 
            -
                  def  | 
| 264 | 
            -
                     | 
| 265 | 
            -
             | 
| 266 | 
            -
             | 
| 267 | 
            -
             | 
| 240 | 
            +
                  def _default_attributes # :nodoc:
         | 
| 241 | 
            +
                    @default_attributes ||= begin
         | 
| 242 | 
            +
                      attributes_hash = with_connection do |connection|
         | 
| 243 | 
            +
                        columns_hash.transform_values do |column|
         | 
| 244 | 
            +
                          ActiveModel::Attribute.from_database(column.name, column.default, type_for_column(connection, column))
         | 
| 245 | 
            +
                        end
         | 
| 246 | 
            +
                      end
         | 
| 247 | 
            +
             | 
| 248 | 
            +
                      attribute_set = ActiveModel::AttributeSet.new(attributes_hash)
         | 
| 249 | 
            +
                      apply_pending_attribute_modifications(attribute_set)
         | 
| 250 | 
            +
                      attribute_set
         | 
| 268 251 | 
             
                    end
         | 
| 269 252 | 
             
                  end
         | 
| 270 253 |  | 
| 254 | 
            +
                  ##
         | 
| 255 | 
            +
                  # :method: type_for_attribute
         | 
| 256 | 
            +
                  # :call-seq: type_for_attribute(attribute_name, &block)
         | 
| 257 | 
            +
                  #
         | 
| 258 | 
            +
                  # See ActiveModel::Attributes::ClassMethods#type_for_attribute.
         | 
| 259 | 
            +
                  #
         | 
| 260 | 
            +
                  # This method will access the database and load the model's schema if
         | 
| 261 | 
            +
                  # necessary.
         | 
| 262 | 
            +
                  #--
         | 
| 263 | 
            +
                  # Implemented by ActiveModel::AttributeRegistration::ClassMethods#type_for_attribute.
         | 
| 264 | 
            +
             | 
| 265 | 
            +
                  ##
         | 
| 266 | 
            +
                  protected
         | 
| 267 | 
            +
                    def reload_schema_from_cache(*)
         | 
| 268 | 
            +
                      reset_default_attributes!
         | 
| 269 | 
            +
                      super
         | 
| 270 | 
            +
                    end
         | 
| 271 | 
            +
             | 
| 271 272 | 
             
                  private
         | 
| 272 273 | 
             
                    NO_DEFAULT_PROVIDED = Object.new # :nodoc:
         | 
| 273 274 | 
             
                    private_constant :NO_DEFAULT_PROVIDED
         | 
| @@ -287,6 +288,18 @@ module ActiveRecord | |
| 287 288 | 
             
                      end
         | 
| 288 289 | 
             
                      _default_attributes[name] = default_attribute
         | 
| 289 290 | 
             
                    end
         | 
| 291 | 
            +
             | 
| 292 | 
            +
                    def reset_default_attributes
         | 
| 293 | 
            +
                      reload_schema_from_cache
         | 
| 294 | 
            +
                    end
         | 
| 295 | 
            +
             | 
| 296 | 
            +
                    def resolve_type_name(name, **options)
         | 
| 297 | 
            +
                      Type.lookup(name, **options, adapter: Type.adapter_name_from(self))
         | 
| 298 | 
            +
                    end
         | 
| 299 | 
            +
             | 
| 300 | 
            +
                    def type_for_column(connection, column)
         | 
| 301 | 
            +
                      hook_attribute_type(column.name, super)
         | 
| 302 | 
            +
                    end
         | 
| 290 303 | 
             
                end
         | 
| 291 304 | 
             
              end
         | 
| 292 305 | 
             
            end
         | 
| @@ -1,5 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require "active_record/associations/nested_error"
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            module ActiveRecord
         | 
| 4 6 | 
             
              # = Active Record Autosave Association
         | 
| 5 7 | 
             
              #
         | 
| @@ -315,7 +317,7 @@ module ActiveRecord | |
| 315 317 | 
             
                  def validate_single_association(reflection)
         | 
| 316 318 | 
             
                    association = association_instance_get(reflection.name)
         | 
| 317 319 | 
             
                    record      = association && association.reader
         | 
| 318 | 
            -
                    association_valid?( | 
| 320 | 
            +
                    association_valid?(association, record) if record && (record.changed_for_autosave? || custom_validation_context?)
         | 
| 319 321 | 
             
                  end
         | 
| 320 322 |  | 
| 321 323 | 
             
                  # Validate the associated records if <tt>:validate</tt> or
         | 
| @@ -324,7 +326,7 @@ module ActiveRecord | |
| 324 326 | 
             
                  def validate_collection_association(reflection)
         | 
| 325 327 | 
             
                    if association = association_instance_get(reflection.name)
         | 
| 326 328 | 
             
                      if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
         | 
| 327 | 
            -
                        records. | 
| 329 | 
            +
                        records.each { |record| association_valid?(association, record) }
         | 
| 328 330 | 
             
                      end
         | 
| 329 331 | 
             
                    end
         | 
| 330 332 | 
             
                  end
         | 
| @@ -332,40 +334,25 @@ module ActiveRecord | |
| 332 334 | 
             
                  # Returns whether or not the association is valid and applies any errors to
         | 
| 333 335 | 
             
                  # the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
         | 
| 334 336 | 
             
                  # enabled records if they're marked_for_destruction? or destroyed.
         | 
| 335 | 
            -
                  def association_valid?( | 
| 336 | 
            -
                    return true if record.destroyed? || ( | 
| 337 | 
            +
                  def association_valid?(association, record)
         | 
| 338 | 
            +
                    return true if record.destroyed? || (association.options[:autosave] && record.marked_for_destruction?)
         | 
| 337 339 |  | 
| 338 340 | 
             
                    context = validation_context if custom_validation_context?
         | 
| 339 341 |  | 
| 340 342 | 
             
                    unless valid = record.valid?(context)
         | 
| 341 | 
            -
                      if  | 
| 342 | 
            -
                         | 
| 343 | 
            -
             | 
| 344 | 
            -
             | 
| 345 | 
            -
                           | 
| 346 | 
            -
             | 
| 347 | 
            -
                          errors.each { |error|
         | 
| 348 | 
            -
                            self.errors.import(
         | 
| 349 | 
            -
                              error,
         | 
| 350 | 
            -
                              attribute: attribute
         | 
| 351 | 
            -
                            )
         | 
| 352 | 
            -
                          }
         | 
| 343 | 
            +
                      if association.options[:autosave]
         | 
| 344 | 
            +
                        record.errors.each { |error|
         | 
| 345 | 
            +
                          self.errors.objects.append(
         | 
| 346 | 
            +
                            Associations::NestedError.new(association, error)
         | 
| 347 | 
            +
                          )
         | 
| 353 348 | 
             
                        }
         | 
| 354 349 | 
             
                      else
         | 
| 355 | 
            -
                        errors.add(reflection.name)
         | 
| 350 | 
            +
                        errors.add(association.reflection.name)
         | 
| 356 351 | 
             
                      end
         | 
| 357 352 | 
             
                    end
         | 
| 358 353 | 
             
                    valid
         | 
| 359 354 | 
             
                  end
         | 
| 360 355 |  | 
| 361 | 
            -
                  def normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
         | 
| 362 | 
            -
                    if indexed_attribute
         | 
| 363 | 
            -
                      "#{reflection.name}[#{index}].#{attribute}"
         | 
| 364 | 
            -
                    else
         | 
| 365 | 
            -
                      "#{reflection.name}.#{attribute}"
         | 
| 366 | 
            -
                    end
         | 
| 367 | 
            -
                  end
         | 
| 368 | 
            -
             | 
| 369 356 | 
             
                  # Is used as an around_save callback to check while saving a collection
         | 
| 370 357 | 
             
                  # association whether or not the parent was a new record before saving.
         | 
| 371 358 | 
             
                  def around_save_collection_association
         | 
| @@ -458,7 +445,8 @@ module ActiveRecord | |
| 458 445 | 
             
                            primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
         | 
| 459 446 |  | 
| 460 447 | 
             
                            primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
         | 
| 461 | 
            -
                               | 
| 448 | 
            +
                              association_id = _read_attribute(primary_key)
         | 
| 449 | 
            +
                              record[foreign_key] = association_id unless record[foreign_key] == association_id
         | 
| 462 450 | 
             
                            end
         | 
| 463 451 | 
             
                            association.set_inverse_instance(record)
         | 
| 464 452 | 
             
                          end
         | 
| @@ -547,10 +535,6 @@ module ActiveRecord | |
| 547 535 | 
             
                    end
         | 
| 548 536 | 
             
                  end
         | 
| 549 537 |  | 
| 550 | 
            -
                  def custom_validation_context?
         | 
| 551 | 
            -
                    validation_context && [:create, :update].exclude?(validation_context)
         | 
| 552 | 
            -
                  end
         | 
| 553 | 
            -
             | 
| 554 538 | 
             
                  def _ensure_no_duplicate_errors
         | 
| 555 539 | 
             
                    errors.uniq!
         | 
| 556 540 | 
             
                  end
         | 
    
        data/lib/active_record/base.rb
    CHANGED
    
    | @@ -233,7 +233,7 @@ module ActiveRecord # :nodoc: | |
| 233 233 | 
             
              #
         | 
| 234 234 | 
             
              # Connections are usually created through
         | 
| 235 235 | 
             
              # {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection] and retrieved
         | 
| 236 | 
            -
              # by ActiveRecord::Base. | 
| 236 | 
            +
              # by ActiveRecord::Base.lease_connection. All classes inheriting from ActiveRecord::Base will use this
         | 
| 237 237 | 
             
              # connection. But you can also set a class-specific connection. For example, if Course is an
         | 
| 238 238 | 
             
              # ActiveRecord::Base, but resides in a different database, you can just say <tt>Course.establish_connection</tt>
         | 
| 239 239 | 
             
              # and Course and all of its subclasses will use this connection instead.
         | 
| @@ -280,7 +280,7 @@ module ActiveRecord # :nodoc: | |
| 280 280 | 
             
              # So it's possible to assign a logger to the class through <tt>Base.logger=</tt> which will then be used by all
         | 
| 281 281 | 
             
              # instances in the current object space.
         | 
| 282 282 | 
             
              class Base
         | 
| 283 | 
            -
                 | 
| 283 | 
            +
                include ActiveModel::API
         | 
| 284 284 |  | 
| 285 285 | 
             
                extend ActiveSupport::Benchmarkable
         | 
| 286 286 | 
             
                extend ActiveSupport::DescendantsTracker
         | 
| @@ -304,7 +304,6 @@ module ActiveRecord # :nodoc: | |
| 304 304 | 
             
                include Scoping
         | 
| 305 305 | 
             
                include Sanitization
         | 
| 306 306 | 
             
                include AttributeAssignment
         | 
| 307 | 
            -
                include ActiveModel::Conversion
         | 
| 308 307 | 
             
                include Integration
         | 
| 309 308 | 
             
                include Validations
         | 
| 310 309 | 
             
                include CounterCache
         |