activerecord 5.2.8.1 → 6.1.6.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (316) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1255 -596
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +7 -5
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +9 -8
  7. data/lib/active_record/association_relation.rb +30 -10
  8. data/lib/active_record/associations/alias_tracker.rb +19 -16
  9. data/lib/active_record/associations/association.rb +100 -41
  10. data/lib/active_record/associations/association_scope.rb +23 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +55 -48
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -6
  13. data/lib/active_record/associations/builder/association.rb +45 -22
  14. data/lib/active_record/associations/builder/belongs_to.rb +29 -59
  15. data/lib/active_record/associations/builder/collection_association.rb +8 -17
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -2
  18. data/lib/active_record/associations/builder/has_one.rb +33 -2
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -1
  20. data/lib/active_record/associations/collection_association.rb +44 -34
  21. data/lib/active_record/associations/collection_proxy.rb +25 -21
  22. data/lib/active_record/associations/foreign_association.rb +20 -0
  23. data/lib/active_record/associations/has_many_association.rb +26 -13
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -18
  25. data/lib/active_record/associations/has_one_association.rb +43 -31
  26. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  27. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  28. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  29. data/lib/active_record/associations/join_dependency.rb +91 -60
  30. data/lib/active_record/associations/preloader/association.rb +69 -43
  31. data/lib/active_record/associations/preloader/through_association.rb +49 -40
  32. data/lib/active_record/associations/preloader.rb +47 -34
  33. data/lib/active_record/associations/singular_association.rb +3 -17
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/associations.rb +137 -25
  36. data/lib/active_record/attribute_assignment.rb +17 -19
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
  38. data/lib/active_record/attribute_methods/dirty.rb +101 -40
  39. data/lib/active_record/attribute_methods/primary_key.rb +20 -25
  40. data/lib/active_record/attribute_methods/query.rb +4 -8
  41. data/lib/active_record/attribute_methods/read.rb +14 -56
  42. data/lib/active_record/attribute_methods/serialization.rb +12 -7
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  44. data/lib/active_record/attribute_methods/write.rb +18 -34
  45. data/lib/active_record/attribute_methods.rb +81 -143
  46. data/lib/active_record/attributes.rb +46 -9
  47. data/lib/active_record/autosave_association.rb +57 -42
  48. data/lib/active_record/base.rb +4 -17
  49. data/lib/active_record/callbacks.rb +158 -43
  50. data/lib/active_record/coders/yaml_column.rb +1 -2
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +211 -90
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +385 -144
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +167 -69
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +229 -99
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
  64. data/lib/active_record/connection_adapters/column.rb +30 -12
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  67. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/database_statements.rb +88 -32
  69. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  70. data/lib/active_record/connection_adapters/mysql/quoting.rb +59 -7
  71. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  72. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  73. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +18 -7
  74. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +142 -19
  75. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
  77. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  78. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  79. data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
  80. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -54
  81. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  94. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  96. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
  99. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  100. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
  101. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  102. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
  104. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  105. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  106. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -120
  107. data/lib/active_record/connection_adapters/schema_cache.rb +159 -21
  108. data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
  109. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +146 -0
  110. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  111. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  112. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
  113. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
  114. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  115. data/lib/active_record/connection_adapters.rb +52 -0
  116. data/lib/active_record/connection_handling.rb +293 -33
  117. data/lib/active_record/core.rb +333 -98
  118. data/lib/active_record/counter_cache.rb +8 -30
  119. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  120. data/lib/active_record/database_configurations/database_config.rb +80 -0
  121. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  122. data/lib/active_record/database_configurations/url_config.rb +53 -0
  123. data/lib/active_record/database_configurations.rb +273 -0
  124. data/lib/active_record/delegated_type.rb +209 -0
  125. data/lib/active_record/destroy_association_async_job.rb +36 -0
  126. data/lib/active_record/dynamic_matchers.rb +3 -4
  127. data/lib/active_record/enum.rb +108 -36
  128. data/lib/active_record/errors.rb +62 -19
  129. data/lib/active_record/explain.rb +10 -6
  130. data/lib/active_record/explain_subscriber.rb +1 -1
  131. data/lib/active_record/fixture_set/file.rb +10 -17
  132. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  133. data/lib/active_record/fixture_set/render_context.rb +17 -0
  134. data/lib/active_record/fixture_set/table_row.rb +152 -0
  135. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  136. data/lib/active_record/fixtures.rb +200 -481
  137. data/lib/active_record/gem_version.rb +3 -3
  138. data/lib/active_record/inheritance.rb +53 -24
  139. data/lib/active_record/insert_all.rb +212 -0
  140. data/lib/active_record/integration.rb +67 -17
  141. data/lib/active_record/internal_metadata.rb +28 -9
  142. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  143. data/lib/active_record/locking/optimistic.rb +37 -23
  144. data/lib/active_record/locking/pessimistic.rb +9 -5
  145. data/lib/active_record/log_subscriber.rb +35 -35
  146. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  147. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  148. data/lib/active_record/middleware/database_selector.rb +77 -0
  149. data/lib/active_record/migration/command_recorder.rb +96 -44
  150. data/lib/active_record/migration/compatibility.rb +145 -64
  151. data/lib/active_record/migration/join_table.rb +0 -1
  152. data/lib/active_record/migration.rb +206 -157
  153. data/lib/active_record/model_schema.rb +148 -22
  154. data/lib/active_record/nested_attributes.rb +4 -7
  155. data/lib/active_record/no_touching.rb +8 -1
  156. data/lib/active_record/null_relation.rb +0 -1
  157. data/lib/active_record/persistence.rb +267 -59
  158. data/lib/active_record/query_cache.rb +21 -4
  159. data/lib/active_record/querying.rb +40 -23
  160. data/lib/active_record/railtie.rb +116 -59
  161. data/lib/active_record/railties/console_sandbox.rb +2 -4
  162. data/lib/active_record/railties/controller_runtime.rb +30 -35
  163. data/lib/active_record/railties/databases.rake +411 -80
  164. data/lib/active_record/readonly_attributes.rb +4 -0
  165. data/lib/active_record/reflection.rb +109 -93
  166. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  167. data/lib/active_record/relation/batches.rb +44 -35
  168. data/lib/active_record/relation/calculations.rb +157 -90
  169. data/lib/active_record/relation/delegation.rb +35 -50
  170. data/lib/active_record/relation/finder_methods.rb +64 -39
  171. data/lib/active_record/relation/from_clause.rb +5 -1
  172. data/lib/active_record/relation/merger.rb +32 -40
  173. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  174. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  179. data/lib/active_record/relation/predicate_builder.rb +62 -45
  180. data/lib/active_record/relation/query_attribute.rb +13 -8
  181. data/lib/active_record/relation/query_methods.rb +476 -187
  182. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  183. data/lib/active_record/relation/spawn_methods.rb +9 -9
  184. data/lib/active_record/relation/where_clause.rb +115 -62
  185. data/lib/active_record/relation.rb +379 -115
  186. data/lib/active_record/result.rb +64 -38
  187. data/lib/active_record/runtime_registry.rb +2 -2
  188. data/lib/active_record/sanitization.rb +22 -41
  189. data/lib/active_record/schema.rb +2 -11
  190. data/lib/active_record/schema_dumper.rb +54 -9
  191. data/lib/active_record/schema_migration.rb +7 -9
  192. data/lib/active_record/scoping/default.rb +4 -8
  193. data/lib/active_record/scoping/named.rb +17 -24
  194. data/lib/active_record/scoping.rb +8 -9
  195. data/lib/active_record/secure_token.rb +16 -8
  196. data/lib/active_record/serialization.rb +5 -3
  197. data/lib/active_record/signed_id.rb +116 -0
  198. data/lib/active_record/statement_cache.rb +49 -6
  199. data/lib/active_record/store.rb +88 -9
  200. data/lib/active_record/suppressor.rb +2 -2
  201. data/lib/active_record/table_metadata.rb +42 -43
  202. data/lib/active_record/tasks/database_tasks.rb +277 -81
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  206. data/lib/active_record/test_databases.rb +24 -0
  207. data/lib/active_record/test_fixtures.rb +287 -0
  208. data/lib/active_record/timestamp.rb +43 -32
  209. data/lib/active_record/touch_later.rb +23 -22
  210. data/lib/active_record/transactions.rb +62 -118
  211. data/lib/active_record/translation.rb +1 -1
  212. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  213. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  214. data/lib/active_record/type/serialized.rb +6 -3
  215. data/lib/active_record/type/time.rb +10 -0
  216. data/lib/active_record/type/type_map.rb +0 -1
  217. data/lib/active_record/type/unsigned_integer.rb +0 -1
  218. data/lib/active_record/type.rb +10 -5
  219. data/lib/active_record/type_caster/connection.rb +15 -15
  220. data/lib/active_record/type_caster/map.rb +8 -8
  221. data/lib/active_record/validations/associated.rb +1 -2
  222. data/lib/active_record/validations/numericality.rb +35 -0
  223. data/lib/active_record/validations/uniqueness.rb +38 -30
  224. data/lib/active_record/validations.rb +4 -3
  225. data/lib/active_record.rb +13 -12
  226. data/lib/arel/alias_predication.rb +9 -0
  227. data/lib/arel/attributes/attribute.rb +41 -0
  228. data/lib/arel/collectors/bind.rb +29 -0
  229. data/lib/arel/collectors/composite.rb +39 -0
  230. data/lib/arel/collectors/plain_string.rb +20 -0
  231. data/lib/arel/collectors/sql_string.rb +27 -0
  232. data/lib/arel/collectors/substitute_binds.rb +35 -0
  233. data/lib/arel/crud.rb +42 -0
  234. data/lib/arel/delete_manager.rb +18 -0
  235. data/lib/arel/errors.rb +9 -0
  236. data/lib/arel/expressions.rb +29 -0
  237. data/lib/arel/factory_methods.rb +49 -0
  238. data/lib/arel/insert_manager.rb +49 -0
  239. data/lib/arel/math.rb +45 -0
  240. data/lib/arel/nodes/and.rb +32 -0
  241. data/lib/arel/nodes/ascending.rb +23 -0
  242. data/lib/arel/nodes/binary.rb +126 -0
  243. data/lib/arel/nodes/bind_param.rb +44 -0
  244. data/lib/arel/nodes/case.rb +55 -0
  245. data/lib/arel/nodes/casted.rb +62 -0
  246. data/lib/arel/nodes/comment.rb +29 -0
  247. data/lib/arel/nodes/count.rb +12 -0
  248. data/lib/arel/nodes/delete_statement.rb +45 -0
  249. data/lib/arel/nodes/descending.rb +23 -0
  250. data/lib/arel/nodes/equality.rb +15 -0
  251. data/lib/arel/nodes/extract.rb +24 -0
  252. data/lib/arel/nodes/false.rb +16 -0
  253. data/lib/arel/nodes/full_outer_join.rb +8 -0
  254. data/lib/arel/nodes/function.rb +44 -0
  255. data/lib/arel/nodes/grouping.rb +11 -0
  256. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  257. data/lib/arel/nodes/in.rb +15 -0
  258. data/lib/arel/nodes/infix_operation.rb +92 -0
  259. data/lib/arel/nodes/inner_join.rb +8 -0
  260. data/lib/arel/nodes/insert_statement.rb +37 -0
  261. data/lib/arel/nodes/join_source.rb +20 -0
  262. data/lib/arel/nodes/matches.rb +18 -0
  263. data/lib/arel/nodes/named_function.rb +23 -0
  264. data/lib/arel/nodes/node.rb +51 -0
  265. data/lib/arel/nodes/node_expression.rb +13 -0
  266. data/lib/arel/nodes/ordering.rb +27 -0
  267. data/lib/arel/nodes/outer_join.rb +8 -0
  268. data/lib/arel/nodes/over.rb +15 -0
  269. data/lib/arel/nodes/regexp.rb +16 -0
  270. data/lib/arel/nodes/right_outer_join.rb +8 -0
  271. data/lib/arel/nodes/select_core.rb +67 -0
  272. data/lib/arel/nodes/select_statement.rb +41 -0
  273. data/lib/arel/nodes/sql_literal.rb +19 -0
  274. data/lib/arel/nodes/string_join.rb +11 -0
  275. data/lib/arel/nodes/table_alias.rb +31 -0
  276. data/lib/arel/nodes/terminal.rb +16 -0
  277. data/lib/arel/nodes/true.rb +16 -0
  278. data/lib/arel/nodes/unary.rb +44 -0
  279. data/lib/arel/nodes/unary_operation.rb +20 -0
  280. data/lib/arel/nodes/unqualified_column.rb +22 -0
  281. data/lib/arel/nodes/update_statement.rb +41 -0
  282. data/lib/arel/nodes/values_list.rb +9 -0
  283. data/lib/arel/nodes/window.rb +126 -0
  284. data/lib/arel/nodes/with.rb +11 -0
  285. data/lib/arel/nodes.rb +70 -0
  286. data/lib/arel/order_predications.rb +13 -0
  287. data/lib/arel/predications.rb +250 -0
  288. data/lib/arel/select_manager.rb +270 -0
  289. data/lib/arel/table.rb +118 -0
  290. data/lib/arel/tree_manager.rb +72 -0
  291. data/lib/arel/update_manager.rb +34 -0
  292. data/lib/arel/visitors/dot.rb +308 -0
  293. data/lib/arel/visitors/mysql.rb +93 -0
  294. data/lib/arel/visitors/postgresql.rb +120 -0
  295. data/lib/arel/visitors/sqlite.rb +38 -0
  296. data/lib/arel/visitors/to_sql.rb +899 -0
  297. data/lib/arel/visitors/visitor.rb +45 -0
  298. data/lib/arel/visitors.rb +13 -0
  299. data/lib/arel/window_predications.rb +9 -0
  300. data/lib/arel.rb +54 -0
  301. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  302. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  303. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  304. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  305. data/lib/rails/generators/active_record/migration.rb +19 -2
  306. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  307. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  308. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  309. metadata +116 -30
  310. data/lib/active_record/attribute_decorators.rb +0 -90
  311. data/lib/active_record/collection_cache_key.rb +0 -53
  312. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  313. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  314. data/lib/active_record/define_callbacks.rb +0 -22
  315. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  316. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -0,0 +1,35 @@
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 <tt>only_integer</tt>
25
+ # is +false+) or applying it to the regular expression <tt>/\A[\+\-]?\d+\z/</tt>
26
+ # (if <tt>only_integer</tt> is set to +true+). Kernel.Float precision
27
+ # defaults to the column's precision value or 15.
28
+ #
29
+ # See ActiveModel::Validations::HelperMethods.validates_numericality_of for more information.
30
+ def validates_numericality_of(*attr_names)
31
+ validates_with NumericalityValidator, _merge_attributes(attr_names)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -12,7 +12,7 @@ module ActiveRecord
12
12
  raise ArgumentError, "#{options[:scope]} is not supported format for :scope option. " \
13
13
  "Pass a symbol or an array of symbols instead: `scope: :user_id`"
14
14
  end
15
- super({ case_sensitive: true }.merge!(options))
15
+ super
16
16
  @klass = options[:class]
17
17
  end
18
18
 
@@ -25,17 +25,26 @@ module ActiveRecord
25
25
  if finder_class.primary_key
26
26
  relation = relation.where.not(finder_class.primary_key => record.id_in_database)
27
27
  else
28
- raise UnknownPrimaryKey.new(finder_class, "Can not validate uniqueness for persisted record without primary key.")
28
+ raise UnknownPrimaryKey.new(finder_class, "Cannot validate uniqueness for persisted record without primary key.")
29
29
  end
30
30
  end
31
31
  relation = scope_relation(record, relation)
32
- relation = relation.merge(options[:conditions]) if options[:conditions]
32
+
33
+ if options[:conditions]
34
+ conditions = options[:conditions]
35
+
36
+ relation = if conditions.arity.zero?
37
+ relation.instance_exec(&conditions)
38
+ else
39
+ relation.instance_exec(record, &conditions)
40
+ end
41
+ end
33
42
 
34
43
  if relation.exists?
35
44
  error_options = options.except(:case_sensitive, :scope, :conditions)
36
45
  error_options[:value] = value
37
46
 
38
- record.errors.add(attribute, :taken, error_options)
47
+ record.errors.add(attribute, :taken, **error_options)
39
48
  end
40
49
  end
41
50
 
@@ -56,33 +65,21 @@ module ActiveRecord
56
65
  end
57
66
 
58
67
  def build_relation(klass, attribute, value)
59
- if reflection = klass._reflect_on_association(attribute)
60
- attribute = reflection.foreign_key
61
- value = value.attributes[reflection.klass.primary_key] unless value.nil?
62
- end
63
-
64
- if value.nil?
65
- return klass.unscoped.where!(attribute => value)
66
- end
67
-
68
- # the attribute may be an aliased attribute
69
- if klass.attribute_alias?(attribute)
70
- attribute = klass.attribute_alias(attribute)
68
+ relation = klass.unscoped
69
+ comparison = relation.bind_attribute(attribute, value) do |attr, bind|
70
+ return relation.none! if bind.unboundable?
71
+
72
+ if !options.key?(:case_sensitive) || bind.nil?
73
+ klass.connection.default_uniqueness_comparison(attr, bind)
74
+ elsif options[:case_sensitive]
75
+ klass.connection.case_sensitive_comparison(attr, bind)
76
+ else
77
+ # will use SQL LOWER function before comparison, unless it detects a case insensitive collation
78
+ klass.connection.case_insensitive_comparison(attr, bind)
79
+ end
71
80
  end
72
81
 
73
- attribute_name = attribute.to_s
74
- value = klass.predicate_builder.build_bind_attribute(attribute_name, value)
75
-
76
- table = klass.arel_table
77
- column = klass.columns_hash[attribute_name]
78
-
79
- comparison = if !options[:case_sensitive]
80
- # will use SQL LOWER function before comparison, unless it detects a case insensitive collation
81
- klass.connection.case_insensitive_comparison(table, attribute, column, value)
82
- else
83
- klass.connection.case_sensitive_comparison(table, attribute, column, value)
84
- end
85
- klass.unscoped.where!(comparison)
82
+ relation.where!(comparison)
86
83
  end
87
84
 
88
85
  def scope_relation(record, relation)
@@ -90,7 +87,7 @@ module ActiveRecord
90
87
  scope_value = if record.class._reflect_on_association(scope_item)
91
88
  record.association(scope_item).reader
92
89
  else
93
- record._read_attribute(scope_item)
90
+ record.read_attribute(scope_item)
94
91
  end
95
92
  relation = relation.where(scope_item => scope_value)
96
93
  end
@@ -138,6 +135,17 @@ module ActiveRecord
138
135
  # validates_uniqueness_of :title, conditions: -> { where.not(status: 'archived') }
139
136
  # end
140
137
  #
138
+ # To build conditions based on the record's state, define the conditions
139
+ # callable with a parameter, which will be the record itself. This
140
+ # example validates the title is unique for the year of publication:
141
+ #
142
+ # class Article < ActiveRecord::Base
143
+ # validates_uniqueness_of :title, conditions: ->(article) {
144
+ # published_at = article.published_at
145
+ # where(published_at: published_at.beginning_of_year..published_at.end_of_year)
146
+ # }
147
+ # end
148
+ #
141
149
  # When the record is created, a check is performed to make sure that no
142
150
  # record exists in the database with the given value for the specified
143
151
  # attribute (that maps to a column). When the record is updated,
@@ -40,15 +40,16 @@ module ActiveRecord
40
40
  include ActiveModel::Validations
41
41
 
42
42
  # The validation process on save can be skipped by passing <tt>validate: false</tt>.
43
+ # The validation context can be changed by passing <tt>context: context</tt>.
43
44
  # The regular {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] method is replaced
44
45
  # with this when the validations module is mixed in, which it is by default.
45
- def save(options = {})
46
+ def save(**options)
46
47
  perform_validations(options) ? super : false
47
48
  end
48
49
 
49
50
  # Attempts to save the record just like {ActiveRecord::Base#save}[rdoc-ref:Base#save] but
50
51
  # will raise an ActiveRecord::RecordInvalid exception instead of returning +false+ if the record is not valid.
51
- def save!(options = {})
52
+ def save!(**options)
52
53
  perform_validations(options) ? super : raise_validation_error
53
54
  end
54
55
 
@@ -71,7 +72,6 @@ module ActiveRecord
71
72
  alias_method :validate, :valid?
72
73
 
73
74
  private
74
-
75
75
  def default_validation_context
76
76
  new_record? ? :create : :update
77
77
  end
@@ -91,3 +91,4 @@ require "active_record/validations/uniqueness"
91
91
  require "active_record/validations/presence"
92
92
  require "active_record/validations/absence"
93
93
  require "active_record/validations/length"
94
+ require "active_record/validations/numericality"
data/lib/active_record.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2004-2018 David Heinemeier Hansson
4
+ # Copyright (c) 2004-2022 David Heinemeier Hansson
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the
@@ -31,6 +31,7 @@ require "yaml"
31
31
 
32
32
  require "active_record/version"
33
33
  require "active_model/attribute_set"
34
+ require "active_record/errors"
34
35
 
35
36
  module ActiveRecord
36
37
  extend ActiveSupport::Autoload
@@ -41,6 +42,7 @@ module ActiveRecord
41
42
  autoload :ConnectionHandling
42
43
  autoload :CounterCache
43
44
  autoload :DynamicMatchers
45
+ autoload :DelegatedType
44
46
  autoload :Enum
45
47
  autoload :InternalMetadata
46
48
  autoload :Explain
@@ -55,7 +57,6 @@ module ActiveRecord
55
57
  autoload :Persistence
56
58
  autoload :QueryCache
57
59
  autoload :Querying
58
- autoload :CollectionCacheKey
59
60
  autoload :ReadonlyAttributes
60
61
  autoload :RecordInvalid, "active_record/validations"
61
62
  autoload :Reflection
@@ -68,17 +69,17 @@ module ActiveRecord
68
69
  autoload :Serialization
69
70
  autoload :StatementCache
70
71
  autoload :Store
72
+ autoload :SignedId
71
73
  autoload :Suppressor
72
74
  autoload :Timestamp
73
75
  autoload :Transactions
74
76
  autoload :Translation
75
77
  autoload :Validations
76
78
  autoload :SecureToken
79
+ autoload :DestroyAssociationAsyncJob
77
80
 
78
81
  eager_autoload do
79
- autoload :ActiveRecordError, "active_record/errors"
80
- autoload :ConnectionNotEstablished, "active_record/errors"
81
- autoload :ConnectionAdapters, "active_record/connection_adapters/abstract_adapter"
82
+ autoload :ConnectionAdapters
82
83
 
83
84
  autoload :Aggregations
84
85
  autoload :Associations
@@ -136,21 +137,19 @@ module ActiveRecord
136
137
  end
137
138
  end
138
139
 
139
- module ConnectionAdapters
140
+ module Scoping
140
141
  extend ActiveSupport::Autoload
141
142
 
142
143
  eager_autoload do
143
- autoload :AbstractAdapter
144
+ autoload :Named
145
+ autoload :Default
144
146
  end
145
147
  end
146
148
 
147
- module Scoping
149
+ module Middleware
148
150
  extend ActiveSupport::Autoload
149
151
 
150
- eager_autoload do
151
- autoload :Named
152
- autoload :Default
153
- end
152
+ autoload :DatabaseSelector, "active_record/middleware/database_selector"
154
153
  end
155
154
 
156
155
  module Tasks
@@ -163,6 +162,7 @@ module ActiveRecord
163
162
  "active_record/tasks/postgresql_database_tasks"
164
163
  end
165
164
 
165
+ autoload :TestDatabases, "active_record/test_databases"
166
166
  autoload :TestFixtures, "active_record/fixtures"
167
167
 
168
168
  def self.eager_load!
@@ -186,3 +186,4 @@ end
186
186
  YAML.load_tags["!ruby/object:ActiveRecord::AttributeSet"] = "ActiveModel::AttributeSet"
187
187
  YAML.load_tags["!ruby/object:ActiveRecord::Attribute::FromDatabase"] = "ActiveModel::Attribute::FromDatabase"
188
188
  YAML.load_tags["!ruby/object:ActiveRecord::LazyAttributeHash"] = "ActiveModel::LazyAttributeHash"
189
+ YAML.load_tags["!ruby/object:ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MysqlString"] = "ActiveRecord::Type::String"
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module AliasPredication
5
+ def as(other)
6
+ Nodes::As.new self, Nodes::SqlLiteral.new(other)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Attributes
5
+ class Attribute < Struct.new :relation, :name
6
+ include Arel::Expressions
7
+ include Arel::Predications
8
+ include Arel::AliasPredication
9
+ include Arel::OrderPredications
10
+ include Arel::Math
11
+
12
+ def type_caster
13
+ relation.type_for_attribute(name)
14
+ end
15
+
16
+ ###
17
+ # Create a node for lowering this attribute
18
+ def lower
19
+ relation.lower self
20
+ end
21
+
22
+ def type_cast_for_database(value)
23
+ relation.type_cast_for_database(name, value)
24
+ end
25
+
26
+ def able_to_type_cast?
27
+ relation.able_to_type_cast?
28
+ end
29
+ end
30
+
31
+ class String < Attribute; end
32
+ class Time < Attribute; end
33
+ class Boolean < Attribute; end
34
+ class Decimal < Attribute; end
35
+ class Float < Attribute; end
36
+ class Integer < Attribute; end
37
+ class Undefined < Attribute; end
38
+ end
39
+
40
+ Attribute = Attributes::Attribute
41
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Collectors
5
+ class Bind
6
+ def initialize
7
+ @binds = []
8
+ end
9
+
10
+ def <<(str)
11
+ self
12
+ end
13
+
14
+ def add_bind(bind)
15
+ @binds << bind
16
+ self
17
+ end
18
+
19
+ def add_binds(binds, proc_for_binds = nil)
20
+ @binds.concat proc_for_binds ? binds.map(&proc_for_binds) : binds
21
+ self
22
+ end
23
+
24
+ def value
25
+ @binds
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Collectors
5
+ class Composite
6
+ attr_accessor :preparable
7
+
8
+ def initialize(left, right)
9
+ @left = left
10
+ @right = right
11
+ end
12
+
13
+ def <<(str)
14
+ left << str
15
+ right << str
16
+ self
17
+ end
18
+
19
+ def add_bind(bind, &block)
20
+ left.add_bind bind, &block
21
+ right.add_bind bind, &block
22
+ self
23
+ end
24
+
25
+ def add_binds(binds, proc_for_binds = nil, &block)
26
+ left.add_binds(binds, proc_for_binds, &block)
27
+ right.add_binds(binds, proc_for_binds, &block)
28
+ self
29
+ end
30
+
31
+ def value
32
+ [left.value, right.value]
33
+ end
34
+
35
+ private
36
+ attr_reader :left, :right
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Collectors
5
+ class PlainString
6
+ def initialize
7
+ @str = +""
8
+ end
9
+
10
+ def value
11
+ @str
12
+ end
13
+
14
+ def <<(str)
15
+ @str << str
16
+ self
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "arel/collectors/plain_string"
4
+
5
+ module Arel # :nodoc: all
6
+ module Collectors
7
+ class SQLString < PlainString
8
+ attr_accessor :preparable
9
+
10
+ def initialize(*)
11
+ super
12
+ @bind_index = 1
13
+ end
14
+
15
+ def add_bind(bind)
16
+ self << yield(@bind_index)
17
+ @bind_index += 1
18
+ self
19
+ end
20
+
21
+ def add_binds(binds, proc_for_binds = nil, &block)
22
+ self << (@bind_index...@bind_index += binds.size).map(&block).join(", ")
23
+ self
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Collectors
5
+ class SubstituteBinds
6
+ attr_accessor :preparable
7
+
8
+ def initialize(quoter, delegate_collector)
9
+ @quoter = quoter
10
+ @delegate = delegate_collector
11
+ end
12
+
13
+ def <<(str)
14
+ delegate << str
15
+ self
16
+ end
17
+
18
+ def add_bind(bind)
19
+ bind = bind.value_for_database if bind.respond_to?(:value_for_database)
20
+ self << quoter.quote(bind)
21
+ end
22
+
23
+ def add_binds(binds, proc_for_binds = nil)
24
+ self << binds.map { |bind| quoter.quote(bind) }.join(", ")
25
+ end
26
+
27
+ def value
28
+ delegate.value
29
+ end
30
+
31
+ private
32
+ attr_reader :quoter, :delegate
33
+ end
34
+ end
35
+ end
data/lib/arel/crud.rb ADDED
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ ###
5
+ # FIXME hopefully we can remove this
6
+ module Crud
7
+ def compile_update(values, pk)
8
+ um = UpdateManager.new
9
+
10
+ if Nodes::SqlLiteral === values
11
+ relation = @ctx.from
12
+ else
13
+ relation = values.first.first.relation
14
+ end
15
+ um.key = pk
16
+ um.table relation
17
+ um.set values
18
+ um.take @ast.limit.expr if @ast.limit
19
+ um.order(*@ast.orders)
20
+ um.wheres = @ctx.wheres
21
+ um
22
+ end
23
+
24
+ def compile_insert(values)
25
+ im = create_insert
26
+ im.insert values
27
+ im
28
+ end
29
+
30
+ def create_insert
31
+ InsertManager.new
32
+ end
33
+
34
+ def compile_delete
35
+ dm = DeleteManager.new
36
+ dm.take @ast.limit.expr if @ast.limit
37
+ dm.wheres = @ctx.wheres
38
+ dm.from @ctx.froms
39
+ dm
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ class DeleteManager < Arel::TreeManager
5
+ include TreeManager::StatementMethods
6
+
7
+ def initialize
8
+ super
9
+ @ast = Nodes::DeleteStatement.new
10
+ @ctx = @ast
11
+ end
12
+
13
+ def from(relation)
14
+ @ast.relation = relation
15
+ self
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ class ArelError < StandardError
5
+ end
6
+
7
+ class EmptyJoinError < ArelError
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Expressions
5
+ def count(distinct = false)
6
+ Nodes::Count.new [self], distinct
7
+ end
8
+
9
+ def sum
10
+ Nodes::Sum.new [self]
11
+ end
12
+
13
+ def maximum
14
+ Nodes::Max.new [self]
15
+ end
16
+
17
+ def minimum
18
+ Nodes::Min.new [self]
19
+ end
20
+
21
+ def average
22
+ Nodes::Avg.new [self]
23
+ end
24
+
25
+ def extract(field)
26
+ Nodes::Extract.new [self], field
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ ###
5
+ # Methods for creating various nodes
6
+ module FactoryMethods
7
+ def create_true
8
+ Arel::Nodes::True.new
9
+ end
10
+
11
+ def create_false
12
+ Arel::Nodes::False.new
13
+ end
14
+
15
+ def create_table_alias(relation, name)
16
+ Nodes::TableAlias.new(relation, name)
17
+ end
18
+
19
+ def create_join(to, constraint = nil, klass = Nodes::InnerJoin)
20
+ klass.new(to, constraint)
21
+ end
22
+
23
+ def create_string_join(to)
24
+ create_join to, nil, Nodes::StringJoin
25
+ end
26
+
27
+ def create_and(clauses)
28
+ Nodes::And.new clauses
29
+ end
30
+
31
+ def create_on(expr)
32
+ Nodes::On.new expr
33
+ end
34
+
35
+ def grouping(expr)
36
+ Nodes::Grouping.new expr
37
+ end
38
+
39
+ ###
40
+ # Create a LOWER() function
41
+ def lower(column)
42
+ Nodes::NamedFunction.new "LOWER", [Nodes.build_quoted(column)]
43
+ end
44
+
45
+ def coalesce(*exprs)
46
+ Nodes::NamedFunction.new "COALESCE", exprs
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ class InsertManager < Arel::TreeManager
5
+ def initialize
6
+ super
7
+ @ast = Nodes::InsertStatement.new
8
+ end
9
+
10
+ def into(table)
11
+ @ast.relation = table
12
+ self
13
+ end
14
+
15
+ def columns; @ast.columns end
16
+ def values=(val); @ast.values = val; end
17
+
18
+ def select(select)
19
+ @ast.select = select
20
+ end
21
+
22
+ def insert(fields)
23
+ return if fields.empty?
24
+
25
+ if String === fields
26
+ @ast.values = Nodes::SqlLiteral.new(fields)
27
+ else
28
+ @ast.relation ||= fields.first.first.relation
29
+
30
+ values = []
31
+
32
+ fields.each do |column, value|
33
+ @ast.columns << column
34
+ values << value
35
+ end
36
+ @ast.values = create_values(values)
37
+ end
38
+ self
39
+ end
40
+
41
+ def create_values(values)
42
+ Nodes::ValuesList.new([values])
43
+ end
44
+
45
+ def create_values_list(rows)
46
+ Nodes::ValuesList.new(rows)
47
+ end
48
+ end
49
+ end