activerecord 7.1.5.1 → 7.2.0.beta1

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