From 279067639f319f3b4bbcaf90c26f286e96df2c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 23 Dec 2009 01:37:19 +0100 Subject: validates_each uses a BlockValidator. --- activemodel/lib/active_model.rb | 1 + activemodel/lib/active_model/validations.rb | 56 +++++++++------------- activemodel/lib/active_model/validations/length.rb | 7 ++- activemodel/lib/active_model/validations/with.rb | 4 +- activemodel/lib/active_model/validator.rb | 22 ++++++++- 5 files changed, 49 insertions(+), 41 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index 6aa80da23f..abb04cd624 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -46,6 +46,7 @@ module ActiveModel autoload :ValidationsRepairHelper autoload :Validator autoload :EachValidator, 'active_model/validator' + autoload :BlockValidator, 'active_model/validator' autoload :VERSION module Serializers diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb index 064ec98f3a..f1a15ec1d8 100644 --- a/activemodel/lib/active_model/validations.rb +++ b/activemodel/lib/active_model/validations.rb @@ -12,6 +12,29 @@ module ActiveModel end module ClassMethods + # Validates each attribute against a block. + # + # class Person < ActiveRecord::Base + # validates_each :first_name, :last_name do |record, attr, value| + # record.errors.add attr, 'starts with z.' if value[0] == ?z + # end + # end + # + # Options: + # * :on - Specifies when this validation is active (default is :save, other options :create, :update). + # * :allow_nil - Skip validation if attribute is +nil+. + # * :allow_blank - Skip validation if attribute is blank. + # * :if - Specifies a method, proc or string to call to determine if the validation should + # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The + # method, proc or string should return or evaluate to a true or false value. + # * :unless - Specifies a method, proc or string to call to determine if the validation should + # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The + # method, proc or string should return or evaluate to a true or false value. + def validates_each(*attr_names, &block) + options = attr_names.extract_options!.symbolize_keys + validates_with BlockValidator, options.merge(:attributes => attr_names.flatten), &block + end + # Adds a validation method or block to the class. This is useful when # overriding the +validate+ instance method becomes too unwieldly and # you're looking for more descriptive declaration of your validations. @@ -39,39 +62,6 @@ 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 - # validates_each :first_name, :last_name do |record, attr, value| - # record.errors.add attr, 'starts with z.' if value[0] == ?z - # end - # end - # - # Options: - # * :on - Specifies when this validation is active (default is :save, other options :create, :update). - # * :allow_nil - Skip validation if attribute is +nil+. - # * :allow_blank - Skip validation if attribute is blank. - # * :if - Specifies a method, proc or string to call to determine if the validation should - # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The - # method, proc or string should return or evaluate to a true or false value. - # * :unless - Specifies a method, proc or string to call to determine if the validation should - # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The - # method, proc or string should return or evaluate to a true or false value. - def validates_each(*attrs) - options = attrs.extract_options!.symbolize_keys - attrs = attrs.flatten - - # Declare the validation. - validate options do |record| - attrs.each do |attr| - value = record.send(:read_attribute_for_validation, attr) - next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank]) - yield record, attr, value - end - end - end - def validate(*args, &block) options = args.last if options.is_a?(Hash) && options.key?(:on) diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb index 1214c5f4bf..04280b401b 100644 --- a/activemodel/lib/active_model/validations/length.rb +++ b/activemodel/lib/active_model/validations/length.rb @@ -10,8 +10,8 @@ module ActiveModel def initialize(options) options[:tokenizer] ||= DEFAULT_TOKENIZER - super @type = (OPTIONS & options.keys).first + super end def check_validity! @@ -108,9 +108,8 @@ module ActiveModel # count words as in above example.) # Defaults to lambda{ |value| value.split(//) } which counts individual characters. def validates_length_of(*attr_names) - options = { :tokenizer => DEFAULT_TOKENIZER } - options.update(attr_names.extract_options!) - validates_with LengthValidator, options.merge(:attributes => attr_names, :type => type) + options = attr_names.extract_options! + validates_with LengthValidator, options.merge(:attributes => attr_names) end alias_method :validates_size_of, :validates_length_of diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb index 626e9d5731..8d521173c6 100644 --- a/activemodel/lib/active_model/validations/with.rb +++ b/activemodel/lib/active_model/validations/with.rb @@ -48,9 +48,9 @@ module ActiveModel # end # end # - def validates_with(*args) + def validates_with(*args, &block) options = args.extract_options! - args.each { |klass| validate(klass.new(options), options) } + args.each { |klass| validate(klass.new(options, &block), options) } end end end diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb index 342c4691ff..8c9f9c7fb3 100644 --- a/activemodel/lib/active_model/validator.rb +++ b/activemodel/lib/active_model/validator.rb @@ -51,7 +51,6 @@ module ActiveModel #:nodoc: # @my_custom_field = options[:field_name] || :first_name # end # end - # class Validator attr_reader :options @@ -64,6 +63,11 @@ module ActiveModel #:nodoc: end end + # EachValidator is a validator which iterates through the attributes given + # in the options hash invoking the validate_each method passing in the + # record, attribute and value. + # + # All ActiveModel validations are built on top of this Validator. class EachValidator < Validator attr_reader :attributes @@ -81,11 +85,25 @@ module ActiveModel #:nodoc: end end - def validate_each(record) + def validate_each(record, attribute, value) raise NotImplementedError end def check_validity! end end + + # BlockValidator is a special EachValidator which receives a block on initialization + # and call this block for each attribute being validated. +validates_each+ uses this + # Validator. + class BlockValidator < EachValidator + def initialize(options, &block) + @block = block + super + end + + def validate_each(record, attribute, value) + @block.call(record, attribute, value) + end + end end -- cgit v1.2.3