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
@@ -5,56 +5,52 @@ require "concurrent/map"
5
5
  module ActiveRecord
6
6
  module Type
7
7
  class TypeMap # :nodoc:
8
- def initialize
8
+ def initialize(parent = nil)
9
9
  @mapping = {}
10
- @cache = Concurrent::Map.new do |h, key|
11
- h.fetch_or_store(key, Concurrent::Map.new)
12
- end
10
+ @parent = parent
11
+ @cache = Concurrent::Map.new
13
12
  end
14
13
 
15
- def lookup(lookup_key, *args)
16
- fetch(lookup_key, *args) { Type.default_value }
14
+ def lookup(lookup_key)
15
+ fetch(lookup_key) { Type.default_value }
17
16
  end
18
17
 
19
- def fetch(lookup_key, *args, &block)
20
- @cache[lookup_key].fetch_or_store(args) do
21
- perform_fetch(lookup_key, *args, &block)
18
+ def fetch(lookup_key, &block)
19
+ @cache.fetch_or_store(lookup_key) do
20
+ perform_fetch(lookup_key, &block)
22
21
  end
23
22
  end
24
23
 
25
24
  def register_type(key, value = nil, &block)
26
25
  raise ::ArgumentError unless value || block
27
- @cache.clear
28
26
 
29
27
  if block
30
28
  @mapping[key] = block
31
29
  else
32
30
  @mapping[key] = proc { value }
33
31
  end
32
+ @cache.clear
34
33
  end
35
34
 
36
35
  def alias_type(key, target_key)
37
- register_type(key) do |sql_type, *args|
36
+ register_type(key) do |sql_type|
38
37
  metadata = sql_type[/\(.*\)/, 0]
39
- lookup("#{target_key}#{metadata}", *args)
38
+ lookup("#{target_key}#{metadata}")
40
39
  end
41
40
  end
42
41
 
43
- def clear
44
- @mapping.clear
45
- end
46
-
47
- private
48
-
49
- def perform_fetch(lookup_key, *args)
42
+ protected
43
+ def perform_fetch(lookup_key, &block)
50
44
  matching_pair = @mapping.reverse_each.detect do |key, _|
51
45
  key === lookup_key
52
46
  end
53
47
 
54
48
  if matching_pair
55
- matching_pair.last.call(lookup_key, *args)
49
+ matching_pair.last.call(lookup_key)
50
+ elsif @parent
51
+ @parent.perform_fetch(lookup_key, &block)
56
52
  else
57
- yield lookup_key, *args
53
+ yield lookup_key
58
54
  end
59
55
  end
60
56
  end
@@ -4,7 +4,6 @@ module ActiveRecord
4
4
  module Type
5
5
  class UnsignedInteger < ActiveModel::Type::Integer # :nodoc:
6
6
  private
7
-
8
7
  def max_value
9
8
  super * 2
10
9
  end
@@ -46,10 +46,13 @@ module ActiveRecord
46
46
  @default_value ||= Value.new
47
47
  end
48
48
 
49
- private
49
+ def adapter_name_from(model) # :nodoc:
50
+ model.connection_db_config.adapter.to_sym
51
+ end
50
52
 
53
+ private
51
54
  def current_adapter_name
52
- ActiveRecord::Base.connection.adapter_name.downcase.to_sym
55
+ adapter_name_from(ActiveRecord::Base)
53
56
  end
54
57
  end
55
58
 
@@ -59,6 +62,7 @@ module ActiveRecord
59
62
  Decimal = ActiveModel::Type::Decimal
60
63
  Float = ActiveModel::Type::Float
61
64
  Integer = ActiveModel::Type::Integer
65
+ ImmutableString = ActiveModel::Type::ImmutableString
62
66
  String = ActiveModel::Type::String
63
67
  Value = ActiveModel::Type::Value
64
68
 
@@ -70,6 +74,7 @@ module ActiveRecord
70
74
  register(:decimal, Type::Decimal, override: false)
71
75
  register(:float, Type::Float, override: false)
72
76
  register(:integer, Type::Integer, override: false)
77
+ register(:immutable_string, Type::ImmutableString, override: false)
73
78
  register(:json, Type::Json, override: false)
74
79
  register(:string, Type::String, override: false)
75
80
  register(:text, Type::Text, override: false)
@@ -9,24 +9,23 @@ module ActiveRecord
9
9
  end
10
10
 
11
11
  def type_cast_for_database(attr_name, value)
12
- return value if value.is_a?(Arel::Nodes::BindParam)
13
12
  type = type_for_attribute(attr_name)
14
13
  type.serialize(value)
15
14
  end
16
15
 
17
16
  def type_for_attribute(attr_name)
18
- schema_cache = connection.schema_cache
17
+ schema_cache = @klass.schema_cache
19
18
 
20
19
  if schema_cache.data_source_exists?(table_name)
21
20
  column = schema_cache.columns_hash(table_name)[attr_name.to_s]
22
- type = connection.lookup_cast_type_from_column(column) if column
21
+ if column
22
+ type = @klass.with_connection { |connection| connection.lookup_cast_type_from_column(column) }
23
+ end
23
24
  end
24
25
 
25
26
  type || Type.default_value
26
27
  end
27
28
 
28
- delegate :connection, to: :@klass, private: true
29
-
30
29
  private
31
30
  attr_reader :table_name
32
31
  end
@@ -3,18 +3,21 @@
3
3
  module ActiveRecord
4
4
  module TypeCaster
5
5
  class Map # :nodoc:
6
- def initialize(types)
7
- @types = types
6
+ def initialize(klass)
7
+ @klass = klass
8
8
  end
9
9
 
10
10
  def type_cast_for_database(attr_name, value)
11
- return value if value.is_a?(Arel::Nodes::BindParam)
12
- type = types.type_for_attribute(attr_name)
11
+ type = type_for_attribute(attr_name)
13
12
  type.serialize(value)
14
13
  end
15
14
 
15
+ def type_for_attribute(name)
16
+ klass.type_for_attribute(name)
17
+ end
18
+
16
19
  private
17
- attr_reader :types
20
+ attr_reader :klass
18
21
  end
19
22
  end
20
23
  end
@@ -14,7 +14,7 @@ module ActiveRecord
14
14
  module ClassMethods
15
15
  # Validates that the specified attributes are not present (as defined by
16
16
  # Object#present?). If the attribute is an association, the associated object
17
- # is considered absent if it was marked for destruction.
17
+ # is also considered not present if it is marked for destruction.
18
18
  #
19
19
  # See ActiveModel::Validations::HelperMethods.validates_absence_of for more information.
20
20
  def validates_absence_of(*attr_names)
@@ -2,17 +2,22 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Validations
5
- class AssociatedValidator < ActiveModel::EachValidator #:nodoc:
5
+ class AssociatedValidator < ActiveModel::EachValidator # :nodoc:
6
6
  def validate_each(record, attribute, value)
7
- if Array(value).reject { |r| valid_object?(r) }.any?
8
- record.errors.add(attribute, :invalid, options.merge(value: value))
7
+ context = record_validation_context_for_association(record)
8
+
9
+ if Array(value).reject { |association| valid_object?(association, context) }.any?
10
+ record.errors.add(attribute, :invalid, **options.merge(value: value))
9
11
  end
10
12
  end
11
13
 
12
14
  private
15
+ def valid_object?(record, context)
16
+ (record.respond_to?(:marked_for_destruction?) && record.marked_for_destruction?) || record.valid?(context)
17
+ end
13
18
 
14
- def valid_object?(record)
15
- (record.respond_to?(:marked_for_destruction?) && record.marked_for_destruction?) || record.valid?
19
+ def record_validation_context_for_association(record)
20
+ record.custom_validation_context? ? record.validation_context : nil
16
21
  end
17
22
  end
18
23
 
@@ -43,14 +48,14 @@ module ActiveRecord
43
48
  # or an array of symbols. (e.g. <tt>on: :create</tt> or
44
49
  # <tt>on: :custom_validation_context</tt> or
45
50
  # <tt>on: [:create, :custom_validation_context]</tt>)
46
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
51
+ # * <tt>:if</tt> - Specifies a method, proc, or string to call to determine
47
52
  # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
48
53
  # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
49
54
  # proc or string should return or evaluate to a +true+ or +false+ value.
50
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to
55
+ # * <tt>:unless</tt> - Specifies a method, proc, or string to call to
51
56
  # determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
52
57
  # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
53
- # method, proc or string should return or evaluate to a +true+ or +false+
58
+ # method, proc, or string should return or evaluate to a +true+ or +false+
54
59
  # value.
55
60
  def validates_associated(*attr_names)
56
61
  validates_with AssociatedValidator, _merge_attributes(attr_names)
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Validations
5
+ class NumericalityValidator < ActiveModel::Validations::NumericalityValidator # :nodoc:
6
+ def validate_each(record, attribute, value, precision: nil, scale: nil)
7
+ precision = [column_precision_for(record, attribute) || Float::DIG, Float::DIG].min
8
+ scale = column_scale_for(record, attribute)
9
+ super(record, attribute, value, precision: precision, scale: scale)
10
+ end
11
+
12
+ private
13
+ def column_precision_for(record, attribute)
14
+ record.class.type_for_attribute(attribute.to_s)&.precision
15
+ end
16
+
17
+ def column_scale_for(record, attribute)
18
+ record.class.type_for_attribute(attribute.to_s)&.scale
19
+ end
20
+ end
21
+
22
+ module ClassMethods
23
+ # Validates whether the value of the specified attribute is numeric by
24
+ # trying to convert it to a float with +Kernel.Float+ (if
25
+ # <tt>only_integer</tt> is +false+) or applying it to the regular
26
+ # expression <tt>/\A[\+\-]?\d+\z/</tt> (if <tt>only_integer</tt> is set to
27
+ # +true+). +Kernel.Float+ precision defaults to the column's precision
28
+ # value or 15.
29
+ #
30
+ # See ActiveModel::Validations::HelperMethods.validates_numericality_of for more information.
31
+ def validates_numericality_of(*attr_names)
32
+ validates_with NumericalityValidator, _merge_attributes(attr_names)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -13,9 +13,8 @@ module ActiveRecord
13
13
 
14
14
  module ClassMethods
15
15
  # Validates that the specified attributes are not blank (as defined by
16
- # Object#blank?), and, if the attribute is an association, that the
17
- # associated object is not marked for destruction. Happens by default
18
- # on save.
16
+ # Object#blank?). If the attribute is an association, the associated object
17
+ # is also considered blank if it is marked for destruction.
19
18
  #
20
19
  # class Person < ActiveRecord::Base
21
20
  # has_one :face
@@ -25,41 +24,19 @@ module ActiveRecord
25
24
  # The face attribute must be in the object and it cannot be blank or marked
26
25
  # for destruction.
27
26
  #
28
- # If you want to validate the presence of a boolean field (where the real values
29
- # are true and false), you will want to use
30
- # <tt>validates_inclusion_of :field_name, in: [true, false]</tt>.
31
- #
32
- # This is due to the way Object#blank? handles boolean values:
33
- # <tt>false.blank? # => true</tt>.
34
- #
35
27
  # This validator defers to the Active Model validation for presence, adding the
36
28
  # check to see that an associated object is not marked for destruction. This
37
29
  # prevents the parent object from validating successfully and saving, which then
38
30
  # deletes the associated object, thus putting the parent object into an invalid
39
31
  # state.
40
32
  #
33
+ # See ActiveModel::Validations::HelperMethods.validates_presence_of for
34
+ # more information.
35
+ #
41
36
  # NOTE: This validation will not fail while using it with an association
42
37
  # if the latter was assigned but not valid. If you want to ensure that
43
38
  # it is both present and valid, you also need to use
44
39
  # {validates_associated}[rdoc-ref:Validations::ClassMethods#validates_associated].
45
- #
46
- # Configuration options:
47
- # * <tt>:message</tt> - A custom error message (default is: "can't be blank").
48
- # * <tt>:on</tt> - Specifies the contexts where this validation is active.
49
- # Runs in all validation contexts by default +nil+. You can pass a symbol
50
- # or an array of symbols. (e.g. <tt>on: :create</tt> or
51
- # <tt>on: :custom_validation_context</tt> or
52
- # <tt>on: [:create, :custom_validation_context]</tt>)
53
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
54
- # the validation should occur (e.g. <tt>if: :allow_validation</tt>, or
55
- # <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
56
- # or string should return or evaluate to a +true+ or +false+ value.
57
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
58
- # if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
59
- # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
60
- # proc or string should return or evaluate to a +true+ or +false+ value.
61
- # * <tt>:strict</tt> - Specifies whether validation should be strict.
62
- # See ActiveModel::Validations#validates! for more information.
63
40
  def validates_presence_of(*attr_names)
64
41
  validates_with PresenceValidator, _merge_attributes(attr_names)
65
42
  end
@@ -14,28 +14,40 @@ module ActiveRecord
14
14
  end
15
15
  super
16
16
  @klass = options[:class]
17
+ @klass = @klass.superclass if @klass.singleton_class?
17
18
  end
18
19
 
19
20
  def validate_each(record, attribute, value)
20
21
  finder_class = find_finder_class_for(record)
21
22
  value = map_enum_attribute(finder_class, attribute, value)
22
23
 
24
+ return if record.persisted? && !validation_needed?(finder_class, record, attribute)
25
+
23
26
  relation = build_relation(finder_class, attribute, value)
24
27
  if record.persisted?
25
28
  if finder_class.primary_key
26
- relation = relation.where.not(finder_class.primary_key => record.id_in_database)
29
+ relation = relation.where.not(finder_class.primary_key => [record.id_in_database])
27
30
  else
28
31
  raise UnknownPrimaryKey.new(finder_class, "Cannot validate uniqueness for persisted record without primary key.")
29
32
  end
30
33
  end
31
34
  relation = scope_relation(record, relation)
32
- relation = relation.merge(options[:conditions]) if options[:conditions]
35
+
36
+ if options[:conditions]
37
+ conditions = options[:conditions]
38
+
39
+ relation = if conditions.arity.zero?
40
+ relation.instance_exec(&conditions)
41
+ else
42
+ relation.instance_exec(record, &conditions)
43
+ end
44
+ end
33
45
 
34
46
  if relation.exists?
35
47
  error_options = options.except(:case_sensitive, :scope, :conditions)
36
48
  error_options[:value] = value
37
49
 
38
- record.errors.add(attribute, :taken, error_options)
50
+ record.errors.add(attribute, :taken, **error_options)
39
51
  end
40
52
  end
41
53
 
@@ -55,18 +67,64 @@ module ActiveRecord
55
67
  class_hierarchy.detect { |klass| !klass.abstract_class? }
56
68
  end
57
69
 
70
+ def validation_needed?(klass, record, attribute)
71
+ return true if options[:conditions] || options.key?(:case_sensitive)
72
+
73
+ scope = Array(options[:scope])
74
+ attributes = scope + [attribute]
75
+ attributes = resolve_attributes(record, attributes)
76
+
77
+ return true if attributes.any? { |attr| record.attribute_changed?(attr) ||
78
+ record.read_attribute(attr).nil? }
79
+
80
+ !covered_by_unique_index?(klass, record, attribute, scope)
81
+ end
82
+
83
+ def covered_by_unique_index?(klass, record, attribute, scope)
84
+ @covered ||= self.attributes.map(&:to_s).select do |attr|
85
+ attributes = scope + [attr]
86
+ attributes = resolve_attributes(record, attributes)
87
+
88
+ klass.schema_cache.indexes(klass.table_name).any? do |index|
89
+ index.unique &&
90
+ index.where.nil? &&
91
+ (Array(index.columns) - attributes).empty?
92
+ end
93
+ end
94
+
95
+ @covered.include?(attribute.to_s)
96
+ end
97
+
98
+ def resolve_attributes(record, attributes)
99
+ attributes.flat_map do |attribute|
100
+ reflection = record.class._reflect_on_association(attribute)
101
+
102
+ if reflection.nil?
103
+ attribute.to_s
104
+ elsif reflection.polymorphic?
105
+ [reflection.foreign_key, reflection.foreign_type]
106
+ else
107
+ reflection.foreign_key
108
+ end
109
+ end
110
+ end
111
+
58
112
  def build_relation(klass, attribute, value)
59
113
  relation = klass.unscoped
60
- comparison = relation.bind_attribute(attribute, value) do |attr, bind|
61
- return relation.none! if bind.unboundable?
114
+ # TODO: Add case-sensitive / case-insensitive operators to Arel
115
+ # to no longer need to checkout a connection here.
116
+ comparison = klass.with_connection do |connection|
117
+ relation.bind_attribute(attribute, value) do |attr, bind|
118
+ return relation.none! if bind.unboundable?
62
119
 
63
- if !options.key?(:case_sensitive) || bind.nil?
64
- klass.connection.default_uniqueness_comparison(attr, bind, klass)
65
- elsif options[:case_sensitive]
66
- klass.connection.case_sensitive_comparison(attr, bind)
67
- else
68
- # will use SQL LOWER function before comparison, unless it detects a case insensitive collation
69
- klass.connection.case_insensitive_comparison(attr, bind)
120
+ if !options.key?(:case_sensitive) || bind.nil?
121
+ connection.default_uniqueness_comparison(attr, bind)
122
+ elsif options[:case_sensitive]
123
+ connection.case_sensitive_comparison(attr, bind)
124
+ else
125
+ # will use SQL LOWER function before comparison, unless it detects a case insensitive collation
126
+ connection.case_insensitive_comparison(attr, bind)
127
+ end
70
128
  end
71
129
  end
72
130
 
@@ -78,7 +136,7 @@ module ActiveRecord
78
136
  scope_value = if record.class._reflect_on_association(scope_item)
79
137
  record.association(scope_item).reader
80
138
  else
81
- record._read_attribute(scope_item)
139
+ record.read_attribute(scope_item)
82
140
  end
83
141
  relation = relation.where(scope_item => scope_value)
84
142
  end
@@ -126,6 +184,17 @@ module ActiveRecord
126
184
  # validates_uniqueness_of :title, conditions: -> { where.not(status: 'archived') }
127
185
  # end
128
186
  #
187
+ # To build conditions based on the record's state, define the conditions
188
+ # callable with a parameter, which will be the record itself. This
189
+ # example validates the title is unique for the year of publication:
190
+ #
191
+ # class Article < ActiveRecord::Base
192
+ # validates_uniqueness_of :title, conditions: ->(article) {
193
+ # published_at = article.published_at
194
+ # where(published_at: published_at.beginning_of_year..published_at.end_of_year)
195
+ # }
196
+ # end
197
+ #
129
198
  # When the record is created, a check is performed to make sure that no
130
199
  # record exists in the database with the given value for the specified
131
200
  # attribute (that maps to a column). When the record is updated,
@@ -141,19 +210,19 @@ module ActiveRecord
141
210
  # <tt>WHERE</tt> SQL fragment to limit the uniqueness constraint lookup
142
211
  # (e.g. <tt>conditions: -> { where(status: 'active') }</tt>).
143
212
  # * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by
144
- # non-text columns (+true+ by default).
213
+ # non-text columns. The default behavior respects the default database collation.
145
214
  # * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the
146
215
  # attribute is +nil+ (default is +false+).
147
216
  # * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the
148
217
  # attribute is blank (default is +false+).
149
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
218
+ # * <tt>:if</tt> - Specifies a method, proc, or string to call to determine
150
219
  # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
151
220
  # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
152
221
  # proc or string should return or evaluate to a +true+ or +false+ value.
153
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to
222
+ # * <tt>:unless</tt> - Specifies a method, proc, or string to call to
154
223
  # determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
155
224
  # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
156
- # method, proc or string should return or evaluate to a +true+ or +false+
225
+ # method, proc, or string should return or evaluate to a +true+ or +false+
157
226
  # value.
158
227
  #
159
228
  # === Concurrency and integrity
@@ -201,7 +270,7 @@ module ActiveRecord
201
270
  # When the database catches such a duplicate insertion,
202
271
  # {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] will raise an ActiveRecord::StatementInvalid
203
272
  # exception. You can either choose to let this error propagate (which
204
- # will result in the default Rails exception page being shown), or you
273
+ # will result in the default \Rails exception page being shown), or you
205
274
  # can catch it and restart the transaction (e.g. by telling the user
206
275
  # that the title already exists, and asking them to re-enter the title).
207
276
  # This technique is also known as
@@ -216,6 +285,7 @@ module ActiveRecord
216
285
  # The following bundled adapters throw the ActiveRecord::RecordNotUnique exception:
217
286
  #
218
287
  # * ActiveRecord::ConnectionAdapters::Mysql2Adapter.
288
+ # * ActiveRecord::ConnectionAdapters::TrilogyAdapter.
219
289
  # * ActiveRecord::ConnectionAdapters::SQLite3Adapter.
220
290
  # * ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.
221
291
  def validates_uniqueness_of(*attr_names)
@@ -30,26 +30,27 @@ module ActiveRecord
30
30
 
31
31
  # = Active Record \Validations
32
32
  #
33
- # Active Record includes the majority of its validations from ActiveModel::Validations
34
- # all of which accept the <tt>:on</tt> argument to define the context where the
35
- # validations are active. Active Record will always supply either the context of
36
- # <tt>:create</tt> or <tt>:update</tt> dependent on whether the model is a
33
+ # Active Record includes the majority of its validations from ActiveModel::Validations.
34
+ #
35
+ # In Active Record, all validations are performed on save by default.
36
+ # Validations accept the <tt>:on</tt> argument to define the context where
37
+ # the validations are active. Active Record will pass either the context of
38
+ # <tt>:create</tt> or <tt>:update</tt> depending on whether the model is a
37
39
  # {new_record?}[rdoc-ref:Persistence#new_record?].
38
40
  module Validations
39
41
  extend ActiveSupport::Concern
40
- include ActiveModel::Validations
41
42
 
42
43
  # The validation process on save can be skipped by passing <tt>validate: false</tt>.
43
44
  # The validation context can be changed by passing <tt>context: context</tt>.
44
45
  # The regular {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] method is replaced
45
46
  # with this when the validations module is mixed in, which it is by default.
46
- def save(options = {})
47
+ def save(**options)
47
48
  perform_validations(options) ? super : false
48
49
  end
49
50
 
50
51
  # Attempts to save the record just like {ActiveRecord::Base#save}[rdoc-ref:Base#save] but
51
52
  # will raise an ActiveRecord::RecordInvalid exception instead of returning +false+ if the record is not valid.
52
- def save!(options = {})
53
+ def save!(**options)
53
54
  perform_validations(options) ? super : raise_validation_error
54
55
  end
55
56
 
@@ -60,6 +61,8 @@ module ActiveRecord
60
61
  #
61
62
  # If the argument is +false+ (default is +nil+), the context is set to <tt>:create</tt> if
62
63
  # {new_record?}[rdoc-ref:Persistence#new_record?] is +true+, and to <tt>:update</tt> if it is not.
64
+ # If the argument is an array of contexts, <tt>post.valid?([:create, :update])</tt>, the validations are
65
+ # run within multiple contexts.
63
66
  #
64
67
  # \Validations with no <tt>:on</tt> option will run no matter the context. \Validations with
65
68
  # some <tt>:on</tt> option will only run in the specified context.
@@ -71,8 +74,11 @@ module ActiveRecord
71
74
 
72
75
  alias_method :validate, :valid?
73
76
 
74
- private
77
+ def custom_validation_context? # :nodoc:
78
+ validation_context && [:create, :update].exclude?(validation_context)
79
+ end
75
80
 
81
+ private
76
82
  def default_validation_context
77
83
  new_record? ? :create : :update
78
84
  end
@@ -92,3 +98,4 @@ require "active_record/validations/uniqueness"
92
98
  require "active_record/validations/presence"
93
99
  require "active_record/validations/absence"
94
100
  require "active_record/validations/length"
101
+ require "active_record/validations/numericality"
@@ -3,7 +3,7 @@
3
3
  require_relative "gem_version"
4
4
 
5
5
  module ActiveRecord
6
- # Returns the version of the currently loaded ActiveRecord as a <tt>Gem::Version</tt>
6
+ # Returns the currently loaded version of Active Record as a +Gem::Version+.
7
7
  def self.version
8
8
  gem_version
9
9
  end