aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel/lib')
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb2
-rw-r--r--activemodel/lib/active_model/callbacks.rb16
-rw-r--r--activemodel/lib/active_model/deprecated_error_methods.rb2
-rw-r--r--activemodel/lib/active_model/dirty.rb2
-rw-r--r--activemodel/lib/active_model/errors.rb76
-rw-r--r--activemodel/lib/active_model/lint.rb4
-rw-r--r--activemodel/lib/active_model/observing.rb1
-rw-r--r--activemodel/lib/active_model/translation.rb2
-rw-r--r--activemodel/lib/active_model/validations.rb15
-rw-r--r--activemodel/lib/active_model/validations/acceptance.rb49
-rw-r--r--activemodel/lib/active_model/validations/callbacks.rb57
-rw-r--r--activemodel/lib/active_model/validations/confirmation.rb46
-rw-r--r--activemodel/lib/active_model/validations/exclusion.rb7
-rw-r--r--activemodel/lib/active_model/validations/format.rb8
-rw-r--r--activemodel/lib/active_model/validations/inclusion.rb7
-rw-r--r--activemodel/lib/active_model/validations/length.rb10
-rw-r--r--activemodel/lib/active_model/validations/numericality.rb15
-rw-r--r--activemodel/lib/active_model/validations/presence.rb4
-rw-r--r--activemodel/lib/active_model/validations/validates.rb4
-rw-r--r--activemodel/lib/active_model/validations/with.rb1
-rw-r--r--activemodel/lib/active_model/validator.rb4
21 files changed, 237 insertions, 95 deletions
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index a7690ba5b9..817640b178 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -46,7 +46,7 @@ module ActiveModel
# end
# end
#
- # Notice that whenever you include ActiveModel::AtributeMethods in your class,
+ # Notice that whenever you include ActiveModel::AttributeMethods in your class,
# it requires you to implement a <tt>attributes</tt> methods which returns a hash
# with each attribute name in your model as hash key and the attribute value as
# hash value.
diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb
index 257644b3fa..e7aad17021 100644
--- a/activemodel/lib/active_model/callbacks.rb
+++ b/activemodel/lib/active_model/callbacks.rb
@@ -2,11 +2,11 @@ require 'active_support/core_ext/array/wrap'
require 'active_support/callbacks'
module ActiveModel
- # == Active Model Call Backs
+ # == Active Model Callbacks
#
# Provides an interface for any class to have Active Record like callbacks.
#
- # Like the Active Record methods, the call back chain is aborted as soon as
+ # Like the Active Record methods, the callback chain is aborted as soon as
# one of the methods in the chain returns false.
#
# First, extend ActiveModel::Callbacks from the class you are creating:
@@ -15,13 +15,13 @@ module ActiveModel
# extend ActiveModel::Callbacks
# end
#
- # Then define a list of methods that you want call backs attached to:
+ # Then define a list of methods that you want callbacks attached to:
#
# define_model_callbacks :create, :update
#
# This will provide all three standard callbacks (before, around and after) around
# both the :create and :update methods. To implement, you need to wrap the methods
- # you want call backs on in a block so that the call backs get a chance to fire:
+ # you want callbacks on in a block so that the callbacks get a chance to fire:
#
# def create
# _run_create_callbacks do
@@ -61,7 +61,7 @@ module ActiveModel
#
# define_model_callbacks :initializer, :only => :after
#
- # Note, the <tt>:only => <type></tt> hash will apply to all call backs defined on
+ # Note, the <tt>:only => <type></tt> hash will apply to all callbacks defined on
# that method call. To get around this you can call the define_model_callbacks
# method as many times as you need.
#
@@ -72,8 +72,8 @@ module ActiveModel
# Would create +after_create+, +before_update+ and +around_destroy+ methods only.
#
# You can pass in a class to before_<type>, after_<type> and around_<type>, in which
- # case the call back will call that class's <action>_<type> method passing the object
- # that the call back is being called on.
+ # case the callback will call that class's <action>_<type> method passing the object
+ # that the callback is being called on.
#
# class MyModel
# extend ActiveModel::Callbacks
@@ -84,7 +84,7 @@ module ActiveModel
#
# class AnotherClass
# def self.before_create( obj )
- # # obj is the MyModel instance that the call back is being called on
+ # # obj is the MyModel instance that the callback is being called on
# end
# end
#
diff --git a/activemodel/lib/active_model/deprecated_error_methods.rb b/activemodel/lib/active_model/deprecated_error_methods.rb
index dd8050c549..adc50773d9 100644
--- a/activemodel/lib/active_model/deprecated_error_methods.rb
+++ b/activemodel/lib/active_model/deprecated_error_methods.rb
@@ -16,7 +16,7 @@ module ActiveModel
end
def add_to_base(msg)
- ActiveSupport::Deprecation.warn "Errors#add_to_base(msg) has been deprecated, use Errors#[:base] << msg instead"
+ ActiveSupport::Deprecation.warn "Errors#add_to_base(msg) has been deprecated, use Errors#add(:base, msg) instead"
self[:base] << msg
end
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index a82ce1bee0..4c80863e3a 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -4,7 +4,7 @@ require 'active_support/hash_with_indifferent_access'
require 'active_support/core_ext/object/duplicable'
module ActiveModel
- # == Active Model Call Backs
+ # == Active Model Dirty
#
# Provides a way to track changes in your object in the same way as
# Active Record does.
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index f4c7400621..d42fc5291d 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -3,6 +3,7 @@
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/hash/reverse_merge'
require 'active_support/ordered_hash'
module ActiveModel
@@ -61,6 +62,8 @@ module ActiveModel
class Errors < ActiveSupport::OrderedHash
include DeprecatedErrorMethods
+ CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank]
+
# Pass in the instance of the object that is using the errors object.
#
# class Person
@@ -162,15 +165,12 @@ module ActiveModel
# # <error>name must be specified</error>
# # </errors>
def to_xml(options={})
- require 'builder' unless defined? ::Builder
- options[:root] ||= "errors"
- options[:indent] ||= 2
- options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
-
- options[:builder].instruct! unless options.delete(:skip_instruct)
- options[:builder].errors do |e|
- to_a.each { |error| e.error(error) }
- end
+ to_a.to_xml options.reverse_merge(:root => "errors", :skip_types => true)
+ end
+
+ # Returns an array as JSON representation for this object.
+ def as_json(options=nil)
+ to_a
end
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
@@ -182,25 +182,44 @@ module ActiveModel
# If +message+ is a proc, it will be called, allowing for things like <tt>Time.now</tt> to be used within an error.
def add(attribute, message = nil, options = {})
message ||= :invalid
- message = generate_message(attribute, message, options) if message.is_a?(Symbol)
- message = message.call if message.is_a?(Proc)
+
+ if message.is_a?(Symbol)
+ message = generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
+ elsif message.is_a?(Proc)
+ message = message.call
+ end
+
self[attribute] << message
end
# Will add an error message to each of the attributes in +attributes+ that is empty.
- def add_on_empty(attributes, custom_message = nil)
+ def add_on_empty(attributes, options = {})
+ if options && !options.is_a?(Hash)
+ options = { :message => options }
+ ActiveSupport::Deprecation.warn \
+ "ActiveModel::Errors#add_on_empty(attributes, custom_message) has been deprecated.\n" +
+ "Instead of passing a custom_message pass an options Hash { :message => custom_message }."
+ end
+
[attributes].flatten.each do |attribute|
value = @base.send(:read_attribute_for_validation, attribute)
is_empty = value.respond_to?(:empty?) ? value.empty? : false
- add(attribute, :empty, :default => custom_message) unless !value.nil? && !is_empty
+ add(attribute, :empty, options) if value.nil? || is_empty
end
end
# Will add an error message to each of the attributes in +attributes+ that is blank (using Object#blank?).
- def add_on_blank(attributes, custom_message = nil)
+ def add_on_blank(attributes, options = {})
+ if options && !options.is_a?(Hash)
+ options = { :message => options }
+ ActiveSupport::Deprecation.warn \
+ "ActiveModel::Errors#add_on_blank(attributes, custom_message) has been deprecated.\n" +
+ "Instead of passing a custom_message pass an options Hash { :message => custom_message }."
+ end
+
[attributes].flatten.each do |attribute|
value = @base.send(:read_attribute_for_validation, attribute)
- add(attribute, :blank, :default => custom_message) if value.blank?
+ add(attribute, :blank, options) if value.blank?
end
end
@@ -246,7 +265,7 @@ module ActiveModel
# (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model name,
# translated attribute name and the value are available for interpolation.
#
- # When using inheritence in your models, it will check all the inherited
+ # When using inheritance in your models, it will check all the inherited
# models too, but only if the model itself hasn't been found. Say you have
# <tt>class Admin < User; end</tt> and you wanted the translation for
# the <tt>:blank</tt> error +message+ for the <tt>title</tt> +attribute+,
@@ -262,24 +281,31 @@ module ActiveModel
# <li><tt>errors.attributes.title.blank</tt></li>
# <li><tt>errors.messages.blank</tt></li>
# </ol>
- def generate_message(attribute, message = :invalid, options = {})
- message, options[:default] = options[:default], message if options[:default].is_a?(Symbol)
+ def generate_message(attribute, type = :invalid, options = {})
+ type = options.delete(:message) if options[:message].is_a?(Symbol)
+
+ if options[:default]
+ ActiveSupport::Deprecation.warn \
+ "ActiveModel::Errors#generate_message(attributes, custom_message) has been deprecated.\n" +
+ "Use ActiveModel::Errors#generate_message(attributes, :message => 'your message') instead."
+ options[:message] = options.delete(:default)
+ end
defaults = @base.class.lookup_ancestors.map do |klass|
- [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.underscore}.attributes.#{attribute}.#{message}",
- :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.underscore}.#{message}" ]
+ [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.underscore}.attributes.#{attribute}.#{type}",
+ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.underscore}.#{type}" ]
end
- defaults << options.delete(:default)
- defaults << :"#{@base.class.i18n_scope}.errors.messages.#{message}"
- defaults << :"errors.attributes.#{attribute}.#{message}"
- defaults << :"errors.messages.#{message}"
+ defaults << options.delete(:message)
+ defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}"
+ defaults << :"errors.attributes.#{attribute}.#{type}"
+ defaults << :"errors.messages.#{type}"
defaults.compact!
defaults.flatten!
key = defaults.shift
- value = @base.send(:read_attribute_for_validation, attribute)
+ value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)
options = {
:default => defaults,
diff --git a/activemodel/lib/active_model/lint.rb b/activemodel/lib/active_model/lint.rb
index a5977bf3cc..fe650053d9 100644
--- a/activemodel/lib/active_model/lint.rb
+++ b/activemodel/lib/active_model/lint.rb
@@ -56,7 +56,7 @@ module ActiveModel
# Returns a boolean that specifies whether the object has been persisted yet.
# This is used when calculating the URL for an object. If the object is
# not persisted, a form for that object, for instance, will be POSTed to the
- # collection. If it is persisted, a form for the object will put PUTed to the
+ # collection. If it is persisted, a form for the object will be PUT to the
# URL for the object.
def test_persisted?
assert model.respond_to?(:persisted?), "The model should respond to persisted?"
@@ -65,7 +65,7 @@ module ActiveModel
# == Naming
#
- # Model.model_name must returns a string with some convenience methods as
+ # Model.model_name must return a string with some convenience methods as
# :human and :partial_path. Check ActiveModel::Naming for more information.
#
def test_model_naming
diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb
index 14f8bf72dc..d0f36ce3b1 100644
--- a/activemodel/lib/active_model/observing.rb
+++ b/activemodel/lib/active_model/observing.rb
@@ -2,7 +2,6 @@ require 'singleton'
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/module/aliasing'
require 'active_support/core_ext/string/inflections'
-require 'active_support/core_ext/string/conversions'
module ActiveModel
module Observing
diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb
index 3228cfed9a..0554677296 100644
--- a/activemodel/lib/active_model/translation.rb
+++ b/activemodel/lib/active_model/translation.rb
@@ -13,7 +13,7 @@ module ActiveModel
# extend ActiveModel::Translation
# end
#
- # TranslatedPerson.human_attribute_name('my_attribue')
+ # TranslatedPerson.human_attribute_name('my_attribute')
# #=> "My attribute"
#
# This also provides the required class methods for hooking into the
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 57487cf75a..5779ba3b29 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -3,6 +3,7 @@ require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/hash/keys'
require 'active_model/errors'
+require 'active_model/validations/callbacks'
module ActiveModel
@@ -91,7 +92,7 @@ module ActiveModel
end
# Adds a validation method or block to the class. This is useful when
- # overriding the +validate+ instance method becomes too unwieldly and
+ # overriding the +validate+ instance method becomes too unwieldy and
# you're looking for more descriptive declaration of your validations.
#
# This can be done with a symbol pointing to a method:
@@ -164,8 +165,7 @@ module ActiveModel
def valid?(context = nil)
current_context, self.validation_context = validation_context, context
errors.clear
- _run_validate_callbacks
- errors.empty?
+ run_validations!
ensure
self.validation_context = current_context
end
@@ -176,7 +176,7 @@ module ActiveModel
!valid?(context)
end
- # Hook method defining how an attribute value should be retieved. By default
+ # Hook method defining how an attribute value should be retrieved. By default
# this is assumed to be an instance named after the attribute. Override this
# method in subclasses should you need to retrieve the value for a given
# attribute differently:
@@ -194,6 +194,13 @@ module ActiveModel
# end
#
alias :read_attribute_for_validation :send
+
+ protected
+
+ def run_validations!
+ _run_validate_callbacks
+ errors.empty?
+ end
end
end
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index 26b12b504b..99b8966def 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -1,4 +1,6 @@
module ActiveModel
+
+ # == Active Model Acceptance Validator
module Validations
class AcceptanceValidator < EachValidator
def initialize(options)
@@ -7,10 +9,10 @@ module ActiveModel
def validate_each(record, attribute, value)
unless value == options[:accept]
- record.errors.add(attribute, :accepted, :default => options[:message])
+ record.errors.add(attribute, :accepted, options.except(:accept, :allow_nil))
end
end
-
+
def setup(klass)
# Note: instance_methods.map(&:to_s) is important for 1.9 compatibility
# as instance_methods returns symbols unlike 1.8 which returns strings.
@@ -22,29 +24,42 @@ module ActiveModel
end
module HelperMethods
- # Encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example:
+ # Encapsulates the pattern of wanting to validate the acceptance of a
+ # terms of service check box (or similar agreement). Example:
#
# class Person < ActiveRecord::Base
# validates_acceptance_of :terms_of_service
# validates_acceptance_of :eula, :message => "must be abided"
# end
#
- # If the database column does not exist, the +terms_of_service+ attribute is entirely virtual. This check is
- # performed only if +terms_of_service+ is not +nil+ and by default on save.
+ # If the database column does not exist, the +terms_of_service+ attribute
+ # is entirely virtual. This check is performed only if +terms_of_service+
+ # is not +nil+ and by default on save.
#
# Configuration options:
- # * <tt>:message</tt> - A custom error message (default is: "must be accepted").
- # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
- # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is true).
- # * <tt>:accept</tt> - Specifies value that is considered accepted. The default value is a string "1", which
- # makes it easy to relate to an HTML checkbox. This should be set to +true+ if you are validating a database
- # column, since the attribute is typecast from "1" to +true+ before validation.
- # * <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 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
- # 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.
+ # * <tt>:message</tt> - A custom error message (default is: "must be
+ # accepted").
+ # * <tt>:on</tt> - Specifies when this validation is active (default is
+ # <tt>:save</tt>, other options are <tt>:create</tt> and
+ # <tt>:update</tt>).
+ # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default
+ # is true).
+ # * <tt>:accept</tt> - Specifies value that is considered accepted.
+ # The default value is a string "1", which makes it easy to relate to
+ # an HTML checkbox. This should be set to +true+ if you are validating
+ # a database column, since the attribute is typecast from "1" to +true+
+ # before validation.
+ # * <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 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 not occur (for example,
+ # <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.
def validates_acceptance_of(*attr_names)
validates_with AcceptanceValidator, _merge_attributes(attr_names)
end
diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb
new file mode 100644
index 0000000000..afd65d3dd5
--- /dev/null
+++ b/activemodel/lib/active_model/validations/callbacks.rb
@@ -0,0 +1,57 @@
+require 'active_support/callbacks'
+
+module ActiveModel
+ module Validations
+ module Callbacks
+ # == Active Model Validation callbacks
+ #
+ # Provides an interface for any class to have <tt>before_validation</tt> and
+ # <tt>after_validation</tt> callbacks.
+ #
+ # First, extend ActiveModel::Callbacks from the class you are creating:
+ #
+ # class MyModel
+ # include ActiveModel::Validations::Callbacks
+ #
+ # before_validation :do_stuff_before_validation
+ # after_validation :do_tuff_after_validation
+ # end
+ #
+ # Like other before_* callbacks if <tt>before_validation</tt> returns false
+ # then <tt>valid?</tt> will not be called.
+ extend ActiveSupport::Concern
+
+ included do
+ include ActiveSupport::Callbacks
+ define_callbacks :validation, :terminator => "result == false", :scope => [:kind, :name]
+ end
+
+ module ClassMethods
+ def before_validation(*args, &block)
+ options = args.last
+ if options.is_a?(Hash) && options[:on]
+ options[:if] = Array.wrap(options[:if])
+ options[:if] << "self.validation_context == :#{options[:on]}"
+ end
+ set_callback(:validation, :before, *args, &block)
+ end
+
+ def after_validation(*args, &block)
+ options = args.extract_options!
+ options[:prepend] = true
+ options[:if] = Array.wrap(options[:if])
+ options[:if] << "!halted && value != false"
+ options[:if] << "self.validation_context == :#{options[:on]}" if options[:on]
+ set_callback(:validation, :after, *(args << options), &block)
+ end
+ end
+
+ protected
+
+ # Overwrite run validations to include callbacks.
+ def run_validations!
+ _run_validation_callbacks { super }
+ end
+ end
+ end
+end
diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb
index 51445343f2..3a80893866 100644
--- a/activemodel/lib/active_model/validations/confirmation.rb
+++ b/activemodel/lib/active_model/validations/confirmation.rb
@@ -1,45 +1,59 @@
module ActiveModel
+
+ # == Active Model Confirmation Validator
module Validations
class ConfirmationValidator < EachValidator
def validate_each(record, attribute, value)
confirmed = record.send(:"#{attribute}_confirmation")
return if confirmed.nil? || value == confirmed
- record.errors.add(attribute, :confirmation, :default => options[:message])
+ record.errors.add(attribute, :confirmation, options)
end
-
+
def setup(klass)
- klass.send(:attr_accessor, *attributes.map { |attribute| :"#{attribute}_confirmation" })
+ klass.send(:attr_accessor, *attributes.map { |attribute| :"#{attribute}_confirmation" })
end
end
module HelperMethods
- # Encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example:
+ # Encapsulates the pattern of wanting to validate a password or email
+ # address field with a confirmation. For example:
#
# Model:
# class Person < ActiveRecord::Base
# validates_confirmation_of :user_name, :password
- # validates_confirmation_of :email_address, :message => "should match confirmation"
+ # validates_confirmation_of :email_address,
+ # :message => "should match confirmation"
# end
#
# View:
# <%= password_field "person", "password" %>
# <%= password_field "person", "password_confirmation" %>
#
- # The added +password_confirmation+ attribute is virtual; it exists only as an in-memory attribute for validating the password.
- # To achieve this, the validation adds accessors to the model for the confirmation attribute. NOTE: This check is performed
- # only if +password_confirmation+ is not +nil+, and by default only on save. To require confirmation, make sure to add a presence
- # check for the confirmation attribute:
+ # The added +password_confirmation+ attribute is virtual; it exists only
+ # as an in-memory attribute for validating the password. To achieve this,
+ # the validation adds accessors to the model for the confirmation
+ # attribute.
+ #
+ # NOTE: This check is performed only if +password_confirmation+ is not
+ # +nil+, and by default only on save. To require confirmation, make sure
+ # to add a presence check for the confirmation attribute:
#
# validates_presence_of :password_confirmation, :if => :password_changed?
#
# Configuration options:
- # * <tt>:message</tt> - A custom error message (default is: "doesn't match confirmation").
- # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</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 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
- # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
+ # * <tt>:message</tt> - A custom error message (default is: "doesn't match
+ # confirmation").
+ # * <tt>:on</tt> - Specifies when this validation is active (default is
+ # <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</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 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 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.
def validates_confirmation_of(*attr_names)
validates_with ConfirmationValidator, _merge_attributes(attr_names)
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index 2ee78f5dd2..4138892786 100644
--- a/activemodel/lib/active_model/validations/exclusion.rb
+++ b/activemodel/lib/active_model/validations/exclusion.rb
@@ -1,4 +1,6 @@
module ActiveModel
+
+ # == Active Model Exclusion Validator
module Validations
class ExclusionValidator < EachValidator
def check_validity!
@@ -7,8 +9,9 @@ module ActiveModel
end
def validate_each(record, attribute, value)
- return unless options[:in].include?(value)
- record.errors.add(attribute, :exclusion, :default => options[:message], :value => value)
+ if options[:in].include?(value)
+ record.errors.add(attribute, :exclusion, options.except(:in).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 c34c860d4d..104f403492 100644
--- a/activemodel/lib/active_model/validations/format.rb
+++ b/activemodel/lib/active_model/validations/format.rb
@@ -1,14 +1,16 @@
module ActiveModel
+
+ # == Active Model Format Validator
module Validations
class FormatValidator < EachValidator
def validate_each(record, attribute, value)
if options[:with] && value.to_s !~ options[:with]
- record.errors.add(attribute, :invalid, :default => options[:message], :value => value)
+ record.errors.add(attribute, :invalid, options.except(:with).merge!(:value => value))
elsif options[:without] && value.to_s =~ options[:without]
- record.errors.add(attribute, :invalid, :default => options[:message], :value => value)
+ record.errors.add(attribute, :invalid, options.except(:without).merge!(:value => value))
end
end
-
+
def check_validity!
unless options.include?(:with) ^ options.include?(:without) # ^ == xor, or "exclusive or"
raise ArgumentError, "Either :with or :without must be supplied (but not both)"
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index 446646d247..049b093618 100644
--- a/activemodel/lib/active_model/validations/inclusion.rb
+++ b/activemodel/lib/active_model/validations/inclusion.rb
@@ -1,4 +1,6 @@
module ActiveModel
+
+ # == Active Model Inclusion Validator
module Validations
class InclusionValidator < EachValidator
def check_validity!
@@ -7,8 +9,9 @@ module ActiveModel
end
def validate_each(record, attribute, value)
- return if options[:in].include?(value)
- record.errors.add(attribute, :inclusion, :default => options[:message], :value => value)
+ unless options[:in].include?(value)
+ record.errors.add(attribute, :inclusion, options.except(:in).merge!(:value => value))
+ end
end
end
diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb
index d7218f4f52..c8a77ad666 100644
--- a/activemodel/lib/active_model/validations/length.rb
+++ b/activemodel/lib/active_model/validations/length.rb
@@ -1,10 +1,13 @@
module ActiveModel
+
+ # == Active Model Length Validator
module Validations
class LengthValidator < EachValidator
MESSAGES = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }.freeze
CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze
DEFAULT_TOKENIZER = lambda { |value| value.split(//) }
+ RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long]
def initialize(options)
if range = (options.delete(:in) || options.delete(:within))
@@ -37,7 +40,8 @@ module ActiveModel
CHECKS.each do |key, validity_check|
next unless check_value = options[key]
- custom_message = options[:message] || options[MESSAGES[key]]
+ default_message = options[MESSAGES[key]]
+ options[:message] ||= default_message if default_message
valid_value = if key == :maximum
value.nil? || value.size.send(validity_check, check_value)
@@ -46,7 +50,9 @@ module ActiveModel
end
next if valid_value
- record.errors.add(attribute, MESSAGES[key], :default => custom_message, :count => check_value)
+
+ record.errors.add(attribute, MESSAGES[key],
+ options.except(*RESERVED_OPTIONS).merge!(:count => check_value))
end
end
end
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index 062b4cd17f..b6aff7aa6b 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -1,10 +1,14 @@
module ActiveModel
+
+ # == Active Model Numericality Validator
module Validations
class NumericalityValidator < EachValidator
CHECKS = { :greater_than => :>, :greater_than_or_equal_to => :>=,
:equal_to => :==, :less_than => :<, :less_than_or_equal_to => :<=,
:odd => :odd?, :even => :even? }.freeze
+ RESERVED_OPTIONS = CHECKS.keys + [:only_integer]
+
def initialize(options)
super(options.reverse_merge(:only_integer => false, :allow_nil => false))
end
@@ -26,13 +30,13 @@ module ActiveModel
return if options[:allow_nil] && raw_value.nil?
unless value = parse_raw_value_as_a_number(raw_value)
- record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => options[:message])
+ record.errors.add(attr_name, :not_a_number, filtered_options(raw_value))
return
end
if options[:only_integer]
unless value = parse_raw_value_as_an_integer(raw_value)
- record.errors.add(attr_name, :not_an_integer, :value => raw_value, :default => options[:message])
+ record.errors.add(attr_name, :not_an_integer, filtered_options(raw_value))
return
end
end
@@ -41,14 +45,14 @@ module ActiveModel
case option
when :odd, :even
unless value.to_i.send(CHECKS[option])
- record.errors.add(attr_name, option, :value => value, :default => options[:message])
+ record.errors.add(attr_name, option, filtered_options(value))
end
else
option_value = option_value.call(record) if option_value.is_a?(Proc)
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, :default => options[:message], :value => value, :count => option_value)
+ record.errors.add(attr_name, option, filtered_options(value).merge(:count => option_value))
end
end
end
@@ -73,6 +77,9 @@ module ActiveModel
raw_value.to_i if raw_value.to_s =~ /\A[+-]?\d+\Z/
end
+ def filtered_options(value)
+ options.except(*RESERVED_OPTIONS).merge!(:value => value)
+ end
end
module HelperMethods
diff --git a/activemodel/lib/active_model/validations/presence.rb b/activemodel/lib/active_model/validations/presence.rb
index b319f4834b..28c4640b17 100644
--- a/activemodel/lib/active_model/validations/presence.rb
+++ b/activemodel/lib/active_model/validations/presence.rb
@@ -1,10 +1,12 @@
require 'active_support/core_ext/object/blank'
module ActiveModel
+
+ # == Active Model Presence Validator
module Validations
class PresenceValidator < EachValidator
def validate(record)
- record.errors.add_on_blank(attributes, options[:message])
+ record.errors.add_on_blank(attributes, options)
end
end
diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb
index 90b244228a..0674640925 100644
--- a/activemodel/lib/active_model/validations/validates.rb
+++ b/activemodel/lib/active_model/validations/validates.rb
@@ -1,6 +1,8 @@
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
@@ -20,7 +22,7 @@ module ActiveModel
# validates :username, :presence => true
# validates :username, :uniqueness => true
#
- # The power of the +validates+ method comes when using cusom validators
+ # The power of the +validates+ method comes when using custom validators
# and default validators in one call for a given attribute e.g.
#
# class EmailValidator < ActiveModel::EachValidator
diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb
index 6dbde5bfad..200efd4eb5 100644
--- a/activemodel/lib/active_model/validations/with.rb
+++ b/activemodel/lib/active_model/validations/with.rb
@@ -9,7 +9,6 @@ module ActiveModel
end
module ClassMethods
-
# Passes the record off to the class or classes specified and allows them
# to add errors based on more complex conditions.
#
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index dd9e39843e..689c617177 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -109,7 +109,7 @@ module ActiveModel #:nodoc:
@kind ||= name.split('::').last.underscore.sub(/_validator$/, '').to_sym unless anonymous?
end
- # Accepts options that will be made availible through the +options+ reader.
+ # Accepts options that will be made available through the +options+ reader.
def initialize(options)
@options = options
end
@@ -155,7 +155,7 @@ module ActiveModel #:nodoc:
end
end
- # Override this method in subclasses with the validation logic, adding
+ # Override this method in subclasses with the validation logic, adding
# errors to the records +errors+ array where necessary.
def validate_each(record, attribute, value)
raise NotImplementedError