Move all the Active Record validations to Active Model
This commit is contained in:
parent
6ed42ebdff
commit
8828b2ca67
|
@ -1,5 +1,43 @@
|
|||
require 'active_model/observing'
|
||||
# disabled until they're tested
|
||||
# require 'active_model/callbacks'
|
||||
# require 'active_model/validations'
|
||||
require 'active_model/base'
|
||||
#--
|
||||
# Copyright (c) 2004-2009 David Heinemeier Hansson
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#++
|
||||
|
||||
begin
|
||||
require 'active_support'
|
||||
rescue LoadError
|
||||
activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
|
||||
if File.directory?(activesupport_path)
|
||||
$:.unshift activesupport_path
|
||||
require 'active_support'
|
||||
end
|
||||
end
|
||||
|
||||
module ActiveModel
|
||||
def self.load_all!
|
||||
[Base]
|
||||
end
|
||||
|
||||
autoload :Base, 'active_model/base'
|
||||
autoload :Validations, 'active_model/validations'
|
||||
autoload :Errors, 'active_model/errors'
|
||||
autoload :DeprecatedErrorMethods, 'active_model/deprecated_error_methods'
|
||||
end
|
||||
|
|
|
@ -1,37 +1,28 @@
|
|||
module ActiveModel
|
||||
module DeprecatedErrorMethods
|
||||
def on(attribute)
|
||||
ActiveSupport::Deprecation.warn "Errors#on have been deprecated, use Errors#[] instead"
|
||||
self[attribute]
|
||||
# ActiveSupport::Deprecation.warn "Errors#on have been deprecated, use Errors#[] instead"
|
||||
errors = self[attribute]
|
||||
errors.size < 2 ? errors.first : errors
|
||||
end
|
||||
|
||||
def on_base
|
||||
ActiveSupport::Deprecation.warn "Errors#on_base have been deprecated, use Errors#[:base] instead"
|
||||
# ActiveSupport::Deprecation.warn "Errors#on_base have been deprecated, use Errors#[:base] instead"
|
||||
on(:base)
|
||||
end
|
||||
|
||||
def add(attribute, msg = Errors.default_error_messages[:invalid])
|
||||
ActiveSupport::Deprecation.warn "Errors#add(attribute, msg) has been deprecated, use Errors#[attribute] << msg instead"
|
||||
self[attribute] << msg
|
||||
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#[:base] << msg instead"
|
||||
self[:base] << msg
|
||||
end
|
||||
|
||||
def invalid?(attribute)
|
||||
ActiveSupport::Deprecation.warn "Errors#invalid?(attribute) has been deprecated, use Errors#[attribute].any? instead"
|
||||
# ActiveSupport::Deprecation.warn "Errors#invalid?(attribute) has been deprecated, use Errors#[attribute].any? instead"
|
||||
self[attribute].any?
|
||||
end
|
||||
|
||||
def full_messages
|
||||
ActiveSupport::Deprecation.warn "Errors#full_messages has been deprecated, use Errors#to_a instead"
|
||||
to_a
|
||||
end
|
||||
|
||||
def each_full
|
||||
ActiveSupport::Deprecation.warn "Errors#each_full has been deprecated, use Errors#to_a.each instead"
|
||||
# ActiveSupport::Deprecation.warn "Errors#each_full has been deprecated, use Errors#to_a.each instead"
|
||||
to_a.each { |error| yield error }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,40 +1,27 @@
|
|||
module ActiveModel
|
||||
class Errors < Hash
|
||||
include DeprecatedErrorMethods
|
||||
|
||||
@@default_error_messages = {
|
||||
:inclusion => "is not included in the list",
|
||||
:exclusion => "is reserved",
|
||||
:invalid => "is invalid",
|
||||
:confirmation => "doesn't match confirmation",
|
||||
:accepted => "must be accepted",
|
||||
:empty => "can't be empty",
|
||||
:blank => "can't be blank",
|
||||
:too_long => "is too long (maximum is %d characters)",
|
||||
:too_short => "is too short (minimum is %d characters)",
|
||||
:wrong_length => "is the wrong length (should be %d characters)",
|
||||
:taken => "has already been taken",
|
||||
:not_a_number => "is not a number",
|
||||
:greater_than => "must be greater than %d",
|
||||
:greater_than_or_equal_to => "must be greater than or equal to %d",
|
||||
:equal_to => "must be equal to %d",
|
||||
:less_than => "must be less than %d",
|
||||
:less_than_or_equal_to => "must be less than or equal to %d",
|
||||
:odd => "must be odd",
|
||||
:even => "must be even"
|
||||
}
|
||||
|
||||
##
|
||||
# :singleton-method:
|
||||
# Holds a hash with all the default error messages that can be replaced by your own copy or localizations.
|
||||
cattr_accessor :default_error_messages
|
||||
|
||||
class << self
|
||||
def default_error_messages
|
||||
message = "Errors.default_error_messages has been deprecated. Please use I18n.translate('activerecord.errors.messages')."
|
||||
ActiveSupport::Deprecation.warn(message)
|
||||
|
||||
I18n.translate 'activerecord.errors.messages'
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(base)
|
||||
@base = base
|
||||
super()
|
||||
end
|
||||
|
||||
alias_method :get, :[]
|
||||
alias_method :set, :[]=
|
||||
|
||||
def [](attribute)
|
||||
if errors = get(attribute.to_sym)
|
||||
errors.size == 1 ? errors.first : errors
|
||||
errors
|
||||
else
|
||||
set(attribute.to_sym, [])
|
||||
end
|
||||
|
@ -55,17 +42,11 @@ module ActiveModel
|
|||
end
|
||||
|
||||
def to_a
|
||||
inject([]) do |errors_with_attributes, (attribute, errors)|
|
||||
if error.blank?
|
||||
errors_with_attributes
|
||||
else
|
||||
if attr == :base
|
||||
errors_with_attributes << error
|
||||
else
|
||||
errors_with_attributes << (attribute.to_s.humanize + " " + error)
|
||||
end
|
||||
end
|
||||
end
|
||||
full_messages
|
||||
end
|
||||
|
||||
def count
|
||||
to_a.size
|
||||
end
|
||||
|
||||
def to_xml(options={})
|
||||
|
@ -78,5 +59,106 @@ module ActiveModel
|
|||
to_a.each { |error| e.error(error) }
|
||||
end
|
||||
end
|
||||
|
||||
# Adds an error message (+messsage+) to the +attribute+, which will be returned on a call to <tt>on(attribute)</tt>
|
||||
# for the same attribute and ensure that this error object returns false when asked if <tt>empty?</tt>. More than one
|
||||
# error can be added to the same +attribute+ in which case an array will be returned on a call to <tt>on(attribute)</tt>.
|
||||
# If no +messsage+ is supplied, :invalid is assumed.
|
||||
# If +message+ is a Symbol, it will be translated, using the appropriate scope (see translate_error).
|
||||
def add(attribute, message = nil, options = {})
|
||||
message ||= :invalid
|
||||
message = generate_message(attribute, message, options) if message.is_a?(Symbol)
|
||||
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)
|
||||
[attributes].flatten.each do |attribute|
|
||||
value = @base.respond_to?(attribute.to_s) ? @base.send(attribute.to_s) : @base[attribute.to_s]
|
||||
is_empty = value.respond_to?(:empty?) ? value.empty? : false
|
||||
add(attribute, :empty, :default => custom_message) unless !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)
|
||||
[attributes].flatten.each do |attribute|
|
||||
value = @base.respond_to?(attribute.to_sym) ? @base.send(attribute.to_sym) : @base[attribute.to_sym]
|
||||
add(attribute, :blank, :default => custom_message) if value.blank?
|
||||
end
|
||||
end
|
||||
|
||||
# Returns all the full error messages in an array.
|
||||
#
|
||||
# class Company < ActiveRecord::Base
|
||||
# validates_presence_of :name, :address, :email
|
||||
# validates_length_of :name, :in => 5..30
|
||||
# end
|
||||
#
|
||||
# company = Company.create(:address => '123 First St.')
|
||||
# company.errors.full_messages # =>
|
||||
# ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Address can't be blank"]
|
||||
def full_messages(options = {})
|
||||
full_messages = []
|
||||
|
||||
each do |attribute, messages|
|
||||
next if messages.empty?
|
||||
|
||||
if attribute == :base
|
||||
messages.each {|m| full_messages << m }
|
||||
else
|
||||
attr_name = @base.class.human_attribute_name(attribute.to_s)
|
||||
prefix = attr_name + I18n.t('activerecord.errors.format.separator', :default => ' ')
|
||||
messages.each do |m|
|
||||
full_messages << "#{prefix}#{m}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
full_messages
|
||||
end
|
||||
|
||||
# Translates an error message in it's default scope (<tt>activerecord.errrors.messages</tt>).
|
||||
# Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>, if it's not there,
|
||||
# it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not there it returns the translation of the
|
||||
# default message (e.g. <tt>activerecord.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 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+, it looks for these translations:
|
||||
#
|
||||
# <ol>
|
||||
# <li><tt>activerecord.errors.models.admin.attributes.title.blank</tt></li>
|
||||
# <li><tt>activerecord.errors.models.admin.blank</tt></li>
|
||||
# <li><tt>activerecord.errors.models.user.attributes.title.blank</tt></li>
|
||||
# <li><tt>activerecord.errors.models.user.blank</tt></li>
|
||||
# <li><tt>activerecord.errors.messages.blank</tt></li>
|
||||
# <li>any default you provided through the +options+ hash (in the activerecord.errors scope)</li>
|
||||
# </ol>
|
||||
def generate_message(attribute, message = :invalid, options = {})
|
||||
message, options[:default] = options[:default], message if options[:default].is_a?(Symbol)
|
||||
|
||||
defaults = @base.class.self_and_descendants_from_active_record.map do |klass|
|
||||
[ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{message}",
|
||||
:"models.#{klass.name.underscore}.#{message}" ]
|
||||
end
|
||||
|
||||
defaults << options.delete(:default)
|
||||
defaults = defaults.compact.flatten << :"messages.#{message}"
|
||||
|
||||
key = defaults.shift
|
||||
value = @base.respond_to?(attribute) ? @base.send(attribute) : nil
|
||||
|
||||
options = { :default => defaults,
|
||||
:model => @base.class.human_name,
|
||||
:attribute => @base.class.human_attribute_name(attribute.to_s),
|
||||
:value => value,
|
||||
:scope => [:activerecord, :errors]
|
||||
}.merge(options)
|
||||
|
||||
I18n.translate(key, options)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -38,7 +38,7 @@ module ActiveModel
|
|||
# end
|
||||
#
|
||||
# This usage applies to +validate_on_create+ and +validate_on_update as well+.
|
||||
#
|
||||
|
||||
# Validates each attribute against a block.
|
||||
#
|
||||
# class Person < ActiveRecord::Base
|
||||
|
@ -48,7 +48,7 @@ module ActiveModel
|
|||
# end
|
||||
#
|
||||
# Options:
|
||||
# * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>)
|
||||
# * <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+.
|
||||
# * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
|
||||
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
||||
|
@ -83,7 +83,7 @@ module ActiveModel
|
|||
|
||||
# Returns the Errors object that holds all information about attribute error messages.
|
||||
def errors
|
||||
@errors ||= Errors.new
|
||||
@errors ||= Errors.new(self)
|
||||
end
|
||||
|
||||
# Runs all the specified validations and returns true if no errors were added otherwise false.
|
||||
|
@ -92,35 +92,52 @@ module ActiveModel
|
|||
|
||||
run_callbacks(:validate)
|
||||
|
||||
if responds_to?(:validate)
|
||||
ActiveSupport::Deprecations.warn "Base#validate has been deprecated, please use Base.validate :method instead"
|
||||
if respond_to?(:validate)
|
||||
# ActiveSupport::Deprecation.warn "Base#validate has been deprecated, please use Base.validate :method instead"
|
||||
validate
|
||||
end
|
||||
|
||||
if new_record?
|
||||
run_callbacks(:validate_on_create)
|
||||
|
||||
if responds_to?(:validate_on_create)
|
||||
ActiveSupport::Deprecations.warn(
|
||||
"Base#validate_on_create has been deprecated, please use Base.validate_on_create :method instead")
|
||||
if respond_to?(:validate_on_create)
|
||||
# ActiveSupport::Deprecation.warn "Base#validate_on_create has been deprecated, please use Base.validate_on_create :method instead"
|
||||
validate_on_create
|
||||
end
|
||||
else
|
||||
run_callbacks(:validate_on_update)
|
||||
|
||||
if responds_to?(:validate_on_update)
|
||||
ActiveSupport::Deprecations.warn(
|
||||
"Base#validate_on_update has been deprecated, please use Base.validate_on_update :method instead")
|
||||
if respond_to?(:validate_on_update)
|
||||
# ActiveSupport::Deprecation.warn "Base#validate_on_update has been deprecated, please use Base.validate_on_update :method instead"
|
||||
validate_on_update
|
||||
end
|
||||
end
|
||||
|
||||
errors.empty?
|
||||
end
|
||||
|
||||
# Performs the opposite of <tt>valid?</tt>. Returns true if errors were added, false otherwise.
|
||||
def invalid?
|
||||
!valid?
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Overwrite this method for validation checks on all saves and use <tt>Errors.add(field, msg)</tt> for invalid attributes.
|
||||
def validate
|
||||
end
|
||||
|
||||
# Overwrite this method for validation checks used only on creation.
|
||||
def validate_on_create
|
||||
end
|
||||
|
||||
# Overwrite this method for validation checks used only on updates.
|
||||
def validate_on_update
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Dir[File.dirname(__FILE__) + "/validations/*.rb"].sort.each do |path|
|
||||
filename = File.basename(path)
|
||||
require "active_model/validations/#{filename}"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,16 +8,16 @@ module ActiveModel
|
|||
# validates_acceptance_of :eula, :message => "must be abided"
|
||||
# end
|
||||
#
|
||||
# If the database column does not exist, the <tt>:terms_of_service</tt> attribute is entirely virtual. This check is
|
||||
# performed only if <tt>:terms_of_service</tt> 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>: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 typecasted from "1" to +true+ before validation.
|
||||
# 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.
|
||||
|
@ -25,19 +25,22 @@ module ActiveModel
|
|||
# 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_acceptance_of(*attr_names)
|
||||
configuration = { :message => ActiveRecord::Errors.default_error_messages[:accepted], :on => :save, :allow_nil => true, :accept => "1" }
|
||||
configuration = { :on => :save, :allow_nil => true, :accept => "1" }
|
||||
configuration.update(attr_names.extract_options!)
|
||||
|
||||
db_cols = begin
|
||||
column_names
|
||||
rescue ActiveRecord::StatementInvalid
|
||||
rescue Exception # To ignore both statement and connection errors
|
||||
[]
|
||||
end
|
||||
|
||||
names = attr_names.reject { |name| db_cols.include?(name.to_s) }
|
||||
attr_accessor(*names)
|
||||
|
||||
validates_each(attr_names,configuration) do |record, attr_name, value|
|
||||
record.errors.add(attr_name, configuration[:message]) unless value == configuration[:accept]
|
||||
unless value == configuration[:accept]
|
||||
record.errors.add(attr_name, :accepted, :default => configuration[:message])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,14 +18,14 @@ module ActiveModel
|
|||
# validates_associated :book
|
||||
# end
|
||||
#
|
||||
# ...this would specify a circular dependency and cause infinite recursion.
|
||||
# this would specify a circular dependency and cause infinite recursion.
|
||||
#
|
||||
# NOTE: This validation will not fail if the association hasn't been assigned. If you want to ensure that the association
|
||||
# is both present and guaranteed to be valid, you also need to use +validates_presence_of+.
|
||||
#
|
||||
# Configuration options:
|
||||
# * <tt>:message</tt> - A custom error message (default is: "is invalid")
|
||||
# * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>)
|
||||
# * <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.
|
||||
|
@ -33,12 +33,13 @@ module ActiveModel
|
|||
# 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_associated(*attr_names)
|
||||
configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save }
|
||||
configuration = { :on => :save }
|
||||
configuration.update(attr_names.extract_options!)
|
||||
|
||||
validates_each(attr_names, configuration) do |record, attr_name, value|
|
||||
record.errors.add(attr_name, configuration[:message]) unless
|
||||
(value.is_a?(Array) ? value : [value]).inject(true) { |v, r| (r.nil? || r.valid?) && v }
|
||||
unless (value.is_a?(Array) ? value : [value]).collect { |r| r.nil? || r.valid? }.all?
|
||||
record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,8 +21,8 @@ module ActiveModel
|
|||
# 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>: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.
|
||||
|
@ -30,13 +30,15 @@ module ActiveModel
|
|||
# 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)
|
||||
configuration = { :message => ActiveRecord::Errors.default_error_messages[:confirmation], :on => :save }
|
||||
configuration = { :on => :save }
|
||||
configuration.update(attr_names.extract_options!)
|
||||
|
||||
attr_accessor(*(attr_names.map { |n| "#{n}_confirmation" }))
|
||||
|
||||
validates_each(attr_names, configuration) do |record, attr_name, value|
|
||||
record.errors.add(attr_name, configuration[:message]) unless record.send("#{attr_name}_confirmation").nil? or value == record.send("#{attr_name}_confirmation")
|
||||
unless record.send("#{attr_name}_confirmation").nil? or value == record.send("#{attr_name}_confirmation")
|
||||
record.errors.add(attr_name, :confirmation, :default => configuration[:message])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,14 +6,14 @@ module ActiveModel
|
|||
# class Person < ActiveRecord::Base
|
||||
# validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here"
|
||||
# validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60"
|
||||
# validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension %s is not allowed"
|
||||
# validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension {{value}} is not allowed"
|
||||
# end
|
||||
#
|
||||
# Configuration options:
|
||||
# * <tt>:in</tt> - An enumerable object of items that the value shouldn't be part of
|
||||
# * <tt>:message</tt> - Specifies a custom error message (default is: "is reserved")
|
||||
# * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the attribute is +nil+ (default is: +false+)
|
||||
# * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the attribute is blank (default is: +false+)
|
||||
# * <tt>:in</tt> - An enumerable object of items that the value shouldn't be part of.
|
||||
# * <tt>:message</tt> - Specifies a custom error message (default is: "is reserved").
|
||||
# * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
|
||||
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
|
||||
# * <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.
|
||||
|
@ -21,7 +21,7 @@ module ActiveModel
|
|||
# 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_exclusion_of(*attr_names)
|
||||
configuration = { :message => ActiveRecord::Errors.default_error_messages[:exclusion], :on => :save }
|
||||
configuration = { :on => :save }
|
||||
configuration.update(attr_names.extract_options!)
|
||||
|
||||
enum = configuration[:in] || configuration[:within]
|
||||
|
@ -29,7 +29,9 @@ module ActiveModel
|
|||
raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?(:include?)
|
||||
|
||||
validates_each(attr_names, configuration) do |record, attr_name, value|
|
||||
record.errors.add(attr_name, configuration[:message] % value) if enum.include?(value)
|
||||
if enum.include?(value)
|
||||
record.errors.add(attr_name, :exclusion, :default => configuration[:message], :value => value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,11 +13,11 @@ module ActiveModel
|
|||
# A regular expression must be provided or else an exception will be raised.
|
||||
#
|
||||
# Configuration options:
|
||||
# * <tt>:message</tt> - A custom error message (default is: "is invalid")
|
||||
# * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the attribute is +nil+ (default is: +false+)
|
||||
# * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the attribute is blank (default is: +false+)
|
||||
# * <tt>:with</tt> - The regular expression used to validate the format with (note: must be supplied!)
|
||||
# * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>)
|
||||
# * <tt>:message</tt> - A custom error message (default is: "is invalid").
|
||||
# * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
|
||||
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
|
||||
# * <tt>:with</tt> - The regular expression used to validate the format with (note: must be supplied!).
|
||||
# * <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.
|
||||
|
@ -25,13 +25,15 @@ module ActiveModel
|
|||
# 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_format_of(*attr_names)
|
||||
configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save, :with => nil }
|
||||
configuration = { :on => :save, :with => nil }
|
||||
configuration.update(attr_names.extract_options!)
|
||||
|
||||
raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp)
|
||||
|
||||
validates_each(attr_names, configuration) do |record, attr_name, value|
|
||||
record.errors.add(attr_name, configuration[:message]) unless value.to_s =~ configuration[:with]
|
||||
unless value.to_s =~ configuration[:with]
|
||||
record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,14 +6,14 @@ module ActiveModel
|
|||
# class Person < ActiveRecord::Base
|
||||
# validates_inclusion_of :gender, :in => %w( m f )
|
||||
# validates_inclusion_of :age, :in => 0..99
|
||||
# validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension %s is not included in the list"
|
||||
# validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension {{value}} is not included in the list"
|
||||
# end
|
||||
#
|
||||
# Configuration options:
|
||||
# * <tt>:in</tt> - An enumerable object of available items
|
||||
# * <tt>:message</tt> - Specifies a custom error message (default is: "is not included in the list")
|
||||
# * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the attribute is null (default is: +false+)
|
||||
# * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the attribute is blank (default is: +false+)
|
||||
# * <tt>:in</tt> - An enumerable object of available items.
|
||||
# * <tt>:message</tt> - Specifies a custom error message (default is: "is not included in the list").
|
||||
# * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
|
||||
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
|
||||
# * <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.
|
||||
|
@ -21,7 +21,7 @@ module ActiveModel
|
|||
# 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_inclusion_of(*attr_names)
|
||||
configuration = { :message => ActiveRecord::Errors.default_error_messages[:inclusion], :on => :save }
|
||||
configuration = { :on => :save }
|
||||
configuration.update(attr_names.extract_options!)
|
||||
|
||||
enum = configuration[:in] || configuration[:within]
|
||||
|
@ -29,7 +29,9 @@ module ActiveModel
|
|||
raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?(:include?)
|
||||
|
||||
validates_each(attr_names, configuration) do |record, attr_name, value|
|
||||
record.errors.add(attr_name, configuration[:message] % value) unless enum.include?(value)
|
||||
unless enum.include?(value)
|
||||
record.errors.add(attr_name, :inclusion, :default => configuration[:message], :value => value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,44 +2,46 @@ module ActiveModel
|
|||
module Validations
|
||||
module ClassMethods
|
||||
ALL_RANGE_OPTIONS = [ :is, :within, :in, :minimum, :maximum ].freeze
|
||||
|
||||
|
||||
# Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time:
|
||||
#
|
||||
# class Person < ActiveRecord::Base
|
||||
# validates_length_of :first_name, :maximum => 30
|
||||
# validates_length_of :last_name, :maximum => 30, :message => "less than %d if you don't mind"
|
||||
# validates_length_of :first_name, :maximum=>30
|
||||
# validates_length_of :last_name, :maximum=>30, :message=>"less than {{count}} if you don't mind"
|
||||
# validates_length_of :fax, :in => 7..32, :allow_nil => true
|
||||
# validates_length_of :phone, :in => 7..32, :allow_blank => true
|
||||
# validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
|
||||
# validates_length_of :fav_bra_size, :minimum => 1, :too_short => "please enter at least %d character"
|
||||
# validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with %d characters... don't play me."
|
||||
# validates_length_of :fav_bra_size, :minimum => 1, :too_short => "please enter at least {{count}} character"
|
||||
# validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with {{count}} characters... don't play me."
|
||||
# validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least {{count}} words."), :tokenizer => lambda {|str| str.scan(/\w+/) }
|
||||
# end
|
||||
#
|
||||
# Configuration options:
|
||||
# * <tt>:minimum</tt> - The minimum size of the attribute
|
||||
# * <tt>:maximum</tt> - The maximum size of the attribute
|
||||
# * <tt>:is</tt> - The exact size of the attribute
|
||||
# * <tt>:within</tt> - A range specifying the minimum and maximum size of the attribute
|
||||
# * <tt>:in</tt> - A synonym (or alias) for <tt>:within</tt>
|
||||
# * <tt>:minimum</tt> - The minimum size of the attribute.
|
||||
# * <tt>:maximum</tt> - The maximum size of the attribute.
|
||||
# * <tt>:is</tt> - The exact size of the attribute.
|
||||
# * <tt>:within</tt> - A range specifying the minimum and maximum size of the attribute.
|
||||
# * <tt>:in</tt> - A synonym(or alias) for <tt>:within</tt>.
|
||||
# * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
|
||||
# * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
|
||||
# * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is %d characters)")
|
||||
# * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is %d characters)")
|
||||
# * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method and the attribute is the wrong size (default is: "is the wrong length (should be %d characters)")
|
||||
# * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>, <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate <tt>:too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message
|
||||
# * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>)
|
||||
# * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is {{count}} characters)").
|
||||
# * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is {{count}} characters)").
|
||||
# * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method and the attribute is the wrong size (default is: "is the wrong length (should be {{count}} characters)").
|
||||
# * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>, <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
|
||||
# * <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.
|
||||
# * <tt>:tokenizer</tt> - Specifies how to split up the attribute string. (e.g. <tt>:tokenizer => lambda {|str| str.scan(/\w+/)}</tt> to
|
||||
# count words as in above example.)
|
||||
# Defaults to <tt>lambda{ |value| value.split(//) }</tt> which counts individual characters.
|
||||
def validates_length_of(*attrs)
|
||||
# Merge given options with defaults.
|
||||
options = {
|
||||
:too_long => ActiveRecord::Errors.default_error_messages[:too_long],
|
||||
:too_short => ActiveRecord::Errors.default_error_messages[:too_short],
|
||||
:wrong_length => ActiveRecord::Errors.default_error_messages[:wrong_length]
|
||||
:tokenizer => lambda {|value| value.split(//)}
|
||||
}.merge(DEFAULT_VALIDATION_OPTIONS)
|
||||
options.update(attrs.extract_options!.symbolize_keys)
|
||||
|
||||
|
@ -57,35 +59,33 @@ module ActiveModel
|
|||
# Get range option and value.
|
||||
option = range_options.first
|
||||
option_value = options[range_options.first]
|
||||
key = {:is => :wrong_length, :minimum => :too_short, :maximum => :too_long}[option]
|
||||
custom_message = options[:message] || options[key]
|
||||
|
||||
case option
|
||||
when :within, :in
|
||||
raise ArgumentError, ":#{option} must be a Range" unless option_value.is_a?(Range)
|
||||
when :within, :in
|
||||
raise ArgumentError, ":#{option} must be a Range" unless option_value.is_a?(Range)
|
||||
|
||||
too_short = options[:too_short] % option_value.begin
|
||||
too_long = options[:too_long] % option_value.end
|
||||
|
||||
validates_each(attrs, options) do |record, attr, value|
|
||||
value = value.split(//) if value.kind_of?(String)
|
||||
if value.nil? or value.size < option_value.begin
|
||||
record.errors.add(attr, too_short)
|
||||
elsif value.size > option_value.end
|
||||
record.errors.add(attr, too_long)
|
||||
end
|
||||
validates_each(attrs, options) do |record, attr, value|
|
||||
value = options[:tokenizer].call(value) if value.kind_of?(String)
|
||||
if value.nil? or value.size < option_value.begin
|
||||
record.errors.add(attr, :too_short, :default => custom_message || options[:too_short], :count => option_value.begin)
|
||||
elsif value.size > option_value.end
|
||||
record.errors.add(attr, :too_long, :default => custom_message || options[:too_long], :count => option_value.end)
|
||||
end
|
||||
when :is, :minimum, :maximum
|
||||
raise ArgumentError, ":#{option} must be a nonnegative Integer" unless option_value.is_a?(Integer) and option_value >= 0
|
||||
end
|
||||
when :is, :minimum, :maximum
|
||||
raise ArgumentError, ":#{option} must be a nonnegative Integer" unless option_value.is_a?(Integer) and option_value >= 0
|
||||
|
||||
# Declare different validations per option.
|
||||
validity_checks = { :is => "==", :minimum => ">=", :maximum => "<=" }
|
||||
message_options = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }
|
||||
# Declare different validations per option.
|
||||
validity_checks = { :is => "==", :minimum => ">=", :maximum => "<=" }
|
||||
|
||||
message = (options[:message] || options[message_options[option]]) % option_value
|
||||
|
||||
validates_each(attrs, options) do |record, attr, value|
|
||||
value = value.split(//) if value.kind_of?(String)
|
||||
record.errors.add(attr, message) unless !value.nil? and value.size.method(validity_checks[option])[option_value]
|
||||
validates_each(attrs, options) do |record, attr, value|
|
||||
value = options[:tokenizer].call(value) if value.kind_of?(String)
|
||||
unless !value.nil? and value.size.method(validity_checks[option])[option_value]
|
||||
record.errors.add(attr, key, :default => custom_message, :count => option_value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -5,10 +5,9 @@ module ActiveModel
|
|||
:equal_to => '==', :less_than => '<', :less_than_or_equal_to => '<=',
|
||||
:odd => 'odd?', :even => 'even?' }.freeze
|
||||
|
||||
|
||||
# Validates whether the value of the specified attribute is numeric by trying to convert it to
|
||||
# a float with Kernel.Float (if <tt>integer</tt> is false) or applying it to the regular expression
|
||||
# <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>integer</tt> is true).
|
||||
# a float with Kernel.Float (if <tt>only_integer</tt> is false) or applying it to the regular expression
|
||||
# <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>only_integer</tt> is set to true).
|
||||
#
|
||||
# class Person < ActiveRecord::Base
|
||||
# validates_numericality_of :value, :on => :create
|
||||
|
@ -50,15 +49,15 @@ module ActiveModel
|
|||
|
||||
if configuration[:only_integer]
|
||||
unless raw_value.to_s =~ /\A[+-]?\d+\Z/
|
||||
record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[:not_a_number])
|
||||
record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message])
|
||||
next
|
||||
end
|
||||
raw_value = raw_value.to_i
|
||||
else
|
||||
begin
|
||||
raw_value = Kernel.Float(raw_value.to_s)
|
||||
begin
|
||||
raw_value = Kernel.Float(raw_value)
|
||||
rescue ArgumentError, TypeError
|
||||
record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[:not_a_number])
|
||||
record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message])
|
||||
next
|
||||
end
|
||||
end
|
||||
|
@ -66,11 +65,13 @@ module ActiveModel
|
|||
numericality_options.each do |option|
|
||||
case option
|
||||
when :odd, :even
|
||||
record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[option]) unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[]
|
||||
unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[]
|
||||
record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message])
|
||||
end
|
||||
else
|
||||
message = configuration[:message] || ActiveRecord::Errors.default_error_messages[option]
|
||||
message = message % configuration[option] if configuration[option]
|
||||
record.errors.add(attr_name, message) unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]]
|
||||
unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]]
|
||||
record.errors.add(attr_name, option, :default => configuration[:message], :value => raw_value, :count => configuration[option])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,28 +7,26 @@ module ActiveModel
|
|||
# validates_presence_of :first_name
|
||||
# end
|
||||
#
|
||||
# The +first_name+ attribute must be in the object and it cannot be blank.
|
||||
# The first_name attribute must be in the object and it cannot be blank.
|
||||
#
|
||||
# If you want to validate the presence of a boolean field (where the real values are +true+ and +false+),
|
||||
# you will want to use
|
||||
# If you want to validate the presence of a boolean field (where the real values are true and false),
|
||||
# you will want to use <tt>validates_inclusion_of :field_name, :in => [true, false]</tt>.
|
||||
#
|
||||
# validates_inclusion_of :field_name, :in => [true, false]
|
||||
#
|
||||
# This is due to the way Object#blank? handles boolean values:
|
||||
#
|
||||
# false.blank? # => true
|
||||
# This is due to the way Object#blank? handles boolean values: <tt>false.blank? # => true</tt>.
|
||||
#
|
||||
# Configuration options:
|
||||
# * <tt>:message</tt> - A custom error message (default is: "can't be blank")
|
||||
# * <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.
|
||||
# * <tt>message</tt> - A custom error message (default is: "can't be blank").
|
||||
# * <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_presence_of(*attr_names)
|
||||
configuration = { :message => ActiveRecord::Errors.default_error_messages[:blank], :on => :save }
|
||||
configuration = { :on => :save }
|
||||
configuration.update(attr_names.extract_options!)
|
||||
|
||||
# can't use validates_each here, because it cannot cope with nonexistent attributes,
|
||||
|
|
|
@ -18,24 +18,84 @@ module ActiveModel
|
|||
# When the record is created, a check is performed to make sure that no record exists in the database with the given value for the specified
|
||||
# attribute (that maps to a column). When the record is updated, the same check is made but disregarding the record itself.
|
||||
#
|
||||
# Because this check is performed outside the database there is still a chance that duplicate values
|
||||
# will be inserted in two parallel transactions. To guarantee against this you should create a
|
||||
# unique index on the field. See +add_index+ for more information.
|
||||
#
|
||||
# Configuration options:
|
||||
# * <tt>:message</tt> - Specifies a custom error message (default is: "has already been taken")
|
||||
# * <tt>:message</tt> - Specifies a custom error message (default is: "has already been taken").
|
||||
# * <tt>:scope</tt> - One or more columns by which to limit the scope of the uniqueness constraint.
|
||||
# * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by non-text columns (+true+ by default).
|
||||
# * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the attribute is +nil+ (default is: +false+)
|
||||
# * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the attribute is blank (default is: +false+)
|
||||
# * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by non-text columns (+true+ by default).
|
||||
# * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
|
||||
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
|
||||
# * <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.
|
||||
#
|
||||
# === Concurrency and integrity
|
||||
#
|
||||
# Using this validation method in conjunction with ActiveRecord::Base#save
|
||||
# does not guarantee the absence of duplicate record insertions, because
|
||||
# uniqueness checks on the application level are inherently prone to race
|
||||
# conditions. For example, suppose that two users try to post a Comment at
|
||||
# the same time, and a Comment's title must be unique. At the database-level,
|
||||
# the actions performed by these users could be interleaved in the following manner:
|
||||
#
|
||||
# User 1 | User 2
|
||||
# ------------------------------------+--------------------------------------
|
||||
# # User 1 checks whether there's |
|
||||
# # already a comment with the title |
|
||||
# # 'My Post'. This is not the case. |
|
||||
# SELECT * FROM comments |
|
||||
# WHERE title = 'My Post' |
|
||||
# |
|
||||
# | # User 2 does the same thing and also
|
||||
# | # infers that his title is unique.
|
||||
# | SELECT * FROM comments
|
||||
# | WHERE title = 'My Post'
|
||||
# |
|
||||
# # User 1 inserts his comment. |
|
||||
# INSERT INTO comments |
|
||||
# (title, content) VALUES |
|
||||
# ('My Post', 'hi!') |
|
||||
# |
|
||||
# | # User 2 does the same thing.
|
||||
# | INSERT INTO comments
|
||||
# | (title, content) VALUES
|
||||
# | ('My Post', 'hello!')
|
||||
# |
|
||||
# | # ^^^^^^
|
||||
# | # Boom! We now have a duplicate
|
||||
# | # title!
|
||||
#
|
||||
# This could even happen if you use transactions with the 'serializable'
|
||||
# isolation level. There are several ways to get around this problem:
|
||||
# - By locking the database table before validating, and unlocking it after
|
||||
# saving. However, table locking is very expensive, and thus not
|
||||
# recommended.
|
||||
# - By locking a lock file before validating, and unlocking it after saving.
|
||||
# This does not work if you've scaled your Rails application across
|
||||
# multiple web servers (because they cannot share lock files, or cannot
|
||||
# do that efficiently), and thus not recommended.
|
||||
# - Creating a unique index on the field, by using
|
||||
# ActiveRecord::ConnectionAdapters::SchemaStatements#add_index. In the
|
||||
# rare case that a race condition occurs, the database will guarantee
|
||||
# the field's uniqueness.
|
||||
#
|
||||
# When the database catches such a duplicate insertion,
|
||||
# ActiveRecord::Base#save will raise an ActiveRecord::StatementInvalid
|
||||
# exception. You can either choose to let this error propagate (which
|
||||
# will result in the default Rails exception page being shown), or you
|
||||
# can catch it and restart the transaction (e.g. by telling the user
|
||||
# that the title already exists, and asking him to re-enter the title).
|
||||
# This technique is also known as optimistic concurrency control:
|
||||
# http://en.wikipedia.org/wiki/Optimistic_concurrency_control
|
||||
#
|
||||
# Active Record currently provides no way to distinguish unique
|
||||
# index constraint errors from other types of database errors, so you
|
||||
# will have to parse the (database-specific) exception message to detect
|
||||
# such a case.
|
||||
def validates_uniqueness_of(*attr_names)
|
||||
configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken] }
|
||||
configuration = { :case_sensitive => true }
|
||||
configuration.update(attr_names.extract_options!)
|
||||
|
||||
validates_each(attr_names,configuration) do |record, attr_name, value|
|
||||
|
@ -53,20 +113,31 @@ module ActiveModel
|
|||
# class (which has a database table to query from).
|
||||
finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? }
|
||||
|
||||
if value.nil? || (configuration[:case_sensitive] || !finder_class.columns_hash[attr_name.to_s].text?)
|
||||
condition_sql = "#{record.class.quoted_table_name}.#{attr_name} #{attribute_condition(value)}"
|
||||
column = finder_class.columns_hash[attr_name.to_s]
|
||||
|
||||
if value.nil?
|
||||
comparison_operator = "IS ?"
|
||||
elsif column.text?
|
||||
comparison_operator = "#{connection.case_sensitive_equality_operator} ?"
|
||||
value = column.limit ? value.to_s[0, column.limit] : value.to_s
|
||||
else
|
||||
comparison_operator = "= ?"
|
||||
end
|
||||
|
||||
sql_attribute = "#{record.class.quoted_table_name}.#{connection.quote_column_name(attr_name)}"
|
||||
|
||||
if value.nil? || (configuration[:case_sensitive] || !column.text?)
|
||||
condition_sql = "#{sql_attribute} #{comparison_operator}"
|
||||
condition_params = [value]
|
||||
else
|
||||
# sqlite has case sensitive SELECT query, while MySQL/Postgresql don't.
|
||||
# Hence, this is needed only for sqlite.
|
||||
condition_sql = "LOWER(#{record.class.quoted_table_name}.#{attr_name}) #{attribute_condition(value)}"
|
||||
condition_params = [value.downcase]
|
||||
condition_sql = "LOWER(#{sql_attribute}) #{comparison_operator}"
|
||||
condition_params = [value.mb_chars.downcase]
|
||||
end
|
||||
|
||||
if scope = configuration[:scope]
|
||||
Array(scope).map do |scope_item|
|
||||
scope_value = record.send(scope_item)
|
||||
condition_sql << " AND #{record.class.quoted_table_name}.#{scope_item} #{attribute_condition(scope_value)}"
|
||||
condition_sql << " AND " << attribute_condition("#{record.class.quoted_table_name}.#{scope_item}", scope_value)
|
||||
condition_params << scope_value
|
||||
end
|
||||
end
|
||||
|
@ -76,26 +147,10 @@ module ActiveModel
|
|||
condition_params << record.send(:id)
|
||||
end
|
||||
|
||||
results = finder_class.with_exclusive_scope do
|
||||
connection.select_all(
|
||||
construct_finder_sql(
|
||||
:select => attr_name,
|
||||
:from => finder_class.quoted_table_name,
|
||||
:conditions => [condition_sql, *condition_params]
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
unless results.length.zero?
|
||||
found = true
|
||||
|
||||
# As MySQL/Postgres don't have case sensitive SELECT queries, we try to find duplicate
|
||||
# column in ruby when case sensitive option
|
||||
if configuration[:case_sensitive] && finder_class.columns_hash[attr_name.to_s].text?
|
||||
found = results.any? { |a| a[attr_name.to_s] == value }
|
||||
finder_class.with_exclusive_scope do
|
||||
if finder_class.exists?([condition_sql, *condition_params])
|
||||
record.errors.add(attr_name, :taken, :default => configuration[:message], :value => value)
|
||||
end
|
||||
|
||||
record.errors.add(attr_name, configuration[:message]) if found
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -31,6 +31,15 @@ rescue LoadError
|
|||
end
|
||||
end
|
||||
|
||||
begin
|
||||
require 'active_model'
|
||||
rescue LoadError
|
||||
$:.unshift "#{File.dirname(__FILE__)}/../../activemodel/lib"
|
||||
require 'active_model'
|
||||
else
|
||||
|
||||
end
|
||||
|
||||
module ActiveRecord
|
||||
# TODO: Review explicit loads to see if they will automatically be handled by the initilizer.
|
||||
def self.load_all!
|
||||
|
|
|
@ -251,7 +251,7 @@ module ActiveRecord
|
|||
unless association.marked_for_destruction?
|
||||
association.errors.each do |attribute, message|
|
||||
attribute = "#{reflection.name}_#{attribute}"
|
||||
errors.add(attribute, message) unless errors.on(attribute)
|
||||
errors[attribute] << message if errors[attribute].empty?
|
||||
end
|
||||
end
|
||||
else
|
||||
|
|
|
@ -3136,7 +3136,7 @@ module ActiveRecord #:nodoc:
|
|||
|
||||
Base.class_eval do
|
||||
extend QueryCache::ClassMethods
|
||||
include Validations
|
||||
include ::ActiveModel::Validations, Validations
|
||||
include Locking::Optimistic, Locking::Pessimistic
|
||||
include AttributeMethods
|
||||
include Dirty
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -713,8 +713,8 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
|
|||
|
||||
def test_should_automatically_validate_the_associated_model
|
||||
@ship.pirate.catchphrase = ''
|
||||
assert !@ship.valid?
|
||||
assert !@ship.errors.on(:pirate_catchphrase).blank?
|
||||
assert @ship.invalid?
|
||||
assert @ship.errors[:pirate_catchphrase].any?
|
||||
end
|
||||
|
||||
def test_should_merge_errors_on_the_associated_model_onto_the_parent_even_if_it_is_not_valid
|
||||
|
|
|
@ -39,8 +39,8 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
def test_default_error_messages_is_deprecated
|
||||
assert_deprecated('ActiveRecord::Errors.default_error_messages') do
|
||||
ActiveRecord::Errors.default_error_messages
|
||||
assert_deprecated('Errors.default_error_messages') do
|
||||
ActiveModel::Errors.default_error_messages
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -70,7 +70,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
# ActiveRecord::Errors
|
||||
# ActiveModel::Errors
|
||||
def test_errors_generate_message_translates_custom_model_attribute_key
|
||||
|
||||
I18n.expects(:translate).with(
|
||||
|
@ -161,7 +161,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
def test_errors_full_messages_translates_human_attribute_name_for_model_attributes
|
||||
@topic.errors.instance_variable_set :@errors, { 'title' => ['empty'] }
|
||||
@topic.errors.add('title', 'empty')
|
||||
I18n.expects(:translate).with(:"topic.title", :default => ['Title'], :scope => [:activerecord, :attributes], :count => 1).returns('Title')
|
||||
@topic.errors.full_messages :locale => 'en'
|
||||
end
|
||||
|
|
|
@ -75,7 +75,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
r.title = "There's no content!"
|
||||
assert !r.valid?
|
||||
assert r.errors.invalid?("content"), "A reply without content should mark that attribute as invalid"
|
||||
assert_equal "Empty", r.errors.on("content"), "A reply without content should contain an error"
|
||||
assert_equal ["Empty"], r.errors["content"], "A reply without content should contain an error"
|
||||
assert_equal 1, r.errors.count
|
||||
end
|
||||
|
||||
|
@ -84,10 +84,10 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
assert !r.valid?
|
||||
|
||||
assert r.errors.invalid?("title"), "A reply without title should mark that attribute as invalid"
|
||||
assert_equal "Empty", r.errors.on("title"), "A reply without title should contain an error"
|
||||
assert_equal ["Empty"], r.errors["title"], "A reply without title should contain an error"
|
||||
|
||||
assert r.errors.invalid?("content"), "A reply without content should mark that attribute as invalid"
|
||||
assert_equal "Empty", r.errors.on("content"), "A reply without content should contain an error"
|
||||
assert_equal ["Empty"], r.errors["content"], "A reply without content should contain an error"
|
||||
|
||||
assert_equal 2, r.errors.count
|
||||
end
|
||||
|
@ -97,7 +97,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
r.title = "Wrong Create"
|
||||
assert !r.valid?
|
||||
assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid"
|
||||
assert_equal "is Wrong Create", r.errors.on("title"), "A reply with a bad content should contain an error"
|
||||
assert_equal "is Wrong Create", r.errors.on(:title), "A reply with a bad content should contain an error"
|
||||
end
|
||||
|
||||
def test_error_on_update
|
||||
|
@ -110,7 +110,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
assert !r.save, "Second save should fail"
|
||||
|
||||
assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid"
|
||||
assert_equal "is Wrong Update", r.errors.on("title"), "A reply with a bad content should contain an error"
|
||||
assert_equal "is Wrong Update", r.errors.on(:title), "A reply with a bad content should contain an error"
|
||||
end
|
||||
|
||||
def test_invalid_record_exception
|
||||
|
@ -177,7 +177,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
r.save
|
||||
|
||||
errors = []
|
||||
r.errors.each { |attr, msg| errors << [attr, msg] }
|
||||
r.errors.each {|attr, messages| errors << [attr.to_s, messages] }
|
||||
|
||||
assert errors.include?(["title", "Empty"])
|
||||
assert errors.include?(["content", "Empty"])
|
||||
|
@ -189,8 +189,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
r.content = "Mismatch"
|
||||
r.save
|
||||
|
||||
errors = []
|
||||
r.errors.each_full { |error| errors << error }
|
||||
errors = r.errors.to_a
|
||||
|
||||
assert_equal "Title is Wrong Create", errors[0]
|
||||
assert_equal "Title is Content Mismatch", errors[1]
|
||||
|
@ -234,8 +233,8 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t = Topic.new("title" => "valid", "content" => "whatever")
|
||||
assert !t.save
|
||||
assert_equal 4, hits
|
||||
assert_equal %w(gotcha gotcha), t.errors.on(:title)
|
||||
assert_equal %w(gotcha gotcha), t.errors.on(:content)
|
||||
assert_equal %w(gotcha gotcha), t.errors[:title]
|
||||
assert_equal %w(gotcha gotcha), t.errors[:content]
|
||||
end
|
||||
|
||||
def test_no_title_confirmation
|
||||
|
@ -277,7 +276,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
|
||||
t = Topic.create("title" => "We should be confirmed","terms_of_service" => "")
|
||||
assert !t.save
|
||||
assert_equal "must be accepted", t.errors.on(:terms_of_service)
|
||||
assert_equal ["must be accepted"], t.errors[:terms_of_service]
|
||||
|
||||
t.terms_of_service = "1"
|
||||
assert t.save
|
||||
|
@ -289,7 +288,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
|
||||
t = Topic.create("title" => "We should be confirmed","eula" => "")
|
||||
assert !t.save
|
||||
assert_equal "must be abided", t.errors.on(:eula)
|
||||
assert_equal ["must be abided"], t.errors[:eula]
|
||||
|
||||
t.eula = "1"
|
||||
assert t.save
|
||||
|
@ -300,7 +299,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
|
||||
t = Topic.create("title" => "We should be confirmed", "terms_of_service" => "")
|
||||
assert !t.save
|
||||
assert_equal "must be accepted", t.errors.on(:terms_of_service)
|
||||
assert_equal ["must be accepted"], t.errors[:terms_of_service]
|
||||
|
||||
t.terms_of_service = "I agree."
|
||||
assert t.save
|
||||
|
@ -328,14 +327,14 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
|
||||
t = Topic.create
|
||||
assert !t.save
|
||||
assert_equal "can't be blank", t.errors.on(:title)
|
||||
assert_equal "can't be blank", t.errors.on(:content)
|
||||
assert_equal ["can't be blank"], t.errors[:title]
|
||||
assert_equal ["can't be blank"], t.errors[:content]
|
||||
|
||||
t.title = "something"
|
||||
t.content = " "
|
||||
|
||||
assert !t.save
|
||||
assert_equal "can't be blank", t.errors.on(:content)
|
||||
assert_equal ["can't be blank"], t.errors[:content]
|
||||
|
||||
t.content = "like stuff"
|
||||
|
||||
|
@ -354,7 +353,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t2 = Topic.new("title" => "I'm unique!")
|
||||
assert !t2.valid?, "Shouldn't be valid"
|
||||
assert !t2.save, "Shouldn't save t2 as unique"
|
||||
assert_equal "has already been taken", t2.errors.on(:title)
|
||||
assert_equal ["has already been taken"], t2.errors[:title]
|
||||
|
||||
t2.title = "Now Im really also unique"
|
||||
assert t2.save, "Should now save t2 as unique"
|
||||
|
@ -441,15 +440,15 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t2 = Topic.new("title" => "I'm UNIQUE!", :parent_id => 1)
|
||||
assert !t2.valid?, "Shouldn't be valid"
|
||||
assert !t2.save, "Shouldn't save t2 as unique"
|
||||
assert t2.errors.on(:title)
|
||||
assert t2.errors.on(:parent_id)
|
||||
assert_equal "has already been taken", t2.errors.on(:title)
|
||||
assert t2.errors[:title].any?
|
||||
assert t2.errors[:parent_id].any?
|
||||
assert_equal ["has already been taken"], t2.errors[:title]
|
||||
|
||||
t2.title = "I'm truly UNIQUE!"
|
||||
assert !t2.valid?, "Shouldn't be valid"
|
||||
assert !t2.save, "Shouldn't save t2 as unique"
|
||||
assert_nil t2.errors.on(:title)
|
||||
assert t2.errors.on(:parent_id)
|
||||
assert t2.errors[:title].empty?
|
||||
assert t2.errors[:parent_id].any?
|
||||
|
||||
t2.parent_id = 4
|
||||
assert t2.save, "Should now save t2 as unique"
|
||||
|
@ -484,16 +483,16 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t2 = Topic.new("title" => "I'M UNIQUE!")
|
||||
assert t2.valid?, "Should be valid"
|
||||
assert t2.save, "Should save t2 as unique"
|
||||
assert !t2.errors.on(:title)
|
||||
assert !t2.errors.on(:parent_id)
|
||||
assert_not_equal "has already been taken", t2.errors.on(:title)
|
||||
assert t2.errors[:title].empty?
|
||||
assert t2.errors[:parent_id].empty?
|
||||
assert_not_equal ["has already been taken"], t2.errors[:title]
|
||||
|
||||
t3 = Topic.new("title" => "I'M uNiQUe!")
|
||||
assert t3.valid?, "Should be valid"
|
||||
assert t3.save, "Should save t2 as unique"
|
||||
assert !t3.errors.on(:title)
|
||||
assert !t3.errors.on(:parent_id)
|
||||
assert_not_equal "has already been taken", t3.errors.on(:title)
|
||||
assert t3.errors[:title].empty?
|
||||
assert t3.errors[:parent_id].empty?
|
||||
assert_not_equal ["has already been taken"], t3.errors[:title]
|
||||
end
|
||||
|
||||
def test_validate_case_sensitive_uniqueness_with_attribute_passed_as_integer
|
||||
|
@ -502,13 +501,13 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
|
||||
t2 = Topic.new('title' => 101)
|
||||
assert !t2.valid?
|
||||
assert t2.errors.on(:title)
|
||||
assert t2.errors[:title]
|
||||
end
|
||||
|
||||
def test_validate_uniqueness_with_non_standard_table_names
|
||||
i1 = WarehouseThing.create(:value => 1000)
|
||||
assert !i1.valid?, "i1 should not be valid"
|
||||
assert i1.errors.on(:value), "Should not be empty"
|
||||
assert i1.errors[:value].any?, "Should not be empty"
|
||||
end
|
||||
|
||||
def test_validates_uniqueness_inside_with_scope
|
||||
|
@ -546,26 +545,26 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
# Should use validation from base class (which is abstract)
|
||||
w2 = IneptWizard.new(:name => "Rincewind", :city => "Quirm")
|
||||
assert !w2.valid?, "w2 shouldn't be valid"
|
||||
assert w2.errors.on(:name), "Should have errors for name"
|
||||
assert_equal "has already been taken", w2.errors.on(:name), "Should have uniqueness message for name"
|
||||
assert w2.errors[:name].any?, "Should have errors for name"
|
||||
assert_equal ["has already been taken"], w2.errors[:name], "Should have uniqueness message for name"
|
||||
|
||||
w3 = Conjurer.new(:name => "Rincewind", :city => "Quirm")
|
||||
assert !w3.valid?, "w3 shouldn't be valid"
|
||||
assert w3.errors.on(:name), "Should have errors for name"
|
||||
assert_equal "has already been taken", w3.errors.on(:name), "Should have uniqueness message for name"
|
||||
assert w3.errors[:name].any?, "Should have errors for name"
|
||||
assert_equal ["has already been taken"], w3.errors[:name], "Should have uniqueness message for name"
|
||||
|
||||
w4 = Conjurer.create(:name => "The Amazing Bonko", :city => "Quirm")
|
||||
assert w4.valid?, "Saving w4"
|
||||
|
||||
w5 = Thaumaturgist.new(:name => "The Amazing Bonko", :city => "Lancre")
|
||||
assert !w5.valid?, "w5 shouldn't be valid"
|
||||
assert w5.errors.on(:name), "Should have errors for name"
|
||||
assert_equal "has already been taken", w5.errors.on(:name), "Should have uniqueness message for name"
|
||||
assert w5.errors[:name].any?, "Should have errors for name"
|
||||
assert_equal ["has already been taken"], w5.errors[:name], "Should have uniqueness message for name"
|
||||
|
||||
w6 = Thaumaturgist.new(:name => "Mustrum Ridcully", :city => "Quirm")
|
||||
assert !w6.valid?, "w6 shouldn't be valid"
|
||||
assert w6.errors.on(:city), "Should have errors for city"
|
||||
assert_equal "has already been taken", w6.errors.on(:city), "Should have uniqueness message for city"
|
||||
assert w6.errors[:city].any?, "Should have errors for city"
|
||||
assert_equal ["has already been taken"], w6.errors[:city], "Should have uniqueness message for city"
|
||||
end
|
||||
|
||||
def test_validate_format
|
||||
|
@ -574,13 +573,13 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t = Topic.create("title" => "i'm incorrect", "content" => "Validation macros rule!")
|
||||
assert !t.valid?, "Shouldn't be valid"
|
||||
assert !t.save, "Shouldn't save because it's invalid"
|
||||
assert_equal "is bad data", t.errors.on(:title)
|
||||
assert_nil t.errors.on(:content)
|
||||
assert_equal ["is bad data"], t.errors[:title]
|
||||
assert t.errors[:content].empty?
|
||||
|
||||
t.title = "Validation macros rule!"
|
||||
|
||||
assert t.save
|
||||
assert_nil t.errors.on(:title)
|
||||
assert t.errors[:title].empty?
|
||||
|
||||
assert_raise(ArgumentError) { Topic.validates_format_of(:title, :content) }
|
||||
end
|
||||
|
@ -600,8 +599,8 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t = Topic.create("title" => "72x", "content" => "6789")
|
||||
assert !t.valid?, "Shouldn't be valid"
|
||||
assert !t.save, "Shouldn't save because it's invalid"
|
||||
assert_equal "is bad data", t.errors.on(:title)
|
||||
assert_nil t.errors.on(:content)
|
||||
assert_equal ["is bad data"], t.errors[:title]
|
||||
assert t.errors[:content].empty?
|
||||
|
||||
t.title = "-11"
|
||||
assert !t.valid?, "Shouldn't be valid"
|
||||
|
@ -618,7 +617,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t.title = "1"
|
||||
|
||||
assert t.save
|
||||
assert_nil t.errors.on(:title)
|
||||
assert t.errors[:title].empty?
|
||||
end
|
||||
|
||||
def test_validate_format_with_formatted_message
|
||||
|
@ -639,7 +638,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t.title = "uhoh"
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "is not included in the list", t.errors["title"]
|
||||
assert_equal "is not included in the list", t.errors.on(:title)
|
||||
|
||||
assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => nil ) }
|
||||
assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => 0) }
|
||||
|
@ -692,7 +691,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t = Topic.create("title" => "uhoh", "content" => "abc")
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "option uhoh is not in the list", t.errors["title"]
|
||||
assert_equal "option uhoh is not in the list", t.errors.on(:title)
|
||||
end
|
||||
|
||||
def test_numericality_with_allow_nil_and_getter_method
|
||||
|
@ -719,7 +718,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t = Topic.create("title" => "monkey")
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "option monkey is restricted", t.errors["title"]
|
||||
assert_equal "option monkey is restricted", t.errors.on(:title)
|
||||
end
|
||||
|
||||
def test_validates_length_of_using_minimum
|
||||
|
@ -731,17 +730,17 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t.title = "not"
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
|
||||
assert_equal "is too short (minimum is 5 characters)", t.errors.on(:title)
|
||||
|
||||
t.title = ""
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
|
||||
assert_equal "is too short (minimum is 5 characters)", t.errors.on(:title)
|
||||
|
||||
t.title = nil
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
|
||||
assert_equal ["is too short (minimum is 5 characters)"], t.errors["title"]
|
||||
end
|
||||
|
||||
def test_optionally_validates_length_of_using_minimum
|
||||
|
@ -763,7 +762,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t.title = "notvalid"
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
|
||||
assert_equal "is too long (maximum is 5 characters)", t.errors.on(:title)
|
||||
|
||||
t.title = ""
|
||||
assert t.valid?
|
||||
|
@ -817,7 +816,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t = Topic.create("title" => "thisisnotvalid", "content" => "whatever")
|
||||
assert !t.save
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "my string is too long: 10", t.errors[:title]
|
||||
assert_equal "my string is too long: 10", t.errors.on(:title)
|
||||
|
||||
t.title = "butthisis"
|
||||
assert t.save
|
||||
|
@ -842,7 +841,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t.title = "not"
|
||||
assert !t.save
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "my string is too short: 5", t.errors[:title]
|
||||
assert_equal "my string is too short: 5", t.errors.on(:title)
|
||||
|
||||
t.title = "valid"
|
||||
t.content = "andthisistoolong"
|
||||
|
@ -862,7 +861,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t.title = "notvalid"
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
|
||||
assert_equal "is the wrong length (should be 5 characters)", t.errors.on(:title)
|
||||
|
||||
t.title = ""
|
||||
assert !t.valid?
|
||||
|
@ -896,13 +895,13 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
|
||||
def test_validates_length_with_globally_modified_error_message
|
||||
ActiveSupport::Deprecation.silence do
|
||||
ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre {{count}}'
|
||||
ActiveModel::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre {{count}}'
|
||||
end
|
||||
Topic.validates_length_of :title, :minimum => 10
|
||||
t = Topic.create(:title => 'too short')
|
||||
assert !t.valid?
|
||||
|
||||
assert_equal 'tu est trops petit hombre 10', t.errors['title']
|
||||
assert_equal 'tu est trops petit hombre 10', t.errors.on(:title)
|
||||
end
|
||||
|
||||
def test_validates_size_of_association
|
||||
|
@ -948,7 +947,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t = Topic.create("title" => "uhoh", "content" => "whatever")
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "boo 5", t.errors["title"]
|
||||
assert_equal "boo 5", t.errors.on(:title)
|
||||
end
|
||||
|
||||
def test_validates_length_of_custom_errors_for_minimum_with_too_short
|
||||
|
@ -956,7 +955,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t = Topic.create("title" => "uhoh", "content" => "whatever")
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "hoo 5", t.errors["title"]
|
||||
assert_equal "hoo 5", t.errors.on(:title)
|
||||
end
|
||||
|
||||
def test_validates_length_of_custom_errors_for_maximum_with_message
|
||||
|
@ -964,44 +963,44 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "boo 5", t.errors["title"]
|
||||
assert_equal ["boo 5"], t.errors[:title]
|
||||
end
|
||||
|
||||
def test_validates_length_of_custom_errors_for_in
|
||||
Topic.validates_length_of(:title, :in => 10..20, :message => "hoo {{count}}")
|
||||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "hoo 10", t.errors["title"]
|
||||
assert t.errors[:title].any?
|
||||
assert_equal ["hoo 10"], t.errors["title"]
|
||||
|
||||
t = Topic.create("title" => "uhohuhohuhohuhohuhohuhohuhohuhoh", "content" => "whatever")
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "hoo 20", t.errors["title"]
|
||||
assert t.errors[:title].any?
|
||||
assert_equal ["hoo 20"], t.errors["title"]
|
||||
end
|
||||
|
||||
def test_validates_length_of_custom_errors_for_maximum_with_too_long
|
||||
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}" )
|
||||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "hoo 5", t.errors["title"]
|
||||
assert t.errors[:title].any?
|
||||
assert_equal ["hoo 5"], t.errors["title"]
|
||||
end
|
||||
|
||||
def test_validates_length_of_custom_errors_for_is_with_message
|
||||
Topic.validates_length_of( :title, :is=>5, :message=>"boo {{count}}" )
|
||||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "boo 5", t.errors["title"]
|
||||
assert t.errors[:title].any?
|
||||
assert_equal ["boo 5"], t.errors["title"]
|
||||
end
|
||||
|
||||
def test_validates_length_of_custom_errors_for_is_with_wrong_length
|
||||
Topic.validates_length_of( :title, :is=>5, :wrong_length=>"hoo {{count}}" )
|
||||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "hoo 5", t.errors["title"]
|
||||
assert t.errors[:title].any?
|
||||
assert_equal ["hoo 5"], t.errors["title"]
|
||||
end
|
||||
|
||||
def test_validates_length_of_using_minimum_utf8
|
||||
|
@ -1013,8 +1012,8 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
|
||||
t.title = "一二三四"
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
|
||||
assert t.errors[:title].any?
|
||||
assert_equal ["is too short (minimum is 5 characters)"], t.errors["title"]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1027,8 +1026,8 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
|
||||
t.title = "一二34五六"
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
|
||||
assert t.errors[:title].any?
|
||||
assert_equal ["is too long (maximum is 5 characters)"], t.errors["title"]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1038,8 +1037,8 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
|
||||
t = Topic.new("title" => "一二", "content" => "12三四五六七")
|
||||
assert !t.valid?
|
||||
assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
|
||||
assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content)
|
||||
assert_equal ["is too short (minimum is 3 characters)"], t.errors[:title]
|
||||
assert_equal ["is too long (maximum is 5 characters)"], t.errors[:content]
|
||||
t.title = "一二三"
|
||||
t.content = "12三"
|
||||
assert t.valid?
|
||||
|
@ -1067,8 +1066,8 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
|
||||
t = Topic.create("title" => "一二三四五六七八九十A", "content" => "whatever")
|
||||
assert !t.save
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "長すぎます: 10", t.errors[:title]
|
||||
assert t.errors[:title].any?
|
||||
assert_equal "長すぎます: 10", t.errors[:title].first
|
||||
|
||||
t.title = "一二三四五六七八九"
|
||||
assert t.save
|
||||
|
@ -1090,16 +1089,16 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
|
||||
t = Topic.create("title" => "一二三4", "content" => "whatever")
|
||||
assert !t.save
|
||||
assert t.errors.on(:title)
|
||||
assert t.errors[:title].any?
|
||||
|
||||
t.title = "1二三4"
|
||||
assert !t.save
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "短すぎます: 5", t.errors[:title]
|
||||
assert t.errors[:title].any?
|
||||
assert_equal "短すぎます: 5", t.errors.on(:title)
|
||||
|
||||
t.title = "一二三四五六七八九十A"
|
||||
assert !t.save
|
||||
assert t.errors.on(:title)
|
||||
assert t.errors[:title].any?
|
||||
|
||||
t.title = "一二345"
|
||||
assert t.save
|
||||
|
@ -1115,8 +1114,8 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
|
||||
t.title = "一二345六"
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
|
||||
assert t.errors[:title].any?
|
||||
assert_equal ["is the wrong length (should be 5 characters)"], t.errors["title"]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1128,8 +1127,8 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
|
||||
t.content = "not long enough"
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:content)
|
||||
assert_equal "Your essay must be at least 5 words.", t.errors[:content]
|
||||
assert t.errors[:content].any?
|
||||
assert_equal ["Your essay must be at least 5 words."], t.errors[:content]
|
||||
end
|
||||
|
||||
def test_validates_size_of_association_utf8
|
||||
|
@ -1138,7 +1137,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 }
|
||||
o = Owner.new('name' => 'あいうえおかきくけこ')
|
||||
assert !o.save
|
||||
assert o.errors.on(:pets)
|
||||
assert o.errors[:pets].any?
|
||||
o.pets.build('name' => 'あいうえおかきくけこ')
|
||||
assert o.valid?
|
||||
end
|
||||
|
@ -1150,7 +1149,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
t.replies << [r = Reply.new("title" => "A reply"), r2 = Reply.new("title" => "Another reply", "content" => "non-empty"), r3 = Reply.new("title" => "Yet another reply"), r4 = Reply.new("title" => "The last reply", "content" => "non-empty")]
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:replies)
|
||||
assert t.errors[:replies].any?
|
||||
assert_equal 1, r.errors.count # make sure all associated objects have been validated
|
||||
assert_equal 0, r2.errors.count
|
||||
assert_equal 1, r3.errors.count
|
||||
|
@ -1166,7 +1165,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
r = Reply.new("title" => "A reply", "content" => "with content!")
|
||||
r.topic = Topic.create("title" => "uhohuhoh")
|
||||
assert !r.valid?
|
||||
assert r.errors.on(:topic)
|
||||
assert r.errors[:topic].any?
|
||||
r.topic.content = "non-empty"
|
||||
assert r.valid?
|
||||
end
|
||||
|
@ -1176,8 +1175,8 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
Topic.validate { |topic| topic.errors.add("title", "will never be valid") }
|
||||
t = Topic.create("title" => "Title", "content" => "whatever")
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "will never be valid", t.errors["title"]
|
||||
assert t.errors[:title].any?
|
||||
assert_equal ["will never be valid"], t.errors["title"]
|
||||
end
|
||||
|
||||
def test_invalid_validator
|
||||
|
@ -1198,7 +1197,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
d = Developer.new
|
||||
d.salary = "0"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors[:salary].last
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1209,7 +1208,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
d.name = "John"
|
||||
d.name_confirmation = "Johnny"
|
||||
assert !d.valid?
|
||||
assert_equal "confirm 'single' and \"double\" quotes", d.errors.on(:name)
|
||||
assert_equal ["confirm 'single' and \"double\" quotes"], d.errors[:name]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1219,7 +1218,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
d = Developer.new
|
||||
d.name = d.name_confirmation = "John 32"
|
||||
assert !d.valid?
|
||||
assert_equal "format 'single' and \"double\" quotes", d.errors.on(:name)
|
||||
assert_equal ["format 'single' and \"double\" quotes"], d.errors[:name]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1229,7 +1228,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
d = Developer.new
|
||||
d.salary = "90,000"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors[:salary].last
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1239,7 +1238,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
d = Developer.new
|
||||
d.name = "Jeffrey"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
|
||||
assert_equal ["This string contains 'single' and \"double\" quotes"], d.errors[:name]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1249,7 +1248,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
d = Developer.new
|
||||
d.name = "Joe"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
|
||||
assert_equal ["This string contains 'single' and \"double\" quotes"], d.errors[:name]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1259,7 +1258,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
d = Developer.new
|
||||
d.name = "Joe"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
|
||||
assert_equal ["This string contains 'single' and \"double\" quotes"], d.errors[:name]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1269,7 +1268,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
d = Developer.new
|
||||
d.name = "Joe"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:non_existent)
|
||||
assert_equal ["This string contains 'single' and \"double\" quotes"], d.errors[:non_existent]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1279,7 +1278,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
d = Developer.new
|
||||
d.name = "David"
|
||||
assert !d.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
|
||||
assert_equal ["This string contains 'single' and \"double\" quotes"], d.errors[:name]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1290,7 +1289,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
r = Reply.create("title" => "A reply", "content" => "with content!")
|
||||
r.topic = Topic.create("title" => "uhohuhoh")
|
||||
assert !r.valid?
|
||||
assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic)
|
||||
assert_equal ["This string contains 'single' and \"double\" quotes"], r.errors[:topic]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1299,8 +1298,8 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true )
|
||||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "hoo 5", t.errors["title"]
|
||||
assert t.errors[:title].any?
|
||||
assert_equal ["hoo 5"], t.errors["title"]
|
||||
end
|
||||
|
||||
def test_unless_validation_using_method_true
|
||||
|
@ -1308,7 +1307,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true )
|
||||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
assert t.valid?
|
||||
assert !t.errors.on(:title)
|
||||
assert !t.errors[:title].any?
|
||||
end
|
||||
|
||||
def test_if_validation_using_method_false
|
||||
|
@ -1316,7 +1315,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true_but_its_not )
|
||||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
assert t.valid?
|
||||
assert !t.errors.on(:title)
|
||||
assert t.errors[:title].empty?
|
||||
end
|
||||
|
||||
def test_unless_validation_using_method_false
|
||||
|
@ -1324,8 +1323,8 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true_but_its_not )
|
||||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "hoo 5", t.errors["title"]
|
||||
assert t.errors[:title].any?
|
||||
assert_equal ["hoo 5"], t.errors["title"]
|
||||
end
|
||||
|
||||
def test_if_validation_using_string_true
|
||||
|
@ -1333,8 +1332,8 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "a = 1; a == 1" )
|
||||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "hoo 5", t.errors["title"]
|
||||
assert t.errors[:title].any?
|
||||
assert_equal ["hoo 5"], t.errors["title"]
|
||||
end
|
||||
|
||||
def test_unless_validation_using_string_true
|
||||
|
@ -1342,7 +1341,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "a = 1; a == 1" )
|
||||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
assert t.valid?
|
||||
assert !t.errors.on(:title)
|
||||
assert t.errors[:title].empty?
|
||||
end
|
||||
|
||||
def test_if_validation_using_string_false
|
||||
|
@ -1350,7 +1349,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "false")
|
||||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
assert t.valid?
|
||||
assert !t.errors.on(:title)
|
||||
assert t.errors[:title].empty?
|
||||
end
|
||||
|
||||
def test_unless_validation_using_string_false
|
||||
|
@ -1358,8 +1357,8 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "false")
|
||||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "hoo 5", t.errors["title"]
|
||||
assert t.errors[:title].any?
|
||||
assert_equal ["hoo 5"], t.errors["title"]
|
||||
end
|
||||
|
||||
def test_if_validation_using_block_true
|
||||
|
@ -1368,8 +1367,8 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
:if => Proc.new { |r| r.content.size > 4 } )
|
||||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "hoo 5", t.errors["title"]
|
||||
assert t.errors[:title].any?
|
||||
assert_equal ["hoo 5"], t.errors["title"]
|
||||
end
|
||||
|
||||
def test_unless_validation_using_block_true
|
||||
|
@ -1378,7 +1377,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
:unless => Proc.new { |r| r.content.size > 4 } )
|
||||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
assert t.valid?
|
||||
assert !t.errors.on(:title)
|
||||
assert t.errors[:title].empty?
|
||||
end
|
||||
|
||||
def test_if_validation_using_block_false
|
||||
|
@ -1387,7 +1386,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
:if => Proc.new { |r| r.title != "uhohuhoh"} )
|
||||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
assert t.valid?
|
||||
assert !t.errors.on(:title)
|
||||
assert t.errors[:title].empty?
|
||||
end
|
||||
|
||||
def test_unless_validation_using_block_false
|
||||
|
@ -1396,8 +1395,8 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
:unless => Proc.new { |r| r.title != "uhohuhoh"} )
|
||||
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
|
||||
assert !t.valid?
|
||||
assert t.errors.on(:title)
|
||||
assert_equal "hoo 5", t.errors["title"]
|
||||
assert t.errors[:title].any?
|
||||
assert_equal ["hoo 5"], t.errors["title"]
|
||||
end
|
||||
|
||||
def test_validates_associated_missing
|
||||
|
@ -1405,7 +1404,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
Reply.validates_presence_of(:topic)
|
||||
r = Reply.create("title" => "A reply", "content" => "with content!")
|
||||
assert !r.valid?
|
||||
assert r.errors.on(:topic)
|
||||
assert r.errors[:topic].any?
|
||||
|
||||
r.topic = Topic.find :first
|
||||
assert r.valid?
|
||||
|
@ -1427,7 +1426,7 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
|
||||
t = Topic.new("title" => "")
|
||||
assert !t.valid?
|
||||
assert_equal "can't be blank", t.errors.on("title").first
|
||||
assert_equal "can't be blank", t.errors["title"].first
|
||||
end
|
||||
|
||||
def test_invalid_should_be_the_opposite_of_valid
|
||||
|
@ -1481,7 +1480,6 @@ class ValidatesNumericalityTest < ActiveRecord::TestCase
|
|||
|
||||
def test_default_validates_numericality_of
|
||||
Topic.validates_numericality_of :approved
|
||||
|
||||
invalid!(NIL + BLANK + JUNK)
|
||||
valid!(FLOATS + INTEGERS + BIGDECIMAL + INFINITY)
|
||||
end
|
||||
|
@ -1578,11 +1576,11 @@ class ValidatesNumericalityTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
private
|
||||
def invalid!(values, error=nil)
|
||||
def invalid!(values, error = nil)
|
||||
with_each_topic_approved_value(values) do |topic, value|
|
||||
assert !topic.valid?, "#{value.inspect} not rejected as a number"
|
||||
assert topic.errors.on(:approved)
|
||||
assert_equal error, topic.errors.on(:approved) if error
|
||||
assert topic.errors[:approved].any?, "FAILED for #{value.inspect}"
|
||||
assert_equal error, topic.errors[:approved].first if error
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1593,7 +1591,7 @@ class ValidatesNumericalityTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def with_each_topic_approved_value(values)
|
||||
topic = Topic.new("title" => "numeric test", "content" => "whatever")
|
||||
topic = Topic.new(:title => "numeric test", :content => "whatever")
|
||||
values.each do |value|
|
||||
topic.approved = value
|
||||
yield topic, value
|
||||
|
|
|
@ -12,25 +12,25 @@ class Reply < Topic
|
|||
attr_accessible :title, :author_name, :author_email_address, :written_on, :content, :last_read
|
||||
|
||||
def validate
|
||||
errors.add("title", "Empty") unless attribute_present? "title"
|
||||
errors[:title] << "Empty" unless attribute_present?("title")
|
||||
end
|
||||
|
||||
def errors_on_empty_content
|
||||
errors.add("content", "Empty") unless attribute_present? "content"
|
||||
errors[:content] << "Empty" unless attribute_present?("content")
|
||||
end
|
||||
|
||||
def validate_on_create
|
||||
if attribute_present?("title") && attribute_present?("content") && content == "Mismatch"
|
||||
errors.add("title", "is Content Mismatch")
|
||||
errors[:title] << "is Content Mismatch"
|
||||
end
|
||||
end
|
||||
|
||||
def title_is_wrong_create
|
||||
errors.add("title", "is Wrong Create") if attribute_present?("title") && title == "Wrong Create"
|
||||
errors[:title] << "is Wrong Create" if attribute_present?("title") && title == "Wrong Create"
|
||||
end
|
||||
|
||||
def validate_on_update
|
||||
errors.add("title", "is Wrong Update") if attribute_present?("title") && title == "Wrong Update"
|
||||
errors[:title] << "is Wrong Update" if attribute_present?("title") && title == "Wrong Update"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue