aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib/active_model/validations.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel/lib/active_model/validations.rb')
-rw-r--r--activemodel/lib/active_model/validations.rb92
1 files changed, 77 insertions, 15 deletions
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 2db4a25f61..c1e344b215 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -4,7 +4,7 @@ require 'active_support/core_ext/hash/except'
module ActiveModel
- # == Active \Model Validations
+ # == Active \Model \Validations
#
# Provides a full validation framework to your objects.
#
@@ -39,6 +39,7 @@ module ActiveModel
extend ActiveSupport::Concern
included do
+ extend ActiveModel::Naming
extend ActiveModel::Callbacks
extend ActiveModel::Translation
@@ -46,7 +47,7 @@ module ActiveModel
include HelperMethods
attr_accessor :validation_context
- define_callbacks :validate, :scope => :name
+ define_callbacks :validate, scope: :name
class_attribute :_validators
self._validators = Hash.new { |h,k| h[k] = [] }
@@ -66,8 +67,11 @@ module ActiveModel
# end
#
# Options:
- # * <tt>:on</tt> - Specifies the context where this validation is active
- # (e.g. <tt>on: :create</tt> or <tt>on: :custom_validation_context</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>:allow_nil</tt> - Skip validation if attribute is +nil+.
# * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
@@ -83,6 +87,8 @@ module ActiveModel
validates_with BlockValidator, _merge_attributes(attr_names), &block
end
+ VALID_OPTIONS_FOR_VALIDATE = [:on, :if, :unless].freeze
+
# Adds a validation method or block to the class. This is useful when
# overriding the +validate+ instance method becomes too unwieldy and
# you're looking for more descriptive declaration of your validations.
@@ -124,10 +130,11 @@ module ActiveModel
# end
#
# Options:
- # * <tt>:on</tt> - Specifies the context where this validation is active
- # (e.g. <tt>on: :create</tt> or <tt>on: :custom_validation_context</tt>)
- # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+.
- # * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
+ # * <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,
@@ -139,11 +146,23 @@ module ActiveModel
# value.
def validate(*args, &block)
options = args.extract_options!
+
+ if args.all? { |arg| arg.is_a?(Symbol) }
+ options.each_key do |k|
+ unless VALID_OPTIONS_FOR_VALIDATE.include?(k)
+ raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{VALID_OPTIONS_FOR_VALIDATE.map(&:inspect).join(', ')}. Perhaps you meant to call `validates` instead of `validate`?")
+ end
+ end
+ end
+
if options.key?(:on)
options = options.dup
options[:if] = Array(options[:if])
- options[:if].unshift("validation_context == :#{options[:on]}")
+ options[:if].unshift ->(o) {
+ Array(options[:on]).include?(o.validation_context)
+ }
end
+
args << options
set_callback(:validate, *args, &block)
end
@@ -169,6 +188,49 @@ module ActiveModel
_validators.values.flatten.uniq
end
+ # Clears all of the validators and validations.
+ #
+ # Note that this will clear anything that is being used to validate
+ # the model for both the +validates_with+ and +validate+ methods.
+ # It clears the validators that are created with an invocation of
+ # +validates_with+ and the callbacks that are set by an invocation
+ # of +validate+.
+ #
+ # class Person
+ # include ActiveModel::Validations
+ #
+ # validates_with MyValidator
+ # validates_with OtherValidator, on: :create
+ # validates_with StrictValidator, strict: true
+ # validate :cannot_be_robot
+ #
+ # def cannot_be_robot
+ # errors.add(:base, 'A person cannot be a robot') if person_is_robot
+ # end
+ # end
+ #
+ # Person.validators
+ # # => [
+ # # #<MyValidator:0x007fbff403e808 @options={}>,
+ # # #<OtherValidator:0x007fbff403d930 @options={on: :create}>,
+ # # #<StrictValidator:0x007fbff3204a30 @options={strict:true}>
+ # # ]
+ #
+ # If one runs <tt>Person.clear_validators!</tt> and then checks to see what
+ # validators this class has, you would obtain:
+ #
+ # Person.validators # => []
+ #
+ # Also, the callback set by <tt>validate :cannot_be_robot</tt> will be erased
+ # so that:
+ #
+ # Person._validate_callbacks.empty? # => true
+ #
+ def clear_validators!
+ reset_callbacks(:validate)
+ _validators.clear
+ end
+
# List all validators that are being used to validate a specific attribute.
#
# class Person
@@ -183,7 +245,6 @@ module ActiveModel
# Person.validators_on(:name)
# # => [
# # #<ActiveModel::Validations::PresenceValidator:0x007fe604914e60 @attributes=[:name], @options={}>,
- # # #<ActiveModel::Validations::InclusionValidator:0x007fe603bb8780 @attributes=[:age], @options={in:0..99}>
# # ]
def validators_on(*attributes)
attributes.flat_map do |attribute|
@@ -239,6 +300,8 @@ module ActiveModel
# Runs all the specified validations and returns +true+ if no errors were
# added otherwise +false+.
#
+ # Aliased as validate.
+ #
# class Person
# include ActiveModel::Validations
#
@@ -273,6 +336,8 @@ module ActiveModel
self.validation_context = current_context
end
+ alias_method :validate, :valid?
+
# Performs the opposite of <tt>valid?</tt>. Returns +true+ if errors were
# added, +false+ otherwise.
#
@@ -327,13 +392,10 @@ module ActiveModel
protected
def run_validations! #:nodoc:
- run_callbacks :validate
+ _run_validate_callbacks
errors.empty?
end
end
end
-Dir[File.dirname(__FILE__) + "/validations/*.rb"].sort.each do |path|
- filename = File.basename(path)
- require "active_model/validations/#{filename}"
-end
+Dir[File.dirname(__FILE__) + "/validations/*.rb"].each { |file| require file }