activerecord 6.1.6 → 7.1.2

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 (309) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1627 -983
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +18 -18
  5. data/lib/active_record/aggregations.rb +17 -14
  6. data/lib/active_record/association_relation.rb +1 -11
  7. data/lib/active_record/associations/association.rb +50 -19
  8. data/lib/active_record/associations/association_scope.rb +17 -12
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -9
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +11 -5
  12. data/lib/active_record/associations/builder/belongs_to.rb +40 -14
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  15. data/lib/active_record/associations/builder/has_many.rb +3 -2
  16. data/lib/active_record/associations/builder/has_one.rb +2 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  18. data/lib/active_record/associations/collection_association.rb +35 -31
  19. data/lib/active_record/associations/collection_proxy.rb +30 -15
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  21. data/lib/active_record/associations/foreign_association.rb +10 -3
  22. data/lib/active_record/associations/has_many_association.rb +28 -18
  23. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  24. data/lib/active_record/associations/has_one_association.rb +20 -10
  25. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency.rb +26 -16
  27. data/lib/active_record/associations/preloader/association.rb +207 -52
  28. data/lib/active_record/associations/preloader/batch.rb +48 -0
  29. data/lib/active_record/associations/preloader/branch.rb +147 -0
  30. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  31. data/lib/active_record/associations/preloader.rb +50 -121
  32. data/lib/active_record/associations/singular_association.rb +9 -3
  33. data/lib/active_record/associations/through_association.rb +25 -14
  34. data/lib/active_record/associations.rb +439 -305
  35. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  36. data/lib/active_record/attribute_assignment.rb +1 -3
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  38. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  39. data/lib/active_record/attribute_methods/primary_key.rb +78 -26
  40. data/lib/active_record/attribute_methods/query.rb +31 -19
  41. data/lib/active_record/attribute_methods/read.rb +25 -10
  42. data/lib/active_record/attribute_methods/serialization.rb +194 -37
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  44. data/lib/active_record/attribute_methods/write.rb +10 -13
  45. data/lib/active_record/attribute_methods.rb +121 -40
  46. data/lib/active_record/attributes.rb +27 -38
  47. data/lib/active_record/autosave_association.rb +61 -30
  48. data/lib/active_record/base.rb +25 -2
  49. data/lib/active_record/callbacks.rb +18 -34
  50. data/lib/active_record/coders/column_serializer.rb +61 -0
  51. data/lib/active_record/coders/json.rb +1 -1
  52. data/lib/active_record/coders/yaml_column.rb +70 -34
  53. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
  54. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +96 -590
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +77 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +360 -138
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -149
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +285 -156
  69. data/lib/active_record/connection_adapters/column.rb +13 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +104 -53
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -11
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
  83. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  87. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  89. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  92. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  93. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  94. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  95. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  96. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  97. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  98. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +394 -74
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +509 -247
  101. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  102. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  103. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
  104. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  105. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  106. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  107. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -102
  108. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  109. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  110. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  111. data/lib/active_record/connection_adapters.rb +9 -6
  112. data/lib/active_record/connection_handling.rb +107 -136
  113. data/lib/active_record/core.rb +202 -223
  114. data/lib/active_record/counter_cache.rb +46 -25
  115. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  116. data/lib/active_record/database_configurations/database_config.rb +21 -12
  117. data/lib/active_record/database_configurations/hash_config.rb +84 -16
  118. data/lib/active_record/database_configurations/url_config.rb +18 -12
  119. data/lib/active_record/database_configurations.rb +95 -59
  120. data/lib/active_record/delegated_type.rb +61 -15
  121. data/lib/active_record/deprecator.rb +7 -0
  122. data/lib/active_record/destroy_association_async_job.rb +3 -1
  123. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  124. data/lib/active_record/dynamic_matchers.rb +1 -1
  125. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  126. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  127. data/lib/active_record/encryption/cipher.rb +53 -0
  128. data/lib/active_record/encryption/config.rb +68 -0
  129. data/lib/active_record/encryption/configurable.rb +60 -0
  130. data/lib/active_record/encryption/context.rb +42 -0
  131. data/lib/active_record/encryption/contexts.rb +76 -0
  132. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  133. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  134. data/lib/active_record/encryption/encryptable_record.rb +224 -0
  135. data/lib/active_record/encryption/encrypted_attribute_type.rb +151 -0
  136. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  137. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  138. data/lib/active_record/encryption/encryptor.rb +155 -0
  139. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  140. data/lib/active_record/encryption/errors.rb +15 -0
  141. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  142. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  143. data/lib/active_record/encryption/key.rb +28 -0
  144. data/lib/active_record/encryption/key_generator.rb +53 -0
  145. data/lib/active_record/encryption/key_provider.rb +46 -0
  146. data/lib/active_record/encryption/message.rb +33 -0
  147. data/lib/active_record/encryption/message_serializer.rb +92 -0
  148. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  149. data/lib/active_record/encryption/properties.rb +76 -0
  150. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  151. data/lib/active_record/encryption/scheme.rb +96 -0
  152. data/lib/active_record/encryption.rb +56 -0
  153. data/lib/active_record/enum.rb +154 -63
  154. data/lib/active_record/errors.rb +171 -15
  155. data/lib/active_record/explain.rb +23 -3
  156. data/lib/active_record/explain_registry.rb +11 -6
  157. data/lib/active_record/explain_subscriber.rb +1 -1
  158. data/lib/active_record/fixture_set/file.rb +15 -1
  159. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  160. data/lib/active_record/fixture_set/render_context.rb +2 -0
  161. data/lib/active_record/fixture_set/table_row.rb +70 -14
  162. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  163. data/lib/active_record/fixtures.rb +131 -86
  164. data/lib/active_record/future_result.rb +164 -0
  165. data/lib/active_record/gem_version.rb +3 -3
  166. data/lib/active_record/inheritance.rb +81 -29
  167. data/lib/active_record/insert_all.rb +135 -22
  168. data/lib/active_record/integration.rb +11 -10
  169. data/lib/active_record/internal_metadata.rb +119 -33
  170. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  171. data/lib/active_record/locking/optimistic.rb +36 -21
  172. data/lib/active_record/locking/pessimistic.rb +15 -6
  173. data/lib/active_record/log_subscriber.rb +52 -19
  174. data/lib/active_record/marshalling.rb +56 -0
  175. data/lib/active_record/message_pack.rb +124 -0
  176. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  177. data/lib/active_record/middleware/database_selector.rb +23 -13
  178. data/lib/active_record/middleware/shard_selector.rb +62 -0
  179. data/lib/active_record/migration/command_recorder.rb +112 -14
  180. data/lib/active_record/migration/compatibility.rb +221 -48
  181. data/lib/active_record/migration/default_strategy.rb +23 -0
  182. data/lib/active_record/migration/execution_strategy.rb +19 -0
  183. data/lib/active_record/migration/join_table.rb +1 -1
  184. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  185. data/lib/active_record/migration.rb +358 -171
  186. data/lib/active_record/model_schema.rb +120 -101
  187. data/lib/active_record/nested_attributes.rb +37 -18
  188. data/lib/active_record/no_touching.rb +3 -3
  189. data/lib/active_record/normalization.rb +167 -0
  190. data/lib/active_record/persistence.rb +405 -85
  191. data/lib/active_record/promise.rb +84 -0
  192. data/lib/active_record/query_cache.rb +3 -21
  193. data/lib/active_record/query_logs.rb +174 -0
  194. data/lib/active_record/query_logs_formatter.rb +41 -0
  195. data/lib/active_record/querying.rb +29 -6
  196. data/lib/active_record/railtie.rb +219 -43
  197. data/lib/active_record/railties/controller_runtime.rb +13 -9
  198. data/lib/active_record/railties/databases.rake +188 -252
  199. data/lib/active_record/railties/job_runtime.rb +23 -0
  200. data/lib/active_record/readonly_attributes.rb +41 -3
  201. data/lib/active_record/reflection.rb +241 -80
  202. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  203. data/lib/active_record/relation/batches.rb +192 -63
  204. data/lib/active_record/relation/calculations.rb +219 -90
  205. data/lib/active_record/relation/delegation.rb +27 -13
  206. data/lib/active_record/relation/finder_methods.rb +108 -51
  207. data/lib/active_record/relation/merger.rb +22 -13
  208. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  209. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  210. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  211. data/lib/active_record/relation/predicate_builder.rb +27 -20
  212. data/lib/active_record/relation/query_attribute.rb +30 -12
  213. data/lib/active_record/relation/query_methods.rb +654 -127
  214. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  215. data/lib/active_record/relation/spawn_methods.rb +20 -3
  216. data/lib/active_record/relation/where_clause.rb +10 -19
  217. data/lib/active_record/relation.rb +262 -120
  218. data/lib/active_record/result.rb +37 -11
  219. data/lib/active_record/runtime_registry.rb +18 -13
  220. data/lib/active_record/sanitization.rb +65 -20
  221. data/lib/active_record/schema.rb +36 -22
  222. data/lib/active_record/schema_dumper.rb +73 -24
  223. data/lib/active_record/schema_migration.rb +68 -33
  224. data/lib/active_record/scoping/default.rb +72 -15
  225. data/lib/active_record/scoping/named.rb +5 -13
  226. data/lib/active_record/scoping.rb +65 -34
  227. data/lib/active_record/secure_password.rb +60 -0
  228. data/lib/active_record/secure_token.rb +21 -3
  229. data/lib/active_record/serialization.rb +6 -1
  230. data/lib/active_record/signed_id.rb +10 -8
  231. data/lib/active_record/store.rb +16 -11
  232. data/lib/active_record/suppressor.rb +13 -15
  233. data/lib/active_record/table_metadata.rb +16 -3
  234. data/lib/active_record/tasks/database_tasks.rb +225 -136
  235. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  236. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  237. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  238. data/lib/active_record/test_databases.rb +1 -1
  239. data/lib/active_record/test_fixtures.rb +123 -99
  240. data/lib/active_record/timestamp.rb +29 -18
  241. data/lib/active_record/token_for.rb +113 -0
  242. data/lib/active_record/touch_later.rb +11 -6
  243. data/lib/active_record/transactions.rb +48 -27
  244. data/lib/active_record/translation.rb +3 -3
  245. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  246. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  247. data/lib/active_record/type/internal/timezone.rb +7 -2
  248. data/lib/active_record/type/serialized.rb +9 -5
  249. data/lib/active_record/type/time.rb +4 -0
  250. data/lib/active_record/type/type_map.rb +17 -20
  251. data/lib/active_record/type.rb +1 -2
  252. data/lib/active_record/validations/absence.rb +1 -1
  253. data/lib/active_record/validations/associated.rb +4 -4
  254. data/lib/active_record/validations/numericality.rb +5 -4
  255. data/lib/active_record/validations/presence.rb +5 -28
  256. data/lib/active_record/validations/uniqueness.rb +51 -6
  257. data/lib/active_record/validations.rb +8 -4
  258. data/lib/active_record/version.rb +1 -1
  259. data/lib/active_record.rb +335 -32
  260. data/lib/arel/attributes/attribute.rb +0 -8
  261. data/lib/arel/crud.rb +28 -22
  262. data/lib/arel/delete_manager.rb +18 -4
  263. data/lib/arel/errors.rb +10 -0
  264. data/lib/arel/factory_methods.rb +4 -0
  265. data/lib/arel/filter_predications.rb +9 -0
  266. data/lib/arel/insert_manager.rb +2 -3
  267. data/lib/arel/nodes/and.rb +4 -0
  268. data/lib/arel/nodes/binary.rb +6 -1
  269. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  270. data/lib/arel/nodes/casted.rb +1 -1
  271. data/lib/arel/nodes/cte.rb +36 -0
  272. data/lib/arel/nodes/delete_statement.rb +12 -13
  273. data/lib/arel/nodes/filter.rb +10 -0
  274. data/lib/arel/nodes/fragments.rb +35 -0
  275. data/lib/arel/nodes/function.rb +1 -0
  276. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  277. data/lib/arel/nodes/insert_statement.rb +2 -2
  278. data/lib/arel/nodes/leading_join.rb +8 -0
  279. data/lib/arel/nodes/node.rb +111 -2
  280. data/lib/arel/nodes/select_core.rb +2 -2
  281. data/lib/arel/nodes/select_statement.rb +2 -2
  282. data/lib/arel/nodes/sql_literal.rb +6 -0
  283. data/lib/arel/nodes/table_alias.rb +4 -0
  284. data/lib/arel/nodes/update_statement.rb +8 -3
  285. data/lib/arel/nodes.rb +5 -0
  286. data/lib/arel/predications.rb +13 -3
  287. data/lib/arel/select_manager.rb +10 -4
  288. data/lib/arel/table.rb +9 -6
  289. data/lib/arel/tree_manager.rb +0 -12
  290. data/lib/arel/update_manager.rb +18 -4
  291. data/lib/arel/visitors/dot.rb +80 -90
  292. data/lib/arel/visitors/mysql.rb +16 -3
  293. data/lib/arel/visitors/postgresql.rb +0 -10
  294. data/lib/arel/visitors/to_sql.rb +139 -19
  295. data/lib/arel/visitors/visitor.rb +2 -2
  296. data/lib/arel.rb +18 -3
  297. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  298. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  299. data/lib/rails/generators/active_record/migration.rb +3 -1
  300. data/lib/rails/generators/active_record/model/USAGE +113 -0
  301. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  302. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  303. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  304. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  305. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  306. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  307. metadata +93 -13
  308. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  309. data/lib/active_record/null_relation.rb +0 -67
@@ -6,6 +6,13 @@ module ActiveRecord
6
6
  module ModelSchema
7
7
  extend ActiveSupport::Concern
8
8
 
9
+ ##
10
+ # :method: id_value
11
+ # :call-seq: id_value
12
+ #
13
+ # Returns the underlying column value for a column named "id". Useful when defining
14
+ # a composite primary key including an "id" column so that the value is readable.
15
+
9
16
  ##
10
17
  # :singleton-method: primary_key_prefix_type
11
18
  # :call-seq: primary_key_prefix_type
@@ -126,9 +133,29 @@ module ActiveRecord
126
133
  # +:immutable_string+. This setting does not affect the behavior of
127
134
  # <tt>attribute :foo, :string</tt>. Defaults to false.
128
135
 
129
- included do
130
- mattr_accessor :primary_key_prefix_type, instance_writer: false
136
+ ##
137
+ # :singleton-method: inheritance_column
138
+ # :call-seq: inheritance_column
139
+ #
140
+ # The name of the table column which stores the class name on single-table
141
+ # inheritance situations.
142
+ #
143
+ # The default inheritance column name is +type+, which means it's a
144
+ # reserved word inside Active Record. To be able to use single-table
145
+ # inheritance with another column name, or to use the column +type+ in
146
+ # your own model for something else, you can set +inheritance_column+:
147
+ #
148
+ # self.inheritance_column = 'zoink'
149
+
150
+ ##
151
+ # :singleton-method: inheritance_column=
152
+ # :call-seq: inheritance_column=(column)
153
+ #
154
+ # Defines the name of the table column which will store the class name on single-table
155
+ # inheritance situations.
131
156
 
157
+ included do
158
+ class_attribute :primary_key_prefix_type, instance_writer: false
132
159
  class_attribute :table_name_prefix, instance_writer: false, default: ""
133
160
  class_attribute :table_name_suffix, instance_writer: false, default: ""
134
161
  class_attribute :schema_migrations_table_name, instance_accessor: false, default: "schema_migrations"
@@ -137,8 +164,15 @@ module ActiveRecord
137
164
  class_attribute :implicit_order_column, instance_accessor: false
138
165
  class_attribute :immutable_strings_by_default, instance_accessor: false
139
166
 
167
+ class_attribute :inheritance_column, instance_accessor: false, default: "type"
168
+ singleton_class.class_eval do
169
+ alias_method :_inheritance_column=, :inheritance_column=
170
+ private :_inheritance_column=
171
+ alias_method :inheritance_column=, :real_inheritance_column=
172
+ end
173
+
140
174
  self.protected_environments = ["production"]
141
- self.inheritance_column = "type"
175
+
142
176
  self.ignored_columns = [].freeze
143
177
 
144
178
  delegate :type_for_attribute, :column_for_attribute, to: :class
@@ -153,8 +187,9 @@ module ActiveRecord
153
187
  # artists, records => artists_records
154
188
  # records, artists => artists_records
155
189
  # music_artists, music_records => music_artists_records
190
+ # music.artists, music.records => music.artists_records
156
191
  def self.derive_join_table_name(first_table, second_table) # :nodoc:
157
- [first_table.to_s, second_table.to_s].sort.join("\0").gsub(/^(.*_)(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
192
+ [first_table.to_s, second_table.to_s].sort.join("\0").gsub(/^(.*[_.])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
158
193
  end
159
194
 
160
195
  module ClassMethods
@@ -197,6 +232,21 @@ module ActiveRecord
197
232
  # the table name guess for an Invoice class becomes "myapp_invoices".
198
233
  # Invoice::Lineitem becomes "myapp_invoice_lineitems".
199
234
  #
235
+ # Active Model Naming's +model_name+ is the base name used to guess the
236
+ # table name. In case a custom Active Model Name is defined, it will be
237
+ # used for the table name as well:
238
+ #
239
+ # class PostRecord < ActiveRecord::Base
240
+ # class << self
241
+ # def model_name
242
+ # ActiveModel::Name.new(self, nil, "Post")
243
+ # end
244
+ # end
245
+ # end
246
+ #
247
+ # PostRecord.table_name
248
+ # # => "posts"
249
+ #
200
250
  # You can also set your own table name explicitly:
201
251
  #
202
252
  # class Mouse < ActiveRecord::Base
@@ -233,9 +283,11 @@ module ActiveRecord
233
283
  end
234
284
 
235
285
  # Computes the table name, (re)sets it internally, and returns it.
236
- def reset_table_name #:nodoc:
237
- self.table_name = if abstract_class?
238
- superclass == Base ? nil : superclass.table_name
286
+ def reset_table_name # :nodoc:
287
+ self.table_name = if self == Base
288
+ nil
289
+ elsif abstract_class?
290
+ superclass.table_name
239
291
  elsif superclass.abstract_class?
240
292
  superclass.table_name || compute_table_name
241
293
  else
@@ -243,11 +295,11 @@ module ActiveRecord
243
295
  end
244
296
  end
245
297
 
246
- def full_table_name_prefix #:nodoc:
298
+ def full_table_name_prefix # :nodoc:
247
299
  (module_parents.detect { |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
248
300
  end
249
301
 
250
- def full_table_name_suffix #:nodoc:
302
+ def full_table_name_suffix # :nodoc:
251
303
  (module_parents.detect { |p| p.respond_to?(:table_name_suffix) } || self).table_name_suffix
252
304
  end
253
305
 
@@ -266,33 +318,14 @@ module ActiveRecord
266
318
  @protected_environments = environments.map(&:to_s)
267
319
  end
268
320
 
269
- # Defines the name of the table column which will store the class name on single-table
270
- # inheritance situations.
271
- #
272
- # The default inheritance column name is +type+, which means it's a
273
- # reserved word inside Active Record. To be able to use single-table
274
- # inheritance with another column name, or to use the column +type+ in
275
- # your own model for something else, you can set +inheritance_column+:
276
- #
277
- # self.inheritance_column = 'zoink'
278
- def inheritance_column
279
- (@inheritance_column ||= nil) || superclass.inheritance_column
280
- end
281
-
282
- # Sets the value of inheritance_column
283
- def inheritance_column=(value)
284
- @inheritance_column = value.to_s
285
- @explicit_inheritance_column = true
321
+ def real_inheritance_column=(value) # :nodoc:
322
+ self._inheritance_column = value.to_s
286
323
  end
287
324
 
288
325
  # The list of columns names the model should ignore. Ignored columns won't have attribute
289
326
  # accessors defined, and won't be referenced in SQL queries.
290
327
  def ignored_columns
291
- if defined?(@ignored_columns)
292
- @ignored_columns
293
- else
294
- superclass.ignored_columns
295
- end
328
+ @ignored_columns || superclass.ignored_columns
296
329
  end
297
330
 
298
331
  # Sets the columns names the model should ignore. Ignored columns won't have attribute
@@ -313,7 +346,7 @@ module ActiveRecord
313
346
  # # name :string, limit: 255
314
347
  # # category :string, limit: 255
315
348
  #
316
- # self.ignored_columns = [:category]
349
+ # self.ignored_columns += [:category]
317
350
  # end
318
351
  #
319
352
  # The schema still contains "category", but now the model omits it, so any meta-driven code or
@@ -339,7 +372,7 @@ module ActiveRecord
339
372
  end
340
373
  end
341
374
 
342
- def reset_sequence_name #:nodoc:
375
+ def reset_sequence_name # :nodoc:
343
376
  @explicit_sequence_name = false
344
377
  @sequence_name = connection.default_sequence_name(table_name, primary_key)
345
378
  end
@@ -398,6 +431,12 @@ module ActiveRecord
398
431
  @columns ||= columns_hash.values.freeze
399
432
  end
400
433
 
434
+ def _returning_columns_for_insert # :nodoc:
435
+ @_returning_columns_for_insert ||= columns.filter_map do |c|
436
+ c.name if connection.return_value_after_insert?(c)
437
+ end
438
+ end
439
+
401
440
  def attribute_types # :nodoc:
402
441
  load_schema
403
442
  @attribute_types ||= Hash.new(Type.default_value)
@@ -430,7 +469,7 @@ module ActiveRecord
430
469
  end
431
470
 
432
471
  # Returns the column object for the named attribute.
433
- # Returns an +ActiveRecord::ConnectionAdapters::NullColumn+ if the
472
+ # Returns an ActiveRecord::ConnectionAdapters::NullColumn if the
434
473
  # named attribute does not exist.
435
474
  #
436
475
  # class Person < ActiveRecord::Base
@@ -486,9 +525,9 @@ module ActiveRecord
486
525
  #
487
526
  # The most common usage pattern for this method is probably in a migration,
488
527
  # when just after creating a table you want to populate it with some default
489
- # values, eg:
528
+ # values, e.g.:
490
529
  #
491
- # class CreateJobLevels < ActiveRecord::Migration[6.0]
530
+ # class CreateJobLevels < ActiveRecord::Migration[7.1]
492
531
  # def up
493
532
  # create_table :job_levels do |t|
494
533
  # t.integer :id
@@ -516,35 +555,61 @@ module ActiveRecord
516
555
  initialize_find_by_cache
517
556
  end
518
557
 
558
+ def load_schema # :nodoc:
559
+ return if schema_loaded?
560
+ @load_schema_monitor.synchronize do
561
+ return if @columns_hash
562
+
563
+ load_schema!
564
+
565
+ @schema_loaded = true
566
+ rescue
567
+ reload_schema_from_cache # If the schema loading failed half way through, we must reset the state.
568
+ raise
569
+ end
570
+ end
571
+
519
572
  protected
520
573
  def initialize_load_schema_monitor
521
574
  @load_schema_monitor = Monitor.new
522
575
  end
523
576
 
577
+ def reload_schema_from_cache(recursive = true)
578
+ @_returning_columns_for_insert = nil
579
+ @arel_table = nil
580
+ @column_names = nil
581
+ @symbol_column_to_string_name_hash = nil
582
+ @attribute_types = nil
583
+ @content_columns = nil
584
+ @default_attributes = nil
585
+ @column_defaults = nil
586
+ @attributes_builder = nil
587
+ @columns = nil
588
+ @columns_hash = nil
589
+ @schema_loaded = false
590
+ @attribute_names = nil
591
+ @yaml_encoder = nil
592
+ if recursive
593
+ subclasses.each do |descendant|
594
+ descendant.send(:reload_schema_from_cache)
595
+ end
596
+ end
597
+ end
598
+
524
599
  private
525
600
  def inherited(child_class)
526
601
  super
527
602
  child_class.initialize_load_schema_monitor
603
+ child_class.reload_schema_from_cache(false)
604
+ child_class.class_eval do
605
+ @ignored_columns = nil
606
+ end
528
607
  end
529
608
 
530
609
  def schema_loaded?
531
610
  defined?(@schema_loaded) && @schema_loaded
532
611
  end
533
612
 
534
- def load_schema
535
- return if schema_loaded?
536
- @load_schema_monitor.synchronize do
537
- return if defined?(@columns_hash) && @columns_hash
538
-
539
- load_schema!
540
-
541
- @schema_loaded = true
542
- rescue
543
- reload_schema_from_cache # If the schema loading failed half way through, we must reset the state.
544
- raise
545
- end
546
- end
547
-
548
613
  def load_schema!
549
614
  unless table_name
550
615
  raise ActiveRecord::TableNotSpecified, "#{self} has no table configured. Set one with #{self}.table_name="
@@ -556,39 +621,19 @@ module ActiveRecord
556
621
  @columns_hash.each do |name, column|
557
622
  type = connection.lookup_cast_type_from_column(column)
558
623
  type = _convert_type_from_options(type)
559
- warn_if_deprecated_type(column)
560
624
  define_attribute(
561
625
  name,
562
626
  type,
563
627
  default: column.default,
564
628
  user_provided_default: false
565
629
  )
566
- end
567
- end
568
-
569
- def reload_schema_from_cache
570
- @arel_table = nil
571
- @column_names = nil
572
- @symbol_column_to_string_name_hash = nil
573
- @attribute_types = nil
574
- @content_columns = nil
575
- @default_attributes = nil
576
- @column_defaults = nil
577
- @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
578
- @attributes_builder = nil
579
- @columns = nil
580
- @columns_hash = nil
581
- @schema_loaded = false
582
- @attribute_names = nil
583
- @yaml_encoder = nil
584
- direct_descendants.each do |descendant|
585
- descendant.send(:reload_schema_from_cache)
630
+ alias_attribute :id_value, :id if name == "id"
586
631
  end
587
632
  end
588
633
 
589
634
  # Guesses the table name, but does not decorate it with prefix and suffix information.
590
- def undecorated_table_name(class_name = base_class.name)
591
- table_name = class_name.to_s.demodulize.underscore
635
+ def undecorated_table_name(model_name)
636
+ table_name = model_name.to_s.demodulize.underscore
592
637
  pluralize_table_names ? table_name.pluralize : table_name
593
638
  end
594
639
 
@@ -602,9 +647,9 @@ module ActiveRecord
602
647
  contained += "_"
603
648
  end
604
649
 
605
- "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{full_table_name_suffix}"
650
+ "#{full_table_name_prefix}#{contained}#{undecorated_table_name(model_name)}#{full_table_name_suffix}"
606
651
  else
607
- # STI subclasses always use their superclass' table.
652
+ # STI subclasses always use their superclass's table.
608
653
  base_class.table_name
609
654
  end
610
655
  end
@@ -616,32 +661,6 @@ module ActiveRecord
616
661
  type
617
662
  end
618
663
  end
619
-
620
- def warn_if_deprecated_type(column)
621
- return if attributes_to_define_after_schema_loads.key?(column.name)
622
- return unless column.respond_to?(:oid)
623
-
624
- if column.array?
625
- array_arguments = ", array: true"
626
- else
627
- array_arguments = ""
628
- end
629
-
630
- if column.sql_type.start_with?("interval")
631
- precision_arguments = column.precision.presence && ", precision: #{column.precision}"
632
- ActiveSupport::Deprecation.warn(<<~WARNING)
633
- The behavior of the `:interval` type will be changing in Rails 7.0
634
- to return an `ActiveSupport::Duration` object. If you'd like to keep
635
- the old behavior, you can add this line to #{self.name} model:
636
-
637
- attribute :#{column.name}, :string#{precision_arguments}#{array_arguments}
638
-
639
- If you'd like the new behavior today, you can add this line:
640
-
641
- attribute :#{column.name}, :interval#{precision_arguments}#{array_arguments}
642
- WARNING
643
- end
644
- end
645
664
  end
646
665
  end
647
666
  end
@@ -5,7 +5,7 @@ require "active_support/core_ext/module/redefine_method"
5
5
  require "active_support/core_ext/hash/indifferent_access"
6
6
 
7
7
  module ActiveRecord
8
- module NestedAttributes #:nodoc:
8
+ module NestedAttributes # :nodoc:
9
9
  class TooManyRecords < ActiveRecordError
10
10
  end
11
11
 
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  class_attribute :nested_attributes_options, instance_writer: false, default: {}
16
16
  end
17
17
 
18
- # = Active Record Nested Attributes
18
+ # = Active Record Nested \Attributes
19
19
  #
20
20
  # Nested attributes allow you to save attributes on associated records
21
21
  # through the parent. By default nested attribute updating is turned off
@@ -180,7 +180,7 @@ module ActiveRecord
180
180
  # member.posts.second.title # => '[UPDATED] other post'
181
181
  #
182
182
  # However, the above applies if the parent model is being updated as well.
183
- # For example, If you wanted to create a +member+ named _joe_ and wanted to
183
+ # For example, if you wanted to create a +member+ named _joe_ and wanted to
184
184
  # update the +posts+ at the same time, that would give an
185
185
  # ActiveRecord::RecordNotFound error.
186
186
  #
@@ -245,18 +245,19 @@ module ActiveRecord
245
245
  #
246
246
  # === Validating the presence of a parent model
247
247
  #
248
- # If you want to validate that a child record is associated with a parent
249
- # record, you can use the +validates_presence_of+ method and the +:inverse_of+
250
- # key as this example illustrates:
248
+ # The +belongs_to+ association validates the presence of the parent model
249
+ # by default. You can disable this behavior by specifying <code>optional: true</code>.
250
+ # This can be used, for example, when conditionally validating the presence
251
+ # of the parent model:
251
252
  #
252
- # class Member < ActiveRecord::Base
253
- # has_many :posts, inverse_of: :member
254
- # accepts_nested_attributes_for :posts
253
+ # class Veterinarian < ActiveRecord::Base
254
+ # has_many :patients, inverse_of: :veterinarian
255
+ # accepts_nested_attributes_for :patients
255
256
  # end
256
257
  #
257
- # class Post < ActiveRecord::Base
258
- # belongs_to :member, inverse_of: :posts
259
- # validates_presence_of :member
258
+ # class Patient < ActiveRecord::Base
259
+ # belongs_to :veterinarian, inverse_of: :patients, optional: true
260
+ # validates :veterinarian, presence: true, unless: -> { awaiting_intake }
260
261
  # end
261
262
  #
262
263
  # Note that if you do not specify the +:inverse_of+ option, then
@@ -279,6 +280,24 @@ module ActiveRecord
279
280
  # member = Member.new
280
281
  # member.avatar_attributes = {icon: 'sad'}
281
282
  # member.avatar.width # => 200
283
+ #
284
+ # === Creating forms with nested attributes
285
+ #
286
+ # Use ActionView::Helpers::FormHelper#fields_for to create form elements for
287
+ # nested attributes.
288
+ #
289
+ # Integration test params should reflect the structure of the form. For
290
+ # example:
291
+ #
292
+ # post members_path, params: {
293
+ # member: {
294
+ # name: 'joe',
295
+ # posts_attributes: {
296
+ # '0' => { title: 'Foo' },
297
+ # '1' => { title: 'Bar' }
298
+ # }
299
+ # }
300
+ # }
282
301
  module ClassMethods
283
302
  REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == "_destroy" || value.blank? } }
284
303
 
@@ -288,7 +307,7 @@ module ActiveRecord
288
307
  # [:allow_destroy]
289
308
  # If true, destroys any members from the attributes hash with a
290
309
  # <tt>_destroy</tt> key and a value that evaluates to +true+
291
- # (e.g. 1, '1', true, or 'true'). This option is off by default.
310
+ # (e.g. 1, '1', true, or 'true'). This option is false by default.
292
311
  # [:reject_if]
293
312
  # Allows you to specify a Proc or a Symbol pointing to a method
294
313
  # that checks whether a record should be built for a certain attribute
@@ -313,11 +332,11 @@ module ActiveRecord
313
332
  # nested attributes are going to be used when an associated record already
314
333
  # exists. In general, an existing record may either be updated with the
315
334
  # new set of attribute values or be replaced by a wholly new record
316
- # containing those values. By default the +:update_only+ option is +false+
335
+ # containing those values. By default the +:update_only+ option is false
317
336
  # and the nested attributes are used to update the existing record only
318
337
  # if they include the record's <tt>:id</tt> value. Otherwise a new
319
338
  # record will be instantiated and used to replace the existing one.
320
- # However if the +:update_only+ option is +true+, the nested attributes
339
+ # However if the +:update_only+ option is true, the nested attributes
321
340
  # are used to update the record's attributes always, regardless of
322
341
  # whether the <tt>:id</tt> is present. The option is ignored for collection
323
342
  # associations.
@@ -374,11 +393,11 @@ module ActiveRecord
374
393
  end
375
394
  end
376
395
 
377
- # Returns ActiveRecord::AutosaveAssociation::marked_for_destruction? It's
396
+ # Returns ActiveRecord::AutosaveAssociation#marked_for_destruction? It's
378
397
  # used in conjunction with fields_for to build a form element for the
379
398
  # destruction of this association.
380
399
  #
381
- # See ActionView::Helpers::FormHelper::fields_for for more info.
400
+ # See ActionView::Helpers::FormHelper#fields_for for more info.
382
401
  def _destroy
383
402
  marked_for_destruction?
384
403
  end
@@ -486,7 +505,7 @@ module ActiveRecord
486
505
  existing_records = if association.loaded?
487
506
  association.target
488
507
  else
489
- attribute_ids = attributes_collection.map { |a| a["id"] || a[:id] }.compact
508
+ attribute_ids = attributes_collection.filter_map { |a| a["id"] || a[:id] }
490
509
  attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
491
510
  end
492
511
 
@@ -26,20 +26,20 @@ module ActiveRecord
26
26
  end
27
27
 
28
28
  class << self
29
- def apply_to(klass) #:nodoc:
29
+ def apply_to(klass) # :nodoc:
30
30
  klasses.push(klass)
31
31
  yield
32
32
  ensure
33
33
  klasses.pop
34
34
  end
35
35
 
36
- def applied_to?(klass) #:nodoc:
36
+ def applied_to?(klass) # :nodoc:
37
37
  klasses.any? { |k| k >= klass }
38
38
  end
39
39
 
40
40
  private
41
41
  def klasses
42
- Thread.current[:no_touching_classes] ||= []
42
+ ActiveSupport::IsolatedExecutionState[:active_record_no_touching_classes] ||= []
43
43
  end
44
44
  end
45
45
 
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord # :nodoc:
4
+ module Normalization
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :normalized_attributes, default: Set.new
9
+
10
+ before_validation :normalize_changed_in_place_attributes
11
+ end
12
+
13
+ # Normalizes a specified attribute using its declared normalizations.
14
+ #
15
+ # ==== Examples
16
+ #
17
+ # class User < ActiveRecord::Base
18
+ # normalizes :email, with: -> email { email.strip.downcase }
19
+ # end
20
+ #
21
+ # legacy_user = User.find(1)
22
+ # legacy_user.email # => " CRUISE-CONTROL@EXAMPLE.COM\n"
23
+ # legacy_user.normalize_attribute(:email)
24
+ # legacy_user.email # => "cruise-control@example.com"
25
+ # legacy_user.save
26
+ def normalize_attribute(name)
27
+ # Treat the value as a new, unnormalized value.
28
+ self[name] = self[name]
29
+ end
30
+
31
+ module ClassMethods
32
+ # Declares a normalization for one or more attributes. The normalization
33
+ # is applied when the attribute is assigned or updated, and the normalized
34
+ # value will be persisted to the database. The normalization is also
35
+ # applied to the corresponding keyword argument of query methods. This
36
+ # allows a record to be created and later queried using unnormalized
37
+ # values.
38
+ #
39
+ # However, to prevent confusion, the normalization will not be applied
40
+ # when the attribute is fetched from the database. This means that if a
41
+ # record was persisted before the normalization was declared, the record's
42
+ # attribute will not be normalized until either it is assigned a new
43
+ # value, or it is explicitly migrated via Normalization#normalize_attribute.
44
+ #
45
+ # Because the normalization may be applied multiple times, it should be
46
+ # _idempotent_. In other words, applying the normalization more than once
47
+ # should have the same result as applying it only once.
48
+ #
49
+ # By default, the normalization will not be applied to +nil+ values. This
50
+ # behavior can be changed with the +:apply_to_nil+ option.
51
+ #
52
+ # Be aware that if your app was created before Rails 7.1, and your app
53
+ # marshals instances of the targeted model (for example, when caching),
54
+ # then you should set ActiveRecord.marshalling_format_version to +7.1+ or
55
+ # higher via either <tt>config.load_defaults 7.1</tt> or
56
+ # <tt>config.active_record.marshalling_format_version = 7.1</tt>.
57
+ # Otherwise, +Marshal+ may attempt to serialize the normalization +Proc+
58
+ # and raise +TypeError+.
59
+ #
60
+ # ==== Options
61
+ #
62
+ # * +:with+ - Any callable object that accepts the attribute's value as
63
+ # its sole argument, and returns it normalized.
64
+ # * +:apply_to_nil+ - Whether to apply the normalization to +nil+ values.
65
+ # Defaults to +false+.
66
+ #
67
+ # ==== Examples
68
+ #
69
+ # class User < ActiveRecord::Base
70
+ # normalizes :email, with: -> email { email.strip.downcase }
71
+ # normalizes :phone, with: -> phone { phone.delete("^0-9").delete_prefix("1") }
72
+ # end
73
+ #
74
+ # user = User.create(email: " CRUISE-CONTROL@EXAMPLE.COM\n")
75
+ # user.email # => "cruise-control@example.com"
76
+ #
77
+ # user = User.find_by(email: "\tCRUISE-CONTROL@EXAMPLE.COM ")
78
+ # user.email # => "cruise-control@example.com"
79
+ # user.email_before_type_cast # => "cruise-control@example.com"
80
+ #
81
+ # User.where(email: "\tCRUISE-CONTROL@EXAMPLE.COM ").count # => 1
82
+ # User.where(["email = ?", "\tCRUISE-CONTROL@EXAMPLE.COM "]).count # => 0
83
+ #
84
+ # User.exists?(email: "\tCRUISE-CONTROL@EXAMPLE.COM ") # => true
85
+ # User.exists?(["email = ?", "\tCRUISE-CONTROL@EXAMPLE.COM "]) # => false
86
+ #
87
+ # User.normalize_value_for(:phone, "+1 (555) 867-5309") # => "5558675309"
88
+ def normalizes(*names, with:, apply_to_nil: false)
89
+ names.each do |name|
90
+ attribute(name) do |cast_type|
91
+ NormalizedValueType.new(cast_type: cast_type, normalizer: with, normalize_nil: apply_to_nil)
92
+ end
93
+ end
94
+
95
+ self.normalized_attributes += names.map(&:to_sym)
96
+ end
97
+
98
+ # Normalizes a given +value+ using normalizations declared for +name+.
99
+ #
100
+ # ==== Examples
101
+ #
102
+ # class User < ActiveRecord::Base
103
+ # normalizes :email, with: -> email { email.strip.downcase }
104
+ # end
105
+ #
106
+ # User.normalize_value_for(:email, " CRUISE-CONTROL@EXAMPLE.COM\n")
107
+ # # => "cruise-control@example.com"
108
+ def normalize_value_for(name, value)
109
+ type_for_attribute(name).cast(value)
110
+ end
111
+ end
112
+
113
+ private
114
+ def normalize_changed_in_place_attributes
115
+ self.class.normalized_attributes.each do |name|
116
+ normalize_attribute(name) if attribute_changed_in_place?(name)
117
+ end
118
+ end
119
+
120
+ class NormalizedValueType < DelegateClass(ActiveModel::Type::Value) # :nodoc:
121
+ include ActiveModel::Type::SerializeCastValue
122
+
123
+ attr_reader :cast_type, :normalizer, :normalize_nil
124
+ alias :normalize_nil? :normalize_nil
125
+
126
+ def initialize(cast_type:, normalizer:, normalize_nil:)
127
+ @cast_type = cast_type
128
+ @normalizer = normalizer
129
+ @normalize_nil = normalize_nil
130
+ super(cast_type)
131
+ end
132
+
133
+ def cast(value)
134
+ normalize(super(value))
135
+ end
136
+
137
+ def serialize(value)
138
+ serialize_cast_value(cast(value))
139
+ end
140
+
141
+ def serialize_cast_value(value)
142
+ ActiveModel::Type::SerializeCastValue.serialize(cast_type, value)
143
+ end
144
+
145
+ def ==(other)
146
+ self.class == other.class &&
147
+ normalize_nil? == other.normalize_nil? &&
148
+ normalizer == other.normalizer &&
149
+ cast_type == other.cast_type
150
+ end
151
+ alias eql? ==
152
+
153
+ def hash
154
+ [self.class, cast_type, normalizer, normalize_nil?].hash
155
+ end
156
+
157
+ def inspect
158
+ Kernel.instance_method(:inspect).bind_call(self)
159
+ end
160
+
161
+ private
162
+ def normalize(value)
163
+ normalizer.call(value) unless value.nil? && !normalize_nil?
164
+ end
165
+ end
166
+ end
167
+ end