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
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/array"
4
- require "active_support/core_ext/hash/except"
5
- require "active_support/core_ext/kernel/singleton_class"
6
-
7
3
  module ActiveRecord
8
4
  # = Active Record \Named \Scopes
9
5
  module Scoping
@@ -23,25 +19,17 @@ module ActiveRecord
23
19
  #
24
20
  # You can define a scope that applies to all finders using
25
21
  # {default_scope}[rdoc-ref:Scoping::Default::ClassMethods#default_scope].
26
- def all
22
+ def all(all_queries: nil)
27
23
  scope = current_scope
28
24
 
29
25
  if scope
30
- if scope._deprecated_scope_source
31
- ActiveSupport::Deprecation.warn(<<~MSG.squish)
32
- Class level methods will no longer inherit scoping from `#{scope._deprecated_scope_source}`
33
- in Rails 6.1. To continue using the scoped relation, pass it into the block directly.
34
- To instead access the full set of models, as Rails 6.1 will, use `#{name}.unscoped`.
35
- MSG
36
- end
37
-
38
26
  if self == scope.klass
39
27
  scope.clone
40
28
  else
41
29
  relation.merge!(scope)
42
30
  end
43
31
  else
44
- default_scoped
32
+ default_scoped(all_queries: all_queries)
45
33
  end
46
34
  end
47
35
 
@@ -53,8 +41,9 @@ module ActiveRecord
53
41
  end
54
42
  end
55
43
 
56
- def default_scoped(scope = relation) # :nodoc:
57
- build_default_scope(scope) || scope
44
+ # Returns a scope for the model with default scopes.
45
+ def default_scoped(scope = relation, all_queries: nil)
46
+ build_default_scope(scope, all_queries: all_queries) || scope
58
47
  end
59
48
 
60
49
  def default_extensions # :nodoc:
@@ -83,10 +72,6 @@ module ActiveRecord
83
72
  # <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>, in effect,
84
73
  # represents the query <tt>Shirt.where(color: 'red')</tt>.
85
74
  #
86
- # You should always pass a callable object to the scopes defined
87
- # with #scope. This ensures that the scope is re-evaluated each
88
- # time it is called.
89
- #
90
75
  # Note that this is simply 'syntactic sugar' for defining an actual
91
76
  # class method:
92
77
  #
@@ -183,12 +168,11 @@ module ActiveRecord
183
168
  "an instance method with the same name."
184
169
  end
185
170
 
186
- valid_scope_name?(name)
187
171
  extension = Module.new(&block) if block
188
172
 
189
173
  if body.respond_to?(:to_proc)
190
174
  singleton_class.define_method(name) do |*args|
191
- scope = all._exec_scope(name, *args, &body)
175
+ scope = all._exec_scope(*args, &body)
192
176
  scope = scope.extending(extension) if extension
193
177
  scope
194
178
  end
@@ -199,17 +183,15 @@ module ActiveRecord
199
183
  scope
200
184
  end
201
185
  end
186
+ singleton_class.send(:ruby2_keywords, name)
202
187
 
203
188
  generate_relation_method(name)
204
189
  end
205
190
 
206
191
  private
207
-
208
- def valid_scope_name?(name)
209
- if respond_to?(name, true) && logger
210
- logger.warn "Creating scope :#{name}. " \
211
- "Overwriting existing method #{self.name}.#{name}."
212
- end
192
+ def singleton_method_added(name)
193
+ super
194
+ generate_relation_method(name) if Kernel.respond_to?(name) && !ActiveRecord::Relation.method_defined?(name)
213
195
  end
214
196
  end
215
197
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/per_thread_registry"
3
+ require "active_support/core_ext/module/delegation"
4
4
 
5
5
  module ActiveRecord
6
6
  module Scoping
@@ -24,11 +24,23 @@ module ActiveRecord
24
24
  end
25
25
 
26
26
  def current_scope(skip_inherited_scope = false)
27
- ScopeRegistry.value_for(:current_scope, self, skip_inherited_scope)
27
+ ScopeRegistry.current_scope(self, skip_inherited_scope)
28
28
  end
29
29
 
30
30
  def current_scope=(scope)
31
- ScopeRegistry.set_value_for(:current_scope, self, scope)
31
+ ScopeRegistry.set_current_scope(self, scope)
32
+ end
33
+
34
+ def global_current_scope(skip_inherited_scope = false)
35
+ ScopeRegistry.global_current_scope(self, skip_inherited_scope)
36
+ end
37
+
38
+ def global_current_scope=(scope)
39
+ ScopeRegistry.set_global_current_scope(self, scope)
40
+ end
41
+
42
+ def scope_registry
43
+ ScopeRegistry.instance
32
44
  end
33
45
  end
34
46
 
@@ -45,8 +57,8 @@ module ActiveRecord
45
57
  end
46
58
 
47
59
  # This class stores the +:current_scope+ and +:ignore_default_scope+ values
48
- # for different classes. The registry is stored as a thread local, which is
49
- # accessed through +ScopeRegistry.current+.
60
+ # for different classes. The registry is stored as either a thread or fiber
61
+ # local depending on the application configuration.
50
62
  #
51
63
  # This class allows you to store and get the scope values on different
52
64
  # classes and different types of scopes. For example, if you are attempting
@@ -54,52 +66,70 @@ module ActiveRecord
54
66
  # following code:
55
67
  #
56
68
  # registry = ActiveRecord::Scoping::ScopeRegistry
57
- # registry.set_value_for(:current_scope, Board, some_new_scope)
69
+ # registry.set_current_scope(Board, some_new_scope)
58
70
  #
59
71
  # Now when you run:
60
72
  #
61
- # registry.value_for(:current_scope, Board)
73
+ # registry.current_scope(Board)
62
74
  #
63
- # You will obtain whatever was defined in +some_new_scope+. The #value_for
64
- # and #set_value_for methods are delegated to the current ScopeRegistry
65
- # object, so the above example code can also be called as:
66
- #
67
- # ActiveRecord::Scoping::ScopeRegistry.set_value_for(:current_scope,
68
- # Board, some_new_scope)
75
+ # You will obtain whatever was defined in +some_new_scope+.
69
76
  class ScopeRegistry # :nodoc:
70
- extend ActiveSupport::PerThreadRegistry
77
+ class << self
78
+ delegate :current_scope, :set_current_scope, :ignore_default_scope, :set_ignore_default_scope,
79
+ :global_current_scope, :set_global_current_scope, to: :instance
71
80
 
72
- VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope]
81
+ def instance
82
+ ActiveSupport::IsolatedExecutionState[:active_record_scope_registry] ||= new
83
+ end
84
+ end
73
85
 
74
86
  def initialize
75
- @registry = Hash.new { |hash, key| hash[key] = {} }
87
+ @current_scope = {}
88
+ @ignore_default_scope = {}
89
+ @global_current_scope = {}
76
90
  end
77
91
 
78
- # Obtains the value for a given +scope_type+ and +model+.
79
- def value_for(scope_type, model, skip_inherited_scope = false)
80
- raise_invalid_scope_type!(scope_type)
81
- return @registry[scope_type][model.name] if skip_inherited_scope
82
- klass = model
83
- base = model.base_class
84
- while klass <= base
85
- value = @registry[scope_type][klass.name]
86
- return value if value
87
- klass = klass.superclass
88
- end
92
+ def current_scope(model, skip_inherited_scope = false)
93
+ value_for(@current_scope, model, skip_inherited_scope)
89
94
  end
90
95
 
91
- # Sets the +value+ for a given +scope_type+ and +model+.
92
- def set_value_for(scope_type, model, value)
93
- raise_invalid_scope_type!(scope_type)
94
- @registry[scope_type][model.name] = value
96
+ def set_current_scope(model, value)
97
+ set_value_for(@current_scope, model, value)
95
98
  end
96
99
 
97
- private
100
+ def ignore_default_scope(model, skip_inherited_scope = false)
101
+ value_for(@ignore_default_scope, model, skip_inherited_scope)
102
+ end
103
+
104
+ def set_ignore_default_scope(model, value)
105
+ set_value_for(@ignore_default_scope, model, value)
106
+ end
107
+
108
+ def global_current_scope(model, skip_inherited_scope = false)
109
+ value_for(@global_current_scope, model, skip_inherited_scope)
110
+ end
98
111
 
99
- def raise_invalid_scope_type!(scope_type)
100
- if !VALID_SCOPE_TYPES.include?(scope_type)
101
- raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
112
+ def set_global_current_scope(model, value)
113
+ set_value_for(@global_current_scope, model, value)
114
+ end
115
+
116
+ private
117
+ # Obtains the value for a given +scope_type+ and +model+.
118
+ def value_for(scope_type, model, skip_inherited_scope = false)
119
+ return scope_type[model.name] if skip_inherited_scope
120
+ klass = model
121
+ base = model.base_class
122
+ while klass != base
123
+ value = scope_type[klass.name]
124
+ return value if value
125
+ klass = klass.superclass
102
126
  end
127
+ scope_type[klass.name]
128
+ end
129
+
130
+ # Sets the +value+ for a given +scope_type+ and +model+.
131
+ def set_value_for(scope_type, model, value)
132
+ scope_type[model.name] = value
103
133
  end
104
134
  end
105
135
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module SecurePassword
5
+ extend ActiveSupport::Concern
6
+
7
+ include ActiveModel::SecurePassword
8
+
9
+ module ClassMethods
10
+ # Given a set of attributes, finds a record using the non-password
11
+ # attributes, and then authenticates that record using the password
12
+ # attributes. Returns the record if authentication succeeds; otherwise,
13
+ # returns +nil+.
14
+ #
15
+ # Regardless of whether a record is found, +authenticate_by+ will
16
+ # cryptographically digest the given password attributes. This behavior
17
+ # helps mitigate timing-based enumeration attacks, wherein an attacker can
18
+ # determine if a passworded record exists even without knowing the
19
+ # password.
20
+ #
21
+ # Raises an ArgumentError if the set of attributes doesn't contain at
22
+ # least one password and one non-password attribute.
23
+ #
24
+ # ==== Examples
25
+ #
26
+ # class User < ActiveRecord::Base
27
+ # has_secure_password
28
+ # end
29
+ #
30
+ # User.create(name: "John Doe", email: "jdoe@example.com", password: "abc123")
31
+ #
32
+ # User.authenticate_by(email: "jdoe@example.com", password: "abc123").name # => "John Doe" (in 373.4ms)
33
+ # User.authenticate_by(email: "jdoe@example.com", password: "wrong") # => nil (in 373.9ms)
34
+ # User.authenticate_by(email: "wrong@example.com", password: "abc123") # => nil (in 373.6ms)
35
+ #
36
+ # User.authenticate_by(email: "jdoe@example.com", password: nil) # => nil (no queries executed)
37
+ # User.authenticate_by(email: "jdoe@example.com", password: "") # => nil (no queries executed)
38
+ #
39
+ # User.authenticate_by(email: "jdoe@example.com") # => ArgumentError
40
+ # User.authenticate_by(password: "abc123") # => ArgumentError
41
+ def authenticate_by(attributes)
42
+ passwords, identifiers = attributes.to_h.partition do |name, value|
43
+ !has_attribute?(name) && has_attribute?("#{name}_digest")
44
+ end.map(&:to_h)
45
+
46
+ raise ArgumentError, "One or more password arguments are required" if passwords.empty?
47
+ raise ArgumentError, "One or more finder arguments are required" if identifiers.empty?
48
+
49
+ return if passwords.any? { |name, value| value.nil? || value.empty? }
50
+
51
+ if record = find_by(identifiers)
52
+ record if passwords.count { |name, value| record.public_send(:"authenticate_#{name}", value) } == passwords.size
53
+ else
54
+ new(passwords)
55
+ nil
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -2,6 +2,10 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module SecureToken
5
+ class MinimumLengthError < StandardError; end
6
+
7
+ MINIMUM_TOKEN_LENGTH = 24
8
+
5
9
  extend ActiveSupport::Concern
6
10
 
7
11
  module ClassMethods
@@ -10,30 +14,52 @@ module ActiveRecord
10
14
  # # Schema: User(token:string, auth_token:string)
11
15
  # class User < ActiveRecord::Base
12
16
  # has_secure_token
13
- # has_secure_token :auth_token
17
+ # has_secure_token :auth_token, length: 36
14
18
  # end
15
19
  #
16
20
  # user = User.new
17
21
  # user.save
18
22
  # user.token # => "pX27zsMN2ViQKta1bGfLmVJE"
19
- # user.auth_token # => "77TMHrHJFvFDwodq8w7Ev2m7"
23
+ # user.auth_token # => "tU9bLuZseefXQ4yQxQo8wjtBvsAfPc78os6R"
20
24
  # user.regenerate_token # => true
21
25
  # user.regenerate_auth_token # => true
22
26
  #
23
- # <tt>SecureRandom::base58</tt> is used to generate the 24-character unique token, so collisions are highly unlikely.
27
+ # +SecureRandom::base58+ is used to generate at minimum a 24-character unique token, so collisions are highly unlikely.
24
28
  #
25
29
  # Note that it's still possible to generate a race condition in the database in the same way that
26
30
  # {validates_uniqueness_of}[rdoc-ref:Validations::ClassMethods#validates_uniqueness_of] can.
27
31
  # You're encouraged to add a unique index in the database to deal with this even more unlikely scenario.
28
- def has_secure_token(attribute = :token)
32
+ #
33
+ # ==== Options
34
+ #
35
+ # [+:length+]
36
+ # Length of the Secure Random, with a minimum of 24 characters. It will
37
+ # default to 24.
38
+ #
39
+ # [+:on+]
40
+ # The callback when the value is generated. When called with <tt>on:
41
+ # :initialize</tt>, the value is generated in an
42
+ # <tt>after_initialize</tt> callback, otherwise the value will be used
43
+ # in a <tt>before_</tt> callback. When not specified, +:on+ will use the value of
44
+ # <tt>config.active_record.generate_secure_token_on</tt>, which defaults to +:initialize+
45
+ # starting in \Rails 7.1.
46
+ def has_secure_token(attribute = :token, length: MINIMUM_TOKEN_LENGTH, on: ActiveRecord.generate_secure_token_on)
47
+ if length < MINIMUM_TOKEN_LENGTH
48
+ raise MinimumLengthError, "Token requires a minimum length of #{MINIMUM_TOKEN_LENGTH} characters."
49
+ end
50
+
29
51
  # Load securerandom only when has_secure_token is used.
30
52
  require "active_support/core_ext/securerandom"
31
- define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token }
32
- before_create { send("#{attribute}=", self.class.generate_unique_secure_token) unless send("#{attribute}?") }
53
+ define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token(length: length) }
54
+ set_callback on, on == :initialize ? :after : :before do
55
+ if new_record? && !query_attribute(attribute)
56
+ send("#{attribute}=", self.class.generate_unique_secure_token(length: length))
57
+ end
58
+ end
33
59
  end
34
60
 
35
- def generate_unique_secure_token
36
- SecureRandom.base58(24)
61
+ def generate_unique_secure_token(length: MINIMUM_TOKEN_LENGTH)
62
+ SecureRandom.base58(length)
37
63
  end
38
64
  end
39
65
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ActiveRecord #:nodoc:
3
+ module ActiveRecord # :nodoc:
4
4
  # = Active Record \Serialization
5
5
  module Serialization
6
6
  extend ActiveSupport::Concern
@@ -11,12 +11,19 @@ module ActiveRecord #:nodoc:
11
11
  end
12
12
 
13
13
  def serializable_hash(options = nil)
14
- options = options.try(:dup) || {}
14
+ if self.class._has_attribute?(self.class.inheritance_column)
15
+ options = options ? options.dup : {}
15
16
 
16
- options[:except] = Array(options[:except]).map(&:to_s)
17
- options[:except] |= Array(self.class.inheritance_column)
17
+ options[:except] = Array(options[:except]).map(&:to_s)
18
+ options[:except] |= Array(self.class.inheritance_column)
19
+ end
18
20
 
19
21
  super(options)
20
22
  end
23
+
24
+ private
25
+ def attribute_names_for_serialization
26
+ attribute_names
27
+ end
21
28
  end
22
29
  end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ # = Active Record Signed Id
5
+ module SignedId
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ ##
10
+ # :singleton-method:
11
+ # Set the secret used for the signed id verifier instance when using Active Record outside of \Rails.
12
+ # Within \Rails, this is automatically set using the \Rails application key generator.
13
+ class_attribute :signed_id_verifier_secret, instance_writer: false
14
+ end
15
+
16
+ module RelationMethods # :nodoc:
17
+ def find_signed(...)
18
+ scoping { model.find_signed(...) }
19
+ end
20
+
21
+ def find_signed!(...)
22
+ scoping { model.find_signed!(...) }
23
+ end
24
+ end
25
+
26
+ module ClassMethods
27
+ # Lets you find a record based on a signed id that's safe to put into the world without risk of tampering.
28
+ # This is particularly useful for things like password reset or email verification, where you want
29
+ # the bearer of the signed id to be able to interact with the underlying record, but usually only within
30
+ # a certain time period.
31
+ #
32
+ # You set the time period that the signed id is valid for during generation, using the instance method
33
+ # <tt>signed_id(expires_in: 15.minutes)</tt>. If the time has elapsed before a signed find is attempted,
34
+ # the signed id will no longer be valid, and nil is returned.
35
+ #
36
+ # It's possible to further restrict the use of a signed id with a purpose. This helps when you have a
37
+ # general base model, like a User, which might have signed ids for several things, like password reset
38
+ # or email verification. The purpose that was set during generation must match the purpose set when
39
+ # finding. If there's a mismatch, nil is again returned.
40
+ #
41
+ # ==== Examples
42
+ #
43
+ # signed_id = User.first.signed_id expires_in: 15.minutes, purpose: :password_reset
44
+ #
45
+ # User.find_signed signed_id # => nil, since the purpose does not match
46
+ #
47
+ # travel 16.minutes
48
+ # User.find_signed signed_id, purpose: :password_reset # => nil, since the signed id has expired
49
+ #
50
+ # travel_back
51
+ # User.find_signed signed_id, purpose: :password_reset # => User.first
52
+ def find_signed(signed_id, purpose: nil)
53
+ raise UnknownPrimaryKey.new(self) if primary_key.nil?
54
+
55
+ if id = signed_id_verifier.verified(signed_id, purpose: combine_signed_id_purposes(purpose))
56
+ find_by primary_key => id
57
+ end
58
+ end
59
+
60
+ # Works like find_signed, but will raise an ActiveSupport::MessageVerifier::InvalidSignature
61
+ # exception if the +signed_id+ has either expired, has a purpose mismatch, is for another record,
62
+ # or has been tampered with. It will also raise an ActiveRecord::RecordNotFound exception if
63
+ # the valid signed id can't find a record.
64
+ #
65
+ # ==== Examples
66
+ #
67
+ # User.find_signed! "bad data" # => ActiveSupport::MessageVerifier::InvalidSignature
68
+ #
69
+ # signed_id = User.first.signed_id
70
+ # User.first.destroy
71
+ # User.find_signed! signed_id # => ActiveRecord::RecordNotFound
72
+ def find_signed!(signed_id, purpose: nil)
73
+ if id = signed_id_verifier.verify(signed_id, purpose: combine_signed_id_purposes(purpose))
74
+ find(id)
75
+ end
76
+ end
77
+
78
+ # The verifier instance that all signed ids are generated and verified from. By default, it'll be initialized
79
+ # with the class-level +signed_id_verifier_secret+, which within Rails comes from
80
+ # {Rails.application.key_generator}[rdoc-ref:Rails::Application#key_generator].
81
+ # By default, it's SHA256 for the digest and JSON for the serialization.
82
+ def signed_id_verifier
83
+ @signed_id_verifier ||= begin
84
+ secret = signed_id_verifier_secret
85
+ secret = secret.call if secret.respond_to?(:call)
86
+
87
+ if secret.nil?
88
+ raise ArgumentError, "You must set ActiveRecord::Base.signed_id_verifier_secret to use signed ids"
89
+ else
90
+ ActiveSupport::MessageVerifier.new secret, digest: "SHA256", serializer: JSON, url_safe: true
91
+ end
92
+ end
93
+ end
94
+
95
+ # Allows you to pass in a custom verifier used for the signed ids. This also allows you to use different
96
+ # verifiers for different classes. This is also helpful if you need to rotate keys, as you can prepare
97
+ # your custom verifier for that in advance. See ActiveSupport::MessageVerifier for details.
98
+ def signed_id_verifier=(verifier)
99
+ @signed_id_verifier = verifier
100
+ end
101
+
102
+ # :nodoc:
103
+ def combine_signed_id_purposes(purpose)
104
+ [ base_class.name.underscore, purpose.to_s ].compact_blank.join("/")
105
+ end
106
+ end
107
+
108
+
109
+ # Returns a signed id that's generated using a preconfigured +ActiveSupport::MessageVerifier+ instance.
110
+ #
111
+ # This signed id is tamper proof, so it's safe to send in an email or otherwise share with the outside world.
112
+ # However, as with any message signed with a +ActiveSupport::MessageVerifier+,
113
+ # {the signed id is not encrypted}[link:classes/ActiveSupport/MessageVerifier.html#class-ActiveSupport::MessageVerifier-label-Signing+is+not+encryption].
114
+ # It's just encoded and protected against tampering.
115
+ #
116
+ # This means that the ID can be decoded by anyone; however, if tampered with (so to point to a different ID),
117
+ # the cryptographic signature will no longer match, and the signed id will be considered invalid and return nil
118
+ # when passed to +find_signed+ (or raise with +find_signed!+).
119
+ #
120
+ # It can furthermore be set to expire (the default is not to expire), and scoped down with a specific purpose.
121
+ # If the expiration date has been exceeded before +find_signed+ is called, the id won't find the designated
122
+ # record. If a purpose is set, this too must match.
123
+ #
124
+ # If you accidentally let a signed id out in the wild that you wish to retract sooner than its expiration date
125
+ # (or maybe you forgot to set an expiration date while meaning to!), you can use the purpose to essentially
126
+ # version the signed_id, like so:
127
+ #
128
+ # user.signed_id purpose: :v2
129
+ #
130
+ # And you then change your +find_signed+ calls to require this new purpose. Any old signed ids that were not
131
+ # created with the purpose will no longer find the record.
132
+ def signed_id(expires_in: nil, expires_at: nil, purpose: nil)
133
+ raise ArgumentError, "Cannot get a signed_id for a new record" if new_record?
134
+
135
+ self.class.signed_id_verifier.generate id, expires_in: expires_in, expires_at: expires_at, purpose: self.class.combine_signed_id_purposes(purpose)
136
+ end
137
+ end
138
+ end
@@ -4,14 +4,14 @@ module ActiveRecord
4
4
  # Statement cache is used to cache a single statement in order to avoid creating the AST again.
5
5
  # Initializing the cache is done by passing the statement in the create block:
6
6
  #
7
- # cache = StatementCache.create(Book.connection) do |params|
7
+ # cache = StatementCache.create(ClothingItem.lease_connection) do |params|
8
8
  # Book.where(name: "my book").where("author_id > 3")
9
9
  # end
10
10
  #
11
11
  # The cached statement is executed by using the
12
12
  # {connection.execute}[rdoc-ref:ConnectionAdapters::DatabaseStatements#execute] method:
13
13
  #
14
- # cache.execute([], Book.connection)
14
+ # cache.execute([], ClothingItem.lease_connection)
15
15
  #
16
16
  # The relation returned by the block is cached, and for each
17
17
  # {execute}[rdoc-ref:ConnectionAdapters::DatabaseStatements#execute]
@@ -20,13 +20,13 @@ module ActiveRecord
20
20
  # If you want to cache the statement without the values you can use the +bind+ method of the
21
21
  # block parameter.
22
22
  #
23
- # cache = StatementCache.create(Book.connection) do |params|
23
+ # cache = StatementCache.create(ClothingItem.lease_connection) do |params|
24
24
  # Book.where(name: params.bind)
25
25
  # end
26
26
  #
27
27
  # And pass the bind values as the first argument of +execute+ call.
28
28
  #
29
- # cache.execute(["my book"], Book.connection)
29
+ # cache.execute(["my book"], ClothingItem.lease_connection)
30
30
  class StatementCache # :nodoc:
31
31
  class Substitute; end # :nodoc:
32
32
 
@@ -50,13 +50,20 @@ module ActiveRecord
50
50
 
51
51
  def sql_for(binds, connection)
52
52
  val = @values.dup
53
- casted_binds = binds.map(&:value_for_database)
54
- @indexes.each { |i| val[i] = connection.quote(casted_binds.shift) }
53
+ @indexes.each do |i|
54
+ value = binds.shift
55
+ if ActiveModel::Attribute === value
56
+ value = value.value_for_database
57
+ end
58
+ val[i] = connection.quote(value)
59
+ end
55
60
  val.join
56
61
  end
57
62
  end
58
63
 
59
64
  class PartialQueryCollector
65
+ attr_accessor :preparable, :retryable
66
+
60
67
  def initialize
61
68
  @parts = []
62
69
  @binds = []
@@ -73,6 +80,15 @@ module ActiveRecord
73
80
  self
74
81
  end
75
82
 
83
+ def add_binds(binds, proc_for_binds = nil)
84
+ @binds.concat proc_for_binds ? binds.map(&proc_for_binds) : binds
85
+ binds.size.times do |i|
86
+ @parts << ", " unless i == 0
87
+ @parts << Substitute.new
88
+ end
89
+ self
90
+ end
91
+
76
92
  def value
77
93
  [@parts, @binds]
78
94
  end
@@ -100,7 +116,7 @@ module ActiveRecord
100
116
  @bound_attributes = bound_attributes
101
117
 
102
118
  bound_attributes.each_with_index do |attr, i|
103
- if Substitute === attr.value
119
+ if ActiveModel::Attribute === attr && Substitute === attr.value
104
120
  @indexes << i
105
121
  end
106
122
  end
@@ -126,14 +142,14 @@ module ActiveRecord
126
142
  @klass = klass
127
143
  end
128
144
 
129
- def execute(params, connection, &block)
145
+ def execute(params, connection, allow_retry: false, &block)
130
146
  bind_values = bind_map.bind params
131
147
 
132
148
  sql = query_builder.sql_for bind_values, connection
133
149
 
134
- klass.find_by_sql(sql, bind_values, preparable: true, &block)
150
+ klass.find_by_sql(sql, bind_values, preparable: true, allow_retry: allow_retry, &block)
135
151
  rescue ::RangeError
136
- nil
152
+ []
137
153
  end
138
154
 
139
155
  def self.unsupported_value?(value)