Implemented strict validation concept

In order to deliver debug information to dev team
instead of display error message to end user
Implemented strict validation concept
that suppose to define validation that always raise exception when fails
This commit is contained in:
Bogdan Gusiev 2011-08-17 17:26:00 +03:00
parent 5912f3f97e
commit 8620bf90c5
12 changed files with 75 additions and 6 deletions

@ -63,7 +63,7 @@ module ActiveModel
class Errors class Errors
include Enumerable include Enumerable
CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank] CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict]
attr_reader :messages attr_reader :messages
@ -218,6 +218,9 @@ def add(attribute, message = nil, options = {})
elsif message.is_a?(Proc) elsif message.is_a?(Proc)
message = message.call message = message.call
end end
if options[:strict]
raise ActiveModel::StrictValidationFailed, message
end
self[attribute] << message self[attribute] << message
end end
@ -319,4 +322,7 @@ def generate_message(attribute, type = :invalid, options = {})
I18n.translate(key, options) I18n.translate(key, options)
end end
end end
class StrictValidationFailed < StandardError
end
end end

@ -58,6 +58,8 @@ module HelperMethods
# <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). # <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).
# The method, proc or string should return or evaluate to a true or # The method, proc or string should return or evaluate to a true or
# false value. # false value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information
def validates_acceptance_of(*attr_names) def validates_acceptance_of(*attr_names)
validates_with AcceptanceValidator, _merge_attributes(attr_names) validates_with AcceptanceValidator, _merge_attributes(attr_names)
end end

@ -58,6 +58,8 @@ module HelperMethods
# <tt>:unless => :skip_validation</tt>, or # <tt>:unless => :skip_validation</tt>, or
# <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The # <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. # method, proc or string should return or evaluate to a true or false value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information
def validates_confirmation_of(*attr_names) def validates_confirmation_of(*attr_names)
validates_with ConfirmationValidator, _merge_attributes(attr_names) validates_with ConfirmationValidator, _merge_attributes(attr_names)
end end

@ -59,6 +59,8 @@ module HelperMethods
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should # * <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 # 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. # method, proc or string should return or evaluate to a true or false value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information
def validates_exclusion_of(*attr_names) def validates_exclusion_of(*attr_names)
validates_with ExclusionValidator, _merge_attributes(attr_names) validates_with ExclusionValidator, _merge_attributes(attr_names)
end end

@ -84,6 +84,8 @@ module HelperMethods
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should # * <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 # 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. # method, proc or string should return or evaluate to a true or false value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information
def validates_format_of(*attr_names) def validates_format_of(*attr_names)
validates_with FormatValidator, _merge_attributes(attr_names) validates_with FormatValidator, _merge_attributes(attr_names)
end end

@ -59,6 +59,8 @@ module HelperMethods
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should # * <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 # 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. # method, proc or string should return or evaluate to a true or false value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information
def validates_inclusion_of(*attr_names) def validates_inclusion_of(*attr_names)
validates_with InclusionValidator, _merge_attributes(attr_names) validates_with InclusionValidator, _merge_attributes(attr_names)
end end

@ -96,6 +96,8 @@ module HelperMethods
# * <tt>:tokenizer</tt> - Specifies how to split up the attribute string. (e.g. <tt>:tokenizer => lambda {|str| str.scan(/\w+/)}</tt> to # * <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.) # count words as in above example.)
# Defaults to <tt>lambda{ |value| value.split(//) }</tt> which counts individual characters. # Defaults to <tt>lambda{ |value| value.split(//) }</tt> which counts individual characters.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information
def validates_length_of(*attr_names) def validates_length_of(*attr_names)
validates_with LengthValidator, _merge_attributes(attr_names) validates_with LengthValidator, _merge_attributes(attr_names)
end end

@ -107,6 +107,8 @@ module HelperMethods
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should # * <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 # 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. # method, proc or string should return or evaluate to a true or false value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information
# #
# The following checks can also be supplied with a proc or a symbol which corresponds to a method: # The following checks can also be supplied with a proc or a symbol which corresponds to a method:
# * <tt>:greater_than</tt> # * <tt>:greater_than</tt>

@ -35,6 +35,8 @@ module HelperMethods
# * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should # * <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>). # 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. # The method, proc or string should return or evaluate to a true or false value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information
# #
def validates_presence_of(*attr_names) def validates_presence_of(*attr_names)
validates_with PresenceValidator, _merge_attributes(attr_names) validates_with PresenceValidator, _merge_attributes(attr_names)

@ -70,8 +70,8 @@ module ClassMethods
# validator's initializer as +options[:in]+ while other types including # validator's initializer as +options[:in]+ while other types including
# regular expressions and strings are passed as +options[:with]+ # regular expressions and strings are passed as +options[:with]+
# #
# Finally, the options +:if+, +:unless+, +:on+, +:allow_blank+ and +:allow_nil+ can be given # Finally, the options +:if+, +:unless+, +:on+, +:allow_blank+, +:allow_nil+ and +:strict+
# to one specific validator, as a hash: # can be given to one specific validator, as a hash:
# #
# validates :password, :presence => { :if => :password_required? }, :confirmation => true # validates :password, :presence => { :if => :password_required? }, :confirmation => true
# #
@ -101,12 +101,24 @@ def validates(*attributes)
end end
end end
# This method is used to define validation that can not be correcterized by end user
# and is considered exceptional.
# So each validator defined with bang or <tt>:strict</tt> option set to <tt>true</tt>
# will always raise <tt>ActiveModel::InternalValidationFailed</tt> instead of adding error
# when validation fails
# See <tt>validates</tt> for more information about validation itself.
def validates!(*attributes)
options = attributes.extract_options!
options[:strict] = true
validates(*(attributes << options))
end
protected protected
# When creating custom validators, it might be useful to be able to specify # When creating custom validators, it might be useful to be able to specify
# additional default keys. This can be done by overwriting this method. # additional default keys. This can be done by overwriting this method.
def _validates_default_keys def _validates_default_keys
[ :if, :unless, :on, :allow_blank, :allow_nil ] [ :if, :unless, :on, :allow_blank, :allow_nil , :strict]
end end
def _parse_validates_options(options) #:nodoc: def _parse_validates_options(options) #:nodoc:

@ -61,7 +61,9 @@ module ClassMethods
# (e.g. <tt>:unless => :skip_validation</tt>, or # (e.g. <tt>:unless => :skip_validation</tt>, or
# <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). # <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. # The method, proc or string should return or evaluate to a true or false value.
# # * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information
# If you pass any additional configuration options, they will be passed # If you pass any additional configuration options, they will be passed
# to the class and available as <tt>options</tt>: # to the class and available as <tt>options</tt>:
# #
@ -140,4 +142,4 @@ def validates_with(*args, &block)
end end
end end
end end
end end

@ -297,4 +297,37 @@ def test_validations_on_the_instance_level
assert auto.valid? assert auto.valid?
end end
def test_strict_validation_in_validates
Topic.validates :title, :strict => true, :presence => true
assert_raises ActiveModel::StrictValidationFailed do
Topic.new.valid?
end
end
def test_strict_validation_not_fails
Topic.validates :title, :strict => true, :presence => true
assert Topic.new(:title => "hello").valid?
end
def test_strict_validation_particular_validator
Topic.validates :title, :presence => {:strict => true}
assert_raises ActiveModel::StrictValidationFailed do
Topic.new.valid?
end
end
def test_strict_validation_in_custom_validator_helper
Topic.validates_presence_of :title, :strict => true
assert_raises ActiveModel::StrictValidationFailed do
Topic.new.valid?
end
end
def test_validates_with_bang
Topic.validates! :title, :presence => true
assert_raises ActiveModel::StrictValidationFailed do
Topic.new.valid?
end
end
end end