activerecord 6.1.7 → 7.2.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 (333) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +616 -1290
  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 +19 -8
  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 +30 -27
  30. data/lib/active_record/associations/join_dependency.rb +28 -20
  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 +429 -522
  40. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  41. data/lib/active_record/attribute_assignment.rb +1 -5
  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 +15 -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 +57 -54
  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 +19 -35
  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 +325 -604
  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 +230 -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 +378 -143
  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 +348 -165
  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 +403 -77
  108. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  109. data/lib/active_record/connection_adapters/postgresql_adapter.rb +520 -253
  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 +310 -253
  125. data/lib/active_record/counter_cache.rb +68 -34
  126. data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -4
  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 +58 -0
  165. data/lib/active_record/enum.rb +170 -62
  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 +59 -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 +145 -158
  199. data/lib/active_record/nested_attributes.rb +61 -23
  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 +18 -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 +229 -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 +332 -103
  214. data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
  215. data/lib/active_record/relation/batches.rb +200 -65
  216. data/lib/active_record/relation/calculations.rb +301 -112
  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 +870 -163
  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 +6 -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 +288 -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 +65 -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/sqlite.rb +25 -0
  317. data/lib/arel/visitors/to_sql.rb +170 -36
  318. data/lib/arel/visitors/visitor.rb +2 -2
  319. data/lib/arel.rb +23 -4
  320. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  321. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  322. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  323. data/lib/rails/generators/active_record/migration.rb +3 -1
  324. data/lib/rails/generators/active_record/model/USAGE +113 -0
  325. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  326. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  327. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  328. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  329. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  330. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  331. metadata +103 -17
  332. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  333. 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
@@ -27,15 +25,17 @@ module ActiveRecord
27
25
  # column which this will persist to.
28
26
  #
29
27
  # +cast_type+ A symbol such as +:string+ or +:integer+, or a type object
30
- # to be used for this attribute. See the examples below for more
31
- # information about providing custom type objects.
28
+ # to be used for this attribute. If this parameter is not passed, the previously
29
+ # defined type (if any) will be used.
30
+ # Otherwise, the type will be ActiveModel::Type::Value.
31
+ # See the examples below for more information about providing custom type objects.
32
32
  #
33
33
  # ==== Options
34
34
  #
35
35
  # The following options are accepted:
36
36
  #
37
37
  # +default+ The default value to use when no value is provided. If this option
38
- # is not passed, the previous default value (if any) will be used.
38
+ # is not passed, the previously defined default value (if any) on the superclass or in the schema will be used.
39
39
  # Otherwise, the default will be +nil+.
40
40
  #
41
41
  # +array+ (PostgreSQL only) specifies that the type should be an array (see the
@@ -137,7 +137,7 @@ module ActiveRecord
137
137
  # expected API. It is recommended that your type objects inherit from an
138
138
  # existing type, or from ActiveRecord::Type::Value
139
139
  #
140
- # class MoneyType < ActiveRecord::Type::Integer
140
+ # class PriceType < ActiveRecord::Type::Integer
141
141
  # def cast(value)
142
142
  # if !value.kind_of?(Numeric) && value.include?('$')
143
143
  # price_in_dollars = value.gsub(/\$/, '').to_f
@@ -149,11 +149,11 @@ module ActiveRecord
149
149
  # end
150
150
  #
151
151
  # # config/initializers/types.rb
152
- # ActiveRecord::Type.register(:money, MoneyType)
152
+ # ActiveRecord::Type.register(:price, PriceType)
153
153
  #
154
154
  # # app/models/store_listing.rb
155
155
  # class StoreListing < ActiveRecord::Base
156
- # attribute :price_in_cents, :money
156
+ # attribute :price_in_cents, :price
157
157
  # end
158
158
  #
159
159
  # store_listing = StoreListing.new(price_in_cents: '$10.00')
@@ -173,7 +173,7 @@ module ActiveRecord
173
173
  # class Money < Struct.new(:amount, :currency)
174
174
  # end
175
175
  #
176
- # class MoneyType < ActiveRecord::Type::Value
176
+ # class PriceType < ActiveRecord::Type::Value
177
177
  # def initialize(currency_converter:)
178
178
  # @currency_converter = currency_converter
179
179
  # end
@@ -188,19 +188,19 @@ module ActiveRecord
188
188
  # end
189
189
  #
190
190
  # # config/initializers/types.rb
191
- # ActiveRecord::Type.register(:money, MoneyType)
191
+ # ActiveRecord::Type.register(:price, PriceType)
192
192
  #
193
193
  # # app/models/product.rb
194
194
  # class Product < ActiveRecord::Base
195
195
  # currency_converter = ConversionRatesFromTheInternet.new
196
- # attribute :price_in_bitcoins, :money, currency_converter: currency_converter
196
+ # attribute :price_in_bitcoins, :price, currency_converter: currency_converter
197
197
  # end
198
198
  #
199
199
  # Product.where(price_in_bitcoins: Money.new(5, "USD"))
200
- # # => SELECT * FROM products WHERE price_in_bitcoins = 0.02230
200
+ # # SELECT * FROM products WHERE price_in_bitcoins = 0.02230
201
201
  #
202
202
  # Product.where(price_in_bitcoins: Money.new(5, "GBP"))
203
- # # => SELECT * FROM products WHERE price_in_bitcoins = 0.03412
203
+ # # SELECT * FROM products WHERE price_in_bitcoins = 0.03412
204
204
  #
205
205
  # ==== Dirty Tracking
206
206
  #
@@ -208,20 +208,12 @@ module ActiveRecord
208
208
  # tracking is performed. The methods +changed?+ and +changed_in_place?+
209
209
  # will be called from ActiveModel::Dirty. See the documentation for those
210
210
  # 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
211
+ #
212
+ #--
213
+ # Implemented by ActiveModel::AttributeRegistration#attribute.
220
214
 
221
- # This is the low level API which sits beneath +attribute+. It only
222
- # 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
+ # This API only accepts type objects, and will do its work immediately instead of
216
+ # waiting for the schema to load. While this method
225
217
  # is provided so it can be used by plugin authors, application code
226
218
  # should probably use ClassMethods#attribute.
227
219
  #
@@ -246,13 +238,38 @@ module ActiveRecord
246
238
  define_default_attribute(name, default, cast_type, from_user: user_provided_default)
247
239
  end
248
240
 
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))
241
+ def _default_attributes # :nodoc:
242
+ @default_attributes ||= begin
243
+ attributes_hash = with_connection do |connection|
244
+ columns_hash.transform_values do |column|
245
+ ActiveModel::Attribute.from_database(column.name, column.default, type_for_column(connection, column))
246
+ end
247
+ end
248
+
249
+ attribute_set = ActiveModel::AttributeSet.new(attributes_hash)
250
+ apply_pending_attribute_modifications(attribute_set)
251
+ attribute_set
253
252
  end
254
253
  end
255
254
 
255
+ ##
256
+ # :method: type_for_attribute
257
+ # :call-seq: type_for_attribute(attribute_name, &block)
258
+ #
259
+ # See ActiveModel::Attributes::ClassMethods#type_for_attribute.
260
+ #
261
+ # This method will access the database and load the model's schema if
262
+ # necessary.
263
+ #--
264
+ # Implemented by ActiveModel::AttributeRegistration::ClassMethods#type_for_attribute.
265
+
266
+ ##
267
+ protected
268
+ def reload_schema_from_cache(*)
269
+ reset_default_attributes!
270
+ super
271
+ end
272
+
256
273
  private
257
274
  NO_DEFAULT_PROVIDED = Object.new # :nodoc:
258
275
  private_constant :NO_DEFAULT_PROVIDED
@@ -273,30 +290,16 @@ module ActiveRecord
273
290
  _default_attributes[name] = default_attribute
274
291
  end
275
292
 
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
293
+ def reset_default_attributes
294
+ reload_schema_from_cache
295
+ end
285
296
 
286
- yield cast_type
287
- end
297
+ def resolve_type_name(name, **options)
298
+ Type.lookup(name, **options, adapter: Type.adapter_name_from(self))
288
299
  end
289
300
 
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
301
+ def type_for_column(connection, column)
302
+ hook_attribute_type(column.name, super)
300
303
  end
301
304
  end
302
305
  end