From 7045c4c279499eb7340fb420398d613497739eef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 8 Jan 2010 08:37:58 +0100 Subject: [PATCH] Allow validates to map some types to specific options. So now you can do: validates :email, :presence => true, :format => /@/ validates :genre, :inclusion => %w(m f) validates :password, :length => 6..20 --- .../active_model/validations/numericality.rb | 7 +-- .../lib/active_model/validations/validates.rb | 44 ++++++++++++----- .../test/cases/validations/validates_test.rb | 48 ++++++++++++++----- activemodel/test/models/person.rb | 2 +- 4 files changed, 74 insertions(+), 27 deletions(-) diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb index 9dfc5125cd9..adf34bf4548 100644 --- a/activemodel/lib/active_model/validations/numericality.rb +++ b/activemodel/lib/active_model/validations/numericality.rb @@ -10,9 +10,10 @@ module ActiveModel end def check_validity! - options.slice(*CHECKS.keys) do |option, value| - next if [:odd, :even].include?(option) - raise ArgumentError, ":#{option} must be a number, a symbol or a proc" unless value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol) + keys = CHECKS.keys - [:odd, :event] + options.slice(*keys) do |option, value| + next if value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol) + raise ArgumentError, ":#{option} must be a number, a symbol or a proc" end end diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb index 61caf32c06c..ea7303c70b1 100644 --- a/activemodel/lib/active_model/validations/validates.rb +++ b/activemodel/lib/active_model/validations/validates.rb @@ -7,6 +7,7 @@ module ActiveModel # custom validator classes in their place such as PresenceValidator. # # Examples of using the default rails validators: + # # validates :terms, :acceptance => true # validates :password, :confirmation => true # validates :username, :exclusion => { :in => %w(admin superuser) } @@ -19,6 +20,7 @@ module ActiveModel # # The power of the +validates+ method comes when using cusom validators # and default validators in one call for a given attribute e.g. + # # class EmailValidator < ActiveModel::EachValidator # def validate_each(record, attribute, value) # record.errors[attribute] << (options[:message] || "is not an email") unless @@ -31,29 +33,32 @@ module ActiveModel # attr_accessor :name, :email # # validates :name, :presence => true, :uniqueness => true, :length => { :maximum => 100 } - # validates :email, :presence => true, :format => { :with => /@/ } + # validates :email, :presence => true, :email => true # end # # Validator classes my also exist within the class being validated # allowing custom modules of validators to be included as needed e.g. - # - # module MyValidators + # + # class Film + # include ActiveModel::Validations + # # class TitleValidator < ActiveModel::EachValidator # def validate_each(record, attribute, value) # record.errors[attribute] << "must start with 'the'" unless =~ /^the/i # end # end + # + # validates :name, :title => true # end # - # class Film - # include ActiveModel::Validations - # include MyValidators - # - # validates :name, :title => true - # end + # The validators hash can also handle regular expressions, ranges and arrays: # - # The options :if, :unless, :on, :allow_blank and :allow_nil can be given to one specific - # validator: + # validates :email, :format => /@/ + # validates :genre, :inclusion => %w(mail female) + # validates :password, :length => 6..20 + # + # Finally, the options :if, :unless, :on, :allow_blank and :allow_nil can be given + # to one specific validator: # # validates :password, :presence => { :if => :password_required? }, :confirmation => true # @@ -78,7 +83,22 @@ module ActiveModel raise ArgumentError, "Unknown validator: '#{key}'" end - validates_with(validator, defaults.merge(options == true ? {} : options)) + validates_with(validator, defaults.merge(_parse_validates_options(options))) + end + end + + protected + + def _parse_validates_options(options) #:nodoc: + case options + when TrueClass + {} + when Hash + options + when Regexp + { :with => options } + when Range, Array + { :in => options } end end end diff --git a/activemodel/test/cases/validations/validates_test.rb b/activemodel/test/cases/validations/validates_test.rb index 181ff38b644..966690d75f8 100644 --- a/activemodel/test/cases/validations/validates_test.rb +++ b/activemodel/test/cases/validations/validates_test.rb @@ -10,43 +10,42 @@ class ValidatesTest < ActiveModel::TestCase def reset_callbacks Person.reset_callbacks(:validate) + PersonWithValidator.reset_callbacks(:validate) end def test_validates_with_built_in_validation Person.validates :title, :numericality => true person = Person.new person.valid? - assert person.errors[:title].include?('is not a number') + assert_equal ['is not a number'], person.errors[:title] end def test_validates_with_built_in_validation_and_options - Person.validates :title, :numericality => { :message => 'my custom message' } + Person.validates :salary, :numericality => { :message => 'my custom message' } person = Person.new person.valid? - assert person.errors[:title].include?('my custom message') + assert_equal ['my custom message'], person.errors[:salary] end def test_validates_with_validator_class Person.validates :karma, :email => true person = Person.new person.valid? - assert person.errors[:karma].include?('is not an email') + assert_equal ['is not an email'], person.errors[:karma] end def test_validates_with_if_as_local_conditions Person.validates :karma, :presence => true, :email => { :unless => :condition_is_true } person = Person.new person.valid? - assert !person.errors[:karma].include?('is not an email') - assert person.errors[:karma].include?('can\'t be blank') + assert_equal ["can't be blank"], person.errors[:karma] end def test_validates_with_if_as_shared_conditions Person.validates :karma, :presence => true, :email => true, :if => :condition_is_true person = Person.new person.valid? - assert person.errors[:karma].include?('is not an email') - assert person.errors[:karma].include?('can\'t be blank') + assert ["can't be blank", "is not an email"], person.errors[:karma].sort end def test_validates_with_unless_shared_conditions @@ -61,11 +60,38 @@ class ValidatesTest < ActiveModel::TestCase assert person.valid? end + def test_validates_with_regexp + Person.validates :karma, :format => /positive|negative/ + person = Person.new + assert person.invalid? + assert_equal ['is invalid'], person.errors[:karma] + person.karma = "positive" + assert person.valid? + end + + def test_validates_with_array + Person.validates :genre, :inclusion => %w(m f) + person = Person.new + assert person.invalid? + assert_equal ['is not included in the list'], person.errors[:genre] + person.genre = "m" + assert person.valid? + end + + def test_validates_with_range + Person.validates :password, :length => 6..20 + person = Person.new + assert person.invalid? + assert_equal ['is too short (minimum is 6 characters)'], person.errors[:password] + person.password = '123456' + assert person.valid? + end + def test_validates_with_validator_class_and_options Person.validates :karma, :email => { :message => 'my custom message' } person = Person.new person.valid? - assert person.errors[:karma].include?('my custom message') + assert_equal ['my custom message'], person.errors[:karma] end def test_validates_with_unknown_validator @@ -76,13 +102,13 @@ class ValidatesTest < ActiveModel::TestCase PersonWithValidator.validates :title, :presence => true person = PersonWithValidator.new person.valid? - assert person.errors[:title].include?('Local validator') + assert_equal ['Local validator'], person.errors[:title] end def test_validates_with_included_validator_and_options PersonWithValidator.validates :title, :presence => { :custom => ' please' } person = PersonWithValidator.new person.valid? - assert person.errors[:title].include?('Local validator please') + assert_equal ['Local validator please'], person.errors[:title] end end \ No newline at end of file diff --git a/activemodel/test/models/person.rb b/activemodel/test/models/person.rb index 53ac68055f9..86e8d73bbb8 100644 --- a/activemodel/test/models/person.rb +++ b/activemodel/test/models/person.rb @@ -2,7 +2,7 @@ class Person include ActiveModel::Validations extend ActiveModel::Translation - attr_accessor :title, :karma, :salary + attr_accessor :title, :karma, :salary, :genre, :password def condition_is_true true