activerecord 6.1.7 → 7.1.0

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 (307) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1516 -1019
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +17 -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 +423 -289
  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 +61 -14
  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 -46
  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 +171 -51
  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 -136
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +622 -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 +148 -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 +18 -1
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -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/range.rb +12 -3
  88. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  91. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  93. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  94. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  96. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  97. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +381 -69
  98. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  99. data/lib/active_record/connection_adapters/postgresql_adapter.rb +492 -230
  100. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  101. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  102. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +65 -53
  103. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  104. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  105. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -102
  107. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  108. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  109. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  110. data/lib/active_record/connection_adapters.rb +9 -6
  111. data/lib/active_record/connection_handling.rb +107 -136
  112. data/lib/active_record/core.rb +194 -224
  113. data/lib/active_record/counter_cache.rb +46 -25
  114. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  115. data/lib/active_record/database_configurations/database_config.rb +21 -12
  116. data/lib/active_record/database_configurations/hash_config.rb +84 -16
  117. data/lib/active_record/database_configurations/url_config.rb +18 -12
  118. data/lib/active_record/database_configurations.rb +95 -59
  119. data/lib/active_record/delegated_type.rb +61 -15
  120. data/lib/active_record/deprecator.rb +7 -0
  121. data/lib/active_record/destroy_association_async_job.rb +3 -1
  122. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  123. data/lib/active_record/dynamic_matchers.rb +1 -1
  124. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  125. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  126. data/lib/active_record/encryption/cipher.rb +53 -0
  127. data/lib/active_record/encryption/config.rb +68 -0
  128. data/lib/active_record/encryption/configurable.rb +60 -0
  129. data/lib/active_record/encryption/context.rb +42 -0
  130. data/lib/active_record/encryption/contexts.rb +76 -0
  131. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  132. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  133. data/lib/active_record/encryption/encryptable_record.rb +224 -0
  134. data/lib/active_record/encryption/encrypted_attribute_type.rb +151 -0
  135. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  136. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  137. data/lib/active_record/encryption/encryptor.rb +155 -0
  138. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  139. data/lib/active_record/encryption/errors.rb +15 -0
  140. data/lib/active_record/encryption/extended_deterministic_queries.rb +172 -0
  141. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  142. data/lib/active_record/encryption/key.rb +28 -0
  143. data/lib/active_record/encryption/key_generator.rb +53 -0
  144. data/lib/active_record/encryption/key_provider.rb +46 -0
  145. data/lib/active_record/encryption/message.rb +33 -0
  146. data/lib/active_record/encryption/message_serializer.rb +92 -0
  147. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  148. data/lib/active_record/encryption/properties.rb +76 -0
  149. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  150. data/lib/active_record/encryption/scheme.rb +96 -0
  151. data/lib/active_record/encryption.rb +56 -0
  152. data/lib/active_record/enum.rb +156 -62
  153. data/lib/active_record/errors.rb +171 -15
  154. data/lib/active_record/explain.rb +23 -3
  155. data/lib/active_record/explain_registry.rb +11 -6
  156. data/lib/active_record/explain_subscriber.rb +1 -1
  157. data/lib/active_record/fixture_set/file.rb +15 -1
  158. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  159. data/lib/active_record/fixture_set/render_context.rb +2 -0
  160. data/lib/active_record/fixture_set/table_row.rb +70 -14
  161. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  162. data/lib/active_record/fixtures.rb +131 -86
  163. data/lib/active_record/future_result.rb +164 -0
  164. data/lib/active_record/gem_version.rb +3 -3
  165. data/lib/active_record/inheritance.rb +81 -29
  166. data/lib/active_record/insert_all.rb +133 -20
  167. data/lib/active_record/integration.rb +11 -10
  168. data/lib/active_record/internal_metadata.rb +117 -33
  169. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  170. data/lib/active_record/locking/optimistic.rb +36 -21
  171. data/lib/active_record/locking/pessimistic.rb +15 -6
  172. data/lib/active_record/log_subscriber.rb +52 -19
  173. data/lib/active_record/marshalling.rb +56 -0
  174. data/lib/active_record/message_pack.rb +124 -0
  175. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  176. data/lib/active_record/middleware/database_selector.rb +23 -13
  177. data/lib/active_record/middleware/shard_selector.rb +62 -0
  178. data/lib/active_record/migration/command_recorder.rb +108 -13
  179. data/lib/active_record/migration/compatibility.rb +221 -48
  180. data/lib/active_record/migration/default_strategy.rb +23 -0
  181. data/lib/active_record/migration/execution_strategy.rb +19 -0
  182. data/lib/active_record/migration/join_table.rb +1 -1
  183. data/lib/active_record/migration.rb +355 -171
  184. data/lib/active_record/model_schema.rb +116 -97
  185. data/lib/active_record/nested_attributes.rb +36 -15
  186. data/lib/active_record/no_touching.rb +3 -3
  187. data/lib/active_record/normalization.rb +159 -0
  188. data/lib/active_record/persistence.rb +405 -85
  189. data/lib/active_record/promise.rb +84 -0
  190. data/lib/active_record/query_cache.rb +3 -21
  191. data/lib/active_record/query_logs.rb +174 -0
  192. data/lib/active_record/query_logs_formatter.rb +41 -0
  193. data/lib/active_record/querying.rb +29 -6
  194. data/lib/active_record/railtie.rb +219 -43
  195. data/lib/active_record/railties/controller_runtime.rb +13 -9
  196. data/lib/active_record/railties/databases.rake +185 -249
  197. data/lib/active_record/railties/job_runtime.rb +23 -0
  198. data/lib/active_record/readonly_attributes.rb +41 -3
  199. data/lib/active_record/reflection.rb +229 -80
  200. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  201. data/lib/active_record/relation/batches.rb +192 -63
  202. data/lib/active_record/relation/calculations.rb +211 -90
  203. data/lib/active_record/relation/delegation.rb +27 -13
  204. data/lib/active_record/relation/finder_methods.rb +108 -51
  205. data/lib/active_record/relation/merger.rb +22 -13
  206. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  207. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  208. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  209. data/lib/active_record/relation/predicate_builder.rb +27 -20
  210. data/lib/active_record/relation/query_attribute.rb +30 -12
  211. data/lib/active_record/relation/query_methods.rb +654 -127
  212. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  213. data/lib/active_record/relation/spawn_methods.rb +20 -3
  214. data/lib/active_record/relation/where_clause.rb +10 -19
  215. data/lib/active_record/relation.rb +262 -120
  216. data/lib/active_record/result.rb +37 -11
  217. data/lib/active_record/runtime_registry.rb +18 -13
  218. data/lib/active_record/sanitization.rb +65 -20
  219. data/lib/active_record/schema.rb +36 -22
  220. data/lib/active_record/schema_dumper.rb +73 -24
  221. data/lib/active_record/schema_migration.rb +68 -33
  222. data/lib/active_record/scoping/default.rb +72 -15
  223. data/lib/active_record/scoping/named.rb +5 -13
  224. data/lib/active_record/scoping.rb +65 -34
  225. data/lib/active_record/secure_password.rb +60 -0
  226. data/lib/active_record/secure_token.rb +21 -3
  227. data/lib/active_record/serialization.rb +6 -1
  228. data/lib/active_record/signed_id.rb +10 -8
  229. data/lib/active_record/store.rb +10 -10
  230. data/lib/active_record/suppressor.rb +13 -15
  231. data/lib/active_record/table_metadata.rb +16 -3
  232. data/lib/active_record/tasks/database_tasks.rb +225 -136
  233. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  234. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  235. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  236. data/lib/active_record/test_databases.rb +1 -1
  237. data/lib/active_record/test_fixtures.rb +116 -96
  238. data/lib/active_record/timestamp.rb +28 -17
  239. data/lib/active_record/token_for.rb +113 -0
  240. data/lib/active_record/touch_later.rb +11 -6
  241. data/lib/active_record/transactions.rb +48 -27
  242. data/lib/active_record/translation.rb +3 -3
  243. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  244. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  245. data/lib/active_record/type/internal/timezone.rb +7 -2
  246. data/lib/active_record/type/serialized.rb +9 -5
  247. data/lib/active_record/type/time.rb +4 -0
  248. data/lib/active_record/type/type_map.rb +17 -20
  249. data/lib/active_record/type.rb +1 -2
  250. data/lib/active_record/validations/absence.rb +1 -1
  251. data/lib/active_record/validations/associated.rb +4 -4
  252. data/lib/active_record/validations/numericality.rb +5 -4
  253. data/lib/active_record/validations/presence.rb +5 -28
  254. data/lib/active_record/validations/uniqueness.rb +51 -6
  255. data/lib/active_record/validations.rb +8 -4
  256. data/lib/active_record/version.rb +1 -1
  257. data/lib/active_record.rb +335 -32
  258. data/lib/arel/attributes/attribute.rb +0 -8
  259. data/lib/arel/crud.rb +28 -22
  260. data/lib/arel/delete_manager.rb +18 -4
  261. data/lib/arel/errors.rb +10 -0
  262. data/lib/arel/factory_methods.rb +4 -0
  263. data/lib/arel/filter_predications.rb +9 -0
  264. data/lib/arel/insert_manager.rb +2 -3
  265. data/lib/arel/nodes/and.rb +4 -0
  266. data/lib/arel/nodes/binary.rb +6 -1
  267. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  268. data/lib/arel/nodes/casted.rb +1 -1
  269. data/lib/arel/nodes/cte.rb +36 -0
  270. data/lib/arel/nodes/delete_statement.rb +12 -13
  271. data/lib/arel/nodes/filter.rb +10 -0
  272. data/lib/arel/nodes/fragments.rb +35 -0
  273. data/lib/arel/nodes/function.rb +1 -0
  274. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  275. data/lib/arel/nodes/insert_statement.rb +2 -2
  276. data/lib/arel/nodes/leading_join.rb +8 -0
  277. data/lib/arel/nodes/node.rb +111 -2
  278. data/lib/arel/nodes/select_core.rb +2 -2
  279. data/lib/arel/nodes/select_statement.rb +2 -2
  280. data/lib/arel/nodes/sql_literal.rb +6 -0
  281. data/lib/arel/nodes/table_alias.rb +4 -0
  282. data/lib/arel/nodes/update_statement.rb +8 -3
  283. data/lib/arel/nodes.rb +5 -0
  284. data/lib/arel/predications.rb +13 -3
  285. data/lib/arel/select_manager.rb +10 -4
  286. data/lib/arel/table.rb +9 -6
  287. data/lib/arel/tree_manager.rb +0 -12
  288. data/lib/arel/update_manager.rb +18 -4
  289. data/lib/arel/visitors/dot.rb +80 -90
  290. data/lib/arel/visitors/mysql.rb +16 -3
  291. data/lib/arel/visitors/postgresql.rb +0 -10
  292. data/lib/arel/visitors/to_sql.rb +139 -19
  293. data/lib/arel/visitors/visitor.rb +2 -2
  294. data/lib/arel.rb +18 -3
  295. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  296. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  297. data/lib/rails/generators/active_record/migration.rb +3 -1
  298. data/lib/rails/generators/active_record/model/USAGE +113 -0
  299. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  300. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  301. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  302. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  303. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  304. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  305. metadata +92 -13
  306. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  307. 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,7 +283,7 @@ 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:
286
+ def reset_table_name # :nodoc:
237
287
  self.table_name = if abstract_class?
238
288
  superclass == Base ? nil : superclass.table_name
239
289
  elsif superclass.abstract_class?
@@ -243,11 +293,11 @@ module ActiveRecord
243
293
  end
244
294
  end
245
295
 
246
- def full_table_name_prefix #:nodoc:
296
+ def full_table_name_prefix # :nodoc:
247
297
  (module_parents.detect { |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
248
298
  end
249
299
 
250
- def full_table_name_suffix #:nodoc:
300
+ def full_table_name_suffix # :nodoc:
251
301
  (module_parents.detect { |p| p.respond_to?(:table_name_suffix) } || self).table_name_suffix
252
302
  end
253
303
 
@@ -266,33 +316,14 @@ module ActiveRecord
266
316
  @protected_environments = environments.map(&:to_s)
267
317
  end
268
318
 
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
319
+ def real_inheritance_column=(value) # :nodoc:
320
+ self._inheritance_column = value.to_s
286
321
  end
287
322
 
288
323
  # The list of columns names the model should ignore. Ignored columns won't have attribute
289
324
  # accessors defined, and won't be referenced in SQL queries.
290
325
  def ignored_columns
291
- if defined?(@ignored_columns)
292
- @ignored_columns
293
- else
294
- superclass.ignored_columns
295
- end
326
+ @ignored_columns || superclass.ignored_columns
296
327
  end
297
328
 
298
329
  # Sets the columns names the model should ignore. Ignored columns won't have attribute
@@ -313,7 +344,7 @@ module ActiveRecord
313
344
  # # name :string, limit: 255
314
345
  # # category :string, limit: 255
315
346
  #
316
- # self.ignored_columns = [:category]
347
+ # self.ignored_columns += [:category]
317
348
  # end
318
349
  #
319
350
  # The schema still contains "category", but now the model omits it, so any meta-driven code or
@@ -339,7 +370,7 @@ module ActiveRecord
339
370
  end
340
371
  end
341
372
 
342
- def reset_sequence_name #:nodoc:
373
+ def reset_sequence_name # :nodoc:
343
374
  @explicit_sequence_name = false
344
375
  @sequence_name = connection.default_sequence_name(table_name, primary_key)
345
376
  end
@@ -398,6 +429,12 @@ module ActiveRecord
398
429
  @columns ||= columns_hash.values.freeze
399
430
  end
400
431
 
432
+ def _returning_columns_for_insert # :nodoc:
433
+ @_returning_columns_for_insert ||= columns.filter_map do |c|
434
+ c.name if connection.return_value_after_insert?(c)
435
+ end
436
+ end
437
+
401
438
  def attribute_types # :nodoc:
402
439
  load_schema
403
440
  @attribute_types ||= Hash.new(Type.default_value)
@@ -486,9 +523,9 @@ module ActiveRecord
486
523
  #
487
524
  # The most common usage pattern for this method is probably in a migration,
488
525
  # when just after creating a table you want to populate it with some default
489
- # values, eg:
526
+ # values, e.g.:
490
527
  #
491
- # class CreateJobLevels < ActiveRecord::Migration[6.0]
528
+ # class CreateJobLevels < ActiveRecord::Migration[7.1]
492
529
  # def up
493
530
  # create_table :job_levels do |t|
494
531
  # t.integer :id
@@ -516,35 +553,61 @@ module ActiveRecord
516
553
  initialize_find_by_cache
517
554
  end
518
555
 
556
+ def load_schema # :nodoc:
557
+ return if schema_loaded?
558
+ @load_schema_monitor.synchronize do
559
+ return if @columns_hash
560
+
561
+ load_schema!
562
+
563
+ @schema_loaded = true
564
+ rescue
565
+ reload_schema_from_cache # If the schema loading failed half way through, we must reset the state.
566
+ raise
567
+ end
568
+ end
569
+
519
570
  protected
520
571
  def initialize_load_schema_monitor
521
572
  @load_schema_monitor = Monitor.new
522
573
  end
523
574
 
575
+ def reload_schema_from_cache(recursive = true)
576
+ @_returning_columns_for_insert = nil
577
+ @arel_table = nil
578
+ @column_names = nil
579
+ @symbol_column_to_string_name_hash = nil
580
+ @attribute_types = nil
581
+ @content_columns = nil
582
+ @default_attributes = nil
583
+ @column_defaults = nil
584
+ @attributes_builder = nil
585
+ @columns = nil
586
+ @columns_hash = nil
587
+ @schema_loaded = false
588
+ @attribute_names = nil
589
+ @yaml_encoder = nil
590
+ if recursive
591
+ subclasses.each do |descendant|
592
+ descendant.send(:reload_schema_from_cache)
593
+ end
594
+ end
595
+ end
596
+
524
597
  private
525
598
  def inherited(child_class)
526
599
  super
527
600
  child_class.initialize_load_schema_monitor
601
+ child_class.reload_schema_from_cache(false)
602
+ child_class.class_eval do
603
+ @ignored_columns = nil
604
+ end
528
605
  end
529
606
 
530
607
  def schema_loaded?
531
608
  defined?(@schema_loaded) && @schema_loaded
532
609
  end
533
610
 
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
611
  def load_schema!
549
612
  unless table_name
550
613
  raise ActiveRecord::TableNotSpecified, "#{self} has no table configured. Set one with #{self}.table_name="
@@ -556,39 +619,21 @@ module ActiveRecord
556
619
  @columns_hash.each do |name, column|
557
620
  type = connection.lookup_cast_type_from_column(column)
558
621
  type = _convert_type_from_options(type)
559
- warn_if_deprecated_type(column)
560
622
  define_attribute(
561
623
  name,
562
624
  type,
563
625
  default: column.default,
564
626
  user_provided_default: false
565
627
  )
628
+ alias_attribute :id_value, :id if name == "id"
566
629
  end
567
- end
568
630
 
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)
586
- end
631
+ super
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,26 @@ 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
287
+ # for updating or destroying nested attributes.
288
+ #
289
+ # === Testing
290
+ #
291
+ # If you are using ActionView::Helpers::FormHelper#fields_for, your integration
292
+ # tests should replicate the HTML structure it provides. For example;
293
+ #
294
+ # post members_path, params: {
295
+ # member: {
296
+ # name: 'joe',
297
+ # posts_attributes: {
298
+ # '0' => { title: 'Foo' },
299
+ # '1' => { title: 'Bar' }
300
+ # }
301
+ # }
302
+ # }
282
303
  module ClassMethods
283
304
  REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == "_destroy" || value.blank? } }
284
305
 
@@ -374,11 +395,11 @@ module ActiveRecord
374
395
  end
375
396
  end
376
397
 
377
- # Returns ActiveRecord::AutosaveAssociation::marked_for_destruction? It's
398
+ # Returns ActiveRecord::AutosaveAssociation#marked_for_destruction? It's
378
399
  # used in conjunction with fields_for to build a form element for the
379
400
  # destruction of this association.
380
401
  #
381
- # See ActionView::Helpers::FormHelper::fields_for for more info.
402
+ # See ActionView::Helpers::FormHelper#fields_for for more info.
382
403
  def _destroy
383
404
  marked_for_destruction?
384
405
  end
@@ -486,7 +507,7 @@ module ActiveRecord
486
507
  existing_records = if association.loaded?
487
508
  association.target
488
509
  else
489
- attribute_ids = attributes_collection.map { |a| a["id"] || a[:id] }.compact
510
+ attribute_ids = attributes_collection.filter_map { |a| a["id"] || a[:id] }
490
511
  attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
491
512
  end
492
513
 
@@ -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,159 @@
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
+ # ==== Options
53
+ #
54
+ # * +:with+ - Any callable object that accepts the attribute's value as
55
+ # its sole argument, and returns it normalized.
56
+ # * +:apply_to_nil+ - Whether to apply the normalization to +nil+ values.
57
+ # Defaults to +false+.
58
+ #
59
+ # ==== Examples
60
+ #
61
+ # class User < ActiveRecord::Base
62
+ # normalizes :email, with: -> email { email.strip.downcase }
63
+ # normalizes :phone, with: -> phone { phone.delete("^0-9").delete_prefix("1") }
64
+ # end
65
+ #
66
+ # user = User.create(email: " CRUISE-CONTROL@EXAMPLE.COM\n")
67
+ # user.email # => "cruise-control@example.com"
68
+ #
69
+ # user = User.find_by(email: "\tCRUISE-CONTROL@EXAMPLE.COM ")
70
+ # user.email # => "cruise-control@example.com"
71
+ # user.email_before_type_cast # => "cruise-control@example.com"
72
+ #
73
+ # User.where(email: "\tCRUISE-CONTROL@EXAMPLE.COM ").count # => 1
74
+ # User.where(["email = ?", "\tCRUISE-CONTROL@EXAMPLE.COM "]).count # => 0
75
+ #
76
+ # User.exists?(email: "\tCRUISE-CONTROL@EXAMPLE.COM ") # => true
77
+ # User.exists?(["email = ?", "\tCRUISE-CONTROL@EXAMPLE.COM "]) # => false
78
+ #
79
+ # User.normalize_value_for(:phone, "+1 (555) 867-5309") # => "5558675309"
80
+ def normalizes(*names, with:, apply_to_nil: false)
81
+ names.each do |name|
82
+ attribute(name) do |cast_type|
83
+ NormalizedValueType.new(cast_type: cast_type, normalizer: with, normalize_nil: apply_to_nil)
84
+ end
85
+ end
86
+
87
+ self.normalized_attributes += names.map(&:to_sym)
88
+ end
89
+
90
+ # Normalizes a given +value+ using normalizations declared for +name+.
91
+ #
92
+ # ==== Examples
93
+ #
94
+ # class User < ActiveRecord::Base
95
+ # normalizes :email, with: -> email { email.strip.downcase }
96
+ # end
97
+ #
98
+ # User.normalize_value_for(:email, " CRUISE-CONTROL@EXAMPLE.COM\n")
99
+ # # => "cruise-control@example.com"
100
+ def normalize_value_for(name, value)
101
+ type_for_attribute(name).cast(value)
102
+ end
103
+ end
104
+
105
+ private
106
+ def normalize_changed_in_place_attributes
107
+ self.class.normalized_attributes.each do |name|
108
+ normalize_attribute(name) if attribute_changed_in_place?(name)
109
+ end
110
+ end
111
+
112
+ class NormalizedValueType < DelegateClass(ActiveModel::Type::Value) # :nodoc:
113
+ include ActiveModel::Type::SerializeCastValue
114
+
115
+ attr_reader :cast_type, :normalizer, :normalize_nil
116
+ alias :normalize_nil? :normalize_nil
117
+
118
+ def initialize(cast_type:, normalizer:, normalize_nil:)
119
+ @cast_type = cast_type
120
+ @normalizer = normalizer
121
+ @normalize_nil = normalize_nil
122
+ super(cast_type)
123
+ end
124
+
125
+ def cast(value)
126
+ normalize(super(value))
127
+ end
128
+
129
+ def serialize(value)
130
+ serialize_cast_value(cast(value))
131
+ end
132
+
133
+ def serialize_cast_value(value)
134
+ ActiveModel::Type::SerializeCastValue.serialize(cast_type, value)
135
+ end
136
+
137
+ def ==(other)
138
+ self.class == other.class &&
139
+ normalize_nil? == other.normalize_nil? &&
140
+ normalizer == other.normalizer &&
141
+ cast_type == other.cast_type
142
+ end
143
+ alias eql? ==
144
+
145
+ def hash
146
+ [self.class, cast_type, normalizer, normalize_nil?].hash
147
+ end
148
+
149
+ def inspect
150
+ Kernel.instance_method(:inspect).bind_call(self)
151
+ end
152
+
153
+ private
154
+ def normalize(value)
155
+ normalizer.call(value) unless value.nil? && !normalize_nil?
156
+ end
157
+ end
158
+ end
159
+ end