Added optionally allow for nil or empty strings with validates_numericality_of #801 [Sebastian Kanthak] Fixed problem with using slashes in validates_format_of regular expressions #801 [Sebastian Kanthak]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@910 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
bfa6bfc24a
commit
b55f59e811
|
@ -1,5 +1,9 @@
|
||||||
*SVN*
|
*SVN*
|
||||||
|
|
||||||
|
* Added optionally allow for nil or empty strings with validates_numericality_of #801 [Sebastian Kanthak]
|
||||||
|
|
||||||
|
* Fixed problem with using slashes in validates_format_of regular expressions #801 [Sebastian Kanthak]
|
||||||
|
|
||||||
* Fixed that SQLite3 exceptions are caught and reported properly #823 [yerejm]
|
* Fixed that SQLite3 exceptions are caught and reported properly #823 [yerejm]
|
||||||
|
|
||||||
* Added that all types of after_find/after_initialized callbacks are triggered if the explicit implementation is present, not only the explicit implementation itself
|
* Added that all types of after_find/after_initialized callbacks are triggered if the explicit implementation is present, not only the explicit implementation itself
|
||||||
|
|
|
@ -270,11 +270,11 @@ module ActiveRecord
|
||||||
def validates_confirmation_of(*attr_names)
|
def validates_confirmation_of(*attr_names)
|
||||||
configuration = { :message => ActiveRecord::Errors.default_error_messages[:confirmation], :on => :save }
|
configuration = { :message => ActiveRecord::Errors.default_error_messages[:confirmation], :on => :save }
|
||||||
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
||||||
configuration[:message].gsub!(/\"/, '\\\\\"')
|
|
||||||
|
|
||||||
for attr_name in attr_names
|
attr_accessor *(attr_names.map { |n| "#{n}_confirmation" })
|
||||||
attr_accessor "#{attr_name}_confirmation"
|
|
||||||
class_eval(%(#{validation_method(configuration[:on])} %{errors.add('#{attr_name}', "#{configuration[:message]}") unless #{attr_name}_confirmation.nil? or #{attr_name} == #{attr_name}_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")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -294,13 +294,13 @@ module ActiveRecord
|
||||||
#
|
#
|
||||||
# NOTE: The agreement is considered valid if it's set to the string "1". This makes it easy to relate it to an HTML checkbox.
|
# NOTE: The agreement is considered valid if it's set to the string "1". This makes it easy to relate it to an HTML checkbox.
|
||||||
def validates_acceptance_of(*attr_names)
|
def validates_acceptance_of(*attr_names)
|
||||||
configuration = { :message => ActiveRecord::Errors.default_error_messages[:accepted], :on => :save }
|
configuration = { :message => ActiveRecord::Errors.default_error_messages[:accepted], :on => :save, :allow_nil => true }
|
||||||
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
||||||
configuration[:message].gsub!(/\"/, '\\\\\"')
|
|
||||||
|
|
||||||
for attr_name in attr_names
|
attr_accessor *attr_names
|
||||||
attr_accessor(attr_name)
|
|
||||||
class_eval(%(#{validation_method(configuration[:on])} %{errors.add('#{attr_name}', "#{configuration[:message]}") unless #{attr_name}.nil? or #{attr_name} == "1"}))
|
validates_each(attr_names,configuration) do |record, attr_name, value|
|
||||||
|
record.errors.add(attr_name, configuration[:message]) unless value == "1"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -312,10 +312,13 @@ module ActiveRecord
|
||||||
def validates_presence_of(*attr_names)
|
def validates_presence_of(*attr_names)
|
||||||
configuration = { :message => ActiveRecord::Errors.default_error_messages[:empty], :on => :save }
|
configuration = { :message => ActiveRecord::Errors.default_error_messages[:empty], :on => :save }
|
||||||
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
||||||
configuration[:message].gsub!(/\"/, '\\\\\"')
|
|
||||||
|
|
||||||
for attr_name in attr_names
|
# can't use validates_each here, because it cannot cope with non-existant attributes,
|
||||||
class_eval(%(#{validation_method(configuration[:on])} %{errors.add_on_empty('#{attr_name}', "#{configuration[:message]}")}))
|
# while errors.add_on_empty can
|
||||||
|
attr_names.each do |attr_name|
|
||||||
|
send(validation_method(configuration[:on])) do |record|
|
||||||
|
record.errors.add_on_empty(attr_name,configuration[:message])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -414,13 +417,14 @@ module ActiveRecord
|
||||||
def validates_uniqueness_of(*attr_names)
|
def validates_uniqueness_of(*attr_names)
|
||||||
configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken] }
|
configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken] }
|
||||||
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
||||||
configuration[:message].gsub!(/\"/, '\\\\\"')
|
|
||||||
|
|
||||||
for attr_name in attr_names
|
|
||||||
if scope = configuration[:scope]
|
if scope = configuration[:scope]
|
||||||
class_eval(%(validate %{errors.add('#{attr_name}', "#{configuration[:message]}") if self.class.find_first(new_record? ? ['#{attr_name} = ? AND #{scope} = ?', #{attr_name}, #{scope}] : ["#{attr_name} = ? AND \\\#{self.class.primary_key} <> ? AND #{scope} = ?", #{attr_name}, id, #{scope}])}))
|
validates_each(attr_names,configuration) do |record, attr_name, value|
|
||||||
|
record.errors.add(attr_name, configuration[:message]) if record.class.find_first(record.new_record? ? ["#{attr_name} = ? AND #{scope} = ?", record.send(attr_name), record.send(scope)] : ["#{attr_name} = ? AND #{record.class.primary_key} <> ? AND #{scope} = ?", record.send(attr_name), record.send(:id), record.send(scope)])
|
||||||
|
end
|
||||||
else
|
else
|
||||||
class_eval(%(validate %{errors.add('#{attr_name}', "#{configuration[:message]}") if self.class.find_first(new_record? ? ['#{attr_name} = ?', #{attr_name}] : ["#{attr_name} = ? AND \\\#{self.class.primary_key} <> ?", #{attr_name}, id])}))
|
validates_each(attr_names,configuration) do |record, attr_name, value|
|
||||||
|
record.errors.add(attr_name, configuration[:message]) if record.class.find_first(record.new_record? ? ["#{attr_name} = ?", record.send(attr_name)] : ["#{attr_name} = ? AND #{record.class.primary_key} <> ?", record.send(attr_name), record.send(:id) ] )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -441,12 +445,11 @@ module ActiveRecord
|
||||||
def validates_format_of(*attr_names)
|
def validates_format_of(*attr_names)
|
||||||
configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save, :with => nil }
|
configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save, :with => nil }
|
||||||
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
||||||
configuration[:message].gsub!(/\"/, '\\\\\"')
|
|
||||||
|
|
||||||
raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp)
|
raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp)
|
||||||
|
|
||||||
for attr_name in attr_names
|
validates_each(attr_names, configuration) do |record, attr_name, value|
|
||||||
class_eval(%(#{validation_method(configuration[:on])} %{errors.add("#{attr_name}", "#{configuration[:message]}") unless #{attr_name} and #{attr_name}.to_s.match(/#{Regexp.quote(configuration[:with].source)}/)}))
|
record.errors.add(attr_name, configuration[:message]) unless value.to_s =~ configuration[:with]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -464,19 +467,13 @@ module ActiveRecord
|
||||||
def validates_inclusion_of(*attr_names)
|
def validates_inclusion_of(*attr_names)
|
||||||
configuration = { :message => ActiveRecord::Errors.default_error_messages[:inclusion], :on => :save }
|
configuration = { :message => ActiveRecord::Errors.default_error_messages[:inclusion], :on => :save }
|
||||||
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
||||||
configuration[:message].gsub!(/\"/, '\\\\\"')
|
|
||||||
|
|
||||||
enum = configuration[:in] || configuration[:within]
|
enum = configuration[:in] || configuration[:within]
|
||||||
allow_nil = configuration[:allow_nil]
|
|
||||||
|
|
||||||
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?")
|
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?")
|
||||||
|
|
||||||
for attr_name in attr_names
|
validates_each(attr_names, configuration) do |record, attr_name, value|
|
||||||
if allow_nil
|
record.errors.add(attr_name, configuration[:message]) unless enum.include?(value)
|
||||||
class_eval(%(#{validation_method(configuration[:on])} %{errors.add("#{attr_name}", "#{configuration[:message]}") unless #{attr_name}.nil? or (#{enum.inspect}).include?(#{attr_name}) }))
|
|
||||||
else
|
|
||||||
class_eval(%(#{validation_method(configuration[:on])} %{errors.add("#{attr_name}", "#{configuration[:message]}") unless (#{enum.inspect}).include?(#{attr_name}) }))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -504,13 +501,10 @@ module ActiveRecord
|
||||||
def validates_associated(*attr_names)
|
def validates_associated(*attr_names)
|
||||||
configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save }
|
configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save }
|
||||||
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
||||||
configuration[:message].gsub!(/\"/, '\\\\\"')
|
|
||||||
|
|
||||||
for attr_name in attr_names
|
validates_each(attr_names, configuration) do |record, attr_name, value|
|
||||||
class_eval(%(#{validation_method(configuration[:on])} %{
|
record.errors.add(attr_name, configuration[:message]) unless
|
||||||
errors.add("#{attr_name}", "#{configuration[:message]}") unless
|
(value.is_a?(Array) ? value : [value]).inject(true) { |memo, r| (r.nil? or r.valid?) and memo }
|
||||||
(#{attr_name}.is_a?(Array) ? #{attr_name} : [#{attr_name}]).inject(true){ |memo, record| (record.nil? or record.valid?) and memo }
|
|
||||||
}))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -526,27 +520,23 @@ module ActiveRecord
|
||||||
# * <tt>message</tt> - A custom error message (default is: "is not a number")
|
# * <tt>message</tt> - A custom error message (default is: "is not a number")
|
||||||
# * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
|
# * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
|
||||||
# * <tt>only_integer</tt> Specifies whether the value has to be an integer, e.g. an integral value (default is false)
|
# * <tt>only_integer</tt> Specifies whether the value has to be an integer, e.g. an integral value (default is false)
|
||||||
|
# * <tt>allow_nil</tt> Skip validation if attribute is nil (default is false). Notice that for fixnum and float columsn empty strings are converted to nil
|
||||||
def validates_numericality_of(*attr_names)
|
def validates_numericality_of(*attr_names)
|
||||||
configuration = { :message => ActiveRecord::Errors.default_error_messages[:not_a_number], :on => :save,
|
configuration = { :message => ActiveRecord::Errors.default_error_messages[:not_a_number], :on => :save,
|
||||||
:integer => false }
|
:only_integer => false, :allow_nil => false }
|
||||||
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
||||||
|
|
||||||
for attr_name in attr_names
|
|
||||||
if configuration[:only_integer]
|
if configuration[:only_integer]
|
||||||
# we have to use a regexp here, because Kernel.Integer accepts nil and "0xdeadbeef", but does not
|
validates_each(attr_names,configuration) do |record, attr_name,value|
|
||||||
# accept "099" and String#to_i accepts everything. The string containing the regexp is evaluated twice
|
record.errors.add(attr_name, configuration[:message]) unless record.send("#{attr_name}_before_type_cast").to_s =~ /^[+-]?\d+$/
|
||||||
# so we have to escape everything properly
|
end
|
||||||
class_eval(%(#{validation_method(configuration[:on])} %{
|
else
|
||||||
errors.add("#{attr_name}", "#{configuration[:message]}") unless #{attr_name}_before_type_cast.to_s =~ /^[\\\\+\\\\-]?\\\\d+$/
|
validates_each(attr_names,configuration) do |record, attr_name,value|
|
||||||
}))
|
begin
|
||||||
else
|
Kernel.Float(record.send("#{attr_name}_before_type_cast").to_s)
|
||||||
class_eval(%(#{validation_method(configuration[:on])} %{
|
rescue ArgumentError, TypeError
|
||||||
begin
|
record.errors.add(attr_name, configuration[:message])
|
||||||
Kernel.Float(#{attr_name}_before_type_cast)
|
|
||||||
rescue ArgumentError, TypeError
|
|
||||||
errors.add("#{attr_name}", "#{configuration[:message]}")
|
|
||||||
end
|
end
|
||||||
}))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -606,39 +606,39 @@ class ValidationsTest < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_validates_numericality_of_with_string
|
def test_validates_numericality_of_with_string
|
||||||
Topic.validates_numericality_of( :replies_count )
|
Topic.validates_numericality_of( :approved )
|
||||||
["not a number","42 not a number","0xdeadbeef","00-1","-+019.0","12.12.13.12",nil].each do |v|
|
["not a number","42 not a number","0xdeadbeef","00-1","-+019.0","12.12.13.12",nil].each do |v|
|
||||||
t = Topic.create("title" => "numeric test", "content" => "whatever", "replies_count" => "not a number")
|
t = Topic.create("title" => "numeric test", "content" => "whatever", "approved" => "not a number")
|
||||||
assert !t.valid?, "#{v} not rejected as a number"
|
assert !t.valid?, "#{v} not rejected as a number"
|
||||||
assert t.errors.on(:replies_count)
|
assert t.errors.on(:approved)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_validates_numericality_of
|
def test_validates_numericality_of
|
||||||
Topic.validates_numericality_of( :replies_count )
|
Topic.validates_numericality_of( :approved, :allow_nil => true )
|
||||||
["10", "10.0", "10.5", "-10.5", "-0.0001","0090","-090","-090.1"].each do |v|
|
["10", "10.0", "10.5", "-10.5", "-0.0001","0090","-090","-090.1",nil,""].each do |v|
|
||||||
t = Topic.create("title" => "numeric test", "content" => "whatever", "replies_count" => v)
|
t = Topic.create("title" => "numeric test", "content" => "whatever", "approved" => v)
|
||||||
assert t.valid?, "#{v} not recognized as a number"
|
assert t.valid?, "#{v} not recognized as a number"
|
||||||
# we cannot check this as replies_count is actually an integer field
|
# we cannot check this as approved is actually an integer field
|
||||||
#assert_in_delta v.to_f, t.replies_count, 0.0000001
|
#assert_in_delta v.to_f, t.approved, 0.0000001
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_validates_numericality_of_int_with_string
|
def test_validates_numericality_of_int_with_string
|
||||||
Topic.validates_numericality_of( :replies_count, :only_integer => true )
|
Topic.validates_numericality_of( :approved, :only_integer => true )
|
||||||
["not a number","42 not a number","0xdeadbeef","0-1","--3","+-3","+3-1",nil].each do |v|
|
["not a number","42 not a number","0xdeadbeef","0-1","--3","+-3","+3-1",nil].each do |v|
|
||||||
t = Topic.create("title" => "numeric test", "content" => "whatever", "replies_count" => v)
|
t = Topic.create("title" => "numeric test", "content" => "whatever", "approved" => v)
|
||||||
assert !t.valid?, "#{v} not rejected as integer"
|
assert !t.valid?, "#{v} not rejected as integer"
|
||||||
assert t.errors.on(:replies_count)
|
assert t.errors.on(:approved)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_validates_numericality_of_int
|
def test_validates_numericality_of_int
|
||||||
Topic.validates_numericality_of( :replies_count, :only_integer => true )
|
Topic.validates_numericality_of( :approved, :only_integer => true, :allow_nil => true )
|
||||||
["42", "+42", "-42", "042", "0042", "-042", 42].each do |v|
|
["42", "+42", "-42", "042", "0042", "-042", 42, nil,""].each do |v|
|
||||||
t = Topic.create("title" => "numeric test", "content" => "whatever", "replies_count" => v)
|
t = Topic.create("title" => "numeric test", "content" => "whatever", "approved" => v)
|
||||||
assert t.valid?, "#{v} not recognized as integer"
|
assert t.valid?, "#{v} not recognized as integer"
|
||||||
assert_equal v.to_i, t.replies_count
|
assert_equal((v.nil? or v == "")? nil : v.to_i, t.approved)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue