Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #8139: active_record_identified_validations.6545.diff

File active_record_identified_validations.6545.diff, 14.1 kB (added by divoxx, 2 years ago)
  • activerecord/lib/active_record/validations.rb

    old new  
    1919  class Errors 
    2020    include Enumerable 
    2121 
     22    class ErrorMessage 
     23      def self.extract_message(error_base, message, *args) 
     24        message = error_base.class.default_error_messages[message] if message.is_a?(Symbol) 
     25        args.empty? ? message : message % args 
     26      end 
     27 
     28      attr_reader :message 
     29      alias_method :to_s, :message 
     30 
     31      def initialize(error_base, message, *args) 
     32        @msg_id  = message 
     33        @message = self.class.extract_message(error_base, message, *args) 
     34      end 
     35 
     36      def message_id 
     37        @msg_id 
     38      end 
     39 
     40      def ==(other) 
     41        case other 
     42          when ErrorMessage 
     43            self.message_id == other.message_id 
     44          when Symbol 
     45            self.message_id == other 
     46          else 
     47            self.message == other.to_s 
     48        end 
     49      end 
     50 
     51    end 
     52 
    2253    def initialize(base) # :nodoc: 
    2354      @base, @errors = base, {} 
    2455    end 
     
    5081      add(:base, msg) 
    5182    end 
    5283 
    53     # Adds an error message (+msg+) to the +attribute+, which will be returned on a call to <tt>on(attribute)</tt> 
     84    # Adds an error message identifier (+msg_id+) to the +attribute+, which will be returned on a call to <tt>on(attribute)</tt> 
    5485    # for the same attribute and ensure that this error object returns false when asked if <tt>empty?</tt>. More than one 
    5586    # error can be added to the same +attribute+ in which case an array will be returned on a call to <tt>on(attribute)</tt>. 
    56     # If no +msg+ is supplied, "invalid" is assumed. 
    57     def add(attribute, msg = @@default_error_messages[:invalid]
     87    # If no +msg_id+ is supplied, :invalid is assumed. 
     88    def add(attribute, msg_id = :invalid, *args
    5889      @errors[attribute.to_s] = [] if @errors[attribute.to_s].nil? 
    59       @errors[attribute.to_s] << msg 
     90      @errors[attribute.to_s] << ErrorMessage.new(self, msg_id, *args) 
    6091    end 
    6192 
    6293    # Will add an error message to each of the attributes in +attributes+ that is empty. 
    63     def add_on_empty(attributes, msg = @@default_error_messages[:empty]
     94    def add_on_empty(attributes, msg_id = :empty
    6495      for attr in [attributes].flatten 
    6596        value = @base.respond_to?(attr.to_s) ? @base.send(attr.to_s) : @base[attr.to_s] 
    66         is_empty = value.respond_to?("empty?") ? value.empty? : false 
    67         add(attr, msg) unless !value.nil? && !is_empty 
     97        is_empty = value.respond_to?(:empty?) ? value.empty? : false 
     98        add(attr, msg_id) unless !value.nil? && !is_empty 
    6899      end 
    69100    end 
    70101 
    71102    # Will add an error message to each of the attributes in +attributes+ that is blank (using Object#blank?). 
    72     def add_on_blank(attributes, msg = @@default_error_messages[:blank]
     103    def add_on_blank(attributes, msg_id = :blank
    73104      for attr in [attributes].flatten 
    74105        value = @base.respond_to?(attr.to_s) ? @base.send(attr.to_s) : @base[attr.to_s] 
    75         add(attr, msg) if value.blank? 
     106        add(attr, msg_id) if value.blank? 
    76107      end 
    77108    end 
    78109 
    79110    # Will add an error message to each of the attributes in +attributes+ that has a length outside of the passed boundary +range+. 
    80     # If the length is above the boundary, the too_long_msg message will be used. If below, the too_short_msg. 
    81     def add_on_boundary_breaking(attributes, range, too_long_msg = @@default_error_messages[:too_long], too_short_msg = @@default_error_messages[:too_short]
     111    # If the length is above the boundary, the too_long_msg_id message will be used. If below, the too_short_msg_id 
     112    def add_on_boundary_breaking(attributes, range, too_long_msg_id = :too_long, too_short_msg_id = :too_short
    82113      for attr in [attributes].flatten 
    83114        value = @base.respond_to?(attr.to_s) ? @base.send(attr.to_s) : @base[attr.to_s] 
    84         add(attr, too_short_msg % range.begin) if value && value.length < range.begin 
    85         add(attr, too_long_msg % range.end) if value && value.length > range.end 
     115        add(attr, too_short_msg_id, range.begin) if value && value.length < range.begin 
     116        add(attr, too_long_msg_id, range.end) if value && value.length > range.end 
    86117      end 
    87118    end 
    88119 
     
    142173    #     name - can't be blank 
    143174    #     address - can't be blank 
    144175    def each 
    145       @errors.each_key { |attr| @errors[attr].each { |msg| yield attr, msg } } 
     176      @errors.each { |attr,errors| errors.each { |error| yield attr, error } } 
    146177    end 
    147178 
    148179    # Yields each full error message added. So Person.errors.add("first_name", "can't be empty") will be returned 
     
    159190    #     Name can't be blank 
    160191    #     Address can't be blank 
    161192    def each_full 
    162       full_messages.each { |msg| yield msg
     193      full_messages.each { |message| yield message
    163194    end 
    164195 
    165196    # Returns all the full error messages in an array. 
     
    175206    def full_messages 
    176207      full_messages = [] 
    177208 
    178       @errors.each_key do |attr
    179         @errors[attr].each do |msg
    180           next if msg.nil? 
     209      @errors.each do |attr,errors
     210        errors.each do |error
     211          next if error.nil? 
    181212 
    182213          if attr == "base" 
    183             full_messages << msg 
     214            full_messages << error.message 
    184215          else 
    185             full_messages << @base.class.human_attribute_name(attr) + " " + msg 
     216            full_messages << @base.class.human_attribute_name(attr) + " " + error.message 
    186217          end 
    187218        end 
    188219      end 
     
    391422      # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    392423      # method, proc or string should return or evaluate to a true or false value. 
    393424      def validates_confirmation_of(*attr_names) 
    394         configuration = { :message => ActiveRecord::Errors.default_error_messages[:confirmation], :on => :save } 
     425        configuration = { :message => :confirmation, :on => :save } 
    395426        configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash) 
    396427 
    397428        attr_accessor *(attr_names.map { |n| "#{n}_confirmation" }) 
     
    420451      # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    421452      # method, proc or string should return or evaluate to a true or false value. 
    422453      def validates_acceptance_of(*attr_names) 
    423         configuration = { :message => ActiveRecord::Errors.default_error_messages[:accepted], :on => :save, :allow_nil => true, :accept => "1" } 
     454        configuration = { :message => :accepted, :on => :save, :allow_nil => true, :accept => "1" } 
    424455        configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash) 
    425456 
    426457        attr_accessor *attr_names 
     
    461492      # failures on saves when both the parent object and the child object are 
    462493      # new. 
    463494      def validates_presence_of(*attr_names) 
    464         configuration = { :message => ActiveRecord::Errors.default_error_messages[:blank], :on => :save } 
     495        configuration = { :message => :blank, :on => :save } 
    465496        configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash) 
    466497 
    467498        # can't use validates_each here, because it cannot cope with nonexistent attributes, 
     
    505536      def validates_length_of(*attrs) 
    506537        # Merge given options with defaults. 
    507538        options = { 
    508           :too_long     => ActiveRecord::Errors.default_error_messages[:too_long]
    509           :too_short    => ActiveRecord::Errors.default_error_messages[:too_short]
    510           :wrong_length => ActiveRecord::Errors.default_error_messages[:wrong_length] 
     539          :too_long     => :too_long
     540          :too_short    => :too_short
     541          :wrong_length => :wrong_length 
    511542        }.merge(DEFAULT_VALIDATION_OPTIONS) 
    512543        options.update(attrs.pop.symbolize_keys) if attrs.last.is_a?(Hash) 
    513544 
     
    530561          when :within, :in 
    531562            raise ArgumentError, ":#{option} must be a Range" unless option_value.is_a?(Range) 
    532563 
    533             too_short = options[:too_short] % option_value.begin 
    534             too_long  = options[:too_long]  % option_value.end 
     564            too_short = [options[:too_short], option_value.begin] 
     565            too_long  = [options[:too_long],  option_value.end] 
    535566 
    536567            validates_each(attrs, options) do |record, attr, value| 
    537568              if value.nil? or value.split(//).size < option_value.begin 
    538                 record.errors.add(attr, too_short) 
     569                record.errors.add(attr, *too_short) 
    539570              elsif value.split(//).size > option_value.end 
    540                 record.errors.add(attr, too_long) 
     571                record.errors.add(attr, *too_long) 
    541572              end 
    542573            end 
    543574          when :is, :minimum, :maximum 
     
    547578            validity_checks = { :is => "==", :minimum => ">=", :maximum => "<=" } 
    548579            message_options = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long } 
    549580 
    550             message = (options[:message] || options[message_options[option]]) % option_value 
     581            message = [(options[:message] || options[message_options[option]]), option_value] 
    551582 
    552583            validates_each(attrs, options) do |record, attr, value| 
    553584              if value.kind_of?(String) 
    554                 record.errors.add(attr, message) unless !value.nil? and value.split(//).size.method(validity_checks[option])[option_value] 
     585                record.errors.add(attr, *message) unless !value.nil? and value.split(//).size.method(validity_checks[option])[option_value] 
    555586              else 
    556                 record.errors.add(attr, message) unless !value.nil? and value.size.method(validity_checks[option])[option_value] 
     587                record.errors.add(attr, *message) unless !value.nil? and value.size.method(validity_checks[option])[option_value] 
    557588              end 
    558589            end 
    559590        end 
     
    588619      # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    589620      # method, proc or string should return or evaluate to a true or false value. 
    590621      def validates_uniqueness_of(*attr_names) 
    591         configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken], :case_sensitive => true } 
     622        configuration = { :message => :taken, :case_sensitive => true } 
    592623        configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash) 
    593624 
    594625        validates_each(attr_names,configuration) do |record, attr_name, value| 
     
    636667      # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    637668      # method, proc or string should return or evaluate to a true or false value. 
    638669      def validates_format_of(*attr_names) 
    639         configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save, :with => nil } 
     670        configuration = { :message => :invalid, :on => :save, :with => nil } 
    640671        configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash) 
    641672 
    642673        raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp) 
     
    661692      # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    662693      # method, proc or string should return or evaluate to a true or false value. 
    663694      def validates_inclusion_of(*attr_names) 
    664         configuration = { :message => ActiveRecord::Errors.default_error_messages[:inclusion], :on => :save } 
     695        configuration = { :message => :inclusion, :on => :save } 
    665696        configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash) 
    666697 
    667698        enum = configuration[:in] || configuration[:within] 
     
    688719      # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    689720      # method, proc or string should return or evaluate to a true or false value. 
    690721      def validates_exclusion_of(*attr_names) 
    691         configuration = { :message => ActiveRecord::Errors.default_error_messages[:exclusion], :on => :save } 
     722        configuration = { :message => :exclusion, :on => :save } 
    692723        configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash) 
    693724 
    694725        enum = configuration[:in] || configuration[:within] 
     
    728759      # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    729760      # method, proc or string should return or evaluate to a true or false value. 
    730761      def validates_associated(*attr_names) 
    731         configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save } 
     762        configuration = { :message => :invalid, :on => :save } 
    732763        configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash) 
    733764 
    734765        validates_each(attr_names, configuration) do |record, attr_name, value| 
     
    754785      # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }).  The 
    755786      # method, proc or string should return or evaluate to a true or false value. 
    756787      def validates_numericality_of(*attr_names) 
    757         configuration = { :message => ActiveRecord::Errors.default_error_messages[:not_a_number], :on => :save, 
     788        configuration = { :message => :not_a_number, :on => :save, 
    758789                           :only_integer => false, :allow_nil => false } 
    759790        configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash) 
    760791 
  • activerecord/test/validations_test.rb

    old new  
    3030    Topic.write_inheritable_attribute(:validate_on_update, nil) 
    3131  end 
    3232 
     33  def test_comparsion_with_msg_id 
     34    Topic.validates_presence_of :title 
     35    t = Topic.new 
     36    assert !t.valid? 
     37    assert_equal :blank, t.errors.on(:title) 
     38  end 
     39   
     40  def test_error_message_to_string_message 
     41    Topic.validates_presence_of :title 
     42    t = Topic.new 
     43    assert !t.valid? 
     44    assert_equal "can't be blank", t.errors.on(:title).to_s 
     45  end 
     46 
    3347  def test_single_field_validation 
    3448    r = Reply.new 
    3549    r.title = "There's no content!"