activerecord 6.1.6 → 7.0.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (243) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1314 -975
  3. data/README.rdoc +1 -1
  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 +19 -21
  17. data/lib/active_record/associations/collection_proxy.rb +10 -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 +49 -13
  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 +124 -95
  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 +7 -5
  39. data/lib/active_record/attribute_methods/serialization.rb +57 -19
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +14 -15
  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 +2 -2
  47. data/lib/active_record/coders/yaml_column.rb +10 -2
  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 +38 -13
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -24
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +105 -81
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -24
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +37 -21
  66. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  69. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  70. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  80. data/lib/active_record/connection_adapters/postgresql/quoting.rb +51 -51
  81. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  83. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  84. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  85. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +37 -19
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +208 -107
  87. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  88. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  89. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +17 -15
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +96 -32
  92. data/lib/active_record/connection_adapters.rb +6 -5
  93. data/lib/active_record/connection_handling.rb +49 -55
  94. data/lib/active_record/core.rb +124 -134
  95. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  96. data/lib/active_record/database_configurations/database_config.rb +12 -9
  97. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  98. data/lib/active_record/database_configurations/url_config.rb +2 -2
  99. data/lib/active_record/database_configurations.rb +15 -32
  100. data/lib/active_record/delegated_type.rb +53 -12
  101. data/lib/active_record/destroy_association_async_job.rb +1 -1
  102. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  103. data/lib/active_record/dynamic_matchers.rb +1 -1
  104. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  105. data/lib/active_record/encryption/cipher.rb +53 -0
  106. data/lib/active_record/encryption/config.rb +44 -0
  107. data/lib/active_record/encryption/configurable.rb +67 -0
  108. data/lib/active_record/encryption/context.rb +35 -0
  109. data/lib/active_record/encryption/contexts.rb +72 -0
  110. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  111. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  112. data/lib/active_record/encryption/encryptable_record.rb +206 -0
  113. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  114. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  115. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  116. data/lib/active_record/encryption/encryptor.rb +155 -0
  117. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  118. data/lib/active_record/encryption/errors.rb +15 -0
  119. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  120. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  121. data/lib/active_record/encryption/key.rb +28 -0
  122. data/lib/active_record/encryption/key_generator.rb +42 -0
  123. data/lib/active_record/encryption/key_provider.rb +46 -0
  124. data/lib/active_record/encryption/message.rb +33 -0
  125. data/lib/active_record/encryption/message_serializer.rb +90 -0
  126. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  127. data/lib/active_record/encryption/properties.rb +76 -0
  128. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  129. data/lib/active_record/encryption/scheme.rb +99 -0
  130. data/lib/active_record/encryption.rb +55 -0
  131. data/lib/active_record/enum.rb +50 -43
  132. data/lib/active_record/errors.rb +67 -4
  133. data/lib/active_record/explain_registry.rb +11 -6
  134. data/lib/active_record/fixture_set/file.rb +15 -1
  135. data/lib/active_record/fixture_set/table_row.rb +41 -6
  136. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  137. data/lib/active_record/fixtures.rb +20 -23
  138. data/lib/active_record/future_result.rb +139 -0
  139. data/lib/active_record/gem_version.rb +4 -4
  140. data/lib/active_record/inheritance.rb +55 -17
  141. data/lib/active_record/insert_all.rb +80 -14
  142. data/lib/active_record/integration.rb +4 -3
  143. data/lib/active_record/internal_metadata.rb +1 -5
  144. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  145. data/lib/active_record/locking/optimistic.rb +10 -9
  146. data/lib/active_record/locking/pessimistic.rb +10 -4
  147. data/lib/active_record/log_subscriber.rb +23 -7
  148. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  149. data/lib/active_record/middleware/database_selector.rb +18 -6
  150. data/lib/active_record/middleware/shard_selector.rb +60 -0
  151. data/lib/active_record/migration/command_recorder.rb +7 -7
  152. data/lib/active_record/migration/compatibility.rb +84 -2
  153. data/lib/active_record/migration/join_table.rb +1 -1
  154. data/lib/active_record/migration.rb +114 -83
  155. data/lib/active_record/model_schema.rb +58 -59
  156. data/lib/active_record/nested_attributes.rb +13 -12
  157. data/lib/active_record/no_touching.rb +3 -3
  158. data/lib/active_record/null_relation.rb +2 -6
  159. data/lib/active_record/persistence.rb +228 -60
  160. data/lib/active_record/query_cache.rb +2 -2
  161. data/lib/active_record/query_logs.rb +138 -0
  162. data/lib/active_record/querying.rb +16 -6
  163. data/lib/active_record/railtie.rb +136 -22
  164. data/lib/active_record/railties/controller_runtime.rb +1 -1
  165. data/lib/active_record/railties/databases.rake +78 -136
  166. data/lib/active_record/readonly_attributes.rb +11 -0
  167. data/lib/active_record/reflection.rb +73 -50
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  169. data/lib/active_record/relation/batches.rb +6 -6
  170. data/lib/active_record/relation/calculations.rb +43 -38
  171. data/lib/active_record/relation/delegation.rb +7 -7
  172. data/lib/active_record/relation/finder_methods.rb +31 -35
  173. data/lib/active_record/relation/merger.rb +20 -13
  174. data/lib/active_record/relation/predicate_builder.rb +1 -6
  175. data/lib/active_record/relation/query_attribute.rb +5 -11
  176. data/lib/active_record/relation/query_methods.rb +276 -67
  177. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  178. data/lib/active_record/relation/spawn_methods.rb +2 -2
  179. data/lib/active_record/relation/where_clause.rb +10 -19
  180. data/lib/active_record/relation.rb +189 -88
  181. data/lib/active_record/result.rb +17 -7
  182. data/lib/active_record/runtime_registry.rb +9 -13
  183. data/lib/active_record/sanitization.rb +17 -12
  184. data/lib/active_record/schema.rb +38 -23
  185. data/lib/active_record/schema_dumper.rb +25 -19
  186. data/lib/active_record/schema_migration.rb +4 -4
  187. data/lib/active_record/scoping/default.rb +60 -13
  188. data/lib/active_record/scoping/named.rb +3 -11
  189. data/lib/active_record/scoping.rb +64 -34
  190. data/lib/active_record/serialization.rb +6 -1
  191. data/lib/active_record/signed_id.rb +3 -3
  192. data/lib/active_record/store.rb +7 -2
  193. data/lib/active_record/suppressor.rb +11 -15
  194. data/lib/active_record/tasks/database_tasks.rb +127 -60
  195. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  196. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
  197. data/lib/active_record/test_databases.rb +1 -1
  198. data/lib/active_record/test_fixtures.rb +16 -9
  199. data/lib/active_record/timestamp.rb +3 -4
  200. data/lib/active_record/transactions.rb +9 -14
  201. data/lib/active_record/translation.rb +3 -3
  202. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  203. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  204. data/lib/active_record/type/internal/timezone.rb +2 -2
  205. data/lib/active_record/type/serialized.rb +1 -1
  206. data/lib/active_record/type/type_map.rb +17 -20
  207. data/lib/active_record/type.rb +1 -2
  208. data/lib/active_record/validations/associated.rb +4 -4
  209. data/lib/active_record/validations/presence.rb +2 -2
  210. data/lib/active_record/validations/uniqueness.rb +4 -4
  211. data/lib/active_record/version.rb +1 -1
  212. data/lib/active_record.rb +217 -27
  213. data/lib/arel/attributes/attribute.rb +0 -8
  214. data/lib/arel/crud.rb +28 -22
  215. data/lib/arel/delete_manager.rb +18 -4
  216. data/lib/arel/filter_predications.rb +9 -0
  217. data/lib/arel/insert_manager.rb +2 -3
  218. data/lib/arel/nodes/casted.rb +1 -1
  219. data/lib/arel/nodes/delete_statement.rb +12 -13
  220. data/lib/arel/nodes/filter.rb +10 -0
  221. data/lib/arel/nodes/function.rb +1 -0
  222. data/lib/arel/nodes/insert_statement.rb +2 -2
  223. data/lib/arel/nodes/select_core.rb +2 -2
  224. data/lib/arel/nodes/select_statement.rb +2 -2
  225. data/lib/arel/nodes/update_statement.rb +8 -3
  226. data/lib/arel/nodes.rb +1 -0
  227. data/lib/arel/predications.rb +11 -3
  228. data/lib/arel/select_manager.rb +10 -4
  229. data/lib/arel/table.rb +0 -1
  230. data/lib/arel/tree_manager.rb +0 -12
  231. data/lib/arel/update_manager.rb +18 -4
  232. data/lib/arel/visitors/dot.rb +80 -90
  233. data/lib/arel/visitors/mysql.rb +8 -2
  234. data/lib/arel/visitors/postgresql.rb +0 -10
  235. data/lib/arel/visitors/to_sql.rb +58 -2
  236. data/lib/arel.rb +2 -1
  237. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  238. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  239. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  240. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  241. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  242. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  243. metadata +55 -11
@@ -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,10 +11,12 @@ 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
@@ -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
@@ -19,12 +19,16 @@ module ActiveRecord
19
19
 
20
20
  if value.is_a?(Hash)
21
21
  set_time_zone_without_conversion(super)
22
+ elsif value.is_a?(Range)
23
+ Range.new(user_input_in_time_zone(value.begin), user_input_in_time_zone(value.end), value.exclude_end?)
22
24
  elsif value.respond_to?(:in_time_zone)
23
25
  begin
24
26
  super(user_input_in_time_zone(value)) || super
25
27
  rescue ArgumentError
26
28
  nil
27
29
  end
30
+ elsif value.respond_to?(:infinite?) && value.infinite?
31
+ value
28
32
  else
29
33
  map_avoiding_infinite_recursion(super) { |v| cast(v) }
30
34
  end
@@ -36,8 +40,10 @@ module ActiveRecord
36
40
 
37
41
  if value.acts_like?(:time)
38
42
  value.in_time_zone
39
- elsif value.is_a?(::Float)
43
+ elsif value.respond_to?(:infinite?) && value.infinite?
40
44
  value
45
+ elsif value.is_a?(Range)
46
+ Range.new(convert_time_to_time_zone(value.begin), convert_time_to_time_zone(value.end), value.exclude_end?)
41
47
  else
42
48
  map_avoiding_infinite_recursion(value) { |v| convert_time_to_time_zone(v) }
43
49
  end
@@ -61,8 +67,7 @@ module ActiveRecord
61
67
  extend ActiveSupport::Concern
62
68
 
63
69
  included do
64
- mattr_accessor :time_zone_aware_attributes, instance_writer: false, default: false
65
-
70
+ class_attribute :time_zone_aware_attributes, instance_writer: false, default: false
66
71
  class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false, default: []
67
72
  class_attribute :time_zone_aware_types, instance_writer: false, default: [ :datetime, :time ]
68
73
  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]"
@@ -385,25 +384,25 @@ module ActiveRecord
385
384
  end
386
385
 
387
386
  def attributes_with_values(attribute_names)
388
- attribute_names.index_with do |name|
389
- _read_attribute(name)
390
- end
387
+ attribute_names.index_with { |name| @attributes[name] }
391
388
  end
392
389
 
393
- # Filters the primary keys and readonly attributes from the attribute names.
390
+ # Filters the primary keys, readonly attributes and virtual columns from the attribute names.
394
391
  def attributes_for_update(attribute_names)
395
392
  attribute_names &= self.class.column_names
396
393
  attribute_names.delete_if do |name|
397
- self.class.readonly_attribute?(name)
394
+ self.class.readonly_attribute?(name) ||
395
+ column_for_attribute(name).virtual?
398
396
  end
399
397
  end
400
398
 
401
- # Filters out the primary keys, from the attribute names, when the primary
399
+ # Filters out the virtual columns and also primary keys, from the attribute names, when the primary
402
400
  # key is to be generated (e.g. the id attribute has no value).
403
401
  def attributes_for_create(attribute_names)
404
402
  attribute_names &= self.class.column_names
405
403
  attribute_names.delete_if do |name|
406
- pk_attribute?(name) && id.nil?
404
+ (pk_attribute?(name) && id.nil?) ||
405
+ column_for_attribute(name).virtual?
407
406
  end
408
407
  end
409
408
 
@@ -414,7 +413,7 @@ module ActiveRecord
414
413
  inspected_value = if value.is_a?(String) && value.length > 50
415
414
  "#{value[0, 50]}...".inspect
416
415
  elsif value.is_a?(Date) || value.is_a?(Time)
417
- %("#{value.to_s(:inspect)}")
416
+ %("#{value.to_fs(:inspect)}")
418
417
  else
419
418
  value.inspect
420
419
  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)
@@ -12,7 +12,7 @@ require "active_record/attributes"
12
12
  require "active_record/type_caster"
13
13
  require "active_record/database_configurations"
14
14
 
15
- module ActiveRecord #:nodoc:
15
+ module ActiveRecord # :nodoc:
16
16
  # = Active Record
17
17
  #
18
18
  # Active Record objects don't specify their attributes directly, but rather infer them from
@@ -137,6 +137,23 @@ module ActiveRecord #:nodoc:
137
137
  # anonymous = User.new(name: "")
138
138
  # anonymous.name? # => false
139
139
  #
140
+ # Query methods will also respect any overrides of default accessors:
141
+ #
142
+ # class User
143
+ # # Has admin boolean column
144
+ # def admin
145
+ # false
146
+ # end
147
+ # end
148
+ #
149
+ # user.update(admin: true)
150
+ #
151
+ # user.read_attribute(:admin) # => true, gets the column value
152
+ # user[:admin] # => true, also gets the column value
153
+ #
154
+ # user.admin # => false, due to the getter override
155
+ # user.admin? # => false, due to the getter override
156
+ #
140
157
  # == Accessing attributes before they have been typecasted
141
158
  #
142
159
  # Sometimes you want to be able to read the raw attribute data without having the column-determined
@@ -310,6 +327,7 @@ module ActiveRecord #:nodoc:
310
327
  include SecureToken
311
328
  include SignedId
312
329
  include Suppressor
330
+ include Encryption::EncryptableRecord
313
331
  end
314
332
 
315
333
  ActiveSupport.run_load_hooks(:active_record, Base)