aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/validations
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/validations')
-rw-r--r--activerecord/lib/active_record/validations/associated.rb8
-rw-r--r--activerecord/lib/active_record/validations/length.rb21
-rw-r--r--activerecord/lib/active_record/validations/presence.rb10
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb23
4 files changed, 46 insertions, 16 deletions
diff --git a/activerecord/lib/active_record/validations/associated.rb b/activerecord/lib/active_record/validations/associated.rb
index b4785d3ba4..47ccef31a5 100644
--- a/activerecord/lib/active_record/validations/associated.rb
+++ b/activerecord/lib/active_record/validations/associated.rb
@@ -29,9 +29,11 @@ module ActiveRecord
# Configuration options:
#
# * <tt>:message</tt> - A custom error message (default is: "is invalid").
- # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
- # validation contexts by default (+nil+), other options are <tt>:create</tt>
- # and <tt>:update</tt>.
+ # * <tt>:on</tt> - Specifies the contexts where this validation is active.
+ # Runs in all validation contexts by default (nil). You can pass a symbol
+ # or an array of symbols. (e.g. <tt>on: :create</tt> or
+ # <tt>on: :custom_validation_context</tt> or
+ # <tt>on: [:create, :custom_validation_context]</tt>)
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
diff --git a/activerecord/lib/active_record/validations/length.rb b/activerecord/lib/active_record/validations/length.rb
new file mode 100644
index 0000000000..ef5a6cbbe7
--- /dev/null
+++ b/activerecord/lib/active_record/validations/length.rb
@@ -0,0 +1,21 @@
+module ActiveRecord
+ module Validations
+ class LengthValidator < ActiveModel::Validations::LengthValidator # :nodoc:
+ def validate_each(record, attribute, association_or_value)
+ if association_or_value.respond_to?(:loaded?) && association_or_value.loaded?
+ association_or_value = association_or_value.target.reject(&:marked_for_destruction?)
+ end
+ super
+ end
+ end
+
+ module ClassMethods
+ # See <tt>ActiveModel::Validation::LengthValidator</tt> for more information.
+ def validates_length_of(*attr_names)
+ validates_with LengthValidator, _merge_attributes(attr_names)
+ end
+
+ alias_method :validates_size_of, :validates_length_of
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/validations/presence.rb b/activerecord/lib/active_record/validations/presence.rb
index e586744818..61b30749d9 100644
--- a/activerecord/lib/active_record/validations/presence.rb
+++ b/activerecord/lib/active_record/validations/presence.rb
@@ -8,7 +8,7 @@ module ActiveRecord
associated_records = Array.wrap(record.send(attribute))
# Superclass validates presence. Ensure present records aren't about to be destroyed.
- if associated_records.present? && associated_records.all? { |r| r.marked_for_destruction? }
+ if associated_records.present? && associated_records.all?(&:marked_for_destruction?)
record.errors.add(attribute, :blank, options)
end
end
@@ -44,9 +44,11 @@ module ActiveRecord
#
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "can't be blank").
- # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
- # validation contexts by default (+nil+), other options are <tt>:create</tt>
- # and <tt>:update</tt>.
+ # * <tt>:on</tt> - Specifies the contexts where this validation is active.
+ # Runs in all validation contexts by default (nil). You can pass a symbol
+ # or an array of symbols. (e.g. <tt>on: :create</tt> or
+ # <tt>on: :custom_validation_context</tt> or
+ # <tt>on: [:create, :custom_validation_context]</tt>)
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
# the validation should occur (e.g. <tt>if: :allow_validation</tt>, or
# <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 2e7b1d7206..f52f91e89c 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -16,9 +16,8 @@ module ActiveRecord
value = map_enum_attribute(finder_class, attribute, value)
relation = build_relation(finder_class, table, attribute, value)
- relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.id)) if record.persisted?
+ relation = relation.where.not(finder_class.primary_key => record.id) if record.persisted?
relation = scope_relation(record, table, relation)
- relation = finder_class.unscoped.where(relation)
relation = relation.merge(options[:conditions]) if options[:conditions]
if relation.exists?
@@ -48,7 +47,7 @@ module ActiveRecord
def build_relation(klass, table, attribute, value) #:nodoc:
if reflection = klass._reflect_on_association(attribute)
attribute = reflection.foreign_key
- value = value.attributes[reflection.primary_key_column.name] unless value.nil?
+ value = value.attributes[reflection.klass.primary_key] unless value.nil?
end
attribute_name = attribute.to_s
@@ -60,15 +59,21 @@ module ActiveRecord
end
column = klass.columns_hash[attribute_name]
- value = klass.connection.type_cast(value, column)
- value = value.to_s[0, column.limit] if value && column.limit && column.text?
+ value = klass.type_for_attribute(attribute_name).type_cast_for_database(value)
+ value = klass.connection.type_cast(value)
+ if value.is_a?(String) && column.limit
+ value = value.to_s[0, column.limit]
+ end
+
+ value = Arel::Nodes::Quoted.new(value)
- if !options[:case_sensitive] && value && column.text?
+ comparison = if !options[:case_sensitive] && value && column.text?
# will use SQL LOWER function before comparison, unless it detects a case insensitive collation
klass.connection.case_insensitive_comparison(table, attribute, column, value)
else
klass.connection.case_sensitive_comparison(table, attribute, column, value)
end
+ klass.unscoped.where(comparison)
end
def scope_relation(record, table, relation)
@@ -77,9 +82,9 @@ module ActiveRecord
scope_value = record.send(reflection.foreign_key)
scope_item = reflection.foreign_key
else
- scope_value = record.read_attribute(scope_item)
+ scope_value = record._read_attribute(scope_item)
end
- relation = relation.and(table[scope_item].eq(scope_value))
+ relation = relation.where(scope_item => scope_value)
end
relation
@@ -150,7 +155,7 @@ module ActiveRecord
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
# proc or string should return or evaluate to a +true+ or +false+ value.
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
- # determine if the validation should ot occur (e.g. <tt>unless: :skip_validation</tt>,
+ # determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
# method, proc or string should return or evaluate to a +true+ or +false+
# value.