From 3c881ba6f99714d92d193aeabb5cc7999a913557 Mon Sep 17 00:00:00 2001
From: Rodrigo Rosenfeld Rosas <rr_rosas@yahoo.com.br>
Date: Thu, 31 Dec 2009 18:55:46 -0200
Subject: [PATCH] Include support for :full_message option to validations.

---
 activemodel/lib/active_model/errors.rb             |   52 ++++++++++++++------
 .../lib/active_model/validations/acceptance.rb     |    2 +-
 .../lib/active_model/validations/confirmation.rb   |    2 +-
 .../lib/active_model/validations/exclusion.rb      |    2 +-
 activemodel/lib/active_model/validations/format.rb |    4 +-
 .../lib/active_model/validations/inclusion.rb      |    2 +-
 activemodel/lib/active_model/validations/length.rb |    2 +-
 .../lib/active_model/validations/numericality.rb   |    6 +-
 .../lib/active_model/validations/presence.rb       |    2 +-
 activemodel/lib/active_model/validator.rb          |    5 ++
 10 files changed, 52 insertions(+), 27 deletions(-)

diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index e8bb629..26800d7 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -55,14 +55,38 @@ module ActiveModel
       end
     end
 
-    # Adds an error message (+messsage+) to the +attribute+, which will be returned on a call to <tt>on(attribute)</tt>
+    class ErrorMessage
+      attr_reader :options, :message
+      def initialize(options)
+        @options = options.is_a?(Hash) ? options : {:message => options}
+        @message = options[:full_message] || options[:message]
+      end
+
+      def full_message?
+        !!options[:full_message]
+      end
+    end
+
+    module FullErrorMessage
+      def full_message?
+        true
+      end
+    end
+
+    # Adds an error message (+message+) to the +attribute+, which will be returned on a call to <tt>on(attribute)</tt>
     # for the same attribute and ensure that this error object returns false when asked if <tt>empty?</tt>. More than one
     # error can be added to the same +attribute+ in which case an array will be returned on a call to <tt>on(attribute)</tt>.
-    # If no +messsage+ is supplied, :invalid is assumed.
+    # If no +message+ is supplied, :invalid is assumed.
     # If +message+ is a Symbol, it will be translated, using the appropriate scope (see translate_error).
     def add(attribute, message = nil, options = {})
       message ||= :invalid
+      full_message = false
+      if (default=options[:default]).is_a? ErrorMessage
+        full_message = default.full_message?
+        options[:default] = default.message
+      end
       message = generate_message(attribute, message, options) if message.is_a?(Symbol)
+      message.extend(FullErrorMessage) if full_message
       self[attribute] << message
     end
 
@@ -83,6 +107,15 @@ module ActiveModel
       end
     end
 
+    def generate_full_message(attribute, predicate)
+      return predicate.to_s if attribute == :base or (predicate.full_message? rescue false)
+      attr_name = @base.class.human_attribute_name(attribute)
+      prefix = attr_name + I18n.t(:"errors.format.separator",
+        :default => ' ', :scope => @base.class.i18n_scope)
+
+      "#{prefix}#{predicate}"
+    end
+
     # Returns all the full error messages in an array.
     #
     #   class Company
@@ -95,22 +128,9 @@ module ActiveModel
     #     ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Address can't be blank"]
     def full_messages
       full_messages = []
-
       each do |attribute, messages|
         messages = Array.wrap(messages)
-        next if messages.empty?
-
-        if attribute == :base
-          messages.each {|m| full_messages << m }
-        else
-          attr_name = @base.class.human_attribute_name(attribute)
-          options = { :default => ' ', :scope => @base.class.i18n_scope }
-          prefix = attr_name + I18n.t(:"errors.format.separator", options)
-
-          messages.each do |m|
-            full_messages <<  "#{prefix}#{m}"
-          end
-        end
+        messages.each {|m| full_messages << generate_full_message(attribute, m)}
       end
 
       full_messages
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index bd9463e..c0293ac 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -7,7 +7,7 @@ module ActiveModel
 
       def validate_each(record, attribute, value)
         unless value == options[:accept]
-          record.errors.add(attribute, :accepted, :default => options[:message])
+          record.errors.add(attribute, :accepted, :default => message)
         end
       end
     end
diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb
index b06effd..4b812f4 100644
--- a/activemodel/lib/active_model/validations/confirmation.rb
+++ b/activemodel/lib/active_model/validations/confirmation.rb
@@ -4,7 +4,7 @@ module ActiveModel
       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])
+        record.errors.add(attribute, :confirmation, :default => message)
       end
     end
 
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index f8759f2..40e5f6d 100644
--- a/activemodel/lib/active_model/validations/exclusion.rb
+++ b/activemodel/lib/active_model/validations/exclusion.rb
@@ -8,7 +8,7 @@ module ActiveModel
 
       def validate_each(record, attribute, value)
         return unless options[:in].include?(value)
-        record.errors.add(attribute, :exclusion, :default => options[:message], :value => value)
+        record.errors.add(attribute, :exclusion, :default => message, :value => value)
       end
     end
 
diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb
index d5427c2..bc7dc3a 100644
--- a/activemodel/lib/active_model/validations/format.rb
+++ b/activemodel/lib/active_model/validations/format.rb
@@ -3,9 +3,9 @@ module ActiveModel
     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)
+          record.errors.add(attribute, :invalid, :default => message, :value => value)
         elsif options[:without] && value.to_s =~ options[:without]
-          record.errors.add(attribute, :invalid, :default => options[:message], :value => value)
+          record.errors.add(attribute, :invalid, :default => message, :value => value)
         end
       end
     end
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index a122e9e..084d0a3 100644
--- a/activemodel/lib/active_model/validations/inclusion.rb
+++ b/activemodel/lib/active_model/validations/inclusion.rb
@@ -8,7 +8,7 @@ module ActiveModel
 
       def validate_each(record, attribute, value)
         return if options[:in].include?(value)
-        record.errors.add(attribute, :inclusion, :default => options[:message], :value => value)
+        record.errors.add(attribute, :inclusion, :default => message, :value => value)
       end
     end
 
diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb
index 6e90a75..0c94550 100644
--- a/activemodel/lib/active_model/validations/length.rb
+++ b/activemodel/lib/active_model/validations/length.rb
@@ -29,7 +29,7 @@ module ActiveModel
         end
 
         checks.each do |key, check_value|
-          custom_message = options[:message] || options[MESSAGES[key]]
+          custom_message = message || options[MESSAGES[key]]
           validity_check = CHECKS[key]
 
           valid_value = if key == :maximum
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index f2aab8c..edbd4aa 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -25,7 +25,7 @@ module ActiveModel
         return if options[:allow_nil] && raw_value.nil?
 
         unless value = parse_raw_value(raw_value, options)
-          record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => options[:message])
+          record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => message)
           return
         end
 
@@ -33,14 +33,14 @@ module ActiveModel
           case option
           when :odd, :even
             unless value.to_i.send(CHECKS[option])
-              record.errors.add(attr_name, option, :value => value, :default => options[:message])
+              record.errors.add(attr_name, option, :value => value, :default => message)
             end
           else
             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)
+              record.errors.add(attr_name, option, :default => message, :value => value, :count => option_value)
             end
           end
         end      
diff --git a/activemodel/lib/active_model/validations/presence.rb b/activemodel/lib/active_model/validations/presence.rb
index a4c6f86..03aaf00 100644
--- a/activemodel/lib/active_model/validations/presence.rb
+++ b/activemodel/lib/active_model/validations/presence.rb
@@ -4,7 +4,7 @@ module ActiveModel
   module Validations
     class PresenceValidator < EachValidator
       def validate(record)
-        record.errors.add_on_blank(attributes, options[:message])
+        record.errors.add_on_blank(attributes, message)
       end
     end
 
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index 8c9f9c7..0897d9e 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -91,6 +91,11 @@ module ActiveModel #:nodoc:
 
     def check_validity!
     end
+
+    def message
+      return nil unless options[:full_message] || options[:message]
+      @message ||= Errors::ErrorMessage.new(options)
+    end
   end
 
   # BlockValidator is a special EachValidator which receives a block on initialization
-- 
1.6.5.7

