activerecord 6.0.0 → 7.2.3

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 (376) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +996 -594
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +34 -34
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +22 -20
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations/alias_tracker.rb +41 -30
  9. data/lib/active_record/associations/association.rb +106 -41
  10. data/lib/active_record/associations/association_scope.rb +30 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +69 -14
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
  13. data/lib/active_record/associations/builder/association.rb +39 -6
  14. data/lib/active_record/associations/builder/belongs_to.rb +47 -17
  15. data/lib/active_record/associations/builder/collection_association.rb +14 -6
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
  17. data/lib/active_record/associations/builder/has_many.rb +7 -3
  18. data/lib/active_record/associations/builder/has_one.rb +13 -16
  19. data/lib/active_record/associations/builder/singular_association.rb +7 -3
  20. data/lib/active_record/associations/collection_association.rb +90 -53
  21. data/lib/active_record/associations/collection_proxy.rb +54 -19
  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 +21 -1
  25. data/lib/active_record/associations/has_many_association.rb +41 -10
  26. data/lib/active_record/associations/has_many_through_association.rb +29 -12
  27. data/lib/active_record/associations/has_one_association.rb +33 -9
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
  30. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  31. data/lib/active_record/associations/join_dependency.rb +97 -54
  32. data/lib/active_record/associations/nested_error.rb +47 -0
  33. data/lib/active_record/associations/preloader/association.rb +237 -54
  34. data/lib/active_record/associations/preloader/batch.rb +48 -0
  35. data/lib/active_record/associations/preloader/branch.rb +153 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +51 -17
  37. data/lib/active_record/associations/preloader.rb +55 -121
  38. data/lib/active_record/associations/singular_association.rb +16 -4
  39. data/lib/active_record/associations/through_association.rb +26 -15
  40. data/lib/active_record/associations.rb +454 -440
  41. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  42. data/lib/active_record/attribute_assignment.rb +11 -14
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
  44. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  45. data/lib/active_record/attribute_methods/dirty.rb +75 -34
  46. data/lib/active_record/attribute_methods/primary_key.rb +53 -31
  47. data/lib/active_record/attribute_methods/query.rb +31 -22
  48. data/lib/active_record/attribute_methods/read.rb +16 -17
  49. data/lib/active_record/attribute_methods/serialization.rb +177 -35
  50. data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
  51. data/lib/active_record/attribute_methods/write.rb +16 -28
  52. data/lib/active_record/attribute_methods.rb +227 -100
  53. data/lib/active_record/attributes.rb +94 -56
  54. data/lib/active_record/autosave_association.rb +119 -73
  55. data/lib/active_record/base.rb +31 -21
  56. data/lib/active_record/callbacks.rb +168 -55
  57. data/lib/active_record/coders/column_serializer.rb +61 -0
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +70 -25
  60. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +367 -565
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
  68. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  69. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  70. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
  71. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
  72. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
  73. data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
  74. data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
  75. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
  76. data/lib/active_record/connection_adapters/column.rb +28 -1
  77. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  78. data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
  79. data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
  80. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  81. data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
  82. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
  83. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
  84. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
  85. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
  86. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  87. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  88. data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
  89. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  90. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  91. data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
  92. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
  95. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  101. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
  106. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  110. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -4
  111. data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
  121. data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  123. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  124. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +102 -24
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
  131. data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
  132. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  133. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  134. data/lib/active_record/connection_adapters.rb +176 -0
  135. data/lib/active_record/connection_handling.rb +243 -115
  136. data/lib/active_record/core.rb +481 -199
  137. data/lib/active_record/counter_cache.rb +69 -32
  138. data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
  139. data/lib/active_record/database_configurations/database_config.rb +77 -10
  140. data/lib/active_record/database_configurations/hash_config.rb +148 -26
  141. data/lib/active_record/database_configurations/url_config.rb +44 -45
  142. data/lib/active_record/database_configurations.rb +190 -114
  143. data/lib/active_record/delegated_type.rb +279 -0
  144. data/lib/active_record/deprecator.rb +7 -0
  145. data/lib/active_record/destroy_association_async_job.rb +38 -0
  146. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  147. data/lib/active_record/dynamic_matchers.rb +5 -6
  148. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  149. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  150. data/lib/active_record/encryption/cipher.rb +53 -0
  151. data/lib/active_record/encryption/config.rb +68 -0
  152. data/lib/active_record/encryption/configurable.rb +60 -0
  153. data/lib/active_record/encryption/context.rb +42 -0
  154. data/lib/active_record/encryption/contexts.rb +76 -0
  155. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  156. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  157. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  158. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  159. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  160. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  161. data/lib/active_record/encryption/encryptor.rb +171 -0
  162. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  163. data/lib/active_record/encryption/errors.rb +15 -0
  164. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  165. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  166. data/lib/active_record/encryption/key.rb +28 -0
  167. data/lib/active_record/encryption/key_generator.rb +53 -0
  168. data/lib/active_record/encryption/key_provider.rb +46 -0
  169. data/lib/active_record/encryption/message.rb +33 -0
  170. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  171. data/lib/active_record/encryption/message_serializer.rb +96 -0
  172. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  173. data/lib/active_record/encryption/properties.rb +76 -0
  174. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  175. data/lib/active_record/encryption/scheme.rb +100 -0
  176. data/lib/active_record/encryption.rb +58 -0
  177. data/lib/active_record/enum.rb +224 -73
  178. data/lib/active_record/errors.rb +254 -36
  179. data/lib/active_record/explain.rb +30 -17
  180. data/lib/active_record/explain_registry.rb +11 -6
  181. data/lib/active_record/explain_subscriber.rb +2 -2
  182. data/lib/active_record/fixture_set/file.rb +22 -15
  183. data/lib/active_record/fixture_set/model_metadata.rb +15 -6
  184. data/lib/active_record/fixture_set/render_context.rb +3 -1
  185. data/lib/active_record/fixture_set/table_row.rb +88 -16
  186. data/lib/active_record/fixture_set/table_rows.rb +4 -5
  187. data/lib/active_record/fixtures.rb +229 -116
  188. data/lib/active_record/future_result.rb +178 -0
  189. data/lib/active_record/gem_version.rb +4 -4
  190. data/lib/active_record/inheritance.rb +121 -48
  191. data/lib/active_record/insert_all.rb +178 -29
  192. data/lib/active_record/integration.rb +16 -14
  193. data/lib/active_record/internal_metadata.rb +132 -21
  194. data/lib/active_record/legacy_yaml_adapter.rb +3 -36
  195. data/lib/active_record/locking/optimistic.rb +64 -33
  196. data/lib/active_record/locking/pessimistic.rb +21 -8
  197. data/lib/active_record/log_subscriber.rb +61 -30
  198. data/lib/active_record/marshalling.rb +59 -0
  199. data/lib/active_record/message_pack.rb +124 -0
  200. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  201. data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
  202. data/lib/active_record/middleware/database_selector.rb +25 -13
  203. data/lib/active_record/middleware/shard_selector.rb +62 -0
  204. data/lib/active_record/migration/command_recorder.rb +160 -55
  205. data/lib/active_record/migration/compatibility.rb +286 -43
  206. data/lib/active_record/migration/default_strategy.rb +22 -0
  207. data/lib/active_record/migration/execution_strategy.rb +19 -0
  208. data/lib/active_record/migration/join_table.rb +1 -2
  209. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  210. data/lib/active_record/migration.rb +421 -193
  211. data/lib/active_record/model_schema.rb +217 -125
  212. data/lib/active_record/nested_attributes.rb +62 -27
  213. data/lib/active_record/no_touching.rb +4 -4
  214. data/lib/active_record/normalization.rb +163 -0
  215. data/lib/active_record/persistence.rb +322 -319
  216. data/lib/active_record/promise.rb +84 -0
  217. data/lib/active_record/query_cache.rb +18 -15
  218. data/lib/active_record/query_logs.rb +193 -0
  219. data/lib/active_record/query_logs_formatter.rb +41 -0
  220. data/lib/active_record/querying.rb +54 -14
  221. data/lib/active_record/railtie.rb +250 -72
  222. data/lib/active_record/railties/console_sandbox.rb +2 -4
  223. data/lib/active_record/railties/controller_runtime.rb +25 -11
  224. data/lib/active_record/railties/databases.rake +312 -197
  225. data/lib/active_record/railties/job_runtime.rb +23 -0
  226. data/lib/active_record/readonly_attributes.rb +45 -3
  227. data/lib/active_record/reflection.rb +389 -146
  228. data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
  229. data/lib/active_record/relation/batches.rb +214 -73
  230. data/lib/active_record/relation/calculations.rb +379 -124
  231. data/lib/active_record/relation/delegation.rb +36 -23
  232. data/lib/active_record/relation/finder_methods.rb +159 -49
  233. data/lib/active_record/relation/from_clause.rb +5 -1
  234. data/lib/active_record/relation/merger.rb +41 -33
  235. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
  236. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
  237. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
  238. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  239. data/lib/active_record/relation/predicate_builder.rb +79 -53
  240. data/lib/active_record/relation/query_attribute.rb +30 -12
  241. data/lib/active_record/relation/query_methods.rb +1156 -279
  242. data/lib/active_record/relation/record_fetch_warning.rb +12 -11
  243. data/lib/active_record/relation/spawn_methods.rb +10 -9
  244. data/lib/active_record/relation/where_clause.rb +100 -66
  245. data/lib/active_record/relation.rb +829 -194
  246. data/lib/active_record/result.rb +76 -56
  247. data/lib/active_record/runtime_registry.rb +71 -13
  248. data/lib/active_record/sanitization.rb +86 -47
  249. data/lib/active_record/schema.rb +39 -23
  250. data/lib/active_record/schema_dumper.rb +140 -33
  251. data/lib/active_record/schema_migration.rb +74 -29
  252. data/lib/active_record/scoping/default.rb +73 -19
  253. data/lib/active_record/scoping/named.rb +10 -28
  254. data/lib/active_record/scoping.rb +65 -35
  255. data/lib/active_record/secure_password.rb +60 -0
  256. data/lib/active_record/secure_token.rb +34 -8
  257. data/lib/active_record/serialization.rb +11 -4
  258. data/lib/active_record/signed_id.rb +138 -0
  259. data/lib/active_record/statement_cache.rb +26 -10
  260. data/lib/active_record/store.rb +19 -14
  261. data/lib/active_record/suppressor.rb +15 -17
  262. data/lib/active_record/table_metadata.rb +46 -36
  263. data/lib/active_record/tasks/database_tasks.rb +371 -205
  264. data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
  265. data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
  266. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
  267. data/lib/active_record/test_databases.rb +5 -4
  268. data/lib/active_record/test_fixtures.rb +189 -104
  269. data/lib/active_record/testing/query_assertions.rb +121 -0
  270. data/lib/active_record/timestamp.rb +35 -25
  271. data/lib/active_record/token_for.rb +123 -0
  272. data/lib/active_record/touch_later.rb +31 -27
  273. data/lib/active_record/transaction.rb +132 -0
  274. data/lib/active_record/transactions.rb +131 -99
  275. data/lib/active_record/translation.rb +3 -5
  276. data/lib/active_record/type/adapter_specific_registry.rb +33 -18
  277. data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
  278. data/lib/active_record/type/internal/timezone.rb +7 -2
  279. data/lib/active_record/type/serialized.rb +11 -6
  280. data/lib/active_record/type/time.rb +14 -0
  281. data/lib/active_record/type/type_map.rb +17 -21
  282. data/lib/active_record/type/unsigned_integer.rb +0 -1
  283. data/lib/active_record/type.rb +7 -2
  284. data/lib/active_record/type_caster/connection.rb +4 -5
  285. data/lib/active_record/type_caster/map.rb +8 -5
  286. data/lib/active_record/validations/absence.rb +1 -1
  287. data/lib/active_record/validations/associated.rb +13 -8
  288. data/lib/active_record/validations/numericality.rb +36 -0
  289. data/lib/active_record/validations/presence.rb +5 -28
  290. data/lib/active_record/validations/uniqueness.rb +88 -18
  291. data/lib/active_record/validations.rb +15 -8
  292. data/lib/active_record/version.rb +1 -1
  293. data/lib/active_record.rb +446 -40
  294. data/lib/arel/alias_predication.rb +1 -1
  295. data/lib/arel/attributes/attribute.rb +4 -8
  296. data/lib/arel/collectors/bind.rb +8 -1
  297. data/lib/arel/collectors/composite.rb +15 -0
  298. data/lib/arel/collectors/sql_string.rb +7 -0
  299. data/lib/arel/collectors/substitute_binds.rb +7 -0
  300. data/lib/arel/crud.rb +30 -22
  301. data/lib/arel/delete_manager.rb +23 -4
  302. data/lib/arel/errors.rb +10 -0
  303. data/lib/arel/factory_methods.rb +4 -0
  304. data/lib/arel/filter_predications.rb +9 -0
  305. data/lib/arel/insert_manager.rb +2 -3
  306. data/lib/arel/nodes/binary.rb +82 -9
  307. data/lib/arel/nodes/bind_param.rb +8 -0
  308. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  309. data/lib/arel/nodes/casted.rb +22 -10
  310. data/lib/arel/nodes/cte.rb +36 -0
  311. data/lib/arel/nodes/delete_statement.rb +14 -13
  312. data/lib/arel/nodes/equality.rb +6 -9
  313. data/lib/arel/nodes/filter.rb +10 -0
  314. data/lib/arel/nodes/fragments.rb +35 -0
  315. data/lib/arel/nodes/function.rb +1 -0
  316. data/lib/arel/nodes/grouping.rb +3 -0
  317. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  318. data/lib/arel/nodes/in.rb +8 -1
  319. data/lib/arel/nodes/infix_operation.rb +13 -1
  320. data/lib/arel/nodes/insert_statement.rb +2 -2
  321. data/lib/arel/nodes/join_source.rb +1 -1
  322. data/lib/arel/nodes/leading_join.rb +8 -0
  323. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  324. data/lib/arel/nodes/node.rb +122 -11
  325. data/lib/arel/nodes/ordering.rb +27 -0
  326. data/lib/arel/nodes/select_core.rb +2 -2
  327. data/lib/arel/nodes/select_statement.rb +2 -2
  328. data/lib/arel/nodes/sql_literal.rb +16 -0
  329. data/lib/arel/nodes/table_alias.rb +11 -3
  330. data/lib/arel/nodes/unary.rb +0 -1
  331. data/lib/arel/nodes/update_statement.rb +11 -4
  332. data/lib/arel/nodes.rb +10 -3
  333. data/lib/arel/predications.rb +31 -28
  334. data/lib/arel/select_manager.rb +18 -9
  335. data/lib/arel/table.rb +21 -10
  336. data/lib/arel/tree_manager.rb +8 -15
  337. data/lib/arel/update_manager.rb +25 -5
  338. data/lib/arel/visitors/dot.rb +94 -90
  339. data/lib/arel/visitors/mysql.rb +34 -6
  340. data/lib/arel/visitors/postgresql.rb +5 -16
  341. data/lib/arel/visitors/sqlite.rb +25 -1
  342. data/lib/arel/visitors/to_sql.rb +227 -81
  343. data/lib/arel/visitors/visitor.rb +2 -3
  344. data/lib/arel/visitors.rb +0 -7
  345. data/lib/arel.rb +37 -15
  346. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  347. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  348. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  349. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  350. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
  351. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  352. data/lib/rails/generators/active_record/migration.rb +9 -3
  353. data/lib/rails/generators/active_record/model/USAGE +113 -0
  354. data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
  355. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  356. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  357. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  358. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  359. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  360. metadata +117 -30
  361. data/lib/active_record/attribute_decorators.rb +0 -90
  362. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  363. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  364. data/lib/active_record/define_callbacks.rb +0 -22
  365. data/lib/active_record/null_relation.rb +0 -68
  366. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  367. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  368. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  369. data/lib/arel/attributes.rb +0 -22
  370. data/lib/arel/visitors/depth_first.rb +0 -204
  371. data/lib/arel/visitors/ibm_db.rb +0 -34
  372. data/lib/arel/visitors/informix.rb +0 -62
  373. data/lib/arel/visitors/mssql.rb +0 -157
  374. data/lib/arel/visitors/oracle.rb +0 -159
  375. data/lib/arel/visitors/oracle12.rb +0 -66
  376. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/json"
4
+
5
+ module ActiveRecord
6
+ module TokenFor
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ class_attribute :token_definitions, instance_accessor: false, instance_predicate: false, default: {}
11
+ class_attribute :generated_token_verifier, instance_accessor: false, instance_predicate: false
12
+ end
13
+
14
+ TokenDefinition = Struct.new(:defining_class, :purpose, :expires_in, :block) do # :nodoc:
15
+ def full_purpose
16
+ @full_purpose ||= [defining_class.name, purpose, expires_in].join("\n")
17
+ end
18
+
19
+ def message_verifier
20
+ defining_class.generated_token_verifier
21
+ end
22
+
23
+ def payload_for(model)
24
+ block ? [model.id, model.instance_eval(&block).as_json] : [model.id]
25
+ end
26
+
27
+ def generate_token(model)
28
+ message_verifier.generate(payload_for(model), expires_in: expires_in, purpose: full_purpose)
29
+ end
30
+
31
+ def resolve_token(token)
32
+ payload = message_verifier.verified(token, purpose: full_purpose)
33
+ model = yield(payload[0]) if payload
34
+ model if model && payload_for(model) == payload
35
+ end
36
+ end
37
+
38
+ module RelationMethods
39
+ # Finds a record using a given +token+ for a predefined +purpose+. Returns
40
+ # +nil+ if the token is invalid or the record was not found.
41
+ def find_by_token_for(purpose, token)
42
+ raise UnknownPrimaryKey.new(self) unless model.primary_key
43
+ model.token_definitions.fetch(purpose).resolve_token(token) { |id| find_by(model.primary_key => id) }
44
+ end
45
+
46
+ # Finds a record using a given +token+ for a predefined +purpose+. Raises
47
+ # ActiveSupport::MessageVerifier::InvalidSignature if the token is invalid
48
+ # (e.g. expired, bad format, etc). Raises ActiveRecord::RecordNotFound if
49
+ # the token is valid but the record was not found.
50
+ def find_by_token_for!(purpose, token)
51
+ model.token_definitions.fetch(purpose).resolve_token(token) { |id| find(id) } ||
52
+ (raise ActiveSupport::MessageVerifier::InvalidSignature)
53
+ end
54
+ end
55
+
56
+ module ClassMethods
57
+ # Defines the behavior of tokens generated for a specific +purpose+.
58
+ # A token can be generated by calling TokenFor#generate_token_for on a
59
+ # record. Later, that record can be fetched by calling #find_by_token_for
60
+ # (or #find_by_token_for!) with the same purpose and token.
61
+ #
62
+ # Tokens are signed so that they are tamper-proof. Thus they can be
63
+ # exposed to outside world as, for example, password reset tokens.
64
+ #
65
+ # By default, tokens do not expire. They can be configured to expire by
66
+ # specifying a duration via the +expires_in+ option. The duration becomes
67
+ # part of the token's signature, so changing the value of +expires_in+
68
+ # will automatically invalidate previously generated tokens.
69
+ #
70
+ # A block may also be specified. When generating a token with
71
+ # TokenFor#generate_token_for, the block will be evaluated in the context
72
+ # of the record, and its return value will be embedded in the token as
73
+ # JSON. Later, when fetching the record with #find_by_token_for, the block
74
+ # will be evaluated again in the context of the fetched record. If the two
75
+ # JSON values do not match, the token will be treated as invalid. Note
76
+ # that the value returned by the block <b>should not contain sensitive
77
+ # information</b> because it will be embedded in the token as
78
+ # <b>human-readable plaintext JSON</b>.
79
+ #
80
+ # ==== Examples
81
+ #
82
+ # class User < ActiveRecord::Base
83
+ # has_secure_password
84
+ #
85
+ # generates_token_for :password_reset, expires_in: 15.minutes do
86
+ # # Last 10 characters of password salt, which changes when password is updated:
87
+ # password_salt&.last(10)
88
+ # end
89
+ # end
90
+ #
91
+ # user = User.first
92
+ #
93
+ # token = user.generate_token_for(:password_reset)
94
+ # User.find_by_token_for(:password_reset, token) # => user
95
+ # # 16 minutes later...
96
+ # User.find_by_token_for(:password_reset, token) # => nil
97
+ #
98
+ # token = user.generate_token_for(:password_reset)
99
+ # User.find_by_token_for(:password_reset, token) # => user
100
+ # user.update!(password: "new password")
101
+ # User.find_by_token_for(:password_reset, token) # => nil
102
+ def generates_token_for(purpose, expires_in: nil, &block)
103
+ self.token_definitions = token_definitions.merge(purpose => TokenDefinition.new(self, purpose, expires_in, block))
104
+ end
105
+
106
+ def find_by_token_for(purpose, token) # :nodoc:
107
+ all.find_by_token_for(purpose, token)
108
+ end
109
+
110
+ def find_by_token_for!(purpose, token) # :nodoc:
111
+ all.find_by_token_for!(purpose, token)
112
+ end
113
+ end
114
+
115
+ # Generates a token for a predefined +purpose+.
116
+ #
117
+ # Use ClassMethods#generates_token_for to define a token purpose and
118
+ # behavior.
119
+ def generate_token_for(purpose)
120
+ self.class.token_definitions.fetch(purpose).generate_token(self)
121
+ end
122
+ end
123
+ end
@@ -3,22 +3,20 @@
3
3
  module ActiveRecord
4
4
  # = Active Record Touch Later
5
5
  module TouchLater # :nodoc:
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- before_commit_without_transaction_enrollment :touch_deferred_attributes
6
+ def before_committed!
7
+ touch_deferred_attributes if has_defer_touch_attrs? && persisted?
8
+ super
10
9
  end
11
10
 
12
11
  def touch_later(*names) # :nodoc:
13
- unless persisted?
14
- raise ActiveRecordError, <<-MSG.squish
15
- cannot touch on a new or destroyed record object. Consider using
16
- persisted?, new_record?, or destroyed? before touching
17
- MSG
18
- end
12
+ _raise_record_not_touched_error unless persisted?
19
13
 
20
14
  @_defer_touch_attrs ||= timestamp_attributes_for_update_in_model
21
- @_defer_touch_attrs |= names
15
+ @_defer_touch_attrs |= names.map! do |name|
16
+ name = name.to_s
17
+ self.class.attribute_aliases[name] || name
18
+ end unless names.empty?
19
+
22
20
  @_touch_time = current_time_from_proper_timezone
23
21
 
24
22
  surreptitiously_touch @_defer_touch_attrs
@@ -26,9 +24,13 @@ module ActiveRecord
26
24
  @_new_record_before_last_commit ||= false
27
25
 
28
26
  # touch the parents as we are not calling the after_save callbacks
29
- self.class.reflect_on_all_associations(:belongs_to).each do |r|
27
+ self.class.reflect_on_all_associations.each do |r|
30
28
  if touch = r.options[:touch]
31
- ActiveRecord::Associations::Builder::BelongsTo.touch_record(self, changes_to_save, r.foreign_key, r.name, touch, :touch_later)
29
+ if r.macro == :belongs_to
30
+ ActiveRecord::Associations::Builder::BelongsTo.touch_record(self, changes_to_save, r.foreign_key, r.name, touch)
31
+ elsif r.macro == :has_one
32
+ ActiveRecord::Associations::Builder::HasOne.touch_record(self, r.name, touch)
33
+ end
32
34
  end
33
35
  end
34
36
  end
@@ -36,31 +38,33 @@ module ActiveRecord
36
38
  def touch(*names, time: nil) # :nodoc:
37
39
  if has_defer_touch_attrs?
38
40
  names |= @_defer_touch_attrs
41
+ super(*names, time: time)
42
+ @_defer_touch_attrs, @_touch_time = nil, nil
43
+ else
44
+ super
39
45
  end
40
- super(*names, time: time)
41
46
  end
42
47
 
43
48
  private
44
-
45
- def surreptitiously_touch(attrs)
46
- attrs.each { |attr| write_attribute attr, @_touch_time }
47
- clear_attribute_changes attrs
49
+ def init_internals
50
+ super
51
+ @_defer_touch_attrs = nil
48
52
  end
49
53
 
50
- def touch_deferred_attributes
51
- if has_defer_touch_attrs? && persisted?
52
- @_skip_dirty_tracking = true
53
- touch(*@_defer_touch_attrs, time: @_touch_time)
54
- @_defer_touch_attrs, @_touch_time = nil, nil
54
+ def surreptitiously_touch(attr_names)
55
+ attr_names.each do |attr_name|
56
+ _write_attribute(attr_name, @_touch_time)
57
+ clear_attribute_change(attr_name)
55
58
  end
56
59
  end
57
60
 
58
- def has_defer_touch_attrs?
59
- defined?(@_defer_touch_attrs) && @_defer_touch_attrs.present?
61
+ def touch_deferred_attributes
62
+ @_skip_dirty_tracking = true
63
+ touch(time: @_touch_time)
60
64
  end
61
65
 
62
- def belongs_to_touch_method
63
- :touch_later
66
+ def has_defer_touch_attrs?
67
+ @_defer_touch_attrs.present?
64
68
  end
65
69
  end
66
70
  end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/digest"
4
+
5
+ module ActiveRecord
6
+ # Class specifies the interface to interact with the current transaction state.
7
+ #
8
+ # It can either map to an actual transaction/savepoint, or represent the
9
+ # absence of a transaction.
10
+ #
11
+ # == State
12
+ #
13
+ # We say that a transaction is _finalized_ when it wraps a real transaction
14
+ # that has been either committed or rolled back.
15
+ #
16
+ # A transaction is _open_ if it wraps a real transaction that is not finalized.
17
+ #
18
+ # On the other hand, a transaction is _closed_ when it is not open. That is,
19
+ # when it represents absence of transaction, or it wraps a real but finalized
20
+ # one.
21
+ #
22
+ # You can check whether a transaction is open or closed with the +open?+ and
23
+ # +closed?+ predicates:
24
+ #
25
+ # if Article.current_transaction.open?
26
+ # # We are inside a real and not finalized transaction.
27
+ # end
28
+ #
29
+ # Closed transactions are `blank?` too.
30
+ #
31
+ # == Callbacks
32
+ #
33
+ # After updating the database state, you may sometimes need to perform some extra work, or reflect these
34
+ # changes in a remote system like clearing or updating a cache:
35
+ #
36
+ # def publish_article(article)
37
+ # article.update!(published: true)
38
+ # NotificationService.article_published(article)
39
+ # end
40
+ #
41
+ # The above code works but has one important flaw, which is that it no longer works properly if called inside
42
+ # a transaction, as it will interact with the remote system before the changes are persisted:
43
+ #
44
+ # Article.transaction do
45
+ # article = create_article(article)
46
+ # publish_article(article)
47
+ # end
48
+ #
49
+ # The callbacks offered by ActiveRecord::Transaction allow to rewriting this method in a way that is compatible
50
+ # with transactions:
51
+ #
52
+ # def publish_article(article)
53
+ # article.update!(published: true)
54
+ # Article.current_transaction.after_commit do
55
+ # NotificationService.article_published(article)
56
+ # end
57
+ # end
58
+ #
59
+ # In the above example, if +publish_article+ is called inside a transaction, the callback will be invoked
60
+ # after the transaction is successfully committed, and if called outside a transaction, the callback will be invoked
61
+ # immediately.
62
+ #
63
+ # == Caveats
64
+ #
65
+ # When using after_commit callbacks, it is important to note that if the callback raises an error, the transaction
66
+ # won't be rolled back as it was already committed. Relying solely on these to synchronize state between multiple
67
+ # systems may lead to consistency issues.
68
+ class Transaction
69
+ def initialize(internal_transaction) # :nodoc:
70
+ @internal_transaction = internal_transaction
71
+ @uuid = nil
72
+ end
73
+
74
+ # Registers a block to be called after the transaction is fully committed.
75
+ #
76
+ # If there is no currently open transactions, the block is called
77
+ # immediately, unless the transaction is finalized, in which case attempting
78
+ # to register the callback raises ActiveRecord::ActiveRecordError.
79
+ #
80
+ # If the transaction has a parent transaction, the callback is transferred to
81
+ # the parent when the current transaction commits, or dropped when the current transaction
82
+ # is rolled back. This operation is repeated until the outermost transaction is reached.
83
+ #
84
+ # If the callback raises an error, the transaction remains committed.
85
+ def after_commit(&block)
86
+ if @internal_transaction.nil?
87
+ yield
88
+ else
89
+ @internal_transaction.after_commit(&block)
90
+ end
91
+ end
92
+
93
+ # Registers a block to be called after the transaction is rolled back.
94
+ #
95
+ # If there is no currently open transactions, the block is not called. But
96
+ # if the transaction is finalized, attempting to register the callback
97
+ # raises ActiveRecord::ActiveRecordError.
98
+ #
99
+ # If the transaction is successfully committed but has a parent
100
+ # transaction, the callback is automatically added to the parent transaction.
101
+ #
102
+ # If the entire chain of nested transactions are all successfully committed,
103
+ # the block is never called.
104
+ #
105
+ # If the transaction is already finalized, attempting to register a callback
106
+ # will raise ActiveRecord::ActiveRecordError.
107
+ def after_rollback(&block)
108
+ @internal_transaction&.after_rollback(&block)
109
+ end
110
+
111
+ # Returns true if the transaction exists and isn't finalized yet.
112
+ def open?
113
+ !closed?
114
+ end
115
+
116
+ # Returns true if the transaction doesn't exist or is finalized.
117
+ def closed?
118
+ @internal_transaction.nil? || @internal_transaction.state.finalized?
119
+ end
120
+
121
+ alias_method :blank?, :closed?
122
+
123
+ # Returns a UUID for this transaction or +nil+ if no transaction is open.
124
+ def uuid
125
+ if @internal_transaction
126
+ @uuid ||= Digest::UUID.uuid_v4
127
+ end
128
+ end
129
+
130
+ NULL_TRANSACTION = new(nil).freeze
131
+ end
132
+ end