From 2476c5312dcbd29f49672f71617a3d34c6a60cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 22 Dec 2009 23:12:21 +0100 Subject: Validator is simply sent to validate method. However, the API needs to change, so validate accepts a record. --- activemodel/lib/active_model/validations/with.rb | 8 +++----- activemodel/lib/active_model/validator.rb | 7 +++---- activemodel/test/cases/validations/with_validation_test.rb | 12 ++++++------ 3 files changed, 12 insertions(+), 15 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb index edc2133ddc..8313aded11 100644 --- a/activemodel/lib/active_model/validations/with.rb +++ b/activemodel/lib/active_model/validations/with.rb @@ -49,12 +49,10 @@ module ActiveModel # end # def validates_with(*args) - configuration = args.extract_options! + options = args.extract_options! - validate configuration do |record| - args.each do |klass| - klass.new(record, configuration.except(:on, :if, :unless)).validate - end + args.each do |klass| + validate klass.new(options.except(:on, :if, :unless)), options end end end diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb index 09de72b757..80a538fcb6 100644 --- a/activemodel/lib/active_model/validator.rb +++ b/activemodel/lib/active_model/validator.rb @@ -54,14 +54,13 @@ module ActiveModel #:nodoc: # end # class Validator - attr_reader :record, :options + attr_reader :options - def initialize(record, options) - @record = record + def initialize(options) @options = options end - def validate + def validate(record) raise "You must override this method" end end diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb index fae87a6188..4350ad69ec 100644 --- a/activemodel/test/cases/validations/with_validation_test.rb +++ b/activemodel/test/cases/validations/with_validation_test.rb @@ -14,24 +14,24 @@ class ValidatesWithTest < ActiveRecord::TestCase OTHER_ERROR_MESSAGE = "Validation error from other validator" class ValidatorThatAddsErrors < ActiveModel::Validator - def validate() + def validate(record) record.errors[:base] << ERROR_MESSAGE end end class OtherValidatorThatAddsErrors < ActiveModel::Validator - def validate() + def validate(record) record.errors[:base] << OTHER_ERROR_MESSAGE end end class ValidatorThatDoesNotAddErrors < ActiveModel::Validator - def validate() + def validate(record) end end class ValidatorThatValidatesOptions < ActiveModel::Validator - def validate() + def validate(record) if options[:field] == :first_name record.errors[:base] << ERROR_MESSAGE end @@ -101,8 +101,8 @@ class ValidatesWithTest < ActiveRecord::TestCase test "passes all non-standard configuration options to the validator class" do topic = Topic.new validator = mock() - validator.expects(:new).with(topic, {:foo => :bar}).returns(validator) - validator.expects(:validate) + validator.expects(:new).with({:foo => :bar}).returns(validator) + validator.expects(:validate).with(topic) Topic.validates_with(validator, :if => "1 == 1", :foo => :bar) assert topic.valid? -- cgit v1.2.3 From f1085f41287687835659fa23079080204fe32e96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 23 Dec 2009 00:36:51 +0100 Subject: Move validations in ActiveModel to validators, however all validatity checks are still in the class method. --- activemodel/lib/active_model.rb | 5 +- .../lib/active_model/validations/acceptance.rb | 18 ++-- .../lib/active_model/validations/confirmation.rb | 20 ++-- .../lib/active_model/validations/exclusion.rb | 21 +++-- activemodel/lib/active_model/validations/format.rb | 28 +++--- .../lib/active_model/validations/inclusion.rb | 21 +++-- activemodel/lib/active_model/validations/length.rb | 91 +++++++++--------- .../lib/active_model/validations/numericality.rb | 105 +++++++++++---------- .../lib/active_model/validations/presence.rb | 15 +-- activemodel/lib/active_model/validations/with.rb | 5 +- activemodel/lib/active_model/validator.rb | 24 ++++- .../test/cases/validations/with_validation_test.rb | 4 +- 12 files changed, 200 insertions(+), 157 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index e0de27b96d..6aa80da23f 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -35,9 +35,9 @@ module ActiveModel autoload :Dirty autoload :Errors autoload :Lint - autoload :Name, 'active_model/naming' + autoload :Name, 'active_model/naming' autoload :Naming - autoload :Observer, 'active_model/observing' + autoload :Observer, 'active_model/observing' autoload :Observing autoload :Serialization autoload :StateMachine @@ -45,6 +45,7 @@ module ActiveModel autoload :Validations autoload :ValidationsRepairHelper autoload :Validator + autoload :EachValidator, 'active_model/validator' autoload :VERSION module Serializers diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb index b65c9b933d..a5de58cd41 100644 --- a/activemodel/lib/active_model/validations/acceptance.rb +++ b/activemodel/lib/active_model/validations/acceptance.rb @@ -1,5 +1,13 @@ module ActiveModel module Validations + class AcceptanceValidator < EachValidator + def validate_each(record, attribute, value) + unless value == options[:accept] + record.errors.add(attribute, :accepted, :default => options[:message]) + end + end + end + module ClassMethods # Encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example: # @@ -25,8 +33,8 @@ module ActiveModel # 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_acceptance_of(*attr_names) - configuration = { :allow_nil => true, :accept => "1" } - configuration.update(attr_names.extract_options!) + options = { :allow_nil => true, :accept => "1" } + options.update(attr_names.extract_options!) db_cols = begin column_names @@ -37,11 +45,7 @@ module ActiveModel names = attr_names.reject { |name| db_cols.include?(name.to_s) } attr_accessor(*names) - validates_each(attr_names,configuration) do |record, attr_name, value| - unless value == configuration[:accept] - record.errors.add(attr_name, :accepted, :default => configuration[:message]) - end - end + validates_with AcceptanceValidator, options.merge(:attributes => attr_names) end end end diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb index d414224dd2..b06effdceb 100644 --- a/activemodel/lib/active_model/validations/confirmation.rb +++ b/activemodel/lib/active_model/validations/confirmation.rb @@ -1,5 +1,13 @@ module ActiveModel module Validations + class ConfirmationValidator < EachValidator + def validate_each(record, attribute, value) + confirmed = record.send(:"#{attribute}_confirmation") + return if confirmed.nil? || value == confirmed + record.errors.add(attribute, :confirmation, :default => options[:message]) + end + end + module ClassMethods # Encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example: # @@ -30,15 +38,9 @@ module ActiveModel # 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_confirmation_of(*attr_names) - configuration = attr_names.extract_options! - - attr_accessor(*(attr_names.map { |n| "#{n}_confirmation" })) - - validates_each(attr_names, configuration) do |record, attr_name, value| - 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 + options = attr_names.extract_options! + attr_accessor(*(attr_names.map { |n| :"#{n}_confirmation" })) + validates_with ConfirmationValidator, options.merge(:attributes => attr_names) end end end diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb index 2cfdec97a5..209b1c76f9 100644 --- a/activemodel/lib/active_model/validations/exclusion.rb +++ b/activemodel/lib/active_model/validations/exclusion.rb @@ -1,5 +1,12 @@ module ActiveModel module Validations + class ExclusionValidator < EachValidator + def validate_each(record, attribute, value) + return unless options[:in].include?(value) + record.errors.add(attribute, :exclusion, :default => options[:message], :value => value) + end + end + module ClassMethods # Validates that the value of the specified attribute is not in a particular enumerable object. # @@ -21,17 +28,13 @@ module ActiveModel # 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_exclusion_of(*attr_names) - configuration = attr_names.extract_options! - - enum = configuration[:in] || configuration[:within] + options = attr_names.extract_options! + options[:in] ||= options.delete(:within) - 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 options[:in].respond_to?(:include?) - validates_each(attr_names, configuration) do |record, attr_name, value| - if enum.include?(value) - record.errors.add(attr_name, :exclusion, :default => configuration[:message], :value => value) - end - end + validates_with ExclusionValidator, options.merge(:attributes => attr_names) end end end diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb index c670dafc7c..d5427c2b03 100644 --- a/activemodel/lib/active_model/validations/format.rb +++ b/activemodel/lib/active_model/validations/format.rb @@ -1,5 +1,15 @@ module ActiveModel module Validations + class FormatValidator < EachValidator + def validate_each(record, attribute, value) + if options[:with] && value.to_s !~ options[:with] + record.errors.add(attribute, :invalid, :default => options[:message], :value => value) + elsif options[:without] && value.to_s =~ options[:without] + record.errors.add(attribute, :invalid, :default => options[:message], :value => value) + end + end + end + module ClassMethods # Validates whether the value of the specified attribute is of the correct form, going by the regular expression provided. # You can require that the attribute matches the regular expression: @@ -33,29 +43,21 @@ module ActiveModel # 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_format_of(*attr_names) - configuration = attr_names.extract_options! + options = attr_names.extract_options! - unless configuration.include?(:with) ^ configuration.include?(:without) # ^ == xor, or "exclusive or" + unless options.include?(:with) ^ options.include?(:without) # ^ == xor, or "exclusive or" raise ArgumentError, "Either :with or :without must be supplied (but not both)" end - if configuration[:with] && !configuration[:with].is_a?(Regexp) + if options[:with] && !options[:with].is_a?(Regexp) raise ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash" end - if configuration[:without] && !configuration[:without].is_a?(Regexp) + if options[:without] && !options[:without].is_a?(Regexp) raise ArgumentError, "A regular expression must be supplied as the :without option of the configuration hash" end - if configuration[:with] - validates_each(attr_names, configuration) do |record, attr_name, value| - record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value) if value.to_s !~ configuration[:with] - end - elsif configuration[:without] - validates_each(attr_names, configuration) do |record, attr_name, value| - record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value) if value.to_s =~ configuration[:without] - end - end + validates_with FormatValidator, options.merge(:attributes => attr_names) end end end diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb index 0d7dc5cd64..d42c95357c 100644 --- a/activemodel/lib/active_model/validations/inclusion.rb +++ b/activemodel/lib/active_model/validations/inclusion.rb @@ -1,5 +1,12 @@ module ActiveModel module Validations + class InclusionValidator < EachValidator + def validate_each(record, attribute, value) + return if options[:in].include?(value) + record.errors.add(attribute, :inclusion, :default => options[:message], :value => value) + end + end + module ClassMethods # Validates whether the value of the specified attribute is available in a particular enumerable object. # @@ -21,17 +28,13 @@ module ActiveModel # 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_inclusion_of(*attr_names) - configuration = attr_names.extract_options! - - enum = configuration[:in] || configuration[:within] + options = attr_names.extract_options! + options[:in] ||= options.delete(:within) - 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 options[:in].respond_to?(:include?) - validates_each(attr_names, configuration) do |record, attr_name, value| - unless enum.include?(value) - record.errors.add(attr_name, :inclusion, :default => configuration[:message], :value => value) - end - end + validates_with InclusionValidator, options.merge(:attributes => attr_names) end end end diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb index e91841bd1c..66b2ae5b18 100644 --- a/activemodel/lib/active_model/validations/length.rb +++ b/activemodel/lib/active_model/validations/length.rb @@ -1,7 +1,44 @@ module ActiveModel module Validations + class LengthValidator < EachValidator + MESSAGES = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }.freeze + CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze + + attr_reader :type + + def initialize(options) + @type = options.delete(:type) + super + end + + def validate_each(record, attribute, value) + checks = options.slice(:minimum, :maximum, :is) + value = options[:tokenizer].call(value) if value.kind_of?(String) + + if [:within, :in].include?(type) + range = options[type] + checks[:minimum], checks[:maximum] = range.begin, range.end + checks[:maximum] -= 1 if range.exclude_end? + end + + checks.each do |key, check_value| + custom_message = options[:message] || options[MESSAGES[key]] + validity_check = CHECKS[key] + + valid_value = if key == :maximum + value.nil? || value.size.send(validity_check, check_value) + else + value && value.size.send(validity_check, check_value) + end + + record.errors.add(attribute, MESSAGES[key], :default => custom_message, :count => check_value) unless valid_value + end + end + end + module ClassMethods ALL_RANGE_OPTIONS = [ :is, :within, :in, :minimum, :maximum ].freeze + DEFAULT_TOKENIZER = lambda { |value| value.split(//) } # Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time: # @@ -38,62 +75,32 @@ module ActiveModel # * :tokenizer - Specifies how to split up the attribute string. (e.g. :tokenizer => lambda {|str| str.scan(/\w+/)} to # count words as in above example.) # Defaults to lambda{ |value| value.split(//) } which counts individual characters. - def validates_length_of(*attrs) - # Merge given options with defaults. - options = { :tokenizer => lambda {|value| value.split(//)} } - options.update(attrs.extract_options!.symbolize_keys) + def validates_length_of(*attr_names) + options = { :tokenizer => DEFAULT_TOKENIZER } + options.update(attr_names.extract_options!) # Ensure that one and only one range option is specified. range_options = ALL_RANGE_OPTIONS & options.keys case range_options.size when 0 - raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.' + raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.' when 1 # Valid number of options; do nothing. else - raise ArgumentError, 'Too many range options specified. Choose only one.' + raise ArgumentError, 'Too many range options specified. Choose only one.' end - # 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] + type = range_options.first + value = options[type] - case option + case type when :within, :in - raise ArgumentError, ":#{option} must be a Range" unless option_value.is_a?(Range) - - validates_each(attrs, options) do |record, attr, value| - value = options[:tokenizer].call(value) if value.kind_of?(String) - - min, max = option_value.begin, option_value.end - max = max - 1 if option_value.exclude_end? - - if value.nil? || value.size < min - record.errors.add(attr, :too_short, :default => custom_message || options[:too_short], :count => min) - elsif value.size > max - record.errors.add(attr, :too_long, :default => custom_message || options[:too_long], :count => max) - end - end + raise ArgumentError, ":#{type} must be a Range" unless value.is_a?(Range) 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 => "<=" } - - validates_each(attrs, options) do |record, attr, value| - value = options[:tokenizer].call(value) if value.kind_of?(String) - - valid_value = if option == :maximum - value.nil? || value.size.send(validity_checks[option], option_value) - else - value && value.size.send(validity_checks[option], option_value) - end - - record.errors.add(attr, key, :default => custom_message, :count => option_value) unless valid_value - end + raise ArgumentError, ":#{type} must be a nonnegative Integer" unless value.is_a?(Integer) && value >= 0 end + + validates_with LengthValidator, options.merge(:attributes => attr_names, :type => type) end alias_method :validates_size_of, :validates_length_of diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb index 32dbcd82d0..8ffa395a2d 100644 --- a/activemodel/lib/active_model/validations/numericality.rb +++ b/activemodel/lib/active_model/validations/numericality.rb @@ -1,9 +1,55 @@ module ActiveModel module Validations + class NumericalityValidator < EachValidator + CHECKS = { :greater_than => '>', :greater_than_or_equal_to => '>=', + :equal_to => '==', :less_than => '<', :less_than_or_equal_to => '<=', + :odd => 'odd?', :even => 'even?' }.freeze + + def validate_each(record, attr_name, value) + before_type_cast = "#{attr_name}_before_type_cast" + + if record.respond_to?(before_type_cast.to_sym) + raw_value = record.send("#{attr_name}_before_type_cast") || value + else + raw_value = value + end + + return if options[:allow_nil] and raw_value.nil? + + if options[:only_integer] + unless raw_value.to_s =~ /\A[+-]?\d+\Z/ + record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => options[:message]) + return + end + raw_value = raw_value.to_i + else + begin + raw_value = Kernel.Float(raw_value) + rescue ArgumentError, TypeError + record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => options[:message]) + return + end + end + + options.slice(*CHECKS.keys).each do |option, option_value| + case option + when :odd, :even + unless raw_value.to_i.method(CHECKS[option])[] + record.errors.add(attr_name, option, :value => raw_value, :default => options[:message]) + end + else + option_value = option_value.call(record) if option_value.is_a? Proc + option_value = record.method(option_value).call if option_value.is_a? Symbol + + unless raw_value.method(CHECKS[option])[option_value] + record.errors.add(attr_name, option, :default => options[:message], :value => raw_value, :count => option_value) + end + end + end + end + end module ClassMethods - ALL_NUMERICALITY_CHECKS = { :greater_than => '>', :greater_than_or_equal_to => '>=', - :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 only_integer is false) or applying it to the regular expression @@ -44,61 +90,18 @@ module ActiveModel # validates_numericality_of :width, :greater_than => :minimum_weight # end # - # - def validates_numericality_of(*attr_names) - configuration = { :only_integer => false, :allow_nil => false } - configuration.update(attr_names.extract_options!) + options = { :only_integer => false, :allow_nil => false } + options.update(attr_names.extract_options!) - numericality_options = ALL_NUMERICALITY_CHECKS.keys & configuration.keys + numericality_options = NumericalityValidator::CHECKS.keys & options.keys (numericality_options - [ :odd, :even ]).each do |option| - value = configuration[option] + value = options[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) end - validates_each(attr_names,configuration) do |record, attr_name, value| - before_type_cast = "#{attr_name}_before_type_cast" - - if record.respond_to?(before_type_cast.to_sym) - raw_value = record.send("#{attr_name}_before_type_cast") || value - else - raw_value = value - end - - next if configuration[:allow_nil] and raw_value.nil? - - if configuration[:only_integer] - unless raw_value.to_s =~ /\A[+-]?\d+\Z/ - 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) - rescue ArgumentError, TypeError - record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message]) - next - end - end - - numericality_options.each do |option| - case option - when :odd, :even - unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[] - record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message]) - end - else - configuration[option] = configuration[option].call(record) if configuration[option].is_a? Proc - configuration[option] = record.method(configuration[option]).call if configuration[option].is_a? Symbol - - 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 + validates_with NumericalityValidator, options.merge(:attributes => attr_names) end end end diff --git a/activemodel/lib/active_model/validations/presence.rb b/activemodel/lib/active_model/validations/presence.rb index 3ff677c137..a4c6f866a7 100644 --- a/activemodel/lib/active_model/validations/presence.rb +++ b/activemodel/lib/active_model/validations/presence.rb @@ -2,6 +2,12 @@ require 'active_support/core_ext/object/blank' module ActiveModel module Validations + class PresenceValidator < EachValidator + def validate(record) + record.errors.add_on_blank(attributes, options[:message]) + end + end + module ClassMethods # Validates that the specified attributes are not blank (as defined by Object#blank?). Happens by default on save. Example: # @@ -28,13 +34,8 @@ module ActiveModel # The method, proc or string should return or evaluate to a true or false value. # def validates_presence_of(*attr_names) - configuration = attr_names.extract_options! - - # can't use validates_each here, because it cannot cope with nonexistent attributes, - # while errors.add_on_empty can - validate configuration do |record| - record.errors.add_on_blank(attr_names, configuration[:message]) - end + options = attr_names.extract_options! + validates_with PresenceValidator, options.merge(:attributes => attr_names) end end end diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb index 8313aded11..626e9d5731 100644 --- a/activemodel/lib/active_model/validations/with.rb +++ b/activemodel/lib/active_model/validations/with.rb @@ -50,10 +50,7 @@ module ActiveModel # def validates_with(*args) options = args.extract_options! - - args.each do |klass| - validate klass.new(options.except(:on, :if, :unless)), options - end + args.each { |klass| validate(klass.new(options), options) } end end end diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb index 80a538fcb6..6885c6800e 100644 --- a/activemodel/lib/active_model/validator.rb +++ b/activemodel/lib/active_model/validator.rb @@ -1,5 +1,4 @@ module ActiveModel #:nodoc: - # A simple base class that can be used along with ActiveModel::Base.validates_with # # class Person < ActiveModel::Base @@ -61,7 +60,28 @@ module ActiveModel #:nodoc: end def validate(record) - raise "You must override this method" + raise NotImplementedError + end + end + + class EachValidator < Validator + attr_reader :attributes + + def initialize(options) + @attributes = options.delete(:attributes) + super + end + + def validate(record) + attributes.each do |attribute| + value = record.send(:read_attribute_for_validation, attribute) + next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank]) + validate_each(record, attribute, value) + end + end + + def validate_each(record) + raise NotImplementedError end end end diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb index 4350ad69ec..953ff8aa2d 100644 --- a/activemodel/test/cases/validations/with_validation_test.rb +++ b/activemodel/test/cases/validations/with_validation_test.rb @@ -98,10 +98,10 @@ class ValidatesWithTest < ActiveRecord::TestCase assert topic.errors[:base].include?(ERROR_MESSAGE) end - test "passes all non-standard configuration options to the validator class" do + test "passes all configuration options to the validator class" do topic = Topic.new validator = mock() - validator.expects(:new).with({:foo => :bar}).returns(validator) + validator.expects(:new).with(:foo => :bar, :if => "1 == 1").returns(validator) validator.expects(:validate).with(topic) Topic.validates_with(validator, :if => "1 == 1", :foo => :bar) -- cgit v1.2.3 From 977a5c43b160d8aa8d1b87bb0feb54db85fe203c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 23 Dec 2009 01:08:27 +0100 Subject: Added check_validity! to EachValidator and refactor existing ones. --- .../lib/active_model/validations/exclusion.rb | 9 +-- .../lib/active_model/validations/inclusion.rb | 9 +-- activemodel/lib/active_model/validations/length.rb | 64 ++++++++++-------- .../lib/active_model/validations/numericality.rb | 78 +++++++++++----------- activemodel/lib/active_model/validator.rb | 4 ++ 5 files changed, 90 insertions(+), 74 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb index 209b1c76f9..f8759f253b 100644 --- a/activemodel/lib/active_model/validations/exclusion.rb +++ b/activemodel/lib/active_model/validations/exclusion.rb @@ -1,6 +1,11 @@ module ActiveModel module Validations class ExclusionValidator < EachValidator + def check_validity! + raise ArgumentError, "An object with the method include? is required must be supplied as the " << + ":in option of the configuration hash" unless options[:in].respond_to?(:include?) + end + def validate_each(record, attribute, value) return unless options[:in].include?(value) record.errors.add(attribute, :exclusion, :default => options[:message], :value => value) @@ -30,10 +35,6 @@ module ActiveModel def validates_exclusion_of(*attr_names) options = attr_names.extract_options! options[:in] ||= options.delete(:within) - - raise ArgumentError, "An object with the method include? is required must be supplied as the " << - ":in option of the configuration hash" unless options[:in].respond_to?(:include?) - validates_with ExclusionValidator, options.merge(:attributes => attr_names) end end diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb index d42c95357c..a122e9e737 100644 --- a/activemodel/lib/active_model/validations/inclusion.rb +++ b/activemodel/lib/active_model/validations/inclusion.rb @@ -1,6 +1,11 @@ module ActiveModel module Validations class InclusionValidator < EachValidator + def check_validity! + raise ArgumentError, "An object with the method include? is required must be supplied as the " << + ":in option of the configuration hash" unless options[:in].respond_to?(:include?) + end + def validate_each(record, attribute, value) return if options[:in].include?(value) record.errors.add(attribute, :inclusion, :default => options[:message], :value => value) @@ -30,10 +35,6 @@ module ActiveModel def validates_inclusion_of(*attr_names) options = attr_names.extract_options! options[:in] ||= options.delete(:within) - - raise ArgumentError, "An object with the method include? is required must be supplied as the " << - ":in option of the configuration hash" unless options[:in].respond_to?(:include?) - validates_with InclusionValidator, options.merge(:attributes => attr_names) end end diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb index 66b2ae5b18..1214c5f4bf 100644 --- a/activemodel/lib/active_model/validations/length.rb +++ b/activemodel/lib/active_model/validations/length.rb @@ -1,16 +1,24 @@ module ActiveModel module Validations class LengthValidator < EachValidator - MESSAGES = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }.freeze - CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze + OPTIONS = [ :is, :within, :in, :minimum, :maximum ].freeze + MESSAGES = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }.freeze + CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze + DEFAULT_TOKENIZER = lambda { |value| value.split(//) } attr_reader :type def initialize(options) - @type = options.delete(:type) + options[:tokenizer] ||= DEFAULT_TOKENIZER super + @type = (OPTIONS & options.keys).first end + def check_validity! + ensure_one_range_option! + ensure_argument_types! + end + def validate_each(record, attribute, value) checks = options.slice(:minimum, :maximum, :is) value = options[:tokenizer].call(value) if value.kind_of?(String) @@ -34,11 +42,35 @@ module ActiveModel record.errors.add(attribute, MESSAGES[key], :default => custom_message, :count => check_value) unless valid_value end end + + protected + + def ensure_one_range_option! #:nodoc: + range_options = OPTIONS & options.keys + + case range_options.size + when 0 + raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.' + when 1 + # Valid number of options; do nothing. + else + raise ArgumentError, 'Too many range options specified. Choose only one.' + end + end + + def ensure_argument_types! #:nodoc: + value = options[type] + + case type + when :within, :in + raise ArgumentError, ":#{type} must be a Range" unless value.is_a?(Range) + when :is, :minimum, :maximum + raise ArgumentError, ":#{type} must be a nonnegative Integer" unless value.is_a?(Integer) && value >= 0 + end + end end module ClassMethods - ALL_RANGE_OPTIONS = [ :is, :within, :in, :minimum, :maximum ].freeze - DEFAULT_TOKENIZER = lambda { |value| value.split(//) } # Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time: # @@ -78,28 +110,6 @@ module ActiveModel def validates_length_of(*attr_names) options = { :tokenizer => DEFAULT_TOKENIZER } options.update(attr_names.extract_options!) - - # Ensure that one and only one range option is specified. - range_options = ALL_RANGE_OPTIONS & options.keys - case range_options.size - when 0 - raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.' - when 1 - # Valid number of options; do nothing. - else - raise ArgumentError, 'Too many range options specified. Choose only one.' - end - - type = range_options.first - value = options[type] - - case type - when :within, :in - raise ArgumentError, ":#{type} must be a Range" unless value.is_a?(Range) - when :is, :minimum, :maximum - raise ArgumentError, ":#{type} must be a nonnegative Integer" unless value.is_a?(Integer) && value >= 0 - end - validates_with LengthValidator, options.merge(:attributes => attr_names, :type => type) end diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb index 8ffa395a2d..914a3133cf 100644 --- a/activemodel/lib/active_model/validations/numericality.rb +++ b/activemodel/lib/active_model/validations/numericality.rb @@ -1,56 +1,64 @@ module ActiveModel module Validations class NumericalityValidator < EachValidator - CHECKS = { :greater_than => '>', :greater_than_or_equal_to => '>=', - :equal_to => '==', :less_than => '<', :less_than_or_equal_to => '<=', - :odd => 'odd?', :even => 'even?' }.freeze + CHECKS = { :greater_than => :>, :greater_than_or_equal_to => :>=, + :equal_to => :==, :less_than => :<, :less_than_or_equal_to => :<=, + :odd => :odd?, :even => :even? }.freeze + + 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) + end + end def validate_each(record, attr_name, value) before_type_cast = "#{attr_name}_before_type_cast" - if record.respond_to?(before_type_cast.to_sym) - raw_value = record.send("#{attr_name}_before_type_cast") || value - else - raw_value = value - end + raw_value = record.send("#{attr_name}_before_type_cast") if record.respond_to?(before_type_cast.to_sym) + raw_value ||= value - return if options[:allow_nil] and raw_value.nil? + return if options[:allow_nil] && raw_value.nil? - if options[:only_integer] - unless raw_value.to_s =~ /\A[+-]?\d+\Z/ - record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => options[:message]) - return - end - raw_value = raw_value.to_i - else - begin - raw_value = Kernel.Float(raw_value) - rescue ArgumentError, TypeError - record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => options[:message]) - return - end + unless value = parse_raw_value(raw_value, options) + record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => options[:message]) + return end options.slice(*CHECKS.keys).each do |option, option_value| case option when :odd, :even - unless raw_value.to_i.method(CHECKS[option])[] - record.errors.add(attr_name, option, :value => raw_value, :default => options[:message]) + unless value.to_i.send(CHECKS[option]) + record.errors.add(attr_name, option, :value => value, :default => options[:message]) end else - option_value = option_value.call(record) if option_value.is_a? Proc - option_value = record.method(option_value).call if option_value.is_a? Symbol - - unless raw_value.method(CHECKS[option])[option_value] - record.errors.add(attr_name, option, :default => options[:message], :value => raw_value, :count => option_value) + option_value = option_value.call(record) if option_value.is_a?(Proc) + option_value = record.send(option_value) if option_value.is_a?(Symbol) + + unless value.send(CHECKS[option], option_value) + record.errors.add(attr_name, option, :default => options[:message], :value => value, :count => option_value) end end end end - end - module ClassMethods + protected + def parse_raw_value(raw_value, options) + if options[:only_integer] + raw_value.to_i if raw_value.to_s =~ /\A[+-]?\d+\Z/ + else + begin + Kernel.Float(raw_value) + rescue ArgumentError, TypeError + nil + end + end + end + + end + + module ClassMethods # Validates whether the value of the specified attribute is numeric by trying to convert it to # a float with Kernel.Float (if only_integer is false) or applying it to the regular expression # /\A[\+\-]?\d+\Z/ (if only_integer is set to true). @@ -93,14 +101,6 @@ module ActiveModel def validates_numericality_of(*attr_names) options = { :only_integer => false, :allow_nil => false } options.update(attr_names.extract_options!) - - numericality_options = NumericalityValidator::CHECKS.keys & options.keys - - (numericality_options - [ :odd, :even ]).each do |option| - value = options[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) - end - validates_with NumericalityValidator, options.merge(:attributes => attr_names) end end diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb index 6885c6800e..342c4691ff 100644 --- a/activemodel/lib/active_model/validator.rb +++ b/activemodel/lib/active_model/validator.rb @@ -70,6 +70,7 @@ module ActiveModel #:nodoc: def initialize(options) @attributes = options.delete(:attributes) super + check_validity! end def validate(record) @@ -83,5 +84,8 @@ module ActiveModel #:nodoc: def validate_each(record) raise NotImplementedError end + + def check_validity! + end end end -- cgit v1.2.3 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 From 44cd9e0e7132abe632664377f13f3edd1106685a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 23 Dec 2009 12:14:00 +0100 Subject: ActiveRecord::Validations are now built on top of Validator as well. --- activemodel/lib/active_model/validations/acceptance.rb | 7 +++++-- activemodel/lib/active_model/validations/length.rb | 3 +-- activemodel/lib/active_model/validations/numericality.rb | 7 +++++-- 3 files changed, 11 insertions(+), 6 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb index a5de58cd41..bd9463ed27 100644 --- a/activemodel/lib/active_model/validations/acceptance.rb +++ b/activemodel/lib/active_model/validations/acceptance.rb @@ -1,6 +1,10 @@ module ActiveModel module Validations class AcceptanceValidator < EachValidator + def initialize(options) + super(options.reverse_merge(:allow_nil => true, :accept => "1")) + end + def validate_each(record, attribute, value) unless value == options[:accept] record.errors.add(attribute, :accepted, :default => options[:message]) @@ -33,8 +37,7 @@ module ActiveModel # 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_acceptance_of(*attr_names) - options = { :allow_nil => true, :accept => "1" } - options.update(attr_names.extract_options!) + options = attr_names.extract_options! db_cols = begin column_names diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb index 04280b401b..6e90a75c17 100644 --- a/activemodel/lib/active_model/validations/length.rb +++ b/activemodel/lib/active_model/validations/length.rb @@ -9,9 +9,8 @@ module ActiveModel attr_reader :type def initialize(options) - options[:tokenizer] ||= DEFAULT_TOKENIZER @type = (OPTIONS & options.keys).first - super + super(options.reverse_merge(:tokenizer => DEFAULT_TOKENIZER)) end def check_validity! diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb index 914a3133cf..f2aab8c5b8 100644 --- a/activemodel/lib/active_model/validations/numericality.rb +++ b/activemodel/lib/active_model/validations/numericality.rb @@ -5,6 +5,10 @@ module ActiveModel :equal_to => :==, :less_than => :<, :less_than_or_equal_to => :<=, :odd => :odd?, :even => :even? }.freeze + def initialize(options) + super(options.reverse_merge(:only_integer => false, :allow_nil => false)) + end + def check_validity! options.slice(*CHECKS.keys) do |option, value| next if [:odd, :even].include?(option) @@ -99,8 +103,7 @@ module ActiveModel # end # def validates_numericality_of(*attr_names) - options = { :only_integer => false, :allow_nil => false } - options.update(attr_names.extract_options!) + options = attr_names.extract_options! validates_with NumericalityValidator, options.merge(:attributes => attr_names) end end -- cgit v1.2.3 From e31077c9aaec05bdf5ea0386eb42fcc039d86a0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 23 Dec 2009 12:28:02 +0100 Subject: Small clean up in Naming and TTranslation tests. --- activemodel/lib/active_model/naming.rb | 26 +++++++++++++++++---- activemodel/lib/active_model/translation.rb | 22 +----------------- activemodel/test/cases/naming_test.rb | 3 ++- activemodel/test/cases/translation_test.rb | 36 ++++++++++++----------------- activemodel/test/models/person.rb | 4 ++++ activemodel/test/models/track_back.rb | 4 ++++ 6 files changed, 48 insertions(+), 47 deletions(-) create mode 100644 activemodel/test/models/track_back.rb (limited to 'activemodel') diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb index 675d62b9a6..4cd68a0c89 100644 --- a/activemodel/lib/active_model/naming.rb +++ b/activemodel/lib/active_model/naming.rb @@ -2,11 +2,11 @@ require 'active_support/inflector' module ActiveModel class Name < String - attr_reader :singular, :plural, :element, :collection, :partial_path, :human + attr_reader :singular, :plural, :element, :collection, :partial_path alias_method :cache_key, :collection - def initialize(klass, name) - super(name) + def initialize(klass) + super(klass.name) @klass = klass @singular = ActiveSupport::Inflector.underscore(self).tr('/', '_').freeze @plural = ActiveSupport::Inflector.pluralize(@singular).freeze @@ -15,13 +15,31 @@ module ActiveModel @collection = ActiveSupport::Inflector.tableize(self).freeze @partial_path = "#{@collection}/#{@element}".freeze end + + # Transform the model name into a more humane format, using I18n. By default, + # it will underscore then humanize the class name (BlogPost.model_name.human #=> "Blog post"). + # Specify +options+ with additional translating options. + def human(options={}) + return @human unless @klass.respond_to?(:lookup_ancestors) && + @klass.respond_to?(:i18n_scope) + + defaults = @klass.lookup_ancestors.map do |klass| + klass.model_name.underscore.to_sym + end + + defaults << options.delete(:default) if options[:default] + defaults << @human + + options.reverse_merge! :scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults + I18n.translate(defaults.shift, options) + end end module Naming # Returns an ActiveModel::Name object for module. It can be # used to retrieve all kinds of naming-related information. def model_name - @_model_name ||= ActiveModel::Name.new(self, name) + @_model_name ||= ActiveModel::Name.new(self) end end end diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb index 42ca463f82..e5ef1e6114 100644 --- a/activemodel/lib/active_model/translation.rb +++ b/activemodel/lib/active_model/translation.rb @@ -37,28 +37,8 @@ module ActiveModel # Model.human_name is deprecated. Use Model.model_name.human instead. def human_name(*args) - ActiveSupport::Deprecation.warn("human_name has been deprecated, please use model_name.human instead", caller[0,1]) + ActiveSupport::Deprecation.warn("human_name has been deprecated, please use model_name.human instead", caller[0,5]) model_name.human(*args) end end - - class Name < String - # Transform the model name into a more humane format, using I18n. By default, - # it will underscore then humanize the class name (BlogPost.human_name #=> "Blog post"). - # Specify +options+ with additional translating options. - def human(options={}) - return @human unless @klass.respond_to?(:lookup_ancestors) && - @klass.respond_to?(:i18n_scope) - - defaults = @klass.lookup_ancestors.map do |klass| - klass.model_name.underscore.to_sym - end - - defaults << options.delete(:default) if options[:default] - defaults << @human - - options.reverse_merge! :scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults - I18n.translate(defaults.shift, options) - end - end end diff --git a/activemodel/test/cases/naming_test.rb b/activemodel/test/cases/naming_test.rb index fe1ea36450..dc39b84ed8 100644 --- a/activemodel/test/cases/naming_test.rb +++ b/activemodel/test/cases/naming_test.rb @@ -1,8 +1,9 @@ require 'cases/helper' +require 'models/track_back' class NamingTest < ActiveModel::TestCase def setup - @model_name = ActiveModel::Name.new(self, 'Post::TrackBack') + @model_name = ActiveModel::Name.new(Post::TrackBack) end def test_singular diff --git a/activemodel/test/cases/translation_test.rb b/activemodel/test/cases/translation_test.rb index d171784963..bfc1ca12e6 100644 --- a/activemodel/test/cases/translation_test.rb +++ b/activemodel/test/cases/translation_test.rb @@ -1,11 +1,5 @@ require 'cases/helper' - -class SuperUser - extend ActiveModel::Translation -end - -class User < SuperUser -end +require 'models/person' class ActiveModelI18nTests < ActiveModel::TestCase @@ -14,38 +8,38 @@ class ActiveModelI18nTests < ActiveModel::TestCase end def test_translated_model_attributes - I18n.backend.store_translations 'en', :activemodel => {:attributes => {:super_user => {:name => 'super_user name attribute'} } } - assert_equal 'super_user name attribute', SuperUser.human_attribute_name('name') + I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:name => 'person name attribute'} } } + assert_equal 'person name attribute', Person.human_attribute_name('name') end def test_translated_model_attributes_with_symbols - I18n.backend.store_translations 'en', :activemodel => {:attributes => {:super_user => {:name => 'super_user name attribute'} } } - assert_equal 'super_user name attribute', SuperUser.human_attribute_name(:name) + I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:name => 'person name attribute'} } } + assert_equal 'person name attribute', Person.human_attribute_name(:name) end def test_translated_model_attributes_with_ancestor - I18n.backend.store_translations 'en', :activemodel => {:attributes => {:user => {:name => 'user name attribute'} } } - assert_equal 'user name attribute', User.human_attribute_name('name') + I18n.backend.store_translations 'en', :activemodel => {:attributes => {:child => {:name => 'child name attribute'} } } + assert_equal 'child name attribute', Child.human_attribute_name('name') end def test_translated_model_attributes_with_ancestors_fallback - I18n.backend.store_translations 'en', :activemodel => {:attributes => {:super_user => {:name => 'super_user name attribute'} } } - assert_equal 'super_user name attribute', User.human_attribute_name('name') + I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:name => 'person name attribute'} } } + assert_equal 'person name attribute', Child.human_attribute_name('name') end def test_translated_model_names - I18n.backend.store_translations 'en', :activemodel => {:models => {:super_user => 'super_user model'} } - assert_equal 'super_user model', SuperUser.model_name.human + I18n.backend.store_translations 'en', :activemodel => {:models => {:person => 'person model'} } + assert_equal 'person model', Person.model_name.human end def test_translated_model_names_with_sti - I18n.backend.store_translations 'en', :activemodel => {:models => {:user => 'user model'} } - assert_equal 'user model', User.model_name.human + I18n.backend.store_translations 'en', :activemodel => {:models => {:child => 'child model'} } + assert_equal 'child model', Child.model_name.human end def test_translated_model_names_with_ancestors_fallback - I18n.backend.store_translations 'en', :activemodel => {:models => {:super_user => 'super_user model'} } - assert_equal 'super_user model', User.model_name.human + I18n.backend.store_translations 'en', :activemodel => {:models => {:person => 'person model'} } + assert_equal 'person model', Child.model_name.human end end diff --git a/activemodel/test/models/person.rb b/activemodel/test/models/person.rb index d98420f900..fad29a51ec 100644 --- a/activemodel/test/models/person.rb +++ b/activemodel/test/models/person.rb @@ -1,5 +1,9 @@ class Person include ActiveModel::Validations + extend ActiveModel::Translation attr_accessor :title, :karma end + +class Child < Person +end diff --git a/activemodel/test/models/track_back.rb b/activemodel/test/models/track_back.rb new file mode 100644 index 0000000000..d137e4ff8f --- /dev/null +++ b/activemodel/test/models/track_back.rb @@ -0,0 +1,4 @@ +class Post + class TrackBack + end +end \ No newline at end of file -- cgit v1.2.3 From 74098e4cb6de01745db8f1d8d567645553ade7c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 23 Dec 2009 13:30:58 +0100 Subject: No need to use ValidationsRepairHelper hack on ActiveModel anymore, Model.reset_callbacks(:validate) is enough. However, tests in ActiveRecord are still coupled, so moved ValidationsRepairHelper back there. --- activemodel/lib/active_model.rb | 1 - .../lib/active_model/validations_repair_helper.rb | 35 -------------- .../validations/acceptance_validation_test.rb | 33 +++++-------- .../validations/conditional_validation_test.rb | 5 +- .../validations/confirmation_validation_test.rb | 34 +++++--------- .../cases/validations/exclusion_validation_test.rb | 23 ++++----- .../cases/validations/format_validation_test.rb | 33 +++++-------- .../test/cases/validations/i18n_validation_test.rb | 1 - .../cases/validations/inclusion_validation_test.rb | 33 +++++-------- .../cases/validations/length_validation_test.rb | 53 +++++---------------- .../validations/numericality_validation_test.rb | 41 +++++----------- .../cases/validations/presence_validation_test.rb | 54 +++++++++++----------- .../test/cases/validations/with_validation_test.rb | 5 +- activemodel/test/cases/validations_test.rb | 5 +- activemodel/test/models/person.rb | 2 +- 15 files changed, 120 insertions(+), 238 deletions(-) delete mode 100644 activemodel/lib/active_model/validations_repair_helper.rb (limited to 'activemodel') diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index abb04cd624..ee83e3eddd 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -43,7 +43,6 @@ module ActiveModel autoload :StateMachine autoload :Translation autoload :Validations - autoload :ValidationsRepairHelper autoload :Validator autoload :EachValidator, 'active_model/validator' autoload :BlockValidator, 'active_model/validator' diff --git a/activemodel/lib/active_model/validations_repair_helper.rb b/activemodel/lib/active_model/validations_repair_helper.rb deleted file mode 100644 index 40741e6dbe..0000000000 --- a/activemodel/lib/active_model/validations_repair_helper.rb +++ /dev/null @@ -1,35 +0,0 @@ -module ActiveModel - module ValidationsRepairHelper - extend ActiveSupport::Concern - - module ClassMethods - def repair_validations(*model_classes) - setup do - @_stored_callbacks = {} - model_classes.each do |k| - @_stored_callbacks[k] = k._validate_callbacks.dup - end - end - teardown do - model_classes.each do |k| - k._validate_callbacks = @_stored_callbacks[k] - k.__update_callbacks(:validate) - end - end - end - end - - def repair_validations(*model_classes, &block) - @__stored_callbacks = {} - model_classes.each do |k| - @__stored_callbacks[k] = k._validate_callbacks.dup - end - return block.call - ensure - model_classes.each do |k| - k._validate_callbacks = @__stored_callbacks[k] - k.__update_callbacks(:validate) - end - end - end -end diff --git a/activemodel/test/cases/validations/acceptance_validation_test.rb b/activemodel/test/cases/validations/acceptance_validation_test.rb index 88e5fdb358..11c9c1edfd 100644 --- a/activemodel/test/cases/validations/acceptance_validation_test.rb +++ b/activemodel/test/cases/validations/acceptance_validation_test.rb @@ -9,9 +9,10 @@ require 'models/person' class AcceptanceValidationTest < ActiveModel::TestCase include ActiveModel::TestsDatabase - include ActiveModel::ValidationsRepairHelper - repair_validations(Topic) + def teardown + Topic.reset_callbacks(:validate) + end def test_terms_of_service_agreement_no_acceptance Topic.validates_acceptance_of(:terms_of_service, :on => :create) @@ -53,28 +54,18 @@ class AcceptanceValidationTest < ActiveModel::TestCase assert t.save end - def test_validates_acceptance_of_with_custom_error_using_quotes - repair_validations(Developer) do - Developer.validates_acceptance_of :salary, :message=> "This string contains 'single' and \"double\" quotes" - d = Developer.new - d.salary = "0" - assert !d.valid? - assert_equal "This string contains 'single' and \"double\" quotes", d.errors[:salary].last - end - end - def test_validates_acceptance_of_for_ruby_class - repair_validations(Person) do - Person.validates_acceptance_of :karma + Person.validates_acceptance_of :karma - p = Person.new - p.karma = "" + p = Person.new + p.karma = "" - assert p.invalid? - assert_equal ["must be accepted"], p.errors[:karma] + assert p.invalid? + assert_equal ["must be accepted"], p.errors[:karma] - p.karma = "1" - assert p.valid? - end + p.karma = "1" + assert p.valid? + ensure + Person.reset_callbacks(:validate) end end diff --git a/activemodel/test/cases/validations/conditional_validation_test.rb b/activemodel/test/cases/validations/conditional_validation_test.rb index 4c716d5d48..5260162a58 100644 --- a/activemodel/test/cases/validations/conditional_validation_test.rb +++ b/activemodel/test/cases/validations/conditional_validation_test.rb @@ -6,9 +6,10 @@ require 'models/topic' class ConditionalValidationTest < ActiveModel::TestCase include ActiveModel::TestsDatabase - include ActiveModel::ValidationsRepairHelper - repair_validations(Topic) + def teardown + Topic.reset_callbacks(:validate) + end def test_if_validation_using_method_true # When the method returns true diff --git a/activemodel/test/cases/validations/confirmation_validation_test.rb b/activemodel/test/cases/validations/confirmation_validation_test.rb index 1d6f2a6ec5..55554d5054 100644 --- a/activemodel/test/cases/validations/confirmation_validation_test.rb +++ b/activemodel/test/cases/validations/confirmation_validation_test.rb @@ -8,9 +8,10 @@ require 'models/person' class ConfirmationValidationTest < ActiveModel::TestCase include ActiveModel::TestsDatabase - include ActiveModel::ValidationsRepairHelper - repair_validations(Topic) + def teardown + Topic.reset_callbacks(:validate) + end def test_no_title_confirmation Topic.validates_confirmation_of(:title) @@ -39,30 +40,19 @@ class ConfirmationValidationTest < ActiveModel::TestCase assert t.save end - def test_validates_confirmation_of_with_custom_error_using_quotes - repair_validations(Developer) do - Developer.validates_confirmation_of :name, :message=> "confirm 'single' and \"double\" quotes" - d = Developer.new - d.name = "John" - d.name_confirmation = "Johnny" - assert !d.valid? - assert_equal ["confirm 'single' and \"double\" quotes"], d.errors[:name] - end - end - def test_validates_confirmation_of_for_ruby_class - repair_validations(Person) do - Person.validates_confirmation_of :karma + Person.validates_confirmation_of :karma - p = Person.new - p.karma_confirmation = "None" - assert p.invalid? + p = Person.new + p.karma_confirmation = "None" + assert p.invalid? - assert_equal ["doesn't match confirmation"], p.errors[:karma] + assert_equal ["doesn't match confirmation"], p.errors[:karma] - p.karma = "None" - assert p.valid? - end + p.karma = "None" + assert p.valid? + ensure + Person.reset_callbacks(:validate) end end diff --git a/activemodel/test/cases/validations/exclusion_validation_test.rb b/activemodel/test/cases/validations/exclusion_validation_test.rb index 584f009e84..7d851f546c 100644 --- a/activemodel/test/cases/validations/exclusion_validation_test.rb +++ b/activemodel/test/cases/validations/exclusion_validation_test.rb @@ -7,9 +7,10 @@ require 'models/person' class ExclusionValidationTest < ActiveModel::TestCase include ActiveModel::TestsDatabase - include ActiveModel::ValidationsRepairHelper - repair_validations(Topic) + def teardown + Topic.reset_callbacks(:validate) + end def test_validates_exclusion_of Topic.validates_exclusion_of( :title, :in => %w( abe monkey ) ) @@ -30,17 +31,17 @@ class ExclusionValidationTest < ActiveModel::TestCase end def test_validates_exclusion_of_for_ruby_class - repair_validations(Person) do - Person.validates_exclusion_of :karma, :in => %w( abe monkey ) + Person.validates_exclusion_of :karma, :in => %w( abe monkey ) - p = Person.new - p.karma = "abe" - assert p.invalid? + p = Person.new + p.karma = "abe" + assert p.invalid? - assert_equal ["is reserved"], p.errors[:karma] + assert_equal ["is reserved"], p.errors[:karma] - p.karma = "Lifo" - assert p.valid? - end + p.karma = "Lifo" + assert p.valid? + ensure + Person.reset_callbacks(:validate) end end diff --git a/activemodel/test/cases/validations/format_validation_test.rb b/activemodel/test/cases/validations/format_validation_test.rb index e19e4bf7b3..e10089208a 100644 --- a/activemodel/test/cases/validations/format_validation_test.rb +++ b/activemodel/test/cases/validations/format_validation_test.rb @@ -8,9 +8,10 @@ require 'models/person' class PresenceValidationTest < ActiveModel::TestCase include ActiveModel::TestsDatabase - include ActiveModel::ValidationsRepairHelper - repair_validations(Topic) + def teardown + Topic.reset_callbacks(:validate) + end def test_validate_format Topic.validates_format_of(:title, :content, :with => /^Validation\smacros \w+!$/, :message => "is bad data") @@ -100,28 +101,18 @@ class PresenceValidationTest < ActiveModel::TestCase assert_raise(ArgumentError) { Topic.validates_format_of(:title, :without => "clearly not a regexp") } end - def test_validates_format_of_with_custom_error_using_quotes - repair_validations(Developer) do - Developer.validates_format_of :name, :with => /^(A-Z*)$/, :message=> "format 'single' and \"double\" quotes" - d = Developer.new - d.name = d.name_confirmation = "John 32" - assert !d.valid? - assert_equal ["format 'single' and \"double\" quotes"], d.errors[:name] - end - end - def test_validates_format_of_for_ruby_class - repair_validations(Person) do - Person.validates_format_of :karma, :with => /\A\d+\Z/ + Person.validates_format_of :karma, :with => /\A\d+\Z/ - p = Person.new - p.karma = "Pixies" - assert p.invalid? + p = Person.new + p.karma = "Pixies" + assert p.invalid? - assert_equal ["is invalid"], p.errors[:karma] + assert_equal ["is invalid"], p.errors[:karma] - p.karma = "1234" - assert p.valid? - end + p.karma = "1234" + assert p.valid? + ensure + Person.reset_callbacks(:validate) end end diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb index 68b1b27f75..2717a09331 100644 --- a/activemodel/test/cases/validations/i18n_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_validation_test.rb @@ -1,6 +1,5 @@ require "cases/helper" require 'cases/tests_database' - require 'models/person' class I18nValidationTest < ActiveModel::TestCase diff --git a/activemodel/test/cases/validations/inclusion_validation_test.rb b/activemodel/test/cases/validations/inclusion_validation_test.rb index bc1b0365d2..6b2bcd9c60 100644 --- a/activemodel/test/cases/validations/inclusion_validation_test.rb +++ b/activemodel/test/cases/validations/inclusion_validation_test.rb @@ -8,9 +8,10 @@ require 'models/person' class InclusionValidationTest < ActiveModel::TestCase include ActiveModel::TestsDatabase - include ActiveModel::ValidationsRepairHelper - repair_validations(Topic) + def teardown + Topic.reset_callbacks(:validate) + end def test_validates_inclusion_of Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ) ) @@ -53,28 +54,18 @@ class InclusionValidationTest < ActiveModel::TestCase assert_equal ["option uhoh is not in the list"], t.errors[:title] end - def test_validates_inclusion_of_with_custom_error_using_quotes - repair_validations(Developer) do - Developer.validates_inclusion_of :salary, :in => 1000..80000, :message=> "This string contains 'single' and \"double\" quotes" - d = Developer.new - d.salary = "90,000" - assert !d.valid? - assert_equal "This string contains 'single' and \"double\" quotes", d.errors[:salary].last - end - end - def test_validates_inclusion_of_for_ruby_class - repair_validations(Person) do - Person.validates_inclusion_of :karma, :in => %w( abe monkey ) + Person.validates_inclusion_of :karma, :in => %w( abe monkey ) - p = Person.new - p.karma = "Lifo" - assert p.invalid? + p = Person.new + p.karma = "Lifo" + assert p.invalid? - assert_equal ["is not included in the list"], p.errors[:karma] + assert_equal ["is not included in the list"], p.errors[:karma] - p.karma = "monkey" - assert p.valid? - end + p.karma = "monkey" + assert p.valid? + ensure + Person.reset_callbacks(:validate) end end diff --git a/activemodel/test/cases/validations/length_validation_test.rb b/activemodel/test/cases/validations/length_validation_test.rb index 2c97b762f1..f3ef5e648a 100644 --- a/activemodel/test/cases/validations/length_validation_test.rb +++ b/activemodel/test/cases/validations/length_validation_test.rb @@ -8,9 +8,10 @@ require 'models/person' class LengthValidationTest < ActiveModel::TestCase include ActiveModel::TestsDatabase - include ActiveModel::ValidationsRepairHelper - repair_validations(Topic) + def teardown + Topic.reset_callbacks(:validate) + end def test_validates_length_of_with_allow_nil Topic.validates_length_of( :title, :is => 5, :allow_nil=>true ) @@ -419,48 +420,18 @@ class LengthValidationTest < ActiveModel::TestCase assert_equal ["Your essay must be at least 5 words."], t.errors[:content] end - def test_validates_length_of_with_custom_too_long_using_quotes - repair_validations(Developer) do - Developer.validates_length_of :name, :maximum => 4, :too_long=> "This string contains 'single' and \"double\" quotes" - d = Developer.new - d.name = "Jeffrey" - assert !d.valid? - assert_equal ["This string contains 'single' and \"double\" quotes"], d.errors[:name] - end - end - - def test_validates_length_of_with_custom_too_short_using_quotes - repair_validations(Developer) do - Developer.validates_length_of :name, :minimum => 4, :too_short=> "This string contains 'single' and \"double\" quotes" - d = Developer.new - d.name = "Joe" - assert !d.valid? - assert_equal ["This string contains 'single' and \"double\" quotes"], d.errors[:name] - end - end - - def test_validates_length_of_with_custom_message_using_quotes - repair_validations(Developer) do - Developer.validates_length_of :name, :minimum => 4, :message=> "This string contains 'single' and \"double\" quotes" - d = Developer.new - d.name = "Joe" - assert !d.valid? - assert_equal ["This string contains 'single' and \"double\" quotes"], d.errors[:name] - end - end - def test_validates_length_of_for_ruby_class - repair_validations(Person) do - Person.validates_length_of :karma, :minimum => 5 + Person.validates_length_of :karma, :minimum => 5 - p = Person.new - p.karma = "Pix" - assert p.invalid? + p = Person.new + p.karma = "Pix" + assert p.invalid? - assert_equal ["is too short (minimum is 5 characters)"], p.errors[:karma] + assert_equal ["is too short (minimum is 5 characters)"], p.errors[:karma] - p.karma = "The Smiths" - assert p.valid? - end + p.karma = "The Smiths" + assert p.valid? + ensure + Person.reset_callbacks(:validate) end end diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb index d3201966dc..75cd654f98 100644 --- a/activemodel/test/cases/validations/numericality_validation_test.rb +++ b/activemodel/test/cases/validations/numericality_validation_test.rb @@ -8,9 +8,10 @@ require 'models/person' class NumericalityValidationTest < ActiveModel::TestCase include ActiveModel::TestsDatabase - include ActiveModel::ValidationsRepairHelper - repair_validations(Topic) + def teardown + Topic.reset_callbacks(:validate) + end NIL = [nil] BLANK = ["", " ", " \t \r \n"] @@ -138,37 +139,19 @@ class NumericalityValidationTest < ActiveModel::TestCase assert_equal ["greater than 4"], topic.errors[:approved] end - def test_numericality_with_getter_method - repair_validations(Developer) do - Developer.validates_numericality_of( :salary ) - developer = Developer.new("name" => "michael", "salary" => nil) - developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end") - assert developer.valid? - end - end - - def test_numericality_with_allow_nil_and_getter_method - repair_validations(Developer) do - Developer.validates_numericality_of( :salary, :allow_nil => true) - developer = Developer.new("name" => "michael", "salary" => nil) - developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end") - assert developer.valid? - end - end - def test_validates_numericality_of_for_ruby_class - repair_validations(Person) do - Person.validates_numericality_of :karma, :allow_nil => false + Person.validates_numericality_of :karma, :allow_nil => false - p = Person.new - p.karma = "Pix" - assert p.invalid? + p = Person.new + p.karma = "Pix" + assert p.invalid? - assert_equal ["is not a number"], p.errors[:karma] + assert_equal ["is not a number"], p.errors[:karma] - p.karma = "1234" - assert p.valid? - end + p.karma = "1234" + assert p.valid? + ensure + Person.reset_callbacks(:validate) end private diff --git a/activemodel/test/cases/validations/presence_validation_test.rb b/activemodel/test/cases/validations/presence_validation_test.rb index 90b0951a77..8b9795a90c 100644 --- a/activemodel/test/cases/validations/presence_validation_test.rb +++ b/activemodel/test/cases/validations/presence_validation_test.rb @@ -9,9 +9,6 @@ require 'models/custom_reader' class PresenceValidationTest < ActiveModel::TestCase include ActiveModel::TestsDatabase - include ActiveModel::ValidationsRepairHelper - - repair_validations(Topic) def test_validate_presences Topic.validates_presence_of(:title, :content) @@ -30,43 +27,44 @@ class PresenceValidationTest < ActiveModel::TestCase t.content = "like stuff" assert t.save + ensure + Topic.reset_callbacks(:validate) end - # def test_validates_presence_of_with_custom_message_using_quotes - # repair_validations(Developer) do - # Developer.validates_presence_of :non_existent, :message=> "This string contains 'single' and \"double\" quotes" - # d = Developer.new - # d.name = "Joe" - # assert !d.valid? - # assert_equal ["This string contains 'single' and \"double\" quotes"], d.errors[:non_existent] - # end - # end + def test_validates_acceptance_of_with_custom_error_using_quotes + Person.validates_presence_of :karma, :message=> "This string contains 'single' and \"double\" quotes" + p = Person.new + assert !p.valid? + assert_equal "This string contains 'single' and \"double\" quotes", p.errors[:karma].last + ensure + Person.reset_callbacks(:validate) + end def test_validates_presence_of_for_ruby_class - repair_validations(Person) do - Person.validates_presence_of :karma + Person.validates_presence_of :karma - p = Person.new - assert p.invalid? + p = Person.new + assert p.invalid? - assert_equal ["can't be blank"], p.errors[:karma] + assert_equal ["can't be blank"], p.errors[:karma] - p.karma = "Cold" - assert p.valid? - end + p.karma = "Cold" + assert p.valid? + ensure + Person.reset_callbacks(:validate) end def test_validates_presence_of_for_ruby_class_with_custom_reader - repair_validations(Person) do - CustomReader.validates_presence_of :karma + CustomReader.validates_presence_of :karma - p = CustomReader.new - assert p.invalid? + p = CustomReader.new + assert p.invalid? - assert_equal ["can't be blank"], p.errors[:karma] + assert_equal ["can't be blank"], p.errors[:karma] - p[:karma] = "Cold" - assert p.valid? - end + p[:karma] = "Cold" + assert p.valid? + ensure + CustomReader.reset_callbacks(:validate) end end diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb index 953ff8aa2d..9e9b925df2 100644 --- a/activemodel/test/cases/validations/with_validation_test.rb +++ b/activemodel/test/cases/validations/with_validation_test.rb @@ -6,9 +6,10 @@ require 'models/topic' class ValidatesWithTest < ActiveRecord::TestCase include ActiveModel::TestsDatabase - include ActiveModel::ValidationsRepairHelper - repair_validations(Topic) + def teardown + Topic.reset_callbacks(:validate) + end ERROR_MESSAGE = "Validation error from validator" OTHER_ERROR_MESSAGE = "Validation error from other validator" diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb index 78565177d8..61910395b5 100644 --- a/activemodel/test/cases/validations_test.rb +++ b/activemodel/test/cases/validations_test.rb @@ -9,11 +9,12 @@ require 'models/custom_reader' class ValidationsTest < ActiveModel::TestCase include ActiveModel::TestsDatabase - include ActiveModel::ValidationsRepairHelper # Most of the tests mess with the validations of Topic, so lets repair it all the time. # Other classes we mess with will be dealt with in the specific tests - repair_validations(Topic) + def teardown + Topic.reset_callbacks(:validate) + end def test_single_field_validation r = Reply.new diff --git a/activemodel/test/models/person.rb b/activemodel/test/models/person.rb index fad29a51ec..c83d768379 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 + attr_accessor :title, :karma, :salary end class Child < Person -- cgit v1.2.3