activerecord 6.1.7.8 → 7.0.8.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (251) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1582 -1018
  3. data/README.rdoc +3 -3
  4. data/lib/active_record/aggregations.rb +1 -1
  5. data/lib/active_record/association_relation.rb +0 -10
  6. data/lib/active_record/associations/association.rb +33 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -3
  8. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  10. data/lib/active_record/associations/builder/association.rb +8 -2
  11. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  12. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  13. data/lib/active_record/associations/builder/has_many.rb +3 -2
  14. data/lib/active_record/associations/builder/has_one.rb +2 -1
  15. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  16. data/lib/active_record/associations/collection_association.rb +20 -22
  17. data/lib/active_record/associations/collection_proxy.rb +15 -5
  18. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  19. data/lib/active_record/associations/has_many_association.rb +8 -5
  20. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  21. data/lib/active_record/associations/has_one_association.rb +10 -7
  22. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency.rb +23 -15
  24. data/lib/active_record/associations/preloader/association.rb +186 -52
  25. data/lib/active_record/associations/preloader/batch.rb +48 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  28. data/lib/active_record/associations/preloader.rb +39 -113
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +3 -3
  31. data/lib/active_record/associations.rb +138 -100
  32. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  33. data/lib/active_record/attribute_assignment.rb +1 -1
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  35. data/lib/active_record/attribute_methods/dirty.rb +49 -16
  36. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  37. data/lib/active_record/attribute_methods/query.rb +2 -2
  38. data/lib/active_record/attribute_methods/read.rb +8 -6
  39. data/lib/active_record/attribute_methods/serialization.rb +57 -19
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +19 -22
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +8 -23
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +14 -16
  47. data/lib/active_record/coders/yaml_column.rb +4 -8
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +52 -23
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +82 -25
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +144 -82
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +115 -85
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +37 -25
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -23
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +4 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  68. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  70. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +19 -1
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -17
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  77. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  81. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  82. data/lib/active_record/connection_adapters/postgresql/quoting.rb +76 -73
  83. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
  84. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  85. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  86. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  87. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +40 -21
  88. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  89. data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -106
  90. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  91. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  92. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +33 -18
  93. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -0
  94. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +19 -17
  95. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +98 -36
  96. data/lib/active_record/connection_adapters.rb +6 -5
  97. data/lib/active_record/connection_handling.rb +49 -55
  98. data/lib/active_record/core.rb +123 -148
  99. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  100. data/lib/active_record/database_configurations/database_config.rb +12 -9
  101. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  102. data/lib/active_record/database_configurations/url_config.rb +2 -2
  103. data/lib/active_record/database_configurations.rb +15 -32
  104. data/lib/active_record/delegated_type.rb +53 -12
  105. data/lib/active_record/destroy_association_async_job.rb +1 -1
  106. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  107. data/lib/active_record/dynamic_matchers.rb +1 -1
  108. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  109. data/lib/active_record/encryption/cipher.rb +53 -0
  110. data/lib/active_record/encryption/config.rb +44 -0
  111. data/lib/active_record/encryption/configurable.rb +67 -0
  112. data/lib/active_record/encryption/context.rb +35 -0
  113. data/lib/active_record/encryption/contexts.rb +72 -0
  114. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  115. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  116. data/lib/active_record/encryption/encryptable_record.rb +206 -0
  117. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  118. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  119. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  120. data/lib/active_record/encryption/encryptor.rb +155 -0
  121. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  122. data/lib/active_record/encryption/errors.rb +15 -0
  123. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  124. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  125. data/lib/active_record/encryption/key.rb +28 -0
  126. data/lib/active_record/encryption/key_generator.rb +42 -0
  127. data/lib/active_record/encryption/key_provider.rb +46 -0
  128. data/lib/active_record/encryption/message.rb +33 -0
  129. data/lib/active_record/encryption/message_serializer.rb +90 -0
  130. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  131. data/lib/active_record/encryption/properties.rb +76 -0
  132. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  133. data/lib/active_record/encryption/scheme.rb +99 -0
  134. data/lib/active_record/encryption.rb +55 -0
  135. data/lib/active_record/enum.rb +50 -43
  136. data/lib/active_record/errors.rb +67 -4
  137. data/lib/active_record/explain_registry.rb +11 -6
  138. data/lib/active_record/explain_subscriber.rb +1 -1
  139. data/lib/active_record/fixture_set/file.rb +15 -1
  140. data/lib/active_record/fixture_set/table_row.rb +41 -6
  141. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  142. data/lib/active_record/fixtures.rb +20 -23
  143. data/lib/active_record/future_result.rb +139 -0
  144. data/lib/active_record/gem_version.rb +5 -5
  145. data/lib/active_record/inheritance.rb +55 -17
  146. data/lib/active_record/insert_all.rb +80 -14
  147. data/lib/active_record/integration.rb +4 -3
  148. data/lib/active_record/internal_metadata.rb +1 -5
  149. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  150. data/lib/active_record/locking/optimistic.rb +36 -21
  151. data/lib/active_record/locking/pessimistic.rb +10 -4
  152. data/lib/active_record/log_subscriber.rb +23 -7
  153. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  154. data/lib/active_record/middleware/database_selector.rb +18 -6
  155. data/lib/active_record/middleware/shard_selector.rb +60 -0
  156. data/lib/active_record/migration/command_recorder.rb +8 -9
  157. data/lib/active_record/migration/compatibility.rb +93 -46
  158. data/lib/active_record/migration/join_table.rb +1 -1
  159. data/lib/active_record/migration.rb +167 -87
  160. data/lib/active_record/model_schema.rb +58 -59
  161. data/lib/active_record/nested_attributes.rb +13 -12
  162. data/lib/active_record/no_touching.rb +3 -3
  163. data/lib/active_record/null_relation.rb +2 -6
  164. data/lib/active_record/persistence.rb +231 -61
  165. data/lib/active_record/query_cache.rb +2 -2
  166. data/lib/active_record/query_logs.rb +149 -0
  167. data/lib/active_record/querying.rb +16 -6
  168. data/lib/active_record/railtie.rb +136 -22
  169. data/lib/active_record/railties/controller_runtime.rb +4 -5
  170. data/lib/active_record/railties/databases.rake +78 -136
  171. data/lib/active_record/readonly_attributes.rb +11 -0
  172. data/lib/active_record/reflection.rb +80 -49
  173. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  174. data/lib/active_record/relation/batches.rb +6 -6
  175. data/lib/active_record/relation/calculations.rb +92 -60
  176. data/lib/active_record/relation/delegation.rb +7 -7
  177. data/lib/active_record/relation/finder_methods.rb +31 -35
  178. data/lib/active_record/relation/merger.rb +20 -13
  179. data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -1
  180. data/lib/active_record/relation/predicate_builder.rb +1 -6
  181. data/lib/active_record/relation/query_attribute.rb +28 -11
  182. data/lib/active_record/relation/query_methods.rb +304 -68
  183. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  184. data/lib/active_record/relation/spawn_methods.rb +2 -2
  185. data/lib/active_record/relation/where_clause.rb +10 -19
  186. data/lib/active_record/relation.rb +189 -88
  187. data/lib/active_record/result.rb +23 -11
  188. data/lib/active_record/runtime_registry.rb +9 -13
  189. data/lib/active_record/sanitization.rb +17 -12
  190. data/lib/active_record/schema.rb +38 -23
  191. data/lib/active_record/schema_dumper.rb +29 -19
  192. data/lib/active_record/schema_migration.rb +4 -4
  193. data/lib/active_record/scoping/default.rb +60 -13
  194. data/lib/active_record/scoping/named.rb +3 -11
  195. data/lib/active_record/scoping.rb +64 -34
  196. data/lib/active_record/serialization.rb +6 -1
  197. data/lib/active_record/signed_id.rb +3 -3
  198. data/lib/active_record/store.rb +2 -2
  199. data/lib/active_record/suppressor.rb +11 -15
  200. data/lib/active_record/table_metadata.rb +6 -2
  201. data/lib/active_record/tasks/database_tasks.rb +127 -60
  202. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  203. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
  204. data/lib/active_record/test_databases.rb +1 -1
  205. data/lib/active_record/test_fixtures.rb +9 -6
  206. data/lib/active_record/timestamp.rb +3 -4
  207. data/lib/active_record/transactions.rb +12 -17
  208. data/lib/active_record/translation.rb +3 -3
  209. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  210. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  211. data/lib/active_record/type/internal/timezone.rb +2 -2
  212. data/lib/active_record/type/serialized.rb +9 -5
  213. data/lib/active_record/type/type_map.rb +17 -20
  214. data/lib/active_record/type.rb +1 -2
  215. data/lib/active_record/validations/associated.rb +4 -4
  216. data/lib/active_record/validations/presence.rb +2 -2
  217. data/lib/active_record/validations/uniqueness.rb +4 -4
  218. data/lib/active_record/version.rb +1 -1
  219. data/lib/active_record.rb +225 -27
  220. data/lib/arel/attributes/attribute.rb +0 -8
  221. data/lib/arel/crud.rb +28 -22
  222. data/lib/arel/delete_manager.rb +18 -4
  223. data/lib/arel/filter_predications.rb +9 -0
  224. data/lib/arel/insert_manager.rb +2 -3
  225. data/lib/arel/nodes/and.rb +4 -0
  226. data/lib/arel/nodes/casted.rb +1 -1
  227. data/lib/arel/nodes/delete_statement.rb +12 -13
  228. data/lib/arel/nodes/filter.rb +10 -0
  229. data/lib/arel/nodes/function.rb +1 -0
  230. data/lib/arel/nodes/insert_statement.rb +2 -2
  231. data/lib/arel/nodes/select_core.rb +2 -2
  232. data/lib/arel/nodes/select_statement.rb +2 -2
  233. data/lib/arel/nodes/update_statement.rb +8 -3
  234. data/lib/arel/nodes.rb +1 -0
  235. data/lib/arel/predications.rb +11 -3
  236. data/lib/arel/select_manager.rb +10 -4
  237. data/lib/arel/table.rb +0 -1
  238. data/lib/arel/tree_manager.rb +0 -12
  239. data/lib/arel/update_manager.rb +18 -4
  240. data/lib/arel/visitors/dot.rb +80 -90
  241. data/lib/arel/visitors/mysql.rb +8 -2
  242. data/lib/arel/visitors/postgresql.rb +0 -10
  243. data/lib/arel/visitors/to_sql.rb +58 -2
  244. data/lib/arel.rb +2 -1
  245. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  246. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  247. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  248. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  249. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  250. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  251. metadata +53 -9
@@ -14,16 +14,43 @@ module ActiveRecord
14
14
  raise "You cannot include Dirty after Timestamp"
15
15
  end
16
16
 
17
- class_attribute :partial_writes, instance_writer: false, default: true
17
+ class_attribute :partial_updates, instance_writer: false, default: true
18
+ class_attribute :partial_inserts, instance_writer: false, default: true
18
19
 
19
20
  # Attribute methods for "changed in last call to save?"
20
- attribute_method_affix(prefix: "saved_change_to_", suffix: "?")
21
- attribute_method_prefix("saved_change_to_")
22
- attribute_method_suffix("_before_last_save")
21
+ attribute_method_affix(prefix: "saved_change_to_", suffix: "?", parameters: "**options")
22
+ attribute_method_prefix("saved_change_to_", parameters: false)
23
+ attribute_method_suffix("_before_last_save", parameters: false)
23
24
 
24
25
  # Attribute methods for "will change if I call save?"
25
- attribute_method_affix(prefix: "will_save_change_to_", suffix: "?")
26
- attribute_method_suffix("_change_to_be_saved", "_in_database")
26
+ attribute_method_affix(prefix: "will_save_change_to_", suffix: "?", parameters: "**options")
27
+ attribute_method_suffix("_change_to_be_saved", "_in_database", parameters: false)
28
+ end
29
+
30
+ module ClassMethods
31
+ def partial_writes
32
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
33
+ ActiveRecord::Base.partial_writes is deprecated and will be removed in Rails 7.1.
34
+ Use `partial_updates` and `partial_inserts` instead.
35
+ MSG
36
+ partial_updates && partial_inserts
37
+ end
38
+
39
+ def partial_writes?
40
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
41
+ `ActiveRecord::Base.partial_writes?` is deprecated and will be removed in Rails 7.1.
42
+ Use `partial_updates?` and `partial_inserts?` instead.
43
+ MSG
44
+ partial_updates? && partial_inserts?
45
+ end
46
+
47
+ def partial_writes=(value)
48
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
49
+ `ActiveRecord::Base.partial_writes=` is deprecated and will be removed in Rails 7.1.
50
+ Use `partial_updates=` and `partial_inserts=` instead.
51
+ MSG
52
+ self.partial_updates = self.partial_inserts = value
53
+ end
27
54
  end
28
55
 
29
56
  # <tt>reload</tt> the record and clears changed attributes.
@@ -156,12 +183,6 @@ module ActiveRecord
156
183
  end
157
184
 
158
185
  private
159
- def write_attribute_without_type_cast(attr_name, value)
160
- result = super
161
- clear_attribute_change(attr_name)
162
- result
163
- end
164
-
165
186
  def _touch_row(attribute_names, time)
166
187
  @_touch_attr_names = Set.new(attribute_names)
167
188
 
@@ -191,20 +212,32 @@ module ActiveRecord
191
212
  @_touch_attr_names, @_skip_dirty_tracking = nil, nil
192
213
  end
193
214
 
194
- def _update_record(attribute_names = attribute_names_for_partial_writes)
215
+ def _update_record(attribute_names = attribute_names_for_partial_updates)
195
216
  affected_rows = super
196
217
  changes_applied
197
218
  affected_rows
198
219
  end
199
220
 
200
- def _create_record(attribute_names = attribute_names_for_partial_writes)
221
+ def _create_record(attribute_names = attribute_names_for_partial_inserts)
201
222
  id = super
202
223
  changes_applied
203
224
  id
204
225
  end
205
226
 
206
- def attribute_names_for_partial_writes
207
- partial_writes? ? changed_attribute_names_to_save : attribute_names
227
+ def attribute_names_for_partial_updates
228
+ partial_updates? ? changed_attribute_names_to_save : attribute_names
229
+ end
230
+
231
+ def attribute_names_for_partial_inserts
232
+ if partial_inserts?
233
+ changed_attribute_names_to_save
234
+ else
235
+ attribute_names.reject do |attr_name|
236
+ if column_for_attribute(attr_name).default_function
237
+ !attribute_changed?(attr_name)
238
+ end
239
+ end
240
+ end
208
241
  end
209
242
  end
210
243
  end
@@ -78,7 +78,7 @@ module ActiveRecord
78
78
  @quoted_primary_key ||= connection.quote_column_name(primary_key)
79
79
  end
80
80
 
81
- def reset_primary_key #:nodoc:
81
+ def reset_primary_key # :nodoc:
82
82
  if base_class?
83
83
  self.primary_key = get_primary_key(base_class.name)
84
84
  else
@@ -86,7 +86,7 @@ module ActiveRecord
86
86
  end
87
87
  end
88
88
 
89
- def get_primary_key(base_name) #:nodoc:
89
+ def get_primary_key(base_name) # :nodoc:
90
90
  if base_name && primary_key_prefix_type == :table_name
91
91
  base_name.foreign_key(false)
92
92
  elsif base_name && primary_key_prefix_type == :table_name_with_underscore
@@ -6,11 +6,11 @@ module ActiveRecord
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
- attribute_method_suffix "?"
9
+ attribute_method_suffix "?", parameters: false
10
10
  end
11
11
 
12
12
  def query_attribute(attr_name)
13
- value = self[attr_name]
13
+ value = self.public_send(attr_name)
14
14
 
15
15
  case value
16
16
  when true then true
@@ -11,17 +11,19 @@ module ActiveRecord
11
11
  ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
12
12
  owner, name
13
13
  ) do |temp_method_name, attr_name_expr|
14
- owner <<
15
- "def #{temp_method_name}" <<
16
- " _read_attribute(#{attr_name_expr}) { |n| missing_attribute(n, caller) }" <<
17
- "end"
14
+ owner.define_cached_method(name, as: temp_method_name, namespace: :active_record) do |batch|
15
+ batch <<
16
+ "def #{temp_method_name}" <<
17
+ " _read_attribute(#{attr_name_expr}) { |n| missing_attribute(n, caller) }" <<
18
+ "end"
19
+ end
18
20
  end
19
21
  end
20
22
  end
21
23
 
22
24
  # Returns the value of the attribute identified by <tt>attr_name</tt> after
23
25
  # it has been typecast (for example, "2004-12-12" in a date column is cast
24
- # to a date object, like Date.new(2004, 12, 12)).
26
+ # to a date object, like <tt>Date.new(2004, 12, 12)</tt>).
25
27
  def read_attribute(attr_name, &block)
26
28
  name = attr_name.to_s
27
29
  name = self.class.attribute_aliases[name] || name
@@ -32,7 +34,7 @@ module ActiveRecord
32
34
 
33
35
  # This method exists to avoid the expensive primary_key check internally, without
34
36
  # breaking compatibility with the read_attribute API
35
- def _read_attribute(attr_name, &block) # :nodoc
37
+ def _read_attribute(attr_name, &block) # :nodoc:
36
38
  @attributes.fetch_value(attr_name, &block)
37
39
  end
38
40
 
@@ -16,15 +16,13 @@ module ActiveRecord
16
16
  end
17
17
 
18
18
  module ClassMethods
19
- # If you have an attribute that needs to be saved to the database as an
20
- # object, and retrieved as the same object, then specify the name of that
21
- # attribute using this method and it will be handled automatically. The
22
- # serialization is done through YAML. If +class_name+ is specified, the
23
- # serialized object must be of that class on assignment and retrieval.
24
- # Otherwise SerializationTypeMismatch will be raised.
19
+ # If you have an attribute that needs to be saved to the database as a
20
+ # serialized object, and retrieved by deserializing into the same object,
21
+ # then specify the name of that attribute using this method and serialization
22
+ # will be handled automatically.
25
23
  #
26
- # Empty objects as <tt>{}</tt>, in the case of +Hash+, or <tt>[]</tt>, in the case of
27
- # +Array+, will always be persisted as null.
24
+ # The serialization format may be YAML, JSON, or any custom format using a
25
+ # custom coder class.
28
26
  #
29
27
  # Keep in mind that database adapters handle certain serialization tasks
30
28
  # for you. For instance: +json+ and +jsonb+ types in PostgreSQL will be
@@ -37,32 +35,71 @@ module ActiveRecord
37
35
  #
38
36
  # ==== Parameters
39
37
  #
40
- # * +attr_name+ - The field name that should be serialized.
41
- # * +class_name_or_coder+ - Optional, a coder object, which responds to +.load+ and +.dump+
42
- # or a class name that the object type should be equal to.
38
+ # * +attr_name+ - The name of the attribute to serialize.
39
+ # * +class_name_or_coder+ - Optional. May be one of the following:
40
+ # * <em>default</em> - The attribute value will be serialized as YAML.
41
+ # The attribute value must respond to +to_yaml+.
42
+ # * +Array+ - The attribute value will be serialized as YAML, but an
43
+ # empty +Array+ will be serialized as +NULL+. The attribute value
44
+ # must be an +Array+.
45
+ # * +Hash+ - The attribute value will be serialized as YAML, but an
46
+ # empty +Hash+ will be serialized as +NULL+. The attribute value
47
+ # must be a +Hash+.
48
+ # * +JSON+ - The attribute value will be serialized as JSON. The
49
+ # attribute value must respond to +to_json+.
50
+ # * <em>custom coder</em> - The attribute value will be serialized
51
+ # using the coder's <tt>dump(value)</tt> method, and will be
52
+ # deserialized using the coder's <tt>load(string)</tt> method. The
53
+ # +dump+ method may return +nil+ to serialize the value as +NULL+.
43
54
  #
44
55
  # ==== Options
45
56
  #
46
- # +default+ The default value to use when no value is provided. If this option
47
- # is not passed, the previous default value (if any) will be used.
48
- # Otherwise, the default will be +nil+.
57
+ # * +:default+ - The default value to use when no value is provided. If
58
+ # this option is not passed, the previous default value (if any) will
59
+ # be used. Otherwise, the default will be +nil+.
49
60
  #
50
- # ==== Example
61
+ # ==== Examples
62
+ #
63
+ # ===== Serialize the +preferences+ attribute using YAML
51
64
  #
52
- # # Serialize a preferences attribute.
53
65
  # class User < ActiveRecord::Base
54
66
  # serialize :preferences
55
67
  # end
56
68
  #
57
- # # Serialize preferences using JSON as coder.
69
+ # ===== Serialize the +preferences+ attribute using JSON
70
+ #
58
71
  # class User < ActiveRecord::Base
59
72
  # serialize :preferences, JSON
60
73
  # end
61
74
  #
62
- # # Serialize preferences as Hash using YAML coder.
75
+ # ===== Serialize the +preferences+ +Hash+ using YAML
76
+ #
63
77
  # class User < ActiveRecord::Base
64
78
  # serialize :preferences, Hash
65
79
  # end
80
+ #
81
+ # ===== Serialize the +preferences+ attribute using a custom coder
82
+ #
83
+ # class Rot13JSON
84
+ # def self.rot13(string)
85
+ # string.tr("a-zA-Z", "n-za-mN-ZA-M")
86
+ # end
87
+ #
88
+ # # Serializes an attribute value to a string that will be stored in the database.
89
+ # def self.dump(value)
90
+ # rot13(ActiveSupport::JSON.dump(value))
91
+ # end
92
+ #
93
+ # # Deserializes a string from the database to an attribute value.
94
+ # def self.load(string)
95
+ # ActiveSupport::JSON.load(rot13(string))
96
+ # end
97
+ # end
98
+ #
99
+ # class User < ActiveRecord::Base
100
+ # serialize :preferences, Rot13JSON
101
+ # end
102
+ #
66
103
  def serialize(attr_name, class_name_or_coder = Object, **options)
67
104
  # When ::JSON is used, force it to go through the Active Support JSON encoder
68
105
  # to ensure special objects (e.g. Active Record models) are dumped correctly
@@ -75,11 +112,12 @@ module ActiveRecord
75
112
  Coders::YAMLColumn.new(attr_name, class_name_or_coder)
76
113
  end
77
114
 
78
- decorate_attribute_type(attr_name.to_s, **options) do |cast_type|
115
+ attribute(attr_name, **options) do |cast_type|
79
116
  if type_incompatible_with_serialize?(cast_type, class_name_or_coder)
80
117
  raise ColumnNotSerializableError.new(attr_name, cast_type)
81
118
  end
82
119
 
120
+ cast_type = cast_type.subtype if Type::Serialized === cast_type
83
121
  Type::Serialized.new(cast_type, coder)
84
122
  end
85
123
  end
@@ -25,6 +25,8 @@ module ActiveRecord
25
25
  rescue ArgumentError
26
26
  nil
27
27
  end
28
+ elsif value.respond_to?(:infinite?) && value.infinite?
29
+ value
28
30
  else
29
31
  map_avoiding_infinite_recursion(super) { |v| cast(v) }
30
32
  end
@@ -36,7 +38,7 @@ module ActiveRecord
36
38
 
37
39
  if value.acts_like?(:time)
38
40
  value.in_time_zone
39
- elsif value.is_a?(::Float)
41
+ elsif value.respond_to?(:infinite?) && value.infinite?
40
42
  value
41
43
  else
42
44
  map_avoiding_infinite_recursion(value) { |v| convert_time_to_time_zone(v) }
@@ -61,8 +63,7 @@ module ActiveRecord
61
63
  extend ActiveSupport::Concern
62
64
 
63
65
  included do
64
- mattr_accessor :time_zone_aware_attributes, instance_writer: false, default: false
65
-
66
+ class_attribute :time_zone_aware_attributes, instance_writer: false, default: false
66
67
  class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false, default: []
67
68
  class_attribute :time_zone_aware_types, instance_writer: false, default: [ :datetime, :time ]
68
69
  end
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
- attribute_method_suffix "="
9
+ attribute_method_suffix "=", parameters: "value"
10
10
  end
11
11
 
12
12
  module ClassMethods # :nodoc:
@@ -15,10 +15,12 @@ module ActiveRecord
15
15
  ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
16
16
  owner, name, writer: true,
17
17
  ) do |temp_method_name, attr_name_expr|
18
- owner <<
19
- "def #{temp_method_name}(value)" <<
20
- " _write_attribute(#{attr_name_expr}, value)" <<
21
- "end"
18
+ owner.define_cached_method("#{name}=", as: temp_method_name, namespace: :active_record) do |batch|
19
+ batch <<
20
+ "def #{temp_method_name}(value)" <<
21
+ " _write_attribute(#{attr_name_expr}, value)" <<
22
+ "end"
23
+ end
22
24
  end
23
25
  end
24
26
  end
@@ -42,11 +44,6 @@ module ActiveRecord
42
44
 
43
45
  alias :attribute= :_write_attribute
44
46
  private :attribute=
45
-
46
- private
47
- def write_attribute_without_type_cast(attr_name, value)
48
- @attributes.write_cast_value(attr_name, value)
49
- end
50
47
  end
51
48
  end
52
49
  end
@@ -23,7 +23,7 @@ module ActiveRecord
23
23
 
24
24
  RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
25
25
 
26
- class GeneratedAttributeMethods < Module #:nodoc:
26
+ class GeneratedAttributeMethods < Module # :nodoc:
27
27
  include Mutex_m
28
28
  end
29
29
 
@@ -39,7 +39,7 @@ module ActiveRecord
39
39
  end
40
40
 
41
41
  module ClassMethods
42
- def inherited(child_class) #:nodoc:
42
+ def inherited(child_class) # :nodoc:
43
43
  child_class.initialize_generated_modules
44
44
  super
45
45
  end
@@ -97,7 +97,7 @@ module ActiveRecord
97
97
  super
98
98
  else
99
99
  # If ThisClass < ... < SomeSuperClass < ... < Base and SomeSuperClass
100
- # defines its own attribute method, then we don't want to overwrite that.
100
+ # defines its own attribute method, then we don't want to override that.
101
101
  defined = method_defined_within?(method_name, superclass, Base) &&
102
102
  ! superclass.instance_method(method_name).owner.is_a?(GeneratedAttributeMethods)
103
103
  defined || super
@@ -267,9 +267,8 @@ module ActiveRecord
267
267
 
268
268
  # Returns an <tt>#inspect</tt>-like string for the value of the
269
269
  # attribute +attr_name+. String attributes are truncated up to 50
270
- # characters, Date and Time attributes are returned in the
271
- # <tt>:db</tt> format. Other attributes return the value of
272
- # <tt>#inspect</tt> without modification.
270
+ # characters. Other attributes return the value of <tt>#inspect</tt>
271
+ # without modification.
273
272
  #
274
273
  # person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
275
274
  #
@@ -277,7 +276,7 @@ module ActiveRecord
277
276
  # # => "\"David Heinemeier Hansson David Heinemeier Hansson ...\""
278
277
  #
279
278
  # person.attribute_for_inspect(:created_at)
280
- # # => "\"2012-10-22 00:15:07\""
279
+ # # => "\"2012-10-22 00:15:07.000000000 +0000\""
281
280
  #
282
281
  # person.attribute_for_inspect(:tag_ids)
283
282
  # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
@@ -311,8 +310,8 @@ module ActiveRecord
311
310
  end
312
311
 
313
312
  # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
314
- # "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
315
- # <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
313
+ # "2004-12-12" in a date column is cast to a date object, like <tt>Date.new(2004, 12, 12)</tt>). It raises
314
+ # ActiveModel::MissingAttributeError if the identified attribute is missing.
316
315
  #
317
316
  # Note: +:id+ is always present.
318
317
  #
@@ -332,7 +331,6 @@ module ActiveRecord
332
331
  end
333
332
 
334
333
  # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
335
- # (Alias for the protected #write_attribute method).
336
334
  #
337
335
  # class Person < ActiveRecord::Base
338
336
  # end
@@ -361,10 +359,9 @@ module ActiveRecord
361
359
  # end
362
360
  #
363
361
  # private
364
- #
365
- # def print_accessed_fields
366
- # p @posts.first.accessed_fields
367
- # end
362
+ # def print_accessed_fields
363
+ # p @posts.first.accessed_fields
364
+ # end
368
365
  # end
369
366
  #
370
367
  # Which allows you to quickly change your code to:
@@ -385,25 +382,25 @@ module ActiveRecord
385
382
  end
386
383
 
387
384
  def attributes_with_values(attribute_names)
388
- attribute_names.index_with do |name|
389
- _read_attribute(name)
390
- end
385
+ attribute_names.index_with { |name| @attributes[name] }
391
386
  end
392
387
 
393
- # Filters the primary keys and readonly attributes from the attribute names.
388
+ # Filters the primary keys, readonly attributes and virtual columns from the attribute names.
394
389
  def attributes_for_update(attribute_names)
395
390
  attribute_names &= self.class.column_names
396
391
  attribute_names.delete_if do |name|
397
- self.class.readonly_attribute?(name)
392
+ self.class.readonly_attribute?(name) ||
393
+ column_for_attribute(name).virtual?
398
394
  end
399
395
  end
400
396
 
401
- # Filters out the primary keys, from the attribute names, when the primary
397
+ # Filters out the virtual columns and also primary keys, from the attribute names, when the primary
402
398
  # key is to be generated (e.g. the id attribute has no value).
403
399
  def attributes_for_create(attribute_names)
404
400
  attribute_names &= self.class.column_names
405
401
  attribute_names.delete_if do |name|
406
- pk_attribute?(name) && id.nil?
402
+ (pk_attribute?(name) && id.nil?) ||
403
+ column_for_attribute(name).virtual?
407
404
  end
408
405
  end
409
406
 
@@ -414,7 +411,7 @@ module ActiveRecord
414
411
  inspected_value = if value.is_a?(String) && value.length > 50
415
412
  "#{value[0, 50]}...".inspect
416
413
  elsif value.is_a?(Date) || value.is_a?(Time)
417
- %("#{value.to_s(:inspect)}")
414
+ %("#{value.to_fs(:inspect)}")
418
415
  else
419
416
  value.inspect
420
417
  end
@@ -12,9 +12,6 @@ module ActiveRecord
12
12
  end
13
13
 
14
14
  module ClassMethods
15
- ##
16
- # :call-seq: attribute(name, cast_type = nil, **options)
17
- #
18
15
  # Defines an attribute with a type on this model. It will override the
19
16
  # type of existing attributes if needed. This allows control over how
20
17
  # values are converted to and from SQL when assigned to a model. It also
@@ -208,14 +205,31 @@ module ActiveRecord
208
205
  # tracking is performed. The methods +changed?+ and +changed_in_place?+
209
206
  # will be called from ActiveModel::Dirty. See the documentation for those
210
207
  # methods in ActiveModel::Type::Value for more details.
211
- def attribute(name, cast_type = nil, **options, &block)
208
+ def attribute(name, cast_type = nil, default: NO_DEFAULT_PROVIDED, **options)
212
209
  name = name.to_s
210
+ name = attribute_aliases[name] || name
211
+
213
212
  reload_schema_from_cache
214
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
+
215
231
  self.attributes_to_define_after_schema_loads =
216
- attributes_to_define_after_schema_loads.merge(
217
- name => [cast_type || block, options]
218
- )
232
+ attributes_to_define_after_schema_loads.merge(name => [cast_type, default])
219
233
  end
220
234
 
221
235
  # This is the low level API which sits beneath +attribute+. It only
@@ -248,8 +262,9 @@ module ActiveRecord
248
262
 
249
263
  def load_schema! # :nodoc:
250
264
  super
251
- attributes_to_define_after_schema_loads.each do |name, (type, options)|
252
- define_attribute(name, _lookup_cast_type(name, type, options), **options.slice(:default))
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)
253
268
  end
254
269
  end
255
270
 
@@ -272,32 +287,6 @@ module ActiveRecord
272
287
  end
273
288
  _default_attributes[name] = default_attribute
274
289
  end
275
-
276
- def decorate_attribute_type(attr_name, **default)
277
- type, options = attributes_to_define_after_schema_loads[attr_name]
278
-
279
- default.with_defaults!(default: options[:default]) if options&.key?(:default)
280
-
281
- attribute(attr_name, **default) do |cast_type|
282
- if type && !type.is_a?(Proc)
283
- cast_type = _lookup_cast_type(attr_name, type, options)
284
- end
285
-
286
- yield cast_type
287
- end
288
- end
289
-
290
- def _lookup_cast_type(name, type, options)
291
- case type
292
- when Symbol
293
- adapter_name = ActiveRecord::Type.adapter_name_from(self)
294
- ActiveRecord::Type.lookup(type, **options.except(:default), adapter: adapter_name)
295
- when Proc
296
- type[type_for_attribute(name)]
297
- else
298
- type || type_for_attribute(name)
299
- end
300
- end
301
290
  end
302
291
  end
303
292
  end
@@ -138,7 +138,7 @@ module ActiveRecord
138
138
  module AutosaveAssociation
139
139
  extend ActiveSupport::Concern
140
140
 
141
- module AssociationBuilderExtension #:nodoc:
141
+ module AssociationBuilderExtension # :nodoc:
142
142
  def self.build(model, reflection)
143
143
  model.send(:add_autosave_association_callbacks, reflection)
144
144
  end
@@ -150,25 +150,10 @@ module ActiveRecord
150
150
 
151
151
  included do
152
152
  Associations::Builder::Association.extensions << AssociationBuilderExtension
153
- mattr_accessor :index_nested_attribute_errors, instance_writer: false, default: false
154
153
  end
155
154
 
156
155
  module ClassMethods # :nodoc:
157
156
  private
158
- if Module.method(:method_defined?).arity == 1 # MRI 2.5 and older
159
- using Module.new {
160
- refine Module do
161
- def method_defined?(method, inherit = true)
162
- if inherit
163
- super(method)
164
- else
165
- instance_methods(false).include?(method.to_sym)
166
- end
167
- end
168
- end
169
- }
170
- end
171
-
172
157
  def define_non_cyclic_method(name, &block)
173
158
  return if method_defined?(name, false)
174
159
 
@@ -210,7 +195,7 @@ module ActiveRecord
210
195
  after_create save_method
211
196
  after_update save_method
212
197
  elsif reflection.has_one?
213
- define_method(save_method) { save_has_one_association(reflection) } unless method_defined?(save_method)
198
+ define_non_cyclic_method(save_method) { save_has_one_association(reflection) }
214
199
  # Configures two callbacks instead of a single after_save so that
215
200
  # the model may rely on their execution order relative to its
216
201
  # own callbacks.
@@ -349,7 +334,7 @@ module ActiveRecord
349
334
 
350
335
  unless valid = record.valid?(context)
351
336
  if reflection.options[:autosave]
352
- indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord::Base.index_nested_attribute_errors)
337
+ indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord.index_nested_attribute_errors)
353
338
 
354
339
  record.errors.group_by_attribute.each { |attribute, errors|
355
340
  attribute = normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
@@ -419,6 +404,8 @@ module ActiveRecord
419
404
  saved = true
420
405
 
421
406
  if autosave != false && (new_record_before_save || record.new_record?)
407
+ association.set_inverse_instance(record)
408
+
422
409
  if autosave
423
410
  saved = association.insert_record(record, false)
424
411
  elsif !reflection.nested?
@@ -459,12 +446,10 @@ module ActiveRecord
459
446
  elsif autosave != false
460
447
  key = reflection.options[:primary_key] ? public_send(reflection.options[:primary_key]) : id
461
448
 
462
- if (autosave && record.changed_for_autosave?) || record_changed?(reflection, record, key)
449
+ if (autosave && record.changed_for_autosave?) || _record_changed?(reflection, record, key)
463
450
  unless reflection.through_reflection
464
451
  record[reflection.foreign_key] = key
465
- if inverse_reflection = reflection.inverse_of
466
- record.association(inverse_reflection.name).inversed_from(self)
467
- end
452
+ association.set_inverse_instance(record)
468
453
  end
469
454
 
470
455
  saved = record.save(validate: !autosave)
@@ -476,7 +461,7 @@ module ActiveRecord
476
461
  end
477
462
 
478
463
  # If the record is new or it has changed, returns true.
479
- def record_changed?(reflection, record, key)
464
+ def _record_changed?(reflection, record, key)
480
465
  record.new_record? ||
481
466
  association_foreign_key_changed?(reflection, record, key) ||
482
467
  record.will_save_change_to_attribute?(reflection.foreign_key)