activerecord 7.0.8.7 → 7.2.2.1

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 (279) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +631 -1944
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +29 -29
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +16 -13
  7. data/lib/active_record/association_relation.rb +2 -2
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +35 -12
  10. data/lib/active_record/associations/association_scope.rb +16 -9
  11. data/lib/active_record/associations/belongs_to_association.rb +23 -8
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  13. data/lib/active_record/associations/builder/association.rb +3 -3
  14. data/lib/active_record/associations/builder/belongs_to.rb +22 -8
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  16. data/lib/active_record/associations/builder/has_many.rb +3 -4
  17. data/lib/active_record/associations/builder/has_one.rb +3 -4
  18. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  19. data/lib/active_record/associations/collection_association.rb +26 -14
  20. data/lib/active_record/associations/collection_proxy.rb +29 -11
  21. data/lib/active_record/associations/errors.rb +265 -0
  22. data/lib/active_record/associations/foreign_association.rb +10 -3
  23. data/lib/active_record/associations/has_many_association.rb +21 -14
  24. data/lib/active_record/associations/has_many_through_association.rb +17 -7
  25. data/lib/active_record/associations/has_one_association.rb +10 -3
  26. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  27. data/lib/active_record/associations/join_dependency.rb +10 -10
  28. data/lib/active_record/associations/nested_error.rb +47 -0
  29. data/lib/active_record/associations/preloader/association.rb +33 -8
  30. data/lib/active_record/associations/preloader/branch.rb +7 -1
  31. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  32. data/lib/active_record/associations/preloader.rb +13 -10
  33. data/lib/active_record/associations/singular_association.rb +7 -1
  34. data/lib/active_record/associations/through_association.rb +22 -11
  35. data/lib/active_record/associations.rb +354 -485
  36. data/lib/active_record/attribute_assignment.rb +0 -4
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  38. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  39. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  40. data/lib/active_record/attribute_methods/primary_key.rb +45 -25
  41. data/lib/active_record/attribute_methods/query.rb +28 -16
  42. data/lib/active_record/attribute_methods/read.rb +8 -7
  43. data/lib/active_record/attribute_methods/serialization.rb +131 -32
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
  45. data/lib/active_record/attribute_methods/write.rb +6 -6
  46. data/lib/active_record/attribute_methods.rb +148 -33
  47. data/lib/active_record/attributes.rb +64 -50
  48. data/lib/active_record/autosave_association.rb +69 -37
  49. data/lib/active_record/base.rb +9 -5
  50. data/lib/active_record/callbacks.rb +11 -25
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -42
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +323 -88
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +217 -63
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
  69. data/lib/active_record/connection_adapters/column.rb +9 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +53 -54
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -10
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  87. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  89. data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
  90. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  91. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  92. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
  93. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
  95. data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
  96. data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
  97. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  98. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
  99. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -46
  100. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  101. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
  102. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  103. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +50 -8
  104. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
  105. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  106. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  107. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  108. data/lib/active_record/connection_adapters.rb +124 -1
  109. data/lib/active_record/connection_handling.rb +96 -104
  110. data/lib/active_record/core.rb +251 -176
  111. data/lib/active_record/counter_cache.rb +68 -34
  112. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  113. data/lib/active_record/database_configurations/database_config.rb +26 -5
  114. data/lib/active_record/database_configurations/hash_config.rb +52 -34
  115. data/lib/active_record/database_configurations/url_config.rb +37 -12
  116. data/lib/active_record/database_configurations.rb +87 -34
  117. data/lib/active_record/delegated_type.rb +39 -10
  118. data/lib/active_record/deprecator.rb +7 -0
  119. data/lib/active_record/destroy_association_async_job.rb +3 -1
  120. data/lib/active_record/dynamic_matchers.rb +2 -2
  121. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  122. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  123. data/lib/active_record/encryption/config.rb +25 -1
  124. data/lib/active_record/encryption/configurable.rb +12 -19
  125. data/lib/active_record/encryption/context.rb +10 -3
  126. data/lib/active_record/encryption/contexts.rb +5 -1
  127. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  128. data/lib/active_record/encryption/encryptable_record.rb +45 -21
  129. data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
  130. data/lib/active_record/encryption/encryptor.rb +18 -3
  131. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  132. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  133. data/lib/active_record/encryption/key_generator.rb +12 -1
  134. data/lib/active_record/encryption/key_provider.rb +1 -1
  135. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  136. data/lib/active_record/encryption/message_serializer.rb +6 -0
  137. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  138. data/lib/active_record/encryption/properties.rb +3 -3
  139. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  140. data/lib/active_record/encryption/scheme.rb +22 -21
  141. data/lib/active_record/encryption.rb +3 -0
  142. data/lib/active_record/enum.rb +129 -28
  143. data/lib/active_record/errors.rb +151 -31
  144. data/lib/active_record/explain.rb +21 -12
  145. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  146. data/lib/active_record/fixture_set/render_context.rb +2 -0
  147. data/lib/active_record/fixture_set/table_row.rb +29 -8
  148. data/lib/active_record/fixtures.rb +167 -97
  149. data/lib/active_record/future_result.rb +47 -8
  150. data/lib/active_record/gem_version.rb +4 -4
  151. data/lib/active_record/inheritance.rb +34 -18
  152. data/lib/active_record/insert_all.rb +72 -22
  153. data/lib/active_record/integration.rb +11 -8
  154. data/lib/active_record/internal_metadata.rb +124 -20
  155. data/lib/active_record/locking/optimistic.rb +8 -7
  156. data/lib/active_record/locking/pessimistic.rb +5 -2
  157. data/lib/active_record/log_subscriber.rb +18 -22
  158. data/lib/active_record/marshalling.rb +59 -0
  159. data/lib/active_record/message_pack.rb +124 -0
  160. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  161. data/lib/active_record/middleware/database_selector.rb +6 -8
  162. data/lib/active_record/middleware/shard_selector.rb +3 -1
  163. data/lib/active_record/migration/command_recorder.rb +106 -8
  164. data/lib/active_record/migration/compatibility.rb +147 -5
  165. data/lib/active_record/migration/default_strategy.rb +22 -0
  166. data/lib/active_record/migration/execution_strategy.rb +19 -0
  167. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  168. data/lib/active_record/migration.rb +234 -117
  169. data/lib/active_record/model_schema.rb +90 -102
  170. data/lib/active_record/nested_attributes.rb +48 -11
  171. data/lib/active_record/normalization.rb +163 -0
  172. data/lib/active_record/persistence.rb +168 -339
  173. data/lib/active_record/promise.rb +84 -0
  174. data/lib/active_record/query_cache.rb +18 -25
  175. data/lib/active_record/query_logs.rb +92 -52
  176. data/lib/active_record/query_logs_formatter.rb +41 -0
  177. data/lib/active_record/querying.rb +33 -8
  178. data/lib/active_record/railtie.rb +129 -85
  179. data/lib/active_record/railties/controller_runtime.rb +22 -7
  180. data/lib/active_record/railties/databases.rake +145 -154
  181. data/lib/active_record/railties/job_runtime.rb +23 -0
  182. data/lib/active_record/readonly_attributes.rb +32 -5
  183. data/lib/active_record/reflection.rb +267 -69
  184. data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
  185. data/lib/active_record/relation/batches.rb +198 -63
  186. data/lib/active_record/relation/calculations.rb +250 -93
  187. data/lib/active_record/relation/delegation.rb +30 -19
  188. data/lib/active_record/relation/finder_methods.rb +93 -18
  189. data/lib/active_record/relation/merger.rb +6 -6
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +18 -3
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  193. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  194. data/lib/active_record/relation/predicate_builder.rb +28 -16
  195. data/lib/active_record/relation/query_attribute.rb +2 -1
  196. data/lib/active_record/relation/query_methods.rb +576 -107
  197. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  198. data/lib/active_record/relation/spawn_methods.rb +5 -4
  199. data/lib/active_record/relation/where_clause.rb +7 -19
  200. data/lib/active_record/relation.rb +580 -90
  201. data/lib/active_record/result.rb +49 -48
  202. data/lib/active_record/runtime_registry.rb +63 -1
  203. data/lib/active_record/sanitization.rb +70 -25
  204. data/lib/active_record/schema.rb +8 -7
  205. data/lib/active_record/schema_dumper.rb +63 -14
  206. data/lib/active_record/schema_migration.rb +75 -24
  207. data/lib/active_record/scoping/default.rb +15 -5
  208. data/lib/active_record/scoping/named.rb +3 -2
  209. data/lib/active_record/scoping.rb +2 -1
  210. data/lib/active_record/secure_password.rb +60 -0
  211. data/lib/active_record/secure_token.rb +21 -3
  212. data/lib/active_record/signed_id.rb +27 -6
  213. data/lib/active_record/statement_cache.rb +7 -7
  214. data/lib/active_record/store.rb +8 -8
  215. data/lib/active_record/suppressor.rb +3 -1
  216. data/lib/active_record/table_metadata.rb +1 -1
  217. data/lib/active_record/tasks/database_tasks.rb +190 -118
  218. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  219. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  220. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  221. data/lib/active_record/test_fixtures.rb +170 -155
  222. data/lib/active_record/testing/query_assertions.rb +121 -0
  223. data/lib/active_record/timestamp.rb +31 -17
  224. data/lib/active_record/token_for.rb +123 -0
  225. data/lib/active_record/touch_later.rb +12 -7
  226. data/lib/active_record/transaction.rb +132 -0
  227. data/lib/active_record/transactions.rb +106 -24
  228. data/lib/active_record/translation.rb +0 -2
  229. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  230. data/lib/active_record/type/internal/timezone.rb +7 -2
  231. data/lib/active_record/type/serialized.rb +1 -3
  232. data/lib/active_record/type/time.rb +4 -0
  233. data/lib/active_record/type_caster/connection.rb +4 -4
  234. data/lib/active_record/validations/absence.rb +1 -1
  235. data/lib/active_record/validations/associated.rb +9 -3
  236. data/lib/active_record/validations/numericality.rb +5 -4
  237. data/lib/active_record/validations/presence.rb +5 -28
  238. data/lib/active_record/validations/uniqueness.rb +61 -11
  239. data/lib/active_record/validations.rb +12 -5
  240. data/lib/active_record/version.rb +1 -1
  241. data/lib/active_record.rb +247 -33
  242. data/lib/arel/alias_predication.rb +1 -1
  243. data/lib/arel/collectors/bind.rb +2 -0
  244. data/lib/arel/collectors/composite.rb +7 -0
  245. data/lib/arel/collectors/sql_string.rb +1 -1
  246. data/lib/arel/collectors/substitute_binds.rb +1 -1
  247. data/lib/arel/errors.rb +10 -0
  248. data/lib/arel/factory_methods.rb +4 -0
  249. data/lib/arel/nodes/binary.rb +6 -7
  250. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  251. data/lib/arel/nodes/cte.rb +36 -0
  252. data/lib/arel/nodes/fragments.rb +35 -0
  253. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  254. data/lib/arel/nodes/leading_join.rb +8 -0
  255. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  256. data/lib/arel/nodes/node.rb +115 -5
  257. data/lib/arel/nodes/sql_literal.rb +13 -0
  258. data/lib/arel/nodes/table_alias.rb +4 -0
  259. data/lib/arel/nodes.rb +6 -2
  260. data/lib/arel/predications.rb +3 -1
  261. data/lib/arel/select_manager.rb +1 -1
  262. data/lib/arel/table.rb +9 -5
  263. data/lib/arel/tree_manager.rb +8 -3
  264. data/lib/arel/update_manager.rb +2 -1
  265. data/lib/arel/visitors/dot.rb +1 -0
  266. data/lib/arel/visitors/mysql.rb +17 -5
  267. data/lib/arel/visitors/postgresql.rb +1 -12
  268. data/lib/arel/visitors/sqlite.rb +25 -0
  269. data/lib/arel/visitors/to_sql.rb +112 -34
  270. data/lib/arel/visitors/visitor.rb +2 -2
  271. data/lib/arel.rb +21 -3
  272. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  273. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  274. data/lib/rails/generators/active_record/migration.rb +3 -1
  275. data/lib/rails/generators/active_record/model/USAGE +113 -0
  276. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  277. metadata +54 -12
  278. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  279. data/lib/active_record/null_relation.rb +0 -63
@@ -30,9 +30,7 @@ module ActiveRecord
30
30
  end
31
31
  end
32
32
 
33
- def inspect
34
- Kernel.instance_method(:inspect).bind_call(self)
35
- end
33
+ define_method(:inspect, Kernel.instance_method(:inspect))
36
34
 
37
35
  def changed_in_place?(raw_old_value, value)
38
36
  return false if value.nil?
@@ -17,6 +17,10 @@ module ActiveRecord
17
17
  end
18
18
  end
19
19
 
20
+ def serialize_cast_value(value) # :nodoc:
21
+ Value.new(super) if value
22
+ end
23
+
20
24
  private
21
25
  def cast_value(value)
22
26
  case value = super
@@ -14,18 +14,18 @@ module ActiveRecord
14
14
  end
15
15
 
16
16
  def type_for_attribute(attr_name)
17
- schema_cache = connection.schema_cache
17
+ schema_cache = @klass.schema_cache
18
18
 
19
19
  if schema_cache.data_source_exists?(table_name)
20
20
  column = schema_cache.columns_hash(table_name)[attr_name.to_s]
21
- 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
22
24
  end
23
25
 
24
26
  type || Type.default_value
25
27
  end
26
28
 
27
- delegate :connection, to: :@klass, private: true
28
-
29
29
  private
30
30
  attr_reader :table_name
31
31
  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)
@@ -4,14 +4,20 @@ module ActiveRecord
4
4
  module Validations
5
5
  class AssociatedValidator < ActiveModel::EachValidator # :nodoc:
6
6
  def validate_each(record, attribute, value)
7
- if Array(value).reject { |r| valid_object?(r) }.any?
7
+ context = record_validation_context_for_association(record)
8
+
9
+ if Array(value).reject { |association| valid_object?(association, context) }.any?
8
10
  record.errors.add(attribute, :invalid, **options.merge(value: value))
9
11
  end
10
12
  end
11
13
 
12
14
  private
13
- def valid_object?(record)
14
- (record.respond_to?(:marked_for_destruction?) && record.marked_for_destruction?) || record.valid?
15
+ def valid_object?(record, context)
16
+ (record.respond_to?(:marked_for_destruction?) && record.marked_for_destruction?) || record.valid?(context)
17
+ end
18
+
19
+ def record_validation_context_for_association(record)
20
+ record.custom_validation_context? ? record.validation_context : nil
15
21
  end
16
22
  end
17
23
 
@@ -21,10 +21,11 @@ module ActiveRecord
21
21
 
22
22
  module ClassMethods
23
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.
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.
28
29
  #
29
30
  # See ActiveModel::Validations::HelperMethods.validates_numericality_of for more information.
30
31
  def validates_numericality_of(*attr_names)
@@ -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,16 +14,19 @@ 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
@@ -64,18 +67,64 @@ module ActiveRecord
64
67
  class_hierarchy.detect { |klass| !klass.abstract_class? }
65
68
  end
66
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
+
67
112
  def build_relation(klass, attribute, value)
68
113
  relation = klass.unscoped
69
- comparison = relation.bind_attribute(attribute, value) do |attr, bind|
70
- 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?
71
119
 
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)
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
79
128
  end
80
129
  end
81
130
 
@@ -221,7 +270,7 @@ module ActiveRecord
221
270
  # When the database catches such a duplicate insertion,
222
271
  # {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] will raise an ActiveRecord::StatementInvalid
223
272
  # exception. You can either choose to let this error propagate (which
224
- # 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
225
274
  # can catch it and restart the transaction (e.g. by telling the user
226
275
  # that the title already exists, and asking them to re-enter the title).
227
276
  # This technique is also known as
@@ -236,6 +285,7 @@ module ActiveRecord
236
285
  # The following bundled adapters throw the ActiveRecord::RecordNotUnique exception:
237
286
  #
238
287
  # * ActiveRecord::ConnectionAdapters::Mysql2Adapter.
288
+ # * ActiveRecord::ConnectionAdapters::TrilogyAdapter.
239
289
  # * ActiveRecord::ConnectionAdapters::SQLite3Adapter.
240
290
  # * ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.
241
291
  def validates_uniqueness_of(*attr_names)
@@ -30,14 +30,15 @@ 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>.
@@ -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,6 +74,10 @@ module ActiveRecord
71
74
 
72
75
  alias_method :validate, :valid?
73
76
 
77
+ def custom_validation_context? # :nodoc:
78
+ validation_context && [:create, :update].exclude?(validation_context)
79
+ end
80
+
74
81
  private
75
82
  def default_validation_context
76
83
  new_record? ? :create : :update
@@ -3,7 +3,7 @@
3
3
  require_relative "gem_version"
4
4
 
5
5
  module ActiveRecord
6
- # Returns the currently loaded version of Active Record 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