activerecord 6.1.7 → 7.2.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 (332) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +520 -1385
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +31 -31
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +17 -14
  7. data/lib/active_record/association_relation.rb +2 -12
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +60 -21
  10. data/lib/active_record/associations/association_scope.rb +17 -12
  11. data/lib/active_record/associations/belongs_to_association.rb +37 -11
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
  13. data/lib/active_record/associations/builder/association.rb +11 -5
  14. data/lib/active_record/associations/builder/belongs_to.rb +41 -14
  15. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +4 -4
  19. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  20. data/lib/active_record/associations/collection_association.rb +46 -36
  21. data/lib/active_record/associations/collection_proxy.rb +44 -16
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/errors.rb +265 -0
  24. data/lib/active_record/associations/foreign_association.rb +10 -3
  25. data/lib/active_record/associations/has_many_association.rb +29 -19
  26. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  27. data/lib/active_record/associations/has_one_association.rb +20 -10
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
  30. data/lib/active_record/associations/join_dependency.rb +23 -15
  31. data/lib/active_record/associations/nested_error.rb +47 -0
  32. data/lib/active_record/associations/preloader/association.rb +212 -53
  33. data/lib/active_record/associations/preloader/batch.rb +48 -0
  34. data/lib/active_record/associations/preloader/branch.rb +153 -0
  35. data/lib/active_record/associations/preloader/through_association.rb +50 -16
  36. data/lib/active_record/associations/preloader.rb +50 -121
  37. data/lib/active_record/associations/singular_association.rb +15 -3
  38. data/lib/active_record/associations/through_association.rb +25 -14
  39. data/lib/active_record/associations.rb +404 -509
  40. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  41. data/lib/active_record/attribute_assignment.rb +2 -14
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  43. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  44. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  45. data/lib/active_record/attribute_methods/primary_key.rb +47 -27
  46. data/lib/active_record/attribute_methods/query.rb +31 -19
  47. data/lib/active_record/attribute_methods/read.rb +14 -11
  48. data/lib/active_record/attribute_methods/serialization.rb +174 -37
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -9
  50. data/lib/active_record/attribute_methods/write.rb +12 -15
  51. data/lib/active_record/attribute_methods.rb +164 -52
  52. data/lib/active_record/attributes.rb +51 -49
  53. data/lib/active_record/autosave_association.rb +74 -57
  54. data/lib/active_record/base.rb +27 -5
  55. data/lib/active_record/callbacks.rb +18 -34
  56. data/lib/active_record/coders/column_serializer.rb +61 -0
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +70 -46
  59. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +327 -612
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -64
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +377 -142
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +345 -166
  75. data/lib/active_record/connection_adapters/column.rb +13 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
  78. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
  79. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  81. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  82. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
  83. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
  85. data/lib/active_record/connection_adapters/pool_config.rb +26 -16
  86. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  87. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  88. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
  89. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  94. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  100. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
  103. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  104. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  105. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
  106. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  107. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +401 -77
  108. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  109. data/lib/active_record/connection_adapters/postgresql_adapter.rb +518 -251
  110. data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
  111. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  112. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
  113. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
  114. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  115. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
  119. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  120. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  121. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  122. data/lib/active_record/connection_adapters.rb +130 -6
  123. data/lib/active_record/connection_handling.rb +132 -146
  124. data/lib/active_record/core.rb +276 -251
  125. data/lib/active_record/counter_cache.rb +68 -34
  126. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -3
  127. data/lib/active_record/database_configurations/database_config.rb +34 -10
  128. data/lib/active_record/database_configurations/hash_config.rb +107 -31
  129. data/lib/active_record/database_configurations/url_config.rb +38 -13
  130. data/lib/active_record/database_configurations.rb +96 -60
  131. data/lib/active_record/delegated_type.rb +90 -20
  132. data/lib/active_record/deprecator.rb +7 -0
  133. data/lib/active_record/destroy_association_async_job.rb +4 -2
  134. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  135. data/lib/active_record/dynamic_matchers.rb +3 -3
  136. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  137. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  138. data/lib/active_record/encryption/cipher.rb +53 -0
  139. data/lib/active_record/encryption/config.rb +68 -0
  140. data/lib/active_record/encryption/configurable.rb +60 -0
  141. data/lib/active_record/encryption/context.rb +42 -0
  142. data/lib/active_record/encryption/contexts.rb +76 -0
  143. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  144. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  145. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  146. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  147. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  148. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  149. data/lib/active_record/encryption/encryptor.rb +170 -0
  150. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  151. data/lib/active_record/encryption/errors.rb +15 -0
  152. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  153. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  154. data/lib/active_record/encryption/key.rb +28 -0
  155. data/lib/active_record/encryption/key_generator.rb +53 -0
  156. data/lib/active_record/encryption/key_provider.rb +46 -0
  157. data/lib/active_record/encryption/message.rb +33 -0
  158. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  159. data/lib/active_record/encryption/message_serializer.rb +96 -0
  160. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  161. data/lib/active_record/encryption/properties.rb +76 -0
  162. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  163. data/lib/active_record/encryption/scheme.rb +100 -0
  164. data/lib/active_record/encryption.rb +56 -0
  165. data/lib/active_record/enum.rb +163 -63
  166. data/lib/active_record/errors.rb +210 -27
  167. data/lib/active_record/explain.rb +21 -12
  168. data/lib/active_record/explain_registry.rb +11 -6
  169. data/lib/active_record/explain_subscriber.rb +1 -1
  170. data/lib/active_record/fixture_set/file.rb +15 -1
  171. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  172. data/lib/active_record/fixture_set/render_context.rb +2 -0
  173. data/lib/active_record/fixture_set/table_row.rb +70 -14
  174. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  175. data/lib/active_record/fixtures.rb +179 -112
  176. data/lib/active_record/future_result.rb +178 -0
  177. data/lib/active_record/gem_version.rb +4 -4
  178. data/lib/active_record/inheritance.rb +85 -31
  179. data/lib/active_record/insert_all.rb +148 -32
  180. data/lib/active_record/integration.rb +14 -10
  181. data/lib/active_record/internal_metadata.rb +123 -23
  182. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  183. data/lib/active_record/locking/optimistic.rb +43 -27
  184. data/lib/active_record/locking/pessimistic.rb +15 -6
  185. data/lib/active_record/log_subscriber.rb +41 -29
  186. data/lib/active_record/marshalling.rb +56 -0
  187. data/lib/active_record/message_pack.rb +124 -0
  188. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  189. data/lib/active_record/middleware/database_selector.rb +23 -13
  190. data/lib/active_record/middleware/shard_selector.rb +62 -0
  191. data/lib/active_record/migration/command_recorder.rb +113 -16
  192. data/lib/active_record/migration/compatibility.rb +235 -46
  193. data/lib/active_record/migration/default_strategy.rb +22 -0
  194. data/lib/active_record/migration/execution_strategy.rb +19 -0
  195. data/lib/active_record/migration/join_table.rb +1 -1
  196. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  197. data/lib/active_record/migration.rb +374 -177
  198. data/lib/active_record/model_schema.rb +143 -159
  199. data/lib/active_record/nested_attributes.rb +48 -21
  200. data/lib/active_record/no_touching.rb +3 -3
  201. data/lib/active_record/normalization.rb +163 -0
  202. data/lib/active_record/persistence.rb +282 -283
  203. data/lib/active_record/promise.rb +84 -0
  204. data/lib/active_record/query_cache.rb +19 -25
  205. data/lib/active_record/query_logs.rb +189 -0
  206. data/lib/active_record/query_logs_formatter.rb +41 -0
  207. data/lib/active_record/querying.rb +44 -9
  208. data/lib/active_record/railtie.rb +234 -71
  209. data/lib/active_record/railties/controller_runtime.rb +25 -11
  210. data/lib/active_record/railties/databases.rake +189 -256
  211. data/lib/active_record/railties/job_runtime.rb +23 -0
  212. data/lib/active_record/readonly_attributes.rb +41 -3
  213. data/lib/active_record/reflection.rb +325 -103
  214. data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
  215. data/lib/active_record/relation/batches.rb +198 -63
  216. data/lib/active_record/relation/calculations.rb +300 -111
  217. data/lib/active_record/relation/delegation.rb +33 -22
  218. data/lib/active_record/relation/finder_methods.rb +123 -52
  219. data/lib/active_record/relation/merger.rb +26 -19
  220. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  221. data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
  222. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  223. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  224. data/lib/active_record/relation/predicate_builder.rb +29 -22
  225. data/lib/active_record/relation/query_attribute.rb +30 -12
  226. data/lib/active_record/relation/query_methods.rb +842 -150
  227. data/lib/active_record/relation/record_fetch_warning.rb +10 -9
  228. data/lib/active_record/relation/spawn_methods.rb +7 -6
  229. data/lib/active_record/relation/where_clause.rb +15 -36
  230. data/lib/active_record/relation.rb +736 -145
  231. data/lib/active_record/result.rb +67 -54
  232. data/lib/active_record/runtime_registry.rb +71 -13
  233. data/lib/active_record/sanitization.rb +84 -34
  234. data/lib/active_record/schema.rb +39 -23
  235. data/lib/active_record/schema_dumper.rb +90 -31
  236. data/lib/active_record/schema_migration.rb +74 -23
  237. data/lib/active_record/scoping/default.rb +72 -15
  238. data/lib/active_record/scoping/named.rb +5 -13
  239. data/lib/active_record/scoping.rb +65 -34
  240. data/lib/active_record/secure_password.rb +60 -0
  241. data/lib/active_record/secure_token.rb +21 -3
  242. data/lib/active_record/serialization.rb +6 -1
  243. data/lib/active_record/signed_id.rb +30 -9
  244. data/lib/active_record/statement_cache.rb +7 -7
  245. data/lib/active_record/store.rb +10 -10
  246. data/lib/active_record/suppressor.rb +13 -15
  247. data/lib/active_record/table_metadata.rb +7 -3
  248. data/lib/active_record/tasks/database_tasks.rb +277 -149
  249. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  250. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  251. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  252. data/lib/active_record/test_databases.rb +1 -1
  253. data/lib/active_record/test_fixtures.rb +173 -155
  254. data/lib/active_record/testing/query_assertions.rb +121 -0
  255. data/lib/active_record/timestamp.rb +32 -19
  256. data/lib/active_record/token_for.rb +123 -0
  257. data/lib/active_record/touch_later.rb +12 -7
  258. data/lib/active_record/transaction.rb +132 -0
  259. data/lib/active_record/transactions.rb +118 -41
  260. data/lib/active_record/translation.rb +3 -5
  261. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  262. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  263. data/lib/active_record/type/internal/timezone.rb +7 -2
  264. data/lib/active_record/type/serialized.rb +9 -7
  265. data/lib/active_record/type/time.rb +4 -0
  266. data/lib/active_record/type/type_map.rb +17 -20
  267. data/lib/active_record/type.rb +1 -2
  268. data/lib/active_record/type_caster/connection.rb +4 -4
  269. data/lib/active_record/validations/absence.rb +1 -1
  270. data/lib/active_record/validations/associated.rb +13 -7
  271. data/lib/active_record/validations/numericality.rb +5 -4
  272. data/lib/active_record/validations/presence.rb +5 -28
  273. data/lib/active_record/validations/uniqueness.rb +64 -15
  274. data/lib/active_record/validations.rb +12 -5
  275. data/lib/active_record/version.rb +1 -1
  276. data/lib/active_record.rb +444 -32
  277. data/lib/arel/alias_predication.rb +1 -1
  278. data/lib/arel/attributes/attribute.rb +0 -8
  279. data/lib/arel/collectors/bind.rb +2 -0
  280. data/lib/arel/collectors/composite.rb +7 -0
  281. data/lib/arel/collectors/sql_string.rb +1 -1
  282. data/lib/arel/collectors/substitute_binds.rb +1 -1
  283. data/lib/arel/crud.rb +28 -22
  284. data/lib/arel/delete_manager.rb +18 -4
  285. data/lib/arel/errors.rb +10 -0
  286. data/lib/arel/factory_methods.rb +4 -0
  287. data/lib/arel/filter_predications.rb +9 -0
  288. data/lib/arel/insert_manager.rb +2 -3
  289. data/lib/arel/nodes/binary.rb +6 -7
  290. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  291. data/lib/arel/nodes/casted.rb +1 -1
  292. data/lib/arel/nodes/cte.rb +36 -0
  293. data/lib/arel/nodes/delete_statement.rb +12 -13
  294. data/lib/arel/nodes/filter.rb +10 -0
  295. data/lib/arel/nodes/fragments.rb +35 -0
  296. data/lib/arel/nodes/function.rb +1 -0
  297. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  298. data/lib/arel/nodes/insert_statement.rb +2 -2
  299. data/lib/arel/nodes/leading_join.rb +8 -0
  300. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  301. data/lib/arel/nodes/node.rb +115 -5
  302. data/lib/arel/nodes/select_core.rb +2 -2
  303. data/lib/arel/nodes/select_statement.rb +2 -2
  304. data/lib/arel/nodes/sql_literal.rb +13 -0
  305. data/lib/arel/nodes/table_alias.rb +4 -0
  306. data/lib/arel/nodes/update_statement.rb +8 -3
  307. data/lib/arel/nodes.rb +7 -2
  308. data/lib/arel/predications.rb +14 -4
  309. data/lib/arel/select_manager.rb +11 -5
  310. data/lib/arel/table.rb +9 -6
  311. data/lib/arel/tree_manager.rb +8 -15
  312. data/lib/arel/update_manager.rb +20 -5
  313. data/lib/arel/visitors/dot.rb +81 -90
  314. data/lib/arel/visitors/mysql.rb +23 -5
  315. data/lib/arel/visitors/postgresql.rb +1 -22
  316. data/lib/arel/visitors/to_sql.rb +170 -36
  317. data/lib/arel/visitors/visitor.rb +2 -2
  318. data/lib/arel.rb +23 -4
  319. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  320. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  321. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  322. data/lib/rails/generators/active_record/migration.rb +3 -1
  323. data/lib/rails/generators/active_record/model/USAGE +113 -0
  324. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  325. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  326. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  327. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  328. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  329. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  330. metadata +100 -14
  331. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  332. data/lib/active_record/null_relation.rb +0 -67
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "mutex_m"
4
3
  require "active_support/core_ext/enumerable"
5
4
 
6
5
  module ActiveRecord
@@ -21,10 +20,10 @@ module ActiveRecord
21
20
  include Serialization
22
21
  end
23
22
 
24
- RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
23
+ RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name superclass)
25
24
 
26
- class GeneratedAttributeMethods < Module #:nodoc:
27
- include Mutex_m
25
+ class GeneratedAttributeMethods < Module # :nodoc:
26
+ LOCK = Monitor.new
28
27
  end
29
28
 
30
29
  class << self
@@ -33,44 +32,118 @@ module ActiveRecord
33
32
  Base.instance_methods +
34
33
  Base.private_instance_methods -
35
34
  Base.superclass.instance_methods -
36
- Base.superclass.private_instance_methods
35
+ Base.superclass.private_instance_methods +
36
+ %i[__id__ dup freeze frozen? hash class clone]
37
37
  ).map { |m| -m.to_s }.to_set.freeze
38
38
  end
39
39
  end
40
40
 
41
41
  module ClassMethods
42
- def inherited(child_class) #:nodoc:
43
- child_class.initialize_generated_modules
44
- super
45
- end
46
-
47
42
  def initialize_generated_modules # :nodoc:
48
43
  @generated_attribute_methods = const_set(:GeneratedAttributeMethods, GeneratedAttributeMethods.new)
49
44
  private_constant :GeneratedAttributeMethods
50
45
  @attribute_methods_generated = false
46
+ @alias_attributes_mass_generated = false
51
47
  include @generated_attribute_methods
52
48
 
53
49
  super
54
50
  end
55
51
 
52
+ # Allows you to make aliases for attributes.
53
+ #
54
+ # class Person < ActiveRecord::Base
55
+ # alias_attribute :nickname, :name
56
+ # end
57
+ #
58
+ # person = Person.create(name: 'Bob')
59
+ # person.name # => "Bob"
60
+ # person.nickname # => "Bob"
61
+ #
62
+ # The alias can also be used for querying:
63
+ #
64
+ # Person.where(nickname: "Bob")
65
+ # # SELECT "people".* FROM "people" WHERE "people"."name" = "Bob"
66
+ def alias_attribute(new_name, old_name)
67
+ super
68
+
69
+ if @alias_attributes_mass_generated
70
+ ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
71
+ generate_alias_attribute_methods(code_generator, new_name, old_name)
72
+ end
73
+ end
74
+ end
75
+
76
+ def eagerly_generate_alias_attribute_methods(_new_name, _old_name) # :nodoc:
77
+ # alias attributes in Active Record are lazily generated
78
+ end
79
+
80
+ def generate_alias_attribute_methods(code_generator, new_name, old_name) # :nodoc:
81
+ attribute_method_patterns.each do |pattern|
82
+ alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
83
+ end
84
+ attribute_method_patterns_cache.clear
85
+ end
86
+
87
+ def alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
88
+ old_name = old_name.to_s
89
+
90
+ if !abstract_class? && !has_attribute?(old_name)
91
+ raise ArgumentError, "#{self.name} model aliases `#{old_name}`, but `#{old_name}` is not an attribute. " \
92
+ "Use `alias_method :#{new_name}, :#{old_name}` or define the method manually."
93
+ else
94
+ define_attribute_method_pattern(pattern, old_name, owner: code_generator, as: new_name, override: true)
95
+ end
96
+ end
97
+
98
+ def attribute_methods_generated? # :nodoc:
99
+ @attribute_methods_generated
100
+ end
101
+
56
102
  # Generates all the attribute related methods for columns in the database
57
103
  # accessors, mutators and query methods.
58
104
  def define_attribute_methods # :nodoc:
59
105
  return false if @attribute_methods_generated
60
106
  # Use a mutex; we don't want two threads simultaneously trying to define
61
107
  # attribute methods.
62
- generated_attribute_methods.synchronize do
108
+ GeneratedAttributeMethods::LOCK.synchronize do
63
109
  return false if @attribute_methods_generated
110
+
64
111
  superclass.define_attribute_methods unless base_class?
65
- super(attribute_names)
112
+
113
+ unless abstract_class?
114
+ load_schema
115
+ super(attribute_names)
116
+ alias_attribute :id_value, :id if _has_attribute?("id")
117
+ end
118
+
66
119
  @attribute_methods_generated = true
120
+
121
+ generate_alias_attributes
67
122
  end
123
+ true
124
+ end
125
+
126
+ def generate_alias_attributes # :nodoc:
127
+ superclass.generate_alias_attributes unless superclass == Base
128
+
129
+ return if @alias_attributes_mass_generated
130
+
131
+ ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
132
+ aliases_by_attribute_name.each do |old_name, new_names|
133
+ new_names.each do |new_name|
134
+ generate_alias_attribute_methods(code_generator, new_name, old_name)
135
+ end
136
+ end
137
+ end
138
+
139
+ @alias_attributes_mass_generated = true
68
140
  end
69
141
 
70
142
  def undefine_attribute_methods # :nodoc:
71
- generated_attribute_methods.synchronize do
72
- super if defined?(@attribute_methods_generated) && @attribute_methods_generated
143
+ GeneratedAttributeMethods::LOCK.synchronize do
144
+ super if @attribute_methods_generated
73
145
  @attribute_methods_generated = false
146
+ @alias_attributes_mass_generated = false
74
147
  end
75
148
  end
76
149
 
@@ -97,7 +170,7 @@ module ActiveRecord
97
170
  super
98
171
  else
99
172
  # If ThisClass < ... < SomeSuperClass < ... < Base and SomeSuperClass
100
- # defines its own attribute method, then we don't want to overwrite that.
173
+ # defines its own attribute method, then we don't want to override that.
101
174
  defined = method_defined_within?(method_name, superclass, Base) &&
102
175
  ! superclass.instance_method(method_name).owner.is_a?(GeneratedAttributeMethods)
103
176
  defined || super
@@ -186,6 +259,16 @@ module ActiveRecord
186
259
  def _has_attribute?(attr_name) # :nodoc:
187
260
  attribute_types.key?(attr_name)
188
261
  end
262
+
263
+ private
264
+ def inherited(child_class)
265
+ super
266
+ child_class.initialize_generated_modules
267
+ child_class.class_eval do
268
+ @alias_attributes_mass_generated = false
269
+ @attribute_names = nil
270
+ end
271
+ end
189
272
  end
190
273
 
191
274
  # A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
@@ -209,9 +292,7 @@ module ActiveRecord
209
292
 
210
293
  # If the result is true then check for the select case.
211
294
  # For queries selecting a subset of columns, return false for unselected columns.
212
- # We check defined?(@attributes) not to issue warnings if called on objects that
213
- # have been allocated but not yet initialized.
214
- if defined?(@attributes)
295
+ if @attributes
215
296
  if name = self.class.symbol_column_to_string(name.to_sym)
216
297
  return _has_attribute?(name)
217
298
  end
@@ -267,9 +348,8 @@ module ActiveRecord
267
348
 
268
349
  # Returns an <tt>#inspect</tt>-like string for the value of the
269
350
  # 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.
351
+ # characters. Other attributes return the value of <tt>#inspect</tt>
352
+ # without modification.
273
353
  #
274
354
  # person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
275
355
  #
@@ -277,7 +357,7 @@ module ActiveRecord
277
357
  # # => "\"David Heinemeier Hansson David Heinemeier Hansson ...\""
278
358
  #
279
359
  # person.attribute_for_inspect(:created_at)
280
- # # => "\"2012-10-22 00:15:07\""
360
+ # # => "\"2012-10-22 00:15:07.000000000 +0000\""
281
361
  #
282
362
  # person.attribute_for_inspect(:tag_ids)
283
363
  # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
@@ -310,37 +390,40 @@ module ActiveRecord
310
390
  !value.nil? && !(value.respond_to?(:empty?) && value.empty?)
311
391
  end
312
392
 
313
- # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
314
- # "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
315
- # <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
316
- #
317
- # Note: +:id+ is always present.
393
+ # Returns the value of the attribute identified by +attr_name+ after it has
394
+ # been type cast. (For information about specific type casting behavior, see
395
+ # the types under ActiveModel::Type.)
318
396
  #
319
397
  # class Person < ActiveRecord::Base
320
398
  # belongs_to :organization
321
399
  # end
322
400
  #
323
- # person = Person.new(name: 'Francesco', age: '22')
324
- # person[:name] # => "Francesco"
325
- # person[:age] # => 22
401
+ # person = Person.new(name: "Francesco", date_of_birth: "2004-12-12")
402
+ # person[:name] # => "Francesco"
403
+ # person[:date_of_birth] # => Date.new(2004, 12, 12)
404
+ # person[:organization_id] # => nil
405
+ #
406
+ # Raises ActiveModel::MissingAttributeError if the attribute is missing.
407
+ # Note, however, that the +id+ attribute will never be considered missing.
326
408
  #
327
- # person = Person.select('id').first
328
- # person[:name] # => ActiveModel::MissingAttributeError: missing attribute: name
329
- # person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute: organization_id
409
+ # person = Person.select(:name).first
410
+ # person[:name] # => "Francesco"
411
+ # person[:date_of_birth] # => ActiveModel::MissingAttributeError: missing attribute 'date_of_birth' for Person
412
+ # person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute 'organization_id' for Person
413
+ # person[:id] # => nil
330
414
  def [](attr_name)
331
415
  read_attribute(attr_name) { |n| missing_attribute(n, caller) }
332
416
  end
333
417
 
334
- # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
335
- # (Alias for the protected #write_attribute method).
418
+ # Updates the attribute identified by +attr_name+ using the specified
419
+ # +value+. The attribute value will be type cast upon being read.
336
420
  #
337
421
  # class Person < ActiveRecord::Base
338
422
  # end
339
423
  #
340
424
  # person = Person.new
341
- # person[:age] = '22'
342
- # person[:age] # => 22
343
- # person[:age].class # => Integer
425
+ # person[:date_of_birth] = "2004-12-12"
426
+ # person[:date_of_birth] # => Date.new(2004, 12, 12)
344
427
  def []=(attr_name, value)
345
428
  write_attribute(attr_name, value)
346
429
  end
@@ -361,10 +444,9 @@ module ActiveRecord
361
444
  # end
362
445
  #
363
446
  # private
364
- #
365
- # def print_accessed_fields
366
- # p @posts.first.accessed_fields
367
- # end
447
+ # def print_accessed_fields
448
+ # p @posts.first.accessed_fields
449
+ # end
368
450
  # end
369
451
  #
370
452
  # Which allows you to quickly change your code to:
@@ -379,31 +461,61 @@ module ActiveRecord
379
461
  end
380
462
 
381
463
  private
464
+ def respond_to_missing?(name, include_private = false)
465
+ if self.class.define_attribute_methods
466
+ # Some methods weren't defined yet.
467
+ return true if self.class.method_defined?(name)
468
+ return true if include_private && self.class.private_method_defined?(name)
469
+ end
470
+
471
+ super
472
+ end
473
+
474
+ def method_missing(name, ...)
475
+ unless self.class.attribute_methods_generated?
476
+ if self.class.method_defined?(name)
477
+ # The method is explicitly defined in the model, but calls a generated
478
+ # method with super. So we must resume the call chain at the right step.
479
+ last_method = method(name)
480
+ last_method = last_method.super_method while last_method.super_method
481
+ self.class.define_attribute_methods
482
+ if last_method.super_method
483
+ return last_method.super_method.call(...)
484
+ end
485
+ elsif self.class.define_attribute_methods
486
+ # Some attribute methods weren't generated yet, we retry the call
487
+ return public_send(name, ...)
488
+ end
489
+ end
490
+
491
+ super
492
+ end
493
+
382
494
  def attribute_method?(attr_name)
383
- # We check defined? because Syck calls respond_to? before actually calling initialize.
384
- defined?(@attributes) && @attributes.key?(attr_name)
495
+ @attributes&.key?(attr_name)
385
496
  end
386
497
 
387
498
  def attributes_with_values(attribute_names)
388
- attribute_names.index_with do |name|
389
- _read_attribute(name)
390
- end
499
+ attribute_names.index_with { |name| @attributes[name] }
391
500
  end
392
501
 
393
- # Filters the primary keys and readonly attributes from the attribute names.
502
+ # Filters the primary keys, readonly attributes and virtual columns from the attribute names.
394
503
  def attributes_for_update(attribute_names)
395
504
  attribute_names &= self.class.column_names
396
505
  attribute_names.delete_if do |name|
397
- self.class.readonly_attribute?(name)
506
+ self.class.readonly_attribute?(name) ||
507
+ self.class.counter_cache_column?(name) ||
508
+ column_for_attribute(name).virtual?
398
509
  end
399
510
  end
400
511
 
401
- # Filters out the primary keys, from the attribute names, when the primary
512
+ # Filters out the virtual columns and also primary keys, from the attribute names, when the primary
402
513
  # key is to be generated (e.g. the id attribute has no value).
403
514
  def attributes_for_create(attribute_names)
404
515
  attribute_names &= self.class.column_names
405
516
  attribute_names.delete_if do |name|
406
- pk_attribute?(name) && id.nil?
517
+ (pk_attribute?(name) && id.nil?) ||
518
+ column_for_attribute(name).virtual?
407
519
  end
408
520
  end
409
521
 
@@ -414,7 +526,7 @@ module ActiveRecord
414
526
  inspected_value = if value.is_a?(String) && value.length > 50
415
527
  "#{value[0, 50]}...".inspect
416
528
  elsif value.is_a?(Date) || value.is_a?(Time)
417
- %("#{value.to_s(:inspect)}")
529
+ %("#{value.to_fs(:inspect)}")
418
530
  else
419
531
  value.inspect
420
532
  end
@@ -6,13 +6,11 @@ module ActiveRecord
6
6
  # See ActiveRecord::Attributes::ClassMethods for documentation
7
7
  module Attributes
8
8
  extend ActiveSupport::Concern
9
+ include ActiveModel::AttributeRegistration
9
10
 
10
- included do
11
- class_attribute :attributes_to_define_after_schema_loads, instance_accessor: false, default: {} # :internal:
12
- end
13
-
11
+ # = Active Record \Attributes
14
12
  module ClassMethods
15
- ##
13
+ # :method: attribute
16
14
  # :call-seq: attribute(name, cast_type = nil, **options)
17
15
  #
18
16
  # Defines an attribute with a type on this model. It will override the
@@ -137,7 +135,7 @@ module ActiveRecord
137
135
  # expected API. It is recommended that your type objects inherit from an
138
136
  # existing type, or from ActiveRecord::Type::Value
139
137
  #
140
- # class MoneyType < ActiveRecord::Type::Integer
138
+ # class PriceType < ActiveRecord::Type::Integer
141
139
  # def cast(value)
142
140
  # if !value.kind_of?(Numeric) && value.include?('$')
143
141
  # price_in_dollars = value.gsub(/\$/, '').to_f
@@ -149,11 +147,11 @@ module ActiveRecord
149
147
  # end
150
148
  #
151
149
  # # config/initializers/types.rb
152
- # ActiveRecord::Type.register(:money, MoneyType)
150
+ # ActiveRecord::Type.register(:price, PriceType)
153
151
  #
154
152
  # # app/models/store_listing.rb
155
153
  # class StoreListing < ActiveRecord::Base
156
- # attribute :price_in_cents, :money
154
+ # attribute :price_in_cents, :price
157
155
  # end
158
156
  #
159
157
  # store_listing = StoreListing.new(price_in_cents: '$10.00')
@@ -173,7 +171,7 @@ module ActiveRecord
173
171
  # class Money < Struct.new(:amount, :currency)
174
172
  # end
175
173
  #
176
- # class MoneyType < ActiveRecord::Type::Value
174
+ # class PriceType < ActiveRecord::Type::Value
177
175
  # def initialize(currency_converter:)
178
176
  # @currency_converter = currency_converter
179
177
  # end
@@ -188,19 +186,19 @@ module ActiveRecord
188
186
  # end
189
187
  #
190
188
  # # config/initializers/types.rb
191
- # ActiveRecord::Type.register(:money, MoneyType)
189
+ # ActiveRecord::Type.register(:price, PriceType)
192
190
  #
193
191
  # # app/models/product.rb
194
192
  # class Product < ActiveRecord::Base
195
193
  # currency_converter = ConversionRatesFromTheInternet.new
196
- # attribute :price_in_bitcoins, :money, currency_converter: currency_converter
194
+ # attribute :price_in_bitcoins, :price, currency_converter: currency_converter
197
195
  # end
198
196
  #
199
197
  # Product.where(price_in_bitcoins: Money.new(5, "USD"))
200
- # # => SELECT * FROM products WHERE price_in_bitcoins = 0.02230
198
+ # # SELECT * FROM products WHERE price_in_bitcoins = 0.02230
201
199
  #
202
200
  # Product.where(price_in_bitcoins: Money.new(5, "GBP"))
203
- # # => SELECT * FROM products WHERE price_in_bitcoins = 0.03412
201
+ # # SELECT * FROM products WHERE price_in_bitcoins = 0.03412
204
202
  #
205
203
  # ==== Dirty Tracking
206
204
  #
@@ -208,20 +206,13 @@ module ActiveRecord
208
206
  # tracking is performed. The methods +changed?+ and +changed_in_place?+
209
207
  # will be called from ActiveModel::Dirty. See the documentation for those
210
208
  # methods in ActiveModel::Type::Value for more details.
211
- def attribute(name, cast_type = nil, **options, &block)
212
- name = name.to_s
213
- reload_schema_from_cache
214
-
215
- self.attributes_to_define_after_schema_loads =
216
- attributes_to_define_after_schema_loads.merge(
217
- name => [cast_type || block, options]
218
- )
219
- end
209
+ #
210
+ #--
211
+ # Implemented by ActiveModel::AttributeRegistration#attribute.
220
212
 
221
213
  # This is the low level API which sits beneath +attribute+. It only
222
214
  # accepts type objects, and will do its work immediately instead of
223
- # waiting for the schema to load. Automatic schema detection and
224
- # ClassMethods#attribute both call this under the hood. While this method
215
+ # waiting for the schema to load. While this method
225
216
  # is provided so it can be used by plugin authors, application code
226
217
  # should probably use ClassMethods#attribute.
227
218
  #
@@ -246,13 +237,38 @@ module ActiveRecord
246
237
  define_default_attribute(name, default, cast_type, from_user: user_provided_default)
247
238
  end
248
239
 
249
- def load_schema! # :nodoc:
250
- 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))
240
+ def _default_attributes # :nodoc:
241
+ @default_attributes ||= begin
242
+ attributes_hash = with_connection do |connection|
243
+ columns_hash.transform_values do |column|
244
+ ActiveModel::Attribute.from_database(column.name, column.default, type_for_column(connection, column))
245
+ end
246
+ end
247
+
248
+ attribute_set = ActiveModel::AttributeSet.new(attributes_hash)
249
+ apply_pending_attribute_modifications(attribute_set)
250
+ attribute_set
253
251
  end
254
252
  end
255
253
 
254
+ ##
255
+ # :method: type_for_attribute
256
+ # :call-seq: type_for_attribute(attribute_name, &block)
257
+ #
258
+ # See ActiveModel::Attributes::ClassMethods#type_for_attribute.
259
+ #
260
+ # This method will access the database and load the model's schema if
261
+ # necessary.
262
+ #--
263
+ # Implemented by ActiveModel::AttributeRegistration::ClassMethods#type_for_attribute.
264
+
265
+ ##
266
+ protected
267
+ def reload_schema_from_cache(*)
268
+ reset_default_attributes!
269
+ super
270
+ end
271
+
256
272
  private
257
273
  NO_DEFAULT_PROVIDED = Object.new # :nodoc:
258
274
  private_constant :NO_DEFAULT_PROVIDED
@@ -273,30 +289,16 @@ module ActiveRecord
273
289
  _default_attributes[name] = default_attribute
274
290
  end
275
291
 
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
292
+ def reset_default_attributes
293
+ reload_schema_from_cache
294
+ end
285
295
 
286
- yield cast_type
287
- end
296
+ def resolve_type_name(name, **options)
297
+ Type.lookup(name, **options, adapter: Type.adapter_name_from(self))
288
298
  end
289
299
 
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
+ def type_for_column(connection, column)
301
+ hook_attribute_type(column.name, super)
300
302
  end
301
303
  end
302
304
  end