activerecord 6.0.3.5 → 6.1.0.rc1
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 +774 -735
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/active_record.rb +7 -14
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +22 -14
- data/lib/active_record/associations.rb +114 -11
- data/lib/active_record/associations/alias_tracker.rb +19 -15
- data/lib/active_record/associations/association.rb +40 -29
- data/lib/active_record/associations/association_scope.rb +17 -15
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +9 -3
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +19 -6
- data/lib/active_record/associations/collection_proxy.rb +13 -5
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -2
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency.rb +72 -50
- data/lib/active_record/associations/join_dependency/join_association.rb +36 -14
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/preloader.rb +11 -5
- data/lib/active_record/associations/preloader/association.rb +51 -25
- data/lib/active_record/associations/preloader/through_association.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/attribute_assignment.rb +10 -8
- data/lib/active_record/attribute_methods.rb +52 -48
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
- data/lib/active_record/attribute_methods/dirty.rb +1 -11
- data/lib/active_record/attribute_methods/primary_key.rb +6 -2
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -11
- data/lib/active_record/attribute_methods/serialization.rb +4 -4
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
- data/lib/active_record/attribute_methods/write.rb +12 -20
- data/lib/active_record/attributes.rb +27 -7
- data/lib/active_record/autosave_association.rb +57 -40
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +32 -22
- data/lib/active_record/coders/yaml_column.rb +1 -1
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +186 -134
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
- data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +112 -27
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
- data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -24
- data/lib/active_record/connection_adapters/abstract_adapter.rb +36 -69
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +129 -88
- data/lib/active_record/connection_adapters/column.rb +15 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -25
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +33 -6
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +11 -7
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +13 -54
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -5
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
- data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +31 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +37 -4
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +49 -50
- data/lib/active_record/connection_handling.rb +210 -71
- data/lib/active_record/core.rb +220 -55
- data/lib/active_record/database_configurations.rb +124 -85
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -40
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/enum.rb +27 -10
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -4
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -2
- data/lib/active_record/fixtures.rb +54 -8
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +40 -18
- data/lib/active_record/insert_all.rb +33 -6
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +15 -4
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +22 -16
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +26 -8
- data/lib/active_record/middleware/database_selector.rb +4 -1
- data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/migration.rb +113 -83
- data/lib/active_record/migration/command_recorder.rb +47 -27
- data/lib/active_record/migration/compatibility.rb +67 -17
- data/lib/active_record/model_schema.rb +88 -13
- data/lib/active_record/nested_attributes.rb +2 -3
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +50 -45
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +11 -6
- data/lib/active_record/railtie.rb +64 -44
- data/lib/active_record/railties/databases.rake +253 -98
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +70 -57
- data/lib/active_record/relation.rb +96 -67
- data/lib/active_record/relation/batches.rb +38 -31
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/calculations.rb +101 -44
- data/lib/active_record/relation/delegation.rb +2 -1
- data/lib/active_record/relation/finder_methods.rb +45 -15
- data/lib/active_record/relation/from_clause.rb +1 -1
- data/lib/active_record/relation/merger.rb +27 -25
- data/lib/active_record/relation/predicate_builder.rb +57 -33
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +330 -195
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +6 -5
- data/lib/active_record/relation/where_clause.rb +104 -57
- data/lib/active_record/result.rb +41 -33
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +0 -4
- data/lib/active_record/scoping/named.rb +6 -17
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +20 -4
- data/lib/active_record/store.rb +2 -2
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +36 -52
- data/lib/active_record/tasks/database_tasks.rb +139 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +37 -16
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/touch_later.rb +21 -21
- data/lib/active_record/transactions.rb +15 -64
- data/lib/active_record/type.rb +8 -1
- data/lib/active_record/type/serialized.rb +6 -2
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/arel.rb +5 -13
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/predications.rb +12 -18
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel/visitors/dot.rb +14 -2
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -4
- data/lib/arel/visitors/to_sql.rb +89 -78
- data/lib/rails/generators/active_record/migration.rb +6 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +27 -28
- data/lib/active_record/advisory_lock_base.rb +0 -18
- data/lib/active_record/attribute_decorators.rb +0 -88
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -203
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -156
- data/lib/arel/visitors/oracle.rb +0 -158
- data/lib/arel/visitors/oracle12.rb +0 -65
- data/lib/arel/visitors/where_sql.rb +0 -22
| @@ -23,6 +23,12 @@ module ActiveRecord | |
| 23 23 | 
             
                # should not be dumped to db/schema.rb.
         | 
| 24 24 | 
             
                cattr_accessor :fk_ignore_pattern, default: /^fk_rails_[0-9a-f]{10}$/
         | 
| 25 25 |  | 
| 26 | 
            +
                ##
         | 
| 27 | 
            +
                # :singleton-method:
         | 
| 28 | 
            +
                # Specify a custom regular expression matching check constraints which name
         | 
| 29 | 
            +
                # should not be dumped to db/schema.rb.
         | 
| 30 | 
            +
                cattr_accessor :chk_ignore_pattern, default: /^chk_rails_[0-9a-f]{10}$/
         | 
| 31 | 
            +
             | 
| 26 32 | 
             
                class << self
         | 
| 27 33 | 
             
                  def dump(connection = ActiveRecord::Base.connection, stream = STDOUT, config = ActiveRecord::Base)
         | 
| 28 34 | 
             
                    connection.create_schema_dumper(generate_options(config)).dump(stream)
         | 
| @@ -72,8 +78,8 @@ module ActiveRecord | |
| 72 78 | 
             
            # of editing this file, please use the migrations feature of Active Record to
         | 
| 73 79 | 
             
            # incrementally modify your database, and then regenerate this schema definition.
         | 
| 74 80 | 
             
            #
         | 
| 75 | 
            -
            # This file is the source Rails uses to define your schema when running `rails
         | 
| 76 | 
            -
            # db:schema:load`. When creating a new database, `rails db:schema:load` tends to
         | 
| 81 | 
            +
            # This file is the source Rails uses to define your schema when running `bin/rails
         | 
| 82 | 
            +
            # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
         | 
| 77 83 | 
             
            # be faster and is potentially less error prone than running all of your
         | 
| 78 84 | 
             
            # migrations from scratch. Old migrations may fail to apply correctly if those
         | 
| 79 85 | 
             
            # migrations use external dependencies or application code.
         | 
| @@ -125,7 +131,10 @@ HEADER | |
| 125 131 | 
             
                        tbl.print ", primary_key: #{pk.inspect}" unless pk == "id"
         | 
| 126 132 | 
             
                        pkcol = columns.detect { |c| c.name == pk }
         | 
| 127 133 | 
             
                        pkcolspec = column_spec_for_primary_key(pkcol)
         | 
| 128 | 
            -
                         | 
| 134 | 
            +
                        unless pkcolspec.empty?
         | 
| 135 | 
            +
                          if pkcolspec != pkcolspec.slice(:id, :default)
         | 
| 136 | 
            +
                            pkcolspec = { id: { type: pkcolspec.delete(:id), **pkcolspec }.compact }
         | 
| 137 | 
            +
                          end
         | 
| 129 138 | 
             
                          tbl.print ", #{format_colspec(pkcolspec)}"
         | 
| 130 139 | 
             
                        end
         | 
| 131 140 | 
             
                      when Array
         | 
| @@ -156,6 +165,7 @@ HEADER | |
| 156 165 | 
             
                      end
         | 
| 157 166 |  | 
| 158 167 | 
             
                      indexes_in_create(table, tbl)
         | 
| 168 | 
            +
                      check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
         | 
| 159 169 |  | 
| 160 170 | 
             
                      tbl.puts "  end"
         | 
| 161 171 | 
             
                      tbl.puts
         | 
| @@ -209,6 +219,24 @@ HEADER | |
| 209 219 | 
             
                    index_parts
         | 
| 210 220 | 
             
                  end
         | 
| 211 221 |  | 
| 222 | 
            +
                  def check_constraints_in_create(table, stream)
         | 
| 223 | 
            +
                    if (check_constraints = @connection.check_constraints(table)).any?
         | 
| 224 | 
            +
                      add_check_constraint_statements = check_constraints.map do |check_constraint|
         | 
| 225 | 
            +
                        parts = [
         | 
| 226 | 
            +
                          "t.check_constraint #{check_constraint.expression.inspect}"
         | 
| 227 | 
            +
                        ]
         | 
| 228 | 
            +
             | 
| 229 | 
            +
                        if check_constraint.export_name_on_schema_dump?
         | 
| 230 | 
            +
                          parts << "name: #{check_constraint.name.inspect}"
         | 
| 231 | 
            +
                        end
         | 
| 232 | 
            +
             | 
| 233 | 
            +
                        "    #{parts.join(', ')}"
         | 
| 234 | 
            +
                      end
         | 
| 235 | 
            +
             | 
| 236 | 
            +
                      stream.puts add_check_constraint_statements.sort.join("\n")
         | 
| 237 | 
            +
                    end
         | 
| 238 | 
            +
                  end
         | 
| 239 | 
            +
             | 
| 212 240 | 
             
                  def foreign_keys(table, stream)
         | 
| 213 241 | 
             
                    if (foreign_keys = @connection.foreign_keys(table)).any?
         | 
| 214 242 | 
             
                      add_foreign_key_statements = foreign_keys.map do |foreign_key|
         | 
| @@ -240,7 +268,9 @@ HEADER | |
| 240 268 | 
             
                  end
         | 
| 241 269 |  | 
| 242 270 | 
             
                  def format_colspec(colspec)
         | 
| 243 | 
            -
                    colspec.map  | 
| 271 | 
            +
                    colspec.map do |key, value|
         | 
| 272 | 
            +
                      "#{key}: #{ value.is_a?(Hash) ? "{ #{format_colspec(value)} }" : value }"
         | 
| 273 | 
            +
                    end.join(", ")
         | 
| 244 274 | 
             
                  end
         | 
| 245 275 |  | 
| 246 276 | 
             
                  def format_options(options)
         | 
| @@ -22,10 +22,6 @@ module ActiveRecord | |
| 22 22 | 
             
                    "#{table_name_prefix}#{schema_migrations_table_name}#{table_name_suffix}"
         | 
| 23 23 | 
             
                  end
         | 
| 24 24 |  | 
| 25 | 
            -
                  def table_exists?
         | 
| 26 | 
            -
                    connection.table_exists?(table_name)
         | 
| 27 | 
            -
                  end
         | 
| 28 | 
            -
             | 
| 29 25 | 
             
                  def create_table
         | 
| 30 26 | 
             
                    unless table_exists?
         | 
| 31 27 | 
             
                      version_options = connection.internal_string_options_for_primary_key
         | 
| @@ -1,9 +1,5 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require "active_support/core_ext/array"
         | 
| 4 | 
            -
            require "active_support/core_ext/hash/except"
         | 
| 5 | 
            -
            require "active_support/core_ext/kernel/singleton_class"
         | 
| 6 | 
            -
             | 
| 7 3 | 
             
            module ActiveRecord
         | 
| 8 4 | 
             
              # = Active Record \Named \Scopes
         | 
| 9 5 | 
             
              module Scoping
         | 
| @@ -27,14 +23,6 @@ module ActiveRecord | |
| 27 23 | 
             
                      scope = current_scope
         | 
| 28 24 |  | 
| 29 25 | 
             
                      if scope
         | 
| 30 | 
            -
                        if scope._deprecated_scope_source
         | 
| 31 | 
            -
                          ActiveSupport::Deprecation.warn(<<~MSG.squish)
         | 
| 32 | 
            -
                            Class level methods will no longer inherit scoping from `#{scope._deprecated_scope_source}`
         | 
| 33 | 
            -
                            in Rails 6.1. To continue using the scoped relation, pass it into the block directly.
         | 
| 34 | 
            -
                            To instead access the full set of models, as Rails 6.1 will, use `#{name}.default_scoped`.
         | 
| 35 | 
            -
                          MSG
         | 
| 36 | 
            -
                        end
         | 
| 37 | 
            -
             | 
| 38 26 | 
             
                        if self == scope.klass
         | 
| 39 27 | 
             
                          scope.clone
         | 
| 40 28 | 
             
                        else
         | 
| @@ -84,10 +72,6 @@ module ActiveRecord | |
| 84 72 | 
             
                    # <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>, in effect,
         | 
| 85 73 | 
             
                    # represents the query <tt>Shirt.where(color: 'red')</tt>.
         | 
| 86 74 | 
             
                    #
         | 
| 87 | 
            -
                    # You should always pass a callable object to the scopes defined
         | 
| 88 | 
            -
                    # with #scope. This ensures that the scope is re-evaluated each
         | 
| 89 | 
            -
                    # time it is called.
         | 
| 90 | 
            -
                    #
         | 
| 91 75 | 
             
                    # Note that this is simply 'syntactic sugar' for defining an actual
         | 
| 92 76 | 
             
                    # class method:
         | 
| 93 77 | 
             
                    #
         | 
| @@ -189,7 +173,7 @@ module ActiveRecord | |
| 189 173 |  | 
| 190 174 | 
             
                      if body.respond_to?(:to_proc)
         | 
| 191 175 | 
             
                        singleton_class.define_method(name) do |*args|
         | 
| 192 | 
            -
                          scope = all._exec_scope( | 
| 176 | 
            +
                          scope = all._exec_scope(*args, &body)
         | 
| 193 177 | 
             
                          scope = scope.extending(extension) if extension
         | 
| 194 178 | 
             
                          scope
         | 
| 195 179 | 
             
                        end
         | 
| @@ -200,11 +184,16 @@ module ActiveRecord | |
| 200 184 | 
             
                          scope
         | 
| 201 185 | 
             
                        end
         | 
| 202 186 | 
             
                      end
         | 
| 187 | 
            +
                      singleton_class.send(:ruby2_keywords, name) if respond_to?(:ruby2_keywords, true)
         | 
| 203 188 |  | 
| 204 189 | 
             
                      generate_relation_method(name)
         | 
| 205 190 | 
             
                    end
         | 
| 206 191 |  | 
| 207 192 | 
             
                    private
         | 
| 193 | 
            +
                      def singleton_method_added(name)
         | 
| 194 | 
            +
                        generate_relation_method(name) if Kernel.respond_to?(name) && !ActiveRecord::Relation.method_defined?(name)
         | 
| 195 | 
            +
                      end
         | 
| 196 | 
            +
             | 
| 208 197 | 
             
                      def valid_scope_name?(name)
         | 
| 209 198 | 
             
                        if respond_to?(name, true) && logger
         | 
| 210 199 | 
             
                          logger.warn "Creating scope :#{name}. " \
         | 
| @@ -2,6 +2,10 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module ActiveRecord
         | 
| 4 4 | 
             
              module SecureToken
         | 
| 5 | 
            +
                class MinimumLengthError < StandardError; end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                MINIMUM_TOKEN_LENGTH = 24
         | 
| 8 | 
            +
             | 
| 5 9 | 
             
                extend ActiveSupport::Concern
         | 
| 6 10 |  | 
| 7 11 | 
             
                module ClassMethods
         | 
| @@ -10,30 +14,34 @@ module ActiveRecord | |
| 10 14 | 
             
                  #   # Schema: User(token:string, auth_token:string)
         | 
| 11 15 | 
             
                  #   class User < ActiveRecord::Base
         | 
| 12 16 | 
             
                  #     has_secure_token
         | 
| 13 | 
            -
                  #     has_secure_token :auth_token
         | 
| 17 | 
            +
                  #     has_secure_token :auth_token, length: 36
         | 
| 14 18 | 
             
                  #   end
         | 
| 15 19 | 
             
                  #
         | 
| 16 20 | 
             
                  #   user = User.new
         | 
| 17 21 | 
             
                  #   user.save
         | 
| 18 22 | 
             
                  #   user.token # => "pX27zsMN2ViQKta1bGfLmVJE"
         | 
| 19 | 
            -
                  #   user.auth_token # => " | 
| 23 | 
            +
                  #   user.auth_token # => "tU9bLuZseefXQ4yQxQo8wjtBvsAfPc78os6R"
         | 
| 20 24 | 
             
                  #   user.regenerate_token # => true
         | 
| 21 25 | 
             
                  #   user.regenerate_auth_token # => true
         | 
| 22 26 | 
             
                  #
         | 
| 23 | 
            -
                  # <tt>SecureRandom::base58</tt> is used to generate  | 
| 27 | 
            +
                  # <tt>SecureRandom::base58</tt> is used to generate at minimum a 24-character unique token, so collisions are highly unlikely.
         | 
| 24 28 | 
             
                  #
         | 
| 25 29 | 
             
                  # Note that it's still possible to generate a race condition in the database in the same way that
         | 
| 26 30 | 
             
                  # {validates_uniqueness_of}[rdoc-ref:Validations::ClassMethods#validates_uniqueness_of] can.
         | 
| 27 31 | 
             
                  # You're encouraged to add a unique index in the database to deal with this even more unlikely scenario.
         | 
| 28 | 
            -
                  def has_secure_token(attribute = :token)
         | 
| 32 | 
            +
                  def has_secure_token(attribute = :token, length: MINIMUM_TOKEN_LENGTH)
         | 
| 33 | 
            +
                    if length < MINIMUM_TOKEN_LENGTH
         | 
| 34 | 
            +
                      raise MinimumLengthError, "Token requires a minimum length of #{MINIMUM_TOKEN_LENGTH} characters."
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
             | 
| 29 37 | 
             
                    # Load securerandom only when has_secure_token is used.
         | 
| 30 38 | 
             
                    require "active_support/core_ext/securerandom"
         | 
| 31 | 
            -
                    define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token }
         | 
| 32 | 
            -
                    before_create { send("#{attribute}=", self.class.generate_unique_secure_token) unless send("#{attribute}?") }
         | 
| 39 | 
            +
                    define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token(length: length) }
         | 
| 40 | 
            +
                    before_create { send("#{attribute}=", self.class.generate_unique_secure_token(length: length)) unless send("#{attribute}?") }
         | 
| 33 41 | 
             
                  end
         | 
| 34 42 |  | 
| 35 | 
            -
                  def generate_unique_secure_token
         | 
| 36 | 
            -
                    SecureRandom.base58( | 
| 43 | 
            +
                  def generate_unique_secure_token(length: MINIMUM_TOKEN_LENGTH)
         | 
| 44 | 
            +
                    SecureRandom.base58(length)
         | 
| 37 45 | 
             
                  end
         | 
| 38 46 | 
             
                end
         | 
| 39 47 | 
             
              end
         | 
| @@ -11,10 +11,12 @@ module ActiveRecord #:nodoc: | |
| 11 11 | 
             
                end
         | 
| 12 12 |  | 
| 13 13 | 
             
                def serializable_hash(options = nil)
         | 
| 14 | 
            -
                   | 
| 14 | 
            +
                  if self.class._has_attribute?(self.class.inheritance_column)
         | 
| 15 | 
            +
                    options = options ? options.dup : {}
         | 
| 15 16 |  | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 17 | 
            +
                    options[:except] = Array(options[:except]).map(&:to_s)
         | 
| 18 | 
            +
                    options[:except] |= Array(self.class.inheritance_column)
         | 
| 19 | 
            +
                  end
         | 
| 18 20 |  | 
| 19 21 | 
             
                  super(options)
         | 
| 20 22 | 
             
                end
         | 
| @@ -0,0 +1,116 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module ActiveRecord
         | 
| 4 | 
            +
              # = Active Record Signed Id
         | 
| 5 | 
            +
              module SignedId
         | 
| 6 | 
            +
                extend ActiveSupport::Concern
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                included do
         | 
| 9 | 
            +
                  ##
         | 
| 10 | 
            +
                  # :singleton-method:
         | 
| 11 | 
            +
                  # Set the secret used for the signed id verifier instance when using Active Record outside of Rails.
         | 
| 12 | 
            +
                  # Within Rails, this is automatically set using the Rails application key generator.
         | 
| 13 | 
            +
                  mattr_accessor :signed_id_verifier_secret, instance_writer: false
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                module ClassMethods
         | 
| 17 | 
            +
                  # Lets you find a record based on a signed id that's safe to put into the world without risk of tampering.
         | 
| 18 | 
            +
                  # This is particularly useful for things like password reset or email verification, where you want
         | 
| 19 | 
            +
                  # the bearer of the signed id to be able to interact with the underlying record, but usually only within
         | 
| 20 | 
            +
                  # a certain time period.
         | 
| 21 | 
            +
                  #
         | 
| 22 | 
            +
                  # You set the time period that the signed id is valid for during generation, using the instance method
         | 
| 23 | 
            +
                  # +signed_id(expires_in: 15.minutes)+. If the time has elapsed before a signed find is attempted,
         | 
| 24 | 
            +
                  # the signed id will no longer be valid, and nil is returned.
         | 
| 25 | 
            +
                  #
         | 
| 26 | 
            +
                  # It's possible to further restrict the use of a signed id with a purpose. This helps when you have a
         | 
| 27 | 
            +
                  # general base model, like a User, which might have signed ids for several things, like password reset
         | 
| 28 | 
            +
                  # or email verification. The purpose that was set during generation must match the purpose set when
         | 
| 29 | 
            +
                  # finding. If there's a mismatch, nil is again returned.
         | 
| 30 | 
            +
                  #
         | 
| 31 | 
            +
                  # ==== Examples
         | 
| 32 | 
            +
                  #
         | 
| 33 | 
            +
                  #   signed_id = User.first.signed_id expires_in: 15.minutes, purpose: :password_reset
         | 
| 34 | 
            +
                  #
         | 
| 35 | 
            +
                  #   User.find_signed signed_id # => nil, since the purpose does not match
         | 
| 36 | 
            +
                  #
         | 
| 37 | 
            +
                  #   travel 16.minutes
         | 
| 38 | 
            +
                  #   User.find_signed signed_id, purpose: :password_reset # => nil, since the signed id has expired
         | 
| 39 | 
            +
                  #
         | 
| 40 | 
            +
                  #   travel_back
         | 
| 41 | 
            +
                  #   User.find_signed signed_id, purpose: :password_reset # => User.first
         | 
| 42 | 
            +
                  def find_signed(signed_id, purpose: nil)
         | 
| 43 | 
            +
                    raise UnknownPrimaryKey.new(self) if primary_key.nil?
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    if id = signed_id_verifier.verified(signed_id, purpose: combine_signed_id_purposes(purpose))
         | 
| 46 | 
            +
                      find_by primary_key => id
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  # Works like +find_signed+, but will raise an +ActiveSupport::MessageVerifier::InvalidSignature+
         | 
| 51 | 
            +
                  # exception if the +signed_id+ has either expired, has a purpose mismatch, is for another record,
         | 
| 52 | 
            +
                  # or has been tampered with. It will also raise an +ActiveRecord::RecordNotFound+ exception if
         | 
| 53 | 
            +
                  # the valid signed id can't find a record.
         | 
| 54 | 
            +
                  #
         | 
| 55 | 
            +
                  # === Examples
         | 
| 56 | 
            +
                  #
         | 
| 57 | 
            +
                  #   User.find_signed! "bad data" # => ActiveSupport::MessageVerifier::InvalidSignature
         | 
| 58 | 
            +
                  #
         | 
| 59 | 
            +
                  #   signed_id = User.first.signed_id
         | 
| 60 | 
            +
                  #   User.first.destroy
         | 
| 61 | 
            +
                  #   User.find_signed! signed_id # => ActiveRecord::RecordNotFound
         | 
| 62 | 
            +
                  def find_signed!(signed_id, purpose: nil)
         | 
| 63 | 
            +
                    if id = signed_id_verifier.verify(signed_id, purpose: combine_signed_id_purposes(purpose))
         | 
| 64 | 
            +
                      find(id)
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  # The verifier instance that all signed ids are generated and verified from. By default, it'll be initialized
         | 
| 69 | 
            +
                  # with the class-level +signed_id_verifier_secret+, which within Rails comes from the
         | 
| 70 | 
            +
                  # Rails.application.key_generator. By default, it's SHA256 for the digest and JSON for the serialization.
         | 
| 71 | 
            +
                  def signed_id_verifier
         | 
| 72 | 
            +
                    @signed_id_verifier ||= begin
         | 
| 73 | 
            +
                      secret = signed_id_verifier_secret
         | 
| 74 | 
            +
                      secret = secret.call if secret.respond_to?(:call)
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                      if secret.nil?
         | 
| 77 | 
            +
                        raise ArgumentError, "You must set ActiveRecord::Base.signed_id_verifier_secret to use signed ids"
         | 
| 78 | 
            +
                      else
         | 
| 79 | 
            +
                        ActiveSupport::MessageVerifier.new secret, digest: "SHA256", serializer: JSON
         | 
| 80 | 
            +
                      end
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  # Allows you to pass in a custom verifier used for the signed ids. This also allows you to use different
         | 
| 85 | 
            +
                  # verifiers for different classes. This is also helpful if you need to rotate keys, as you can prepare
         | 
| 86 | 
            +
                  # your custom verifier for that in advance. See +ActiveSupport::MessageVerifier+ for details.
         | 
| 87 | 
            +
                  def signed_id_verifier=(verifier)
         | 
| 88 | 
            +
                    @signed_id_verifier = verifier
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  # :nodoc:
         | 
| 92 | 
            +
                  def combine_signed_id_purposes(purpose)
         | 
| 93 | 
            +
                    [ base_class.name.underscore, purpose.to_s ].compact_blank.join("/")
         | 
| 94 | 
            +
                  end
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
             | 
| 98 | 
            +
                # Returns a signed id that's generated using a preconfigured +ActiveSupport::MessageVerifier+ instance.
         | 
| 99 | 
            +
                # This signed id is tamper proof, so it's safe to send in an email or otherwise share with the outside world.
         | 
| 100 | 
            +
                # It can further more be set to expire (the default is not to expire), and scoped down with a specific purpose.
         | 
| 101 | 
            +
                # If the expiration date has been exceeded before +find_signed+ is called, the id won't find the designated
         | 
| 102 | 
            +
                # record. If a purpose is set, this too must match.
         | 
| 103 | 
            +
                #
         | 
| 104 | 
            +
                # If you accidentally let a signed id out in the wild that you wish to retract sooner than its expiration date
         | 
| 105 | 
            +
                # (or maybe you forgot to set an expiration date while meaning to!), you can use the purpose to essentially
         | 
| 106 | 
            +
                # version the signed_id, like so:
         | 
| 107 | 
            +
                #
         | 
| 108 | 
            +
                #   user.signed_id purpose: :v2
         | 
| 109 | 
            +
                #
         | 
| 110 | 
            +
                # And you then change your +find_signed+ calls to require this new purpose. Any old signed ids that were not
         | 
| 111 | 
            +
                # created with the purpose will no longer find the record.
         | 
| 112 | 
            +
                def signed_id(expires_in: nil, purpose: nil)
         | 
| 113 | 
            +
                  self.class.signed_id_verifier.generate id, expires_in: expires_in, purpose: self.class.combine_signed_id_purposes(purpose)
         | 
| 114 | 
            +
                end
         | 
| 115 | 
            +
              end
         | 
| 116 | 
            +
            end
         | 
| @@ -50,13 +50,20 @@ module ActiveRecord | |
| 50 50 |  | 
| 51 51 | 
             
                  def sql_for(binds, connection)
         | 
| 52 52 | 
             
                    val = @values.dup
         | 
| 53 | 
            -
                     | 
| 54 | 
            -
             | 
| 53 | 
            +
                    @indexes.each do |i|
         | 
| 54 | 
            +
                      value = binds.shift
         | 
| 55 | 
            +
                      if ActiveModel::Attribute === value
         | 
| 56 | 
            +
                        value = value.value_for_database
         | 
| 57 | 
            +
                      end
         | 
| 58 | 
            +
                      val[i] = connection.quote(value)
         | 
| 59 | 
            +
                    end
         | 
| 55 60 | 
             
                    val.join
         | 
| 56 61 | 
             
                  end
         | 
| 57 62 | 
             
                end
         | 
| 58 63 |  | 
| 59 64 | 
             
                class PartialQueryCollector
         | 
| 65 | 
            +
                  attr_accessor :preparable
         | 
| 66 | 
            +
             | 
| 60 67 | 
             
                  def initialize
         | 
| 61 68 | 
             
                    @parts = []
         | 
| 62 69 | 
             
                    @binds = []
         | 
| @@ -73,6 +80,15 @@ module ActiveRecord | |
| 73 80 | 
             
                    self
         | 
| 74 81 | 
             
                  end
         | 
| 75 82 |  | 
| 83 | 
            +
                  def add_binds(binds)
         | 
| 84 | 
            +
                    @binds.concat binds
         | 
| 85 | 
            +
                    binds.size.times do |i|
         | 
| 86 | 
            +
                      @parts << ", " unless i == 0
         | 
| 87 | 
            +
                      @parts << Substitute.new
         | 
| 88 | 
            +
                    end
         | 
| 89 | 
            +
                    self
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
             | 
| 76 92 | 
             
                  def value
         | 
| 77 93 | 
             
                    [@parts, @binds]
         | 
| 78 94 | 
             
                  end
         | 
| @@ -100,7 +116,7 @@ module ActiveRecord | |
| 100 116 | 
             
                    @bound_attributes = bound_attributes
         | 
| 101 117 |  | 
| 102 118 | 
             
                    bound_attributes.each_with_index do |attr, i|
         | 
| 103 | 
            -
                      if Substitute === attr.value
         | 
| 119 | 
            +
                      if ActiveModel::Attribute === attr && Substitute === attr.value
         | 
| 104 120 | 
             
                        @indexes << i
         | 
| 105 121 | 
             
                      end
         | 
| 106 122 | 
             
                    end
         | 
| @@ -133,7 +149,7 @@ module ActiveRecord | |
| 133 149 |  | 
| 134 150 | 
             
                  klass.find_by_sql(sql, bind_values, preparable: true, &block)
         | 
| 135 151 | 
             
                rescue ::RangeError
         | 
| 136 | 
            -
                   | 
| 152 | 
            +
                  []
         | 
| 137 153 | 
             
                end
         | 
| 138 154 |  | 
| 139 155 | 
             
                def self.unsupported_value?(value)
         | 
    
        data/lib/active_record/store.rb
    CHANGED
    
    | @@ -23,7 +23,7 @@ module ActiveRecord | |
| 23 23 | 
             
              # You can set custom coder to encode/decode your serialized attributes to/from different formats.
         | 
| 24 24 | 
             
              # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
         | 
| 25 25 | 
             
              #
         | 
| 26 | 
            -
              # NOTE: If you are using structured database data types ( | 
| 26 | 
            +
              # NOTE: If you are using structured database data types (e.g. PostgreSQL +hstore+/+json+, or MySQL 5.7+
         | 
| 27 27 | 
             
              # +json+) there is no need for the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
         | 
| 28 28 | 
             
              # Simply use {.store_accessor}[rdoc-ref:ClassMethods#store_accessor] instead to generate
         | 
| 29 29 | 
             
              # the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
         | 
| @@ -251,7 +251,7 @@ module ActiveRecord | |
| 251 251 | 
             
                      attribute = object.send(store_attribute)
         | 
| 252 252 | 
             
                      unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess)
         | 
| 253 253 | 
             
                        attribute = IndifferentCoder.as_indifferent_hash(attribute)
         | 
| 254 | 
            -
                        object. | 
| 254 | 
            +
                        object.public_send :"#{store_attribute}=", attribute
         | 
| 255 255 | 
             
                      end
         | 
| 256 256 | 
             
                      attribute
         | 
| 257 257 | 
             
                    end
         | 
| @@ -40,11 +40,11 @@ module ActiveRecord | |
| 40 40 | 
             
                  end
         | 
| 41 41 | 
             
                end
         | 
| 42 42 |  | 
| 43 | 
            -
                def save( | 
| 43 | 
            +
                def save(**) # :nodoc:
         | 
| 44 44 | 
             
                  SuppressorRegistry.suppressed[self.class.name] ? true : super
         | 
| 45 45 | 
             
                end
         | 
| 46 46 |  | 
| 47 | 
            -
                def save!( | 
| 47 | 
            +
                def save!(**) # :nodoc:
         | 
| 48 48 | 
             
                  SuppressorRegistry.suppressed[self.class.name] ? true : super
         | 
| 49 49 | 
             
                end
         | 
| 50 50 | 
             
              end
         | 
| @@ -2,89 +2,73 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module ActiveRecord
         | 
| 4 4 | 
             
              class TableMetadata # :nodoc:
         | 
| 5 | 
            -
                delegate : | 
| 5 | 
            +
                delegate :join_primary_key, :join_foreign_key, :join_foreign_type, to: :reflection
         | 
| 6 6 |  | 
| 7 | 
            -
                def initialize(klass, arel_table,  | 
| 7 | 
            +
                def initialize(klass, arel_table, reflection = nil)
         | 
| 8 8 | 
             
                  @klass = klass
         | 
| 9 | 
            -
                  @types = types
         | 
| 10 9 | 
             
                  @arel_table = arel_table
         | 
| 11 | 
            -
                  @ | 
| 12 | 
            -
                end
         | 
| 13 | 
            -
             | 
| 14 | 
            -
                def resolve_column_aliases(hash)
         | 
| 15 | 
            -
                  new_hash = hash.dup
         | 
| 16 | 
            -
                  hash.each_key do |key|
         | 
| 17 | 
            -
                    if key.is_a?(Symbol) && new_key = klass.attribute_aliases[key.to_s]
         | 
| 18 | 
            -
                      new_hash[new_key] = new_hash.delete(key)
         | 
| 19 | 
            -
                    end
         | 
| 20 | 
            -
                  end
         | 
| 21 | 
            -
                  new_hash
         | 
| 22 | 
            -
                end
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                def arel_attribute(column_name)
         | 
| 25 | 
            -
                  if klass
         | 
| 26 | 
            -
                    klass.arel_attribute(column_name, arel_table)
         | 
| 27 | 
            -
                  else
         | 
| 28 | 
            -
                    arel_table[column_name]
         | 
| 29 | 
            -
                  end
         | 
| 10 | 
            +
                  @reflection = reflection
         | 
| 30 11 | 
             
                end
         | 
| 31 12 |  | 
| 32 13 | 
             
                def type(column_name)
         | 
| 33 | 
            -
                   | 
| 14 | 
            +
                  arel_table.type_for_attribute(column_name)
         | 
| 34 15 | 
             
                end
         | 
| 35 16 |  | 
| 36 17 | 
             
                def has_column?(column_name)
         | 
| 37 | 
            -
                  klass | 
| 18 | 
            +
                  klass&.columns_hash.key?(column_name)
         | 
| 38 19 | 
             
                end
         | 
| 39 20 |  | 
| 40 | 
            -
                def associated_with?( | 
| 41 | 
            -
                  klass  | 
| 21 | 
            +
                def associated_with?(table_name)
         | 
| 22 | 
            +
                  klass&._reflect_on_association(table_name) || klass&._reflect_on_association(table_name.singularize)
         | 
| 42 23 | 
             
                end
         | 
| 43 24 |  | 
| 44 25 | 
             
                def associated_table(table_name)
         | 
| 45 | 
            -
                   | 
| 26 | 
            +
                  reflection = klass._reflect_on_association(table_name) || klass._reflect_on_association(table_name.singularize)
         | 
| 46 27 |  | 
| 47 | 
            -
                  if ! | 
| 48 | 
            -
                    self
         | 
| 49 | 
            -
                   | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 28 | 
            +
                  if !reflection && table_name == arel_table.name
         | 
| 29 | 
            +
                    return self
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  reflection ||= yield table_name if block_given?
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  if reflection && !reflection.polymorphic?
         | 
| 35 | 
            +
                    association_klass = reflection.klass
         | 
| 36 | 
            +
                    arel_table = association_klass.arel_table
         | 
| 37 | 
            +
                    arel_table = arel_table.alias(table_name) if arel_table.name != table_name
         | 
| 38 | 
            +
                    TableMetadata.new(association_klass, arel_table, reflection)
         | 
| 53 39 | 
             
                  else
         | 
| 54 40 | 
             
                    type_caster = TypeCaster::Connection.new(klass, table_name)
         | 
| 55 41 | 
             
                    arel_table = Arel::Table.new(table_name, type_caster: type_caster)
         | 
| 56 | 
            -
                    TableMetadata.new(nil, arel_table,  | 
| 42 | 
            +
                    TableMetadata.new(nil, arel_table, reflection)
         | 
| 57 43 | 
             
                  end
         | 
| 58 44 | 
             
                end
         | 
| 59 45 |  | 
| 60 | 
            -
                def associated_predicate_builder(table_name)
         | 
| 61 | 
            -
                  associated_table(table_name).predicate_builder
         | 
| 62 | 
            -
                end
         | 
| 63 | 
            -
             | 
| 64 46 | 
             
                def polymorphic_association?
         | 
| 65 | 
            -
                   | 
| 47 | 
            +
                  reflection&.polymorphic?
         | 
| 66 48 | 
             
                end
         | 
| 67 49 |  | 
| 68 | 
            -
                def  | 
| 69 | 
            -
                   | 
| 50 | 
            +
                def through_association?
         | 
| 51 | 
            +
                  reflection&.through_reflection?
         | 
| 70 52 | 
             
                end
         | 
| 71 53 |  | 
| 72 54 | 
             
                def reflect_on_aggregation(aggregation_name)
         | 
| 73 | 
            -
                  klass | 
| 55 | 
            +
                  klass&.reflect_on_aggregation(aggregation_name)
         | 
| 74 56 | 
             
                end
         | 
| 57 | 
            +
                alias :aggregated_with? :reflect_on_aggregation
         | 
| 75 58 |  | 
| 76 | 
            -
                 | 
| 77 | 
            -
                   | 
| 78 | 
            -
                     | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
                     | 
| 83 | 
            -
                      PredicateBuilder.new(self)
         | 
| 84 | 
            -
                    end
         | 
| 59 | 
            +
                def predicate_builder
         | 
| 60 | 
            +
                  if klass
         | 
| 61 | 
            +
                    predicate_builder = klass.predicate_builder.dup
         | 
| 62 | 
            +
                    predicate_builder.instance_variable_set(:@table, self)
         | 
| 63 | 
            +
                    predicate_builder
         | 
| 64 | 
            +
                  else
         | 
| 65 | 
            +
                    PredicateBuilder.new(self)
         | 
| 85 66 | 
             
                  end
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                attr_reader :arel_table
         | 
| 86 70 |  | 
| 87 71 | 
             
                private
         | 
| 88 | 
            -
                  attr_reader :klass, : | 
| 72 | 
            +
                  attr_reader :klass, :reflection
         | 
| 89 73 | 
             
              end
         | 
| 90 74 | 
             
            end
         |