activerecord 5.2.8 → 6.0.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 +299 -788
- data/MIT-LICENSE +3 -1
- data/README.rdoc +1 -1
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/associations/association.rb +35 -19
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/belongs_to.rb +14 -50
- data/lib/active_record/associations/builder/collection_association.rb +3 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/collection_association.rb +11 -25
- data/lib/active_record/associations/collection_proxy.rb +32 -6
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +25 -18
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/join_dependency.rb +15 -20
- data/lib/active_record/associations/preloader/association.rb +1 -2
- data/lib/active_record/associations/preloader.rb +32 -29
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/associations.rb +16 -12
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods/dirty.rb +64 -26
- data/lib/active_record/attribute_methods/primary_key.rb +8 -7
- data/lib/active_record/attribute_methods/read.rb +16 -48
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +15 -16
- data/lib/active_record/attribute_methods.rb +34 -56
- data/lib/active_record/autosave_association.rb +7 -21
- data/lib/active_record/base.rb +2 -2
- data/lib/active_record/callbacks.rb +3 -17
- data/lib/active_record/collection_cache_key.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
- data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
- data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
- data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
- data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
- data/lib/active_record/connection_handling.rb +132 -26
- data/lib/active_record/core.rb +76 -43
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +74 -0
- data/lib/active_record/database_configurations.rb +184 -0
- data/lib/active_record/enum.rb +22 -7
- data/lib/active_record/errors.rb +24 -21
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +140 -472
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +12 -2
- data/lib/active_record/integration.rb +56 -16
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +2 -2
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/migration/command_recorder.rb +35 -5
- data/lib/active_record/migration/compatibility.rb +34 -16
- data/lib/active_record/migration.rb +38 -37
- data/lib/active_record/model_schema.rb +30 -9
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +18 -7
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +19 -11
- data/lib/active_record/railtie.rb +71 -42
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +94 -43
- data/lib/active_record/reflection.rb +60 -44
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +38 -28
- data/lib/active_record/relation/delegation.rb +4 -13
- data/lib/active_record/relation/finder_methods.rb +12 -25
- data/lib/active_record/relation/merger.rb +2 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder.rb +4 -6
- data/lib/active_record/relation/query_attribute.rb +15 -12
- data/lib/active_record/relation/query_methods.rb +29 -52
- data/lib/active_record/relation/where_clause.rb +4 -0
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/relation.rb +150 -69
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +2 -39
- data/lib/active_record/schema.rb +1 -10
- data/lib/active_record/schema_dumper.rb +12 -6
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping/default.rb +10 -3
- data/lib/active_record/scoping/named.rb +10 -14
- data/lib/active_record/scoping.rb +9 -8
- data/lib/active_record/statement_cache.rb +32 -5
- data/lib/active_record/store.rb +39 -8
- data/lib/active_record/table_metadata.rb +1 -4
- data/lib/active_record/tasks/database_tasks.rb +89 -23
- data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +38 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/transactions.rb +3 -22
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type_caster/connection.rb +1 -6
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations/uniqueness.rb +13 -25
- data/lib/active_record.rb +2 -1
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -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 +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -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 +18 -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 +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -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 +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -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 +63 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -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.rb +16 -0
- data/lib/arel/nodes/values_list.rb +24 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +67 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/depth_first.rb +199 -0
- data/lib/arel/visitors/dot.rb +292 -0
- data/lib/arel/visitors/ibm_db.rb +21 -0
- data/lib/arel/visitors/informix.rb +56 -0
- data/lib/arel/visitors/mssql.rb +143 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +67 -0
- data/lib/arel/visitors/postgresql.rb +116 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +913 -0
- data/lib/arel/visitors/visitor.rb +42 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +44 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- metadata +104 -26
    
        data/MIT-LICENSE
    CHANGED
    
    | @@ -1,4 +1,6 @@ | |
| 1 | 
            -
            Copyright (c) 2004- | 
| 1 | 
            +
            Copyright (c) 2004-2019 David Heinemeier Hansson
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Arel originally copyright (c) 2007-2016 Nick Kallen, Bryan Helmkamp, Emilio Tagua, Aaron Patterson
         | 
| 2 4 |  | 
| 3 5 | 
             
            Permission is hereby granted, free of charge, to any person obtaining
         | 
| 4 6 | 
             
            a copy of this software and associated documentation files (the
         | 
    
        data/README.rdoc
    CHANGED
    
    | @@ -192,7 +192,7 @@ The latest version of Active Record can be installed with RubyGems: | |
| 192 192 |  | 
| 193 193 | 
             
            Source code can be downloaded as part of the Rails project on GitHub:
         | 
| 194 194 |  | 
| 195 | 
            -
            * https://github.com/rails/rails/tree/ | 
| 195 | 
            +
            * https://github.com/rails/rails/tree/master/activerecord
         | 
| 196 196 |  | 
| 197 197 |  | 
| 198 198 | 
             
            == License
         | 
    
        data/examples/performance.rb
    CHANGED
    
    
| @@ -3,8 +3,6 @@ | |
| 3 3 | 
             
            module ActiveRecord
         | 
| 4 4 | 
             
              # See ActiveRecord::Aggregations::ClassMethods for documentation
         | 
| 5 5 | 
             
              module Aggregations
         | 
| 6 | 
            -
                extend ActiveSupport::Concern
         | 
| 7 | 
            -
             | 
| 8 6 | 
             
                def initialize_dup(*) # :nodoc:
         | 
| 9 7 | 
             
                  @aggregation_cache = {}
         | 
| 10 8 | 
             
                  super
         | 
| @@ -225,6 +223,10 @@ module ActiveRecord | |
| 225 223 | 
             
                    def composed_of(part_id, options = {})
         | 
| 226 224 | 
             
                      options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
         | 
| 227 225 |  | 
| 226 | 
            +
                      unless self < Aggregations
         | 
| 227 | 
            +
                        include Aggregations
         | 
| 228 | 
            +
                      end
         | 
| 229 | 
            +
             | 
| 228 230 | 
             
                      name        = part_id.id2name
         | 
| 229 231 | 
             
                      class_name  = options[:class_name]  || name.camelize
         | 
| 230 232 | 
             
                      mapping     = options[:mapping]     || [ name, name ]
         | 
| @@ -40,7 +40,9 @@ module ActiveRecord | |
| 40 40 | 
             
                  end
         | 
| 41 41 |  | 
| 42 42 | 
             
                  # Reloads the \target and returns +self+ on success.
         | 
| 43 | 
            -
                   | 
| 43 | 
            +
                  # The QueryCache is cleared if +force+ is true.
         | 
| 44 | 
            +
                  def reload(force = false)
         | 
| 45 | 
            +
                    klass.connection.clear_query_cache if force && klass
         | 
| 44 46 | 
             
                    reset
         | 
| 45 47 | 
             
                    reset_scope
         | 
| 46 48 | 
             
                    load_target
         | 
| @@ -79,18 +81,6 @@ module ActiveRecord | |
| 79 81 | 
             
                    target_scope.merge!(association_scope)
         | 
| 80 82 | 
             
                  end
         | 
| 81 83 |  | 
| 82 | 
            -
                  # The scope for this association.
         | 
| 83 | 
            -
                  #
         | 
| 84 | 
            -
                  # Note that the association_scope is merged into the target_scope only when the
         | 
| 85 | 
            -
                  # scope method is called. This is because at that point the call may be surrounded
         | 
| 86 | 
            -
                  # by scope.scoping { ... } or with_scope { ... } etc, which affects the scope which
         | 
| 87 | 
            -
                  # actually gets built.
         | 
| 88 | 
            -
                  def association_scope
         | 
| 89 | 
            -
                    if klass
         | 
| 90 | 
            -
                      @association_scope ||= AssociationScope.scope(self)
         | 
| 91 | 
            -
                    end
         | 
| 92 | 
            -
                  end
         | 
| 93 | 
            -
             | 
| 94 84 | 
             
                  def reset_scope
         | 
| 95 85 | 
             
                    @association_scope = nil
         | 
| 96 86 | 
             
                  end
         | 
| @@ -129,12 +119,6 @@ module ActiveRecord | |
| 129 119 | 
             
                    reflection.klass
         | 
| 130 120 | 
             
                  end
         | 
| 131 121 |  | 
| 132 | 
            -
                  # Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
         | 
| 133 | 
            -
                  # through association's scope)
         | 
| 134 | 
            -
                  def target_scope
         | 
| 135 | 
            -
                    AssociationRelation.create(klass, self).merge!(klass.all)
         | 
| 136 | 
            -
                  end
         | 
| 137 | 
            -
             | 
| 138 122 | 
             
                  def extensions
         | 
| 139 123 | 
             
                    extensions = klass.default_extensions | reflection.extensions
         | 
| 140 124 |  | 
| @@ -195,6 +179,38 @@ module ActiveRecord | |
| 195 179 | 
             
                  end
         | 
| 196 180 |  | 
| 197 181 | 
             
                  private
         | 
| 182 | 
            +
                    def find_target
         | 
| 183 | 
            +
                      scope = self.scope
         | 
| 184 | 
            +
                      return scope.to_a if skip_statement_cache?(scope)
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                      conn = klass.connection
         | 
| 187 | 
            +
                      sc = reflection.association_scope_cache(conn, owner) do |params|
         | 
| 188 | 
            +
                        as = AssociationScope.create { params.bind }
         | 
| 189 | 
            +
                        target_scope.merge!(as.scope(self))
         | 
| 190 | 
            +
                      end
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                      binds = AssociationScope.get_bind_values(owner, reflection.chain)
         | 
| 193 | 
            +
                      sc.execute(binds, conn) { |record| set_inverse_instance(record) } || []
         | 
| 194 | 
            +
                    end
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                    # The scope for this association.
         | 
| 197 | 
            +
                    #
         | 
| 198 | 
            +
                    # Note that the association_scope is merged into the target_scope only when the
         | 
| 199 | 
            +
                    # scope method is called. This is because at that point the call may be surrounded
         | 
| 200 | 
            +
                    # by scope.scoping { ... } or unscoped { ... } etc, which affects the scope which
         | 
| 201 | 
            +
                    # actually gets built.
         | 
| 202 | 
            +
                    def association_scope
         | 
| 203 | 
            +
                      if klass
         | 
| 204 | 
            +
                        @association_scope ||= AssociationScope.scope(self)
         | 
| 205 | 
            +
                      end
         | 
| 206 | 
            +
                    end
         | 
| 207 | 
            +
             | 
| 208 | 
            +
                    # Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
         | 
| 209 | 
            +
                    # through association's scope)
         | 
| 210 | 
            +
                    def target_scope
         | 
| 211 | 
            +
                      AssociationRelation.create(klass, self).merge!(klass.all)
         | 
| 212 | 
            +
                    end
         | 
| 213 | 
            +
             | 
| 198 214 | 
             
                    def scope_for_create
         | 
| 199 215 | 
             
                      scope.scope_for_create
         | 
| 200 216 | 
             
                    end
         | 
| @@ -26,7 +26,9 @@ module ActiveRecord | |
| 26 26 | 
             
                    chain = get_chain(reflection, association, scope.alias_tracker)
         | 
| 27 27 |  | 
| 28 28 | 
             
                    scope.extending! reflection.extensions
         | 
| 29 | 
            -
                    add_constraints(scope, owner, chain)
         | 
| 29 | 
            +
                    scope = add_constraints(scope, owner, chain)
         | 
| 30 | 
            +
                    scope.limit!(1) unless reflection.collection?
         | 
| 31 | 
            +
                    scope
         | 
| 30 32 | 
             
                  end
         | 
| 31 33 |  | 
| 32 34 | 
             
                  def self.get_bind_values(owner, chain)
         | 
| @@ -46,13 +48,9 @@ module ActiveRecord | |
| 46 48 | 
             
                    binds
         | 
| 47 49 | 
             
                  end
         | 
| 48 50 |  | 
| 49 | 
            -
                   | 
| 50 | 
            -
                  # Workaround for Ruby 2.2 "private attribute?" warning.
         | 
| 51 | 
            -
                  protected
         | 
| 52 | 
            -
             | 
| 51 | 
            +
                  private
         | 
| 53 52 | 
             
                    attr_reader :value_transformation
         | 
| 54 53 |  | 
| 55 | 
            -
                  private
         | 
| 56 54 | 
             
                    def join(table, constraint)
         | 
| 57 55 | 
             
                      table.create_join(table, table.create_on(constraint))
         | 
| 58 56 | 
             
                    end
         | 
| @@ -16,21 +16,6 @@ module ActiveRecord | |
| 16 16 | 
             
                    end
         | 
| 17 17 | 
             
                  end
         | 
| 18 18 |  | 
| 19 | 
            -
                  def replace(record)
         | 
| 20 | 
            -
                    if record
         | 
| 21 | 
            -
                      raise_on_type_mismatch!(record)
         | 
| 22 | 
            -
                      update_counters_on_replace(record)
         | 
| 23 | 
            -
                      set_inverse_instance(record)
         | 
| 24 | 
            -
                      @updated = true
         | 
| 25 | 
            -
                    else
         | 
| 26 | 
            -
                      decrement_counters
         | 
| 27 | 
            -
                    end
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                    replace_keys(record)
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                    self.target = record
         | 
| 32 | 
            -
                  end
         | 
| 33 | 
            -
             | 
| 34 19 | 
             
                  def inversed_from(record)
         | 
| 35 20 | 
             
                    replace_keys(record)
         | 
| 36 21 | 
             
                    super
         | 
| @@ -49,30 +34,60 @@ module ActiveRecord | |
| 49 34 | 
             
                    @updated
         | 
| 50 35 | 
             
                  end
         | 
| 51 36 |  | 
| 52 | 
            -
                  def decrement_counters | 
| 37 | 
            +
                  def decrement_counters
         | 
| 53 38 | 
             
                    update_counters(-1)
         | 
| 54 39 | 
             
                  end
         | 
| 55 40 |  | 
| 56 | 
            -
                  def increment_counters | 
| 41 | 
            +
                  def increment_counters
         | 
| 57 42 | 
             
                    update_counters(1)
         | 
| 58 43 | 
             
                  end
         | 
| 59 44 |  | 
| 45 | 
            +
                  def decrement_counters_before_last_save
         | 
| 46 | 
            +
                    if reflection.polymorphic?
         | 
| 47 | 
            +
                      model_was = owner.attribute_before_last_save(reflection.foreign_type).try(:constantize)
         | 
| 48 | 
            +
                    else
         | 
| 49 | 
            +
                      model_was = klass
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    foreign_key_was = owner.attribute_before_last_save(reflection.foreign_key)
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    if foreign_key_was && model_was < ActiveRecord::Base
         | 
| 55 | 
            +
                      update_counters_via_scope(model_was, foreign_key_was, -1)
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
             | 
| 60 59 | 
             
                  def target_changed?
         | 
| 61 60 | 
             
                    owner.saved_change_to_attribute?(reflection.foreign_key)
         | 
| 62 61 | 
             
                  end
         | 
| 63 62 |  | 
| 64 63 | 
             
                  private
         | 
| 64 | 
            +
                    def replace(record)
         | 
| 65 | 
            +
                      if record
         | 
| 66 | 
            +
                        raise_on_type_mismatch!(record)
         | 
| 67 | 
            +
                        set_inverse_instance(record)
         | 
| 68 | 
            +
                        @updated = true
         | 
| 69 | 
            +
                      end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                      replace_keys(record)
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                      self.target = record
         | 
| 74 | 
            +
                    end
         | 
| 65 75 |  | 
| 66 76 | 
             
                    def update_counters(by)
         | 
| 67 77 | 
             
                      if require_counter_update? && foreign_key_present?
         | 
| 68 78 | 
             
                        if target && !stale_target?
         | 
| 69 79 | 
             
                          target.increment!(reflection.counter_cache_column, by, touch: reflection.options[:touch])
         | 
| 70 80 | 
             
                        else
         | 
| 71 | 
            -
                          klass. | 
| 81 | 
            +
                          update_counters_via_scope(klass, owner._read_attribute(reflection.foreign_key), by)
         | 
| 72 82 | 
             
                        end
         | 
| 73 83 | 
             
                      end
         | 
| 74 84 | 
             
                    end
         | 
| 75 85 |  | 
| 86 | 
            +
                    def update_counters_via_scope(klass, foreign_key, by)
         | 
| 87 | 
            +
                      scope = klass.unscoped.where!(primary_key(klass) => foreign_key)
         | 
| 88 | 
            +
                      scope.update_counters(reflection.counter_cache_column => by, touch: reflection.options[:touch])
         | 
| 89 | 
            +
                    end
         | 
| 90 | 
            +
             | 
| 76 91 | 
             
                    def find_target?
         | 
| 77 92 | 
             
                      !loaded? && foreign_key_present? && klass
         | 
| 78 93 | 
             
                    end
         | 
| @@ -81,25 +96,12 @@ module ActiveRecord | |
| 81 96 | 
             
                      reflection.counter_cache_column && owner.persisted?
         | 
| 82 97 | 
             
                    end
         | 
| 83 98 |  | 
| 84 | 
            -
                    def update_counters_on_replace(record)
         | 
| 85 | 
            -
                      if require_counter_update? && different_target?(record)
         | 
| 86 | 
            -
                        owner.instance_variable_set :@_after_replace_counter_called, true
         | 
| 87 | 
            -
                        record.increment!(reflection.counter_cache_column, touch: reflection.options[:touch])
         | 
| 88 | 
            -
                        decrement_counters
         | 
| 89 | 
            -
                      end
         | 
| 90 | 
            -
                    end
         | 
| 91 | 
            -
             | 
| 92 | 
            -
                    # Checks whether record is different to the current target, without loading it
         | 
| 93 | 
            -
                    def different_target?(record)
         | 
| 94 | 
            -
                      record._read_attribute(primary_key(record)) != owner._read_attribute(reflection.foreign_key)
         | 
| 95 | 
            -
                    end
         | 
| 96 | 
            -
             | 
| 97 99 | 
             
                    def replace_keys(record)
         | 
| 98 | 
            -
                      owner[reflection.foreign_key] = record ? record._read_attribute(primary_key(record)) : nil
         | 
| 100 | 
            +
                      owner[reflection.foreign_key] = record ? record._read_attribute(primary_key(record.class)) : nil
         | 
| 99 101 | 
             
                    end
         | 
| 100 102 |  | 
| 101 | 
            -
                    def primary_key( | 
| 102 | 
            -
                      reflection.association_primary_key( | 
| 103 | 
            +
                    def primary_key(klass)
         | 
| 104 | 
            +
                      reflection.association_primary_key(klass)
         | 
| 103 105 | 
             
                    end
         | 
| 104 106 |  | 
| 105 107 | 
             
                    def foreign_key_present?
         | 
| @@ -113,14 +115,6 @@ module ActiveRecord | |
| 113 115 | 
             
                      inverse && inverse.has_one?
         | 
| 114 116 | 
             
                    end
         | 
| 115 117 |  | 
| 116 | 
            -
                    def target_id
         | 
| 117 | 
            -
                      if options[:primary_key]
         | 
| 118 | 
            -
                        owner.send(reflection.name).try(:id)
         | 
| 119 | 
            -
                      else
         | 
| 120 | 
            -
                        owner._read_attribute(reflection.foreign_key)
         | 
| 121 | 
            -
                      end
         | 
| 122 | 
            -
                    end
         | 
| 123 | 
            -
             | 
| 124 118 | 
             
                    def stale_state
         | 
| 125 119 | 
             
                      result = owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
         | 
| 126 120 | 
             
                      result && result.to_s
         | 
| @@ -19,10 +19,6 @@ module ActiveRecord | |
| 19 19 | 
             
                      owner[reflection.foreign_type] = record ? record.class.polymorphic_name : nil
         | 
| 20 20 | 
             
                    end
         | 
| 21 21 |  | 
| 22 | 
            -
                    def different_target?(record)
         | 
| 23 | 
            -
                      super || record.class != klass
         | 
| 24 | 
            -
                    end
         | 
| 25 | 
            -
             | 
| 26 22 | 
             
                    def inverse_reflection_for(record)
         | 
| 27 23 | 
             
                      reflection.polymorphic_inverse_of(record.class)
         | 
| 28 24 | 
             
                    end
         | 
| @@ -21,58 +21,16 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 21 21 | 
             
                  add_default_callbacks(model, reflection)       if reflection.options[:default]
         | 
| 22 22 | 
             
                end
         | 
| 23 23 |  | 
| 24 | 
            -
                def self.define_accessors(mixin, reflection)
         | 
| 25 | 
            -
                  super
         | 
| 26 | 
            -
                  add_counter_cache_methods mixin
         | 
| 27 | 
            -
                end
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                def self.add_counter_cache_methods(mixin)
         | 
| 30 | 
            -
                  return if mixin.method_defined? :belongs_to_counter_cache_after_update
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                  mixin.class_eval do
         | 
| 33 | 
            -
                    def belongs_to_counter_cache_after_update(reflection)
         | 
| 34 | 
            -
                      foreign_key  = reflection.foreign_key
         | 
| 35 | 
            -
                      cache_column = reflection.counter_cache_column
         | 
| 36 | 
            -
             | 
| 37 | 
            -
                      if (@_after_replace_counter_called ||= false)
         | 
| 38 | 
            -
                        @_after_replace_counter_called = false
         | 
| 39 | 
            -
                      elsif association(reflection.name).target_changed?
         | 
| 40 | 
            -
                        if reflection.polymorphic?
         | 
| 41 | 
            -
                          model     = attribute_in_database(reflection.foreign_type).try(:constantize)
         | 
| 42 | 
            -
                          model_was = attribute_before_last_save(reflection.foreign_type).try(:constantize)
         | 
| 43 | 
            -
                        else
         | 
| 44 | 
            -
                          model     = reflection.klass
         | 
| 45 | 
            -
                          model_was = reflection.klass
         | 
| 46 | 
            -
                        end
         | 
| 47 | 
            -
             | 
| 48 | 
            -
                        foreign_key_was = attribute_before_last_save foreign_key
         | 
| 49 | 
            -
                        foreign_key     = attribute_in_database foreign_key
         | 
| 50 | 
            -
             | 
| 51 | 
            -
                        if foreign_key && model.respond_to?(:increment_counter)
         | 
| 52 | 
            -
                          foreign_key = counter_cache_target(reflection, model, foreign_key)
         | 
| 53 | 
            -
                          model.increment_counter(cache_column, foreign_key)
         | 
| 54 | 
            -
                        end
         | 
| 55 | 
            -
             | 
| 56 | 
            -
                        if foreign_key_was && model_was.respond_to?(:decrement_counter)
         | 
| 57 | 
            -
                          foreign_key_was = counter_cache_target(reflection, model_was, foreign_key_was)
         | 
| 58 | 
            -
                          model_was.decrement_counter(cache_column, foreign_key_was)
         | 
| 59 | 
            -
                        end
         | 
| 60 | 
            -
                      end
         | 
| 61 | 
            -
                    end
         | 
| 62 | 
            -
             | 
| 63 | 
            -
                    private
         | 
| 64 | 
            -
                      def counter_cache_target(reflection, model, foreign_key)
         | 
| 65 | 
            -
                        primary_key = reflection.association_primary_key(model)
         | 
| 66 | 
            -
                        model.unscoped.where!(primary_key => foreign_key)
         | 
| 67 | 
            -
                      end
         | 
| 68 | 
            -
                  end
         | 
| 69 | 
            -
                end
         | 
| 70 | 
            -
             | 
| 71 24 | 
             
                def self.add_counter_cache_callbacks(model, reflection)
         | 
| 72 25 | 
             
                  cache_column = reflection.counter_cache_column
         | 
| 73 26 |  | 
| 74 27 | 
             
                  model.after_update lambda { |record|
         | 
| 75 | 
            -
                     | 
| 28 | 
            +
                    association = association(reflection.name)
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    if association.target_changed?
         | 
| 31 | 
            +
                      association.increment_counters
         | 
| 32 | 
            +
                      association.decrement_counters_before_last_save
         | 
| 33 | 
            +
                    end
         | 
| 76 34 | 
             
                  }
         | 
| 77 35 |  | 
| 78 36 | 
             
                  klass = reflection.class_name.safe_constantize
         | 
| @@ -123,12 +81,18 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 123 81 | 
             
                    BelongsTo.touch_record(record, record.send(changes_method), foreign_key, n, touch, belongs_to_touch_method)
         | 
| 124 82 | 
             
                  }}
         | 
| 125 83 |  | 
| 126 | 
            -
                   | 
| 84 | 
            +
                  if reflection.counter_cache_column
         | 
| 85 | 
            +
                    touch_callback = callback.(:saved_changes)
         | 
| 86 | 
            +
                    update_callback = lambda { |record|
         | 
| 87 | 
            +
                      instance_exec(record, &touch_callback) unless association(reflection.name).target_changed?
         | 
| 88 | 
            +
                    }
         | 
| 89 | 
            +
                    model.after_update update_callback, if: :saved_changes?
         | 
| 90 | 
            +
                  else
         | 
| 127 91 | 
             
                    model.after_create callback.(:saved_changes), if: :saved_changes?
         | 
| 92 | 
            +
                    model.after_update callback.(:saved_changes), if: :saved_changes?
         | 
| 128 93 | 
             
                    model.after_destroy callback.(:changes_to_save)
         | 
| 129 94 | 
             
                  end
         | 
| 130 95 |  | 
| 131 | 
            -
                  model.after_update callback.(:saved_changes), if: :saved_changes?
         | 
| 132 96 | 
             
                  model.after_touch callback.(:changes_to_save)
         | 
| 133 97 | 
             
                end
         | 
| 134 98 |  | 
| @@ -20,11 +20,11 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 20 20 | 
             
                  }
         | 
| 21 21 | 
             
                end
         | 
| 22 22 |  | 
| 23 | 
            -
                def self.define_extensions(model, name | 
| 23 | 
            +
                def self.define_extensions(model, name)
         | 
| 24 24 | 
             
                  if block_given?
         | 
| 25 25 | 
             
                    extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
         | 
| 26 | 
            -
                    extension = Module.new(& | 
| 27 | 
            -
                    model. | 
| 26 | 
            +
                    extension = Module.new(&Proc.new)
         | 
| 27 | 
            +
                    model.module_parent.const_set(extension_module_name, extension)
         | 
| 28 28 | 
             
                  end
         | 
| 29 29 | 
             
                end
         | 
| 30 30 |  | 
| @@ -2,39 +2,6 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module ActiveRecord::Associations::Builder # :nodoc:
         | 
| 4 4 | 
             
              class HasAndBelongsToMany # :nodoc:
         | 
| 5 | 
            -
                class JoinTableResolver # :nodoc:
         | 
| 6 | 
            -
                  KnownTable = Struct.new :join_table
         | 
| 7 | 
            -
             | 
| 8 | 
            -
                  class KnownClass # :nodoc:
         | 
| 9 | 
            -
                    def initialize(lhs_class, rhs_class_name)
         | 
| 10 | 
            -
                      @lhs_class      = lhs_class
         | 
| 11 | 
            -
                      @rhs_class_name = rhs_class_name
         | 
| 12 | 
            -
                      @join_table     = nil
         | 
| 13 | 
            -
                    end
         | 
| 14 | 
            -
             | 
| 15 | 
            -
                    def join_table
         | 
| 16 | 
            -
                      @join_table ||= [@lhs_class.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
         | 
| 17 | 
            -
                    end
         | 
| 18 | 
            -
             | 
| 19 | 
            -
                    private
         | 
| 20 | 
            -
             | 
| 21 | 
            -
                      def klass
         | 
| 22 | 
            -
                        @lhs_class.send(:compute_type, @rhs_class_name)
         | 
| 23 | 
            -
                      end
         | 
| 24 | 
            -
                  end
         | 
| 25 | 
            -
             | 
| 26 | 
            -
                  def self.build(lhs_class, name, options)
         | 
| 27 | 
            -
                    if options[:join_table]
         | 
| 28 | 
            -
                      KnownTable.new options[:join_table].to_s
         | 
| 29 | 
            -
                    else
         | 
| 30 | 
            -
                      class_name = options.fetch(:class_name) {
         | 
| 31 | 
            -
                        name.to_s.camelize.singularize
         | 
| 32 | 
            -
                      }
         | 
| 33 | 
            -
                      KnownClass.new lhs_class, class_name.to_s
         | 
| 34 | 
            -
                    end
         | 
| 35 | 
            -
                  end
         | 
| 36 | 
            -
                end
         | 
| 37 | 
            -
             | 
| 38 5 | 
             
                attr_reader :lhs_model, :association_name, :options
         | 
| 39 6 |  | 
| 40 7 | 
             
                def initialize(association_name, lhs_model, options)
         | 
| @@ -44,8 +11,6 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 44 11 | 
             
                end
         | 
| 45 12 |  | 
| 46 13 | 
             
                def through_model
         | 
| 47 | 
            -
                  habtm = JoinTableResolver.build lhs_model, association_name, options
         | 
| 48 | 
            -
             | 
| 49 14 | 
             
                  join_model = Class.new(ActiveRecord::Base) {
         | 
| 50 15 | 
             
                    class << self
         | 
| 51 16 | 
             
                      attr_accessor :left_model
         | 
| @@ -56,7 +21,9 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 56 21 | 
             
                    end
         | 
| 57 22 |  | 
| 58 23 | 
             
                    def self.table_name
         | 
| 59 | 
            -
                       | 
| 24 | 
            +
                      # Table name needs to be resolved lazily
         | 
| 25 | 
            +
                      # because RHS class might not have been loaded
         | 
| 26 | 
            +
                      @table_name ||= table_name_resolver.call
         | 
| 60 27 | 
             
                    end
         | 
| 61 28 |  | 
| 62 29 | 
             
                    def self.compute_type(class_name)
         | 
| @@ -86,7 +53,7 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 86 53 | 
             
                  }
         | 
| 87 54 |  | 
| 88 55 | 
             
                  join_model.name                = "HABTM_#{association_name.to_s.camelize}"
         | 
| 89 | 
            -
                  join_model.table_name_resolver =  | 
| 56 | 
            +
                  join_model.table_name_resolver = -> { table_name }
         | 
| 90 57 | 
             
                  join_model.left_model          = lhs_model
         | 
| 91 58 |  | 
| 92 59 | 
             
                  join_model.add_left_association :left_side, anonymous_class: lhs_model
         | 
| @@ -96,7 +63,7 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 96 63 |  | 
| 97 64 | 
             
                def middle_reflection(join_model)
         | 
| 98 65 | 
             
                  middle_name = [lhs_model.name.downcase.pluralize,
         | 
| 99 | 
            -
                                 association_name].join("_" | 
| 66 | 
            +
                                 association_name].join("_").gsub("::", "_").to_sym
         | 
| 100 67 | 
             
                  middle_options = middle_options join_model
         | 
| 101 68 |  | 
| 102 69 | 
             
                  HasMany.create_reflection(lhs_model,
         | 
| @@ -117,6 +84,18 @@ module ActiveRecord::Associations::Builder # :nodoc: | |
| 117 84 | 
             
                    middle_options
         | 
| 118 85 | 
             
                  end
         | 
| 119 86 |  | 
| 87 | 
            +
                  def table_name
         | 
| 88 | 
            +
                    if options[:join_table]
         | 
| 89 | 
            +
                      options[:join_table].to_s
         | 
| 90 | 
            +
                    else
         | 
| 91 | 
            +
                      class_name = options.fetch(:class_name) {
         | 
| 92 | 
            +
                        association_name.to_s.camelize.singularize
         | 
| 93 | 
            +
                      }
         | 
| 94 | 
            +
                      klass = lhs_model.send(:compute_type, class_name.to_s)
         | 
| 95 | 
            +
                      [lhs_model.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
         | 
| 96 | 
            +
                    end
         | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
             | 
| 120 99 | 
             
                  def belongs_to_options(options)
         | 
| 121 100 | 
             
                    rhs_options = {}
         | 
| 122 101 |  | 
| @@ -109,8 +109,9 @@ module ActiveRecord | |
| 109 109 | 
             
                    end
         | 
| 110 110 | 
             
                  end
         | 
| 111 111 |  | 
| 112 | 
            -
                  # Add +records+ to this association.  | 
| 113 | 
            -
                  #  | 
| 112 | 
            +
                  # Add +records+ to this association. Returns +self+ so method calls may
         | 
| 113 | 
            +
                  # be chained. Since << flattens its argument list and inserts each record,
         | 
| 114 | 
            +
                  # +push+ and +concat+ behave identically.
         | 
| 114 115 | 
             
                  def concat(*records)
         | 
| 115 116 | 
             
                    records = records.flatten
         | 
| 116 117 | 
             
                    if owner.new_record?
         | 
| @@ -211,9 +212,11 @@ module ActiveRecord | |
| 211 212 | 
             
                  def size
         | 
| 212 213 | 
             
                    if !find_target? || loaded?
         | 
| 213 214 | 
             
                      target.size
         | 
| 215 | 
            +
                    elsif @association_ids
         | 
| 216 | 
            +
                      @association_ids.size
         | 
| 214 217 | 
             
                    elsif !association_scope.group_values.empty?
         | 
| 215 218 | 
             
                      load_target.size
         | 
| 216 | 
            -
                    elsif !association_scope.distinct_value && target. | 
| 219 | 
            +
                    elsif !association_scope.distinct_value && !target.empty?
         | 
| 217 220 | 
             
                      unsaved_records = target.select(&:new_record?)
         | 
| 218 221 | 
             
                      unsaved_records.size + count_records
         | 
| 219 222 | 
             
                    else
         | 
| @@ -230,10 +233,10 @@ module ActiveRecord | |
| 230 233 | 
             
                  # loaded and you are going to fetch the records anyway it is better to
         | 
| 231 234 | 
             
                  # check <tt>collection.length.zero?</tt>.
         | 
| 232 235 | 
             
                  def empty?
         | 
| 233 | 
            -
                    if loaded?
         | 
| 236 | 
            +
                    if loaded? || @association_ids
         | 
| 234 237 | 
             
                      size.zero?
         | 
| 235 238 | 
             
                    else
         | 
| 236 | 
            -
                       | 
| 239 | 
            +
                      target.empty? && !scope.exists?
         | 
| 237 240 | 
             
                    end
         | 
| 238 241 | 
             
                  end
         | 
| 239 242 |  | 
| @@ -300,23 +303,6 @@ module ActiveRecord | |
| 300 303 | 
             
                  end
         | 
| 301 304 |  | 
| 302 305 | 
             
                  private
         | 
| 303 | 
            -
             | 
| 304 | 
            -
                    def find_target
         | 
| 305 | 
            -
                      scope = self.scope
         | 
| 306 | 
            -
                      return scope.to_a if skip_statement_cache?(scope)
         | 
| 307 | 
            -
             | 
| 308 | 
            -
                      conn = klass.connection
         | 
| 309 | 
            -
                      sc = reflection.association_scope_cache(conn, owner) do |params|
         | 
| 310 | 
            -
                        as = AssociationScope.create { params.bind }
         | 
| 311 | 
            -
                        target_scope.merge!(as.scope(self))
         | 
| 312 | 
            -
                      end
         | 
| 313 | 
            -
             | 
| 314 | 
            -
                      binds = AssociationScope.get_bind_values(owner, reflection.chain)
         | 
| 315 | 
            -
                      sc.execute(binds, conn) do |record|
         | 
| 316 | 
            -
                        set_inverse_instance(record)
         | 
| 317 | 
            -
                      end
         | 
| 318 | 
            -
                    end
         | 
| 319 | 
            -
             | 
| 320 306 | 
             
                    # We have some records loaded from the database (persisted) and some that are
         | 
| 321 307 | 
             
                    # in-memory (memory). The same record may be represented in the persisted array
         | 
| 322 308 | 
             
                    # and in the memory array.
         | 
| @@ -361,6 +347,7 @@ module ActiveRecord | |
| 361 347 | 
             
                          add_to_target(record) do
         | 
| 362 348 | 
             
                            result = insert_record(record, true, raise) {
         | 
| 363 349 | 
             
                              @_was_loaded = loaded?
         | 
| 350 | 
            +
                              @association_ids = nil
         | 
| 364 351 | 
             
                            }
         | 
| 365 352 | 
             
                          end
         | 
| 366 353 | 
             
                          raise ActiveRecord::Rollback unless result
         | 
| @@ -396,8 +383,7 @@ module ActiveRecord | |
| 396 383 | 
             
                      records.each { |record| callback(:before_remove, record) }
         | 
| 397 384 |  | 
| 398 385 | 
             
                      delete_records(existing_records, method) if existing_records.any?
         | 
| 399 | 
            -
                       | 
| 400 | 
            -
                      @association_ids = nil
         | 
| 386 | 
            +
                      @target -= records
         | 
| 401 387 |  | 
| 402 388 | 
             
                      records.each { |record| callback(:after_remove, record) }
         | 
| 403 389 | 
             
                    end
         | 
| @@ -438,6 +424,7 @@ module ActiveRecord | |
| 438 424 | 
             
                          unless owner.new_record?
         | 
| 439 425 | 
             
                            result &&= insert_record(record, true, raise) {
         | 
| 440 426 | 
             
                              @_was_loaded = loaded?
         | 
| 427 | 
            +
                              @association_ids = nil
         | 
| 441 428 | 
             
                            }
         | 
| 442 429 | 
             
                          end
         | 
| 443 430 | 
             
                        end
         | 
| @@ -460,7 +447,6 @@ module ActiveRecord | |
| 460 447 | 
             
                      if index
         | 
| 461 448 | 
             
                        target[index] = record
         | 
| 462 449 | 
             
                      elsif @_was_loaded || !loaded?
         | 
| 463 | 
            -
                        @association_ids = nil
         | 
| 464 450 | 
             
                        target << record
         | 
| 465 451 | 
             
                      end
         | 
| 466 452 |  | 
| @@ -366,6 +366,34 @@ module ActiveRecord | |
| 366 366 | 
             
                    @association.create!(attributes, &block)
         | 
| 367 367 | 
             
                  end
         | 
| 368 368 |  | 
| 369 | 
            +
                  # Add one or more records to the collection by setting their foreign keys
         | 
| 370 | 
            +
                  # to the association's primary key. Since #<< flattens its argument list and
         | 
| 371 | 
            +
                  # inserts each record, +push+ and #concat behave identically. Returns +self+
         | 
| 372 | 
            +
                  # so method calls may be chained.
         | 
| 373 | 
            +
                  #
         | 
| 374 | 
            +
                  #   class Person < ActiveRecord::Base
         | 
| 375 | 
            +
                  #     has_many :pets
         | 
| 376 | 
            +
                  #   end
         | 
| 377 | 
            +
                  #
         | 
| 378 | 
            +
                  #   person.pets.size # => 0
         | 
| 379 | 
            +
                  #   person.pets.concat(Pet.new(name: 'Fancy-Fancy'))
         | 
| 380 | 
            +
                  #   person.pets.concat(Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo'))
         | 
| 381 | 
            +
                  #   person.pets.size # => 3
         | 
| 382 | 
            +
                  #
         | 
| 383 | 
            +
                  #   person.id # => 1
         | 
| 384 | 
            +
                  #   person.pets
         | 
| 385 | 
            +
                  #   # => [
         | 
| 386 | 
            +
                  #   #       #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
         | 
| 387 | 
            +
                  #   #       #<Pet id: 2, name: "Spook", person_id: 1>,
         | 
| 388 | 
            +
                  #   #       #<Pet id: 3, name: "Choo-Choo", person_id: 1>
         | 
| 389 | 
            +
                  #   #    ]
         | 
| 390 | 
            +
                  #
         | 
| 391 | 
            +
                  #   person.pets.concat([Pet.new(name: 'Brain'), Pet.new(name: 'Benny')])
         | 
| 392 | 
            +
                  #   person.pets.size # => 5
         | 
| 393 | 
            +
                  def concat(*records)
         | 
| 394 | 
            +
                    @association.concat(*records)
         | 
| 395 | 
            +
                  end
         | 
| 396 | 
            +
             | 
| 369 397 | 
             
                  # Replaces this collection with +other_array+. This will perform a diff
         | 
| 370 398 | 
             
                  # and delete/add only records that have changed.
         | 
| 371 399 | 
             
                  #
         | 
| @@ -1005,9 +1033,8 @@ module ActiveRecord | |
| 1005 1033 | 
             
                  end
         | 
| 1006 1034 |  | 
| 1007 1035 | 
             
                  # Adds one or more +records+ to the collection by setting their foreign keys
         | 
| 1008 | 
            -
                  # to the association's primary key.  | 
| 1009 | 
            -
                  #  | 
| 1010 | 
            -
                  # so several appends may be chained together.
         | 
| 1036 | 
            +
                  # to the association's primary key. Returns +self+, so several appends may be
         | 
| 1037 | 
            +
                  # chained together.
         | 
| 1011 1038 | 
             
                  #
         | 
| 1012 1039 | 
             
                  #   class Person < ActiveRecord::Base
         | 
| 1013 1040 | 
             
                  #     has_many :pets
         | 
| @@ -1030,7 +1057,6 @@ module ActiveRecord | |
| 1030 1057 | 
             
                  end
         | 
| 1031 1058 | 
             
                  alias_method :push, :<<
         | 
| 1032 1059 | 
             
                  alias_method :append, :<<
         | 
| 1033 | 
            -
                  alias_method :concat, :<<
         | 
| 1034 1060 |  | 
| 1035 1061 | 
             
                  def prepend(*args)
         | 
| 1036 1062 | 
             
                    raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
         | 
| @@ -1062,7 +1088,7 @@ module ActiveRecord | |
| 1062 1088 | 
             
                  #   person.pets.reload # fetches pets from the database
         | 
| 1063 1089 | 
             
                  #   # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
         | 
| 1064 1090 | 
             
                  def reload
         | 
| 1065 | 
            -
                    proxy_association.reload
         | 
| 1091 | 
            +
                    proxy_association.reload(true)
         | 
| 1066 1092 | 
             
                    reset_scope
         | 
| 1067 1093 | 
             
                  end
         | 
| 1068 1094 |  | 
| @@ -1099,7 +1125,7 @@ module ActiveRecord | |
| 1099 1125 | 
             
                    SpawnMethods,
         | 
| 1100 1126 | 
             
                  ].flat_map { |klass|
         | 
| 1101 1127 | 
             
                    klass.public_instance_methods(false)
         | 
| 1102 | 
            -
                  } - self.public_instance_methods(false) - [:select] + [:scoping]
         | 
| 1128 | 
            +
                  } - self.public_instance_methods(false) - [:select] + [:scoping, :values]
         | 
| 1103 1129 |  | 
| 1104 1130 | 
             
                  delegate(*delegate_methods, to: :scope)
         | 
| 1105 1131 |  |