aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib/active_model/validations
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel/lib/active_model/validations')
-rw-r--r--activemodel/lib/active_model/validations/absence.rb31
-rw-r--r--activemodel/lib/active_model/validations/acceptance.rb11
-rw-r--r--activemodel/lib/active_model/validations/callbacks.rb17
-rw-r--r--activemodel/lib/active_model/validations/clusivity.rb33
-rw-r--r--activemodel/lib/active_model/validations/confirmation.rb19
-rw-r--r--activemodel/lib/active_model/validations/exclusion.rb5
-rw-r--r--activemodel/lib/active_model/validations/format.rb5
-rw-r--r--activemodel/lib/active_model/validations/inclusion.rb7
-rw-r--r--activemodel/lib/active_model/validations/length.rb26
-rw-r--r--activemodel/lib/active_model/validations/numericality.rb19
-rw-r--r--activemodel/lib/active_model/validations/presence.rb7
-rw-r--r--activemodel/lib/active_model/validations/validates.rb17
-rw-r--r--activemodel/lib/active_model/validations/with.rb5
13 files changed, 130 insertions, 72 deletions
diff --git a/activemodel/lib/active_model/validations/absence.rb b/activemodel/lib/active_model/validations/absence.rb
new file mode 100644
index 0000000000..1a1863370b
--- /dev/null
+++ b/activemodel/lib/active_model/validations/absence.rb
@@ -0,0 +1,31 @@
+module ActiveModel
+ module Validations
+ # == Active Model Absence Validator
+ class AbsenceValidator < EachValidator #:nodoc:
+ def validate_each(record, attr_name, value)
+ record.errors.add(attr_name, :present, options) if value.present?
+ end
+ end
+
+ module HelperMethods
+ # Validates that the specified attributes are blank (as defined by
+ # Object#blank?). Happens by default on save.
+ #
+ # class Person < ActiveRecord::Base
+ # validates_absence_of :first_name
+ # end
+ #
+ # The first_name attribute must be in the object and it must be blank.
+ #
+ # Configuration options:
+ # * <tt>:message</tt> - A custom error message (default is: "must be blank").
+ #
+ # There is also a list of default options supported by every validator:
+ # +:if+, +:unless+, +:on+ and +:strict+.
+ # See <tt>ActiveModel::Validation#validates</tt> for more information
+ def validates_absence_of(*attr_names)
+ validates_with AbsenceValidator, _merge_attributes(attr_names)
+ end
+ end
+ end
+end
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index 8d5ebf527f..139de16326 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -1,10 +1,10 @@
module ActiveModel
- # == Active Model Acceptance Validator
module Validations
- class AcceptanceValidator < EachValidator #:nodoc:
+ class AcceptanceValidator < EachValidator # :nodoc:
def initialize(options)
- super({ :allow_nil => true, :accept => "1" }.merge!(options))
+ super({ allow_nil: true, accept: "1" }.merge!(options))
+ setup!(options[:class])
end
def validate_each(record, attribute, value)
@@ -13,7 +13,8 @@ module ActiveModel
end
end
- def setup(klass)
+ private
+ def setup!(klass)
attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
klass.send(:attr_reader, *attr_readers)
@@ -27,7 +28,7 @@ module ActiveModel
#
# class Person < ActiveRecord::Base
# validates_acceptance_of :terms_of_service
- # validates_acceptance_of :eula, message: "must be abided"
+ # validates_acceptance_of :eula, message: 'must be abided'
# end
#
# If the database column does not exist, the +terms_of_service+ attribute
diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb
index c153ef4309..fde53b9f89 100644
--- a/activemodel/lib/active_model/validations/callbacks.rb
+++ b/activemodel/lib/active_model/validations/callbacks.rb
@@ -1,8 +1,6 @@
-require 'active_support/callbacks'
-
module ActiveModel
module Validations
- # == Active Model Validation callbacks
+ # == Active \Model Validation Callbacks
#
# Provides an interface for any class to have +before_validation+ and
# +after_validation+ callbacks.
@@ -24,7 +22,10 @@ module ActiveModel
included do
include ActiveSupport::Callbacks
- define_callbacks :validation, :terminator => "result == false", :skip_after_callbacks_if_terminated => true, :scope => [:kind, :name]
+ define_callbacks :validation,
+ terminator: ->(_,result) { result == false },
+ skip_after_callbacks_if_terminated: true,
+ scope: [:kind, :name]
end
module ClassMethods
@@ -57,7 +58,9 @@ module ActiveModel
if options.is_a?(Hash) && options[:on]
options[:if] = Array(options[:if])
options[:on] = Array(options[:on])
- options[:if].unshift("#{options[:on]}.include? self.validation_context")
+ options[:if].unshift lambda { |o|
+ options[:on].include? o.validation_context
+ }
end
set_callback(:validation, :before, *args, &block)
end
@@ -85,8 +88,8 @@ module ActiveModel
# person = Person.new
# person.name = ''
# person.valid? # => false
- # person.status # => false
- #  person.name = 'bob'
+ # person.status # => false
+ # person.name = 'bob'
# person.valid? # => true
# person.status # => true
def after_validation(*args, &block)
diff --git a/activemodel/lib/active_model/validations/clusivity.rb b/activemodel/lib/active_model/validations/clusivity.rb
index 3d7067fbcb..fd6cc1edb4 100644
--- a/activemodel/lib/active_model/validations/clusivity.rb
+++ b/activemodel/lib/active_model/validations/clusivity.rb
@@ -3,7 +3,7 @@ require 'active_support/core_ext/range'
module ActiveModel
module Validations
module Clusivity #:nodoc:
- ERROR_MESSAGE = "An object with the method #include? or a proc, lambda or symbol is required, " <<
+ ERROR_MESSAGE = "An object with the method #include? or a proc, lambda or symbol is required, " \
"and must be supplied as the :in (or :within) option of the configuration hash"
def check_validity!
@@ -15,26 +15,33 @@ module ActiveModel
private
def include?(record, value)
- exclusions = if delimiter.respond_to?(:call)
- delimiter.call(record)
- elsif delimiter.respond_to?(:to_sym)
- record.send(delimiter)
- else
- delimiter
- end
+ members = if delimiter.respond_to?(:call)
+ delimiter.call(record)
+ elsif delimiter.respond_to?(:to_sym)
+ record.send(delimiter)
+ else
+ delimiter
+ end
- exclusions.send(inclusion_method(exclusions), value)
+ members.send(inclusion_method(members), value)
end
def delimiter
@delimiter ||= options[:in] || options[:within]
end
- # In Ruby 1.9 <tt>Range#include?</tt> on non-numeric ranges checks all possible values in the
- # range for equality, so it may be slow for large ranges. The new <tt>Range#cover?</tt>
- # uses the previous logic of comparing a value with the range endpoints.
+ # In Ruby 1.9 <tt>Range#include?</tt> on non-number-or-time-ish ranges checks all
+ # possible values in the range for equality, which is slower but more accurate.
+ # <tt>Range#cover?</tt> uses the previous logic of comparing a value with the range
+ # endpoints, which is fast but is only accurate on Numeric, Time, or DateTime ranges.
def inclusion_method(enumerable)
- enumerable.is_a?(Range) ? :cover? : :include?
+ return :include? unless enumerable.is_a?(Range)
+ case enumerable.first
+ when Numeric, Time, DateTime
+ :cover?
+ else
+ :include?
+ end
end
end
end
diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb
index baa034eca6..b0542661af 100644
--- a/activemodel/lib/active_model/validations/confirmation.rb
+++ b/activemodel/lib/active_model/validations/confirmation.rb
@@ -1,19 +1,28 @@
module ActiveModel
- # == Active Model Confirmation Validator
module Validations
- class ConfirmationValidator < EachValidator #:nodoc:
+ class ConfirmationValidator < EachValidator # :nodoc:
+ def initialize(options)
+ super
+ setup!(options[:class])
+ end
+
def validate_each(record, attribute, value)
if (confirmed = record.send("#{attribute}_confirmation")) && (value != confirmed)
human_attribute_name = record.class.human_attribute_name(attribute)
- record.errors.add(:"#{attribute}_confirmation", :confirmation, options.merge(:attribute => human_attribute_name))
+ record.errors.add(:"#{attribute}_confirmation", :confirmation, options.merge(attribute: human_attribute_name))
end
end
- def setup(klass)
- klass.send(:attr_accessor, *attributes.map do |attribute|
+ private
+ def setup!(klass)
+ klass.send(:attr_reader, *attributes.map do |attribute|
:"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
end.compact)
+
+ klass.send(:attr_writer, *attributes.map do |attribute|
+ :"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation=")
+ end.compact)
end
end
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index 3ec552c372..48bf5cd802 100644
--- a/activemodel/lib/active_model/validations/exclusion.rb
+++ b/activemodel/lib/active_model/validations/exclusion.rb
@@ -2,14 +2,13 @@ require "active_model/validations/clusivity"
module ActiveModel
- # == Active Model Exclusion Validator
module Validations
- class ExclusionValidator < EachValidator #:nodoc:
+ class ExclusionValidator < EachValidator # :nodoc:
include Clusivity
def validate_each(record, attribute, value)
if include?(record, value)
- record.errors.add(attribute, :exclusion, options.except(:in, :within).merge!(:value => value))
+ record.errors.add(attribute, :exclusion, options.except(:in, :within).merge!(value: value))
end
end
end
diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb
index 80150229a0..be7cae588f 100644
--- a/activemodel/lib/active_model/validations/format.rb
+++ b/activemodel/lib/active_model/validations/format.rb
@@ -1,8 +1,7 @@
module ActiveModel
- # == Active Model Format Validator
module Validations
- class FormatValidator < EachValidator #:nodoc:
+ class FormatValidator < EachValidator # :nodoc:
def validate_each(record, attribute, value)
if options[:with]
regexp = option_call(record, :with)
@@ -30,7 +29,7 @@ module ActiveModel
end
def record_error(record, attribute, name, value)
- record.errors.add(attribute, :invalid, options.except(name).merge!(:value => value))
+ record.errors.add(attribute, :invalid, options.except(name).merge!(value: value))
end
def regexp_using_multiline_anchors?(regexp)
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index babc8982da..24337614c5 100644
--- a/activemodel/lib/active_model/validations/inclusion.rb
+++ b/activemodel/lib/active_model/validations/inclusion.rb
@@ -2,14 +2,13 @@ require "active_model/validations/clusivity"
module ActiveModel
- # == Active Model Inclusion Validator
module Validations
- class InclusionValidator < EachValidator #:nodoc:
+ class InclusionValidator < EachValidator # :nodoc:
include Clusivity
def validate_each(record, attribute, value)
unless include?(record, value)
- record.errors.add(attribute, :inclusion, options.except(:in, :within).merge!(:value => value))
+ record.errors.add(attribute, :inclusion, options.except(:in, :within).merge!(value: value))
end
end
end
@@ -29,7 +28,7 @@ module ActiveModel
# Configuration options:
# * <tt>:in</tt> - An enumerable object of available items. This can be
# supplied as a proc, lambda or symbol which returns an enumerable. If the
- # enumerable is a range the test is performed with <tt>Range#cover?</tt>,
+ # enumerable is a numerical range the test is performed with <tt>Range#cover?</tt>,
# otherwise with <tt>include?</tt>.
# * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
# * <tt>:message</tt> - Specifies a custom error message (default is: "is
diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb
index e4a1f9e80a..ddfd8a342e 100644
--- a/activemodel/lib/active_model/validations/length.rb
+++ b/activemodel/lib/active_model/validations/length.rb
@@ -1,10 +1,10 @@
module ActiveModel
- # == Active Model Length Validator
+ # == Active \Model Length \Validator
module Validations
- class LengthValidator < EachValidator #:nodoc:
- MESSAGES = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }.freeze
- CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze
+ class LengthValidator < EachValidator # :nodoc:
+ MESSAGES = { is: :wrong_length, minimum: :too_short, maximum: :too_long }.freeze
+ CHECKS = { is: :==, minimum: :>=, maximum: :<= }.freeze
RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long]
@@ -14,6 +14,10 @@ module ActiveModel
options[:minimum], options[:maximum] = range.min, range.max
end
+ if options[:allow_blank] == false && options[:minimum].nil? && options[:is].nil?
+ options[:minimum] = 1
+ end
+
super
end
@@ -37,10 +41,13 @@ module ActiveModel
value = tokenize(value)
value_length = value.respond_to?(:length) ? value.length : value.to_s.length
errors_options = options.except(*RESERVED_OPTIONS)
-
+
CHECKS.each do |key, validity_check|
next unless check_value = options[key]
- next if value_length.send(validity_check, check_value)
+
+ if !value.nil? || skip_nil_check?(key)
+ next if value_length.send(validity_check, check_value)
+ end
errors_options[:count] = check_value
@@ -58,6 +65,10 @@ module ActiveModel
options[:tokenizer].call(value)
end || value
end
+
+ def skip_nil_check?(key)
+ key == :maximum && options[:allow_nil].nil? && options[:allow_blank].nil?
+ end
end
module HelperMethods
@@ -79,7 +90,8 @@ module ActiveModel
#
# Configuration options:
# * <tt>:minimum</tt> - The minimum size of the attribute.
- # * <tt>:maximum</tt> - The maximum size of the attribute.
+ # * <tt>:maximum</tt> - The maximum size of the attribute. Allows +nil+ by
+ # default if not used with :minimum.
# * <tt>:is</tt> - The exact size of the attribute.
# * <tt>:within</tt> - A range specifying the minimum and maximum size of
# the attribute.
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index edebca94a8..c6abe45f4a 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -1,11 +1,10 @@
module ActiveModel
- # == Active Model Numericality Validator
module Validations
- class NumericalityValidator < EachValidator #:nodoc:
- CHECKS = { :greater_than => :>, :greater_than_or_equal_to => :>=,
- :equal_to => :==, :less_than => :<, :less_than_or_equal_to => :<=,
- :odd => :odd?, :even => :even?, :other_than => :!= }.freeze
+ class NumericalityValidator < EachValidator # :nodoc:
+ CHECKS = { greater_than: :>, greater_than_or_equal_to: :>=,
+ equal_to: :==, less_than: :<, less_than_or_equal_to: :<=,
+ odd: :odd?, even: :even?, other_than: :!= }.freeze
RESERVED_OPTIONS = CHECKS.keys + [:only_integer]
@@ -18,9 +17,9 @@ module ActiveModel
end
def validate_each(record, attr_name, value)
- before_type_cast = "#{attr_name}_before_type_cast"
+ before_type_cast = :"#{attr_name}_before_type_cast"
- raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast.to_sym)
+ raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast)
raw_value ||= value
return if options[:allow_nil] && raw_value.nil?
@@ -48,7 +47,7 @@ module ActiveModel
option_value = record.send(option_value) if option_value.is_a?(Symbol)
unless value.send(CHECKS[option], option_value)
- record.errors.add(attr_name, option, filtered_options(value).merge(:count => option_value))
+ record.errors.add(attr_name, option, filtered_options(value).merge(count: option_value))
end
end
end
@@ -74,7 +73,7 @@ module ActiveModel
end
def filtered_options(value)
- options.except(*RESERVED_OPTIONS).merge!(:value => value)
+ options.except(*RESERVED_OPTIONS).merge!(value: value)
end
end
@@ -126,7 +125,7 @@ module ActiveModel
# For example:
#
# class Person < ActiveRecord::Base
- # validates_numericality_of :width, less_than: Proc.new { |person| person.height }
+ # validates_numericality_of :width, less_than: ->(person) { person.height }
# validates_numericality_of :width, greater_than: :minimum_weight
# end
def validates_numericality_of(*attr_names)
diff --git a/activemodel/lib/active_model/validations/presence.rb b/activemodel/lib/active_model/validations/presence.rb
index f159e40858..ab8c8359fc 100644
--- a/activemodel/lib/active_model/validations/presence.rb
+++ b/activemodel/lib/active_model/validations/presence.rb
@@ -1,11 +1,10 @@
module ActiveModel
- # == Active Model Presence Validator
module Validations
- class PresenceValidator < EachValidator #:nodoc:
- def validate(record)
- record.errors.add_on_blank(attributes, options)
+ class PresenceValidator < EachValidator # :nodoc:
+ def validate_each(record, attr_name, value)
+ record.errors.add(attr_name, :blank, options) if value.blank?
end
end
diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb
index 03046a543a..9a1ff2ad39 100644
--- a/activemodel/lib/active_model/validations/validates.rb
+++ b/activemodel/lib/active_model/validations/validates.rb
@@ -1,7 +1,6 @@
require 'active_support/core_ext/hash/slice'
module ActiveModel
- # == Active Model validates method
module Validations
module ClassMethods
# This method is a shortcut to all default validators and any custom
@@ -105,7 +104,7 @@ module ActiveModel
raise ArgumentError, "You need to supply at least one attribute" if attributes.empty?
raise ArgumentError, "You need to supply at least one validation" if validations.empty?
- defaults.merge!(:attributes => attributes)
+ defaults[:attributes] = attributes
validations.each do |key, options|
next unless options
@@ -129,15 +128,15 @@ module ActiveModel
# the validation itself.
#
# class Person
- #  include ActiveModel::Validations
+ # include ActiveModel::Validations
#
# attr_accessor :name
# validates! :name, presence: true
# end
#
# person = Person.new
- #  person.name = ''
- #  person.valid?
+ # person.name = ''
+ # person.valid?
# # => ActiveModel::StrictValidationFailed: Name can't be blank
def validates!(*attributes)
options = attributes.extract_options!
@@ -149,20 +148,20 @@ module ActiveModel
# When creating custom validators, it might be useful to be able to specify
# additional default keys. This can be done by overwriting this method.
- def _validates_default_keys #:nodoc:
+ def _validates_default_keys # :nodoc:
[:if, :unless, :on, :allow_blank, :allow_nil , :strict]
end
- def _parse_validates_options(options) #:nodoc:
+ def _parse_validates_options(options) # :nodoc:
case options
when TrueClass
{}
when Hash
options
when Range, Array
- { :in => options }
+ { in: options }
else
- { :with => options }
+ { with: options }
end
end
end
diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb
index 869591cd9e..16bd6670d1 100644
--- a/activemodel/lib/active_model/validations/with.rb
+++ b/activemodel/lib/active_model/validations/with.rb
@@ -10,7 +10,7 @@ module ActiveModel
end
end
- class WithValidator < EachValidator #:nodoc:
+ class WithValidator < EachValidator # :nodoc:
def validate_each(record, attr, val)
method_name = options[:with]
@@ -83,9 +83,10 @@ module ActiveModel
# end
def validates_with(*args, &block)
options = args.extract_options!
+ options[:class] = self
+
args.each do |klass|
validator = klass.new(options, &block)
- validator.setup(self) if validator.respond_to?(:setup)
if validator.respond_to?(:attributes) && !validator.attributes.empty?
validator.attributes.each do |attribute|