Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save AquaGeek/971657 to your computer and use it in GitHub Desktop.
Save AquaGeek/971657 to your computer and use it in GitHub Desktop.
Rails Lighthouse ticket #3768
From 67eb80e72d8d4782334aabf3b120adf6fdca5cda Mon Sep 17 00:00:00 2001
From: Rodrigo Rosenfeld Rosas <[email protected]>
Date: Thu, 25 Feb 2010 00:29:43 -0300
Subject: [PATCH] Include support for :full_message option to validations.
---
activemodel/lib/active_model/errors.rb | 54 +++++++++++++------
.../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, 53 insertions(+), 28 deletions(-)
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index d832027..282e201 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -161,20 +161,52 @@ 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 < String
+ attr_reader :options, :message
+ def initialize(options=nil)
+ if options.is_a? ErrorMessage
+ @options = options.options
+ return super(options.message)
+ end
+ @options = options.is_a?(Hash) ? options : {:message => options}
+ @message = @options[:full_message] || @options[:message]
+ super(@message.to_s) # in case of symbols...
+ end
+
+ def full_message?
+ !!options[:full_message] rescue false # options could be nil for ErrorMessage.new.sub, for instance
+ 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).
# If +message+ is a Proc, it will be called, allowing for things like Time.now to be used within an error
def add(attribute, message = nil, options = {})
message ||= :invalid
+ full_message = (m=options[:default]).full_message? rescue false
+ options[:default] = m.message rescue m
message = generate_message(attribute, message, options) if message.is_a?(Symbol)
message = message.call if message.is_a?(Proc)
+ message = ErrorMessage.new :full_message => message if full_message
self[attribute] << message
end
+ def generate_full_message(attribute, predicate)
+ predicate = ErrorMessage.new(predicate) unless predicate.is_a? ErrorMessage
+ return (predicate.message.is_a?(Symbol) ? I18n.t(predicate.message) : predicate) if
+ attribute == :base or predicate.full_message?
+
+ attr_name = attribute.to_s.gsub('.', '_').humanize
+ attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
+ options = { :default => "{{attribute}} {{message}}", :attribute => attr_name }
+
+ I18n.t(:"errors.format", options.merge(:message => predicate))
+ end
+
# Will add an error message to each of the attributes in +attributes+ that is empty.
def add_on_empty(attributes, custom_message = nil)
[attributes].flatten.each do |attribute|
@@ -206,20 +238,8 @@ module ActiveModel
full_messages = []
each do |attribute, messages|
- messages = Array(messages)
- next if messages.empty?
-
- if attribute == :base
- messages.each {|m| full_messages << m }
- else
- attr_name = attribute.to_s.gsub('.', '_').humanize
- attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
- options = { :default => "{{attribute}} {{message}}", :attribute => attr_name }
-
- messages.each do |m|
- full_messages << I18n.t(:"errors.format", options.merge(:message => m))
- end
- end
+ messages = Array.wrap(messages)
+ 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 0423fcd..d02a9e3 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
diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb
index 8041d4b..37649ee 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
def setup(klass)
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index 7ee718c..8bc2cf9 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 9a9e7ec..f92d51b 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
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index 0c1334f..f8bcf73 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 9ceb754..c7ea251 100644
--- a/activemodel/lib/active_model/validations/length.rb
+++ b/activemodel/lib/active_model/validations/length.rb
@@ -37,7 +37,7 @@ module ActiveModel
CHECKS.each do |key, validity_check|
next unless check_value = options[key]
- custom_message = options[:message] || options[MESSAGES[key]]
+ custom_message = message || options[MESSAGES[key]]
valid_value = if key == :maximum
value.nil? || value.size.send(validity_check, check_value)
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index c6d84c5..95e71b8 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -26,7 +26,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
@@ -34,14 +34,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 4a71cf7..3f4a846 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 b61f0cb..48413b8 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -158,6 +158,11 @@ module ActiveModel #:nodoc:
# ArgumentError when invalid options are supplied.
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.7.0
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 1431228..a2c2554 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -170,6 +170,20 @@ module ActiveModel
self
end
+ # Represents an error message. Contains the model instance (+base+), the +attribute+ and the +options+
+ # passed to validators.
+ class ErrorMessage < String
+ attr_reader :base, :attribute, :options
+ def initialize(message, base, attribute, options)
+ super(message)
+ @base, @attribute, @options = base, attribute, options
+ end
+
+ def full_message?
+ @full_message ||= options.include? :full_message
+ end
+ end
+
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
# <tt>on(attribute)</tt> for the same attribute. 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>.
@@ -180,12 +194,17 @@ module ActiveModel
def add(attribute, message = nil, options = {})
message ||= :invalid
+ fm = options[:full_message]
+ opts = fm ? options.merge(:message => fm) : options
+ opts.delete(:full_message) if fm
+
if message.is_a?(Symbol)
- message = generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
+ message = generate_message(attribute, message, opts.except(*CALLBACKS_OPTIONS))
elsif message.is_a?(Proc)
message = message.call
end
+ message = ErrorMessage.new(message, @base, attribute, options) if message
self[attribute] << message
end
@@ -245,7 +264,8 @@ module ActiveModel
options = { :default => "%{attribute} %{message}", :attribute => attr_name }
messages.each do |m|
- full_messages << I18n.t(:"errors.format", options.merge(:message => m))
+ full_messages << ((m.respond_to?(:full_message?) && m.full_message?) ?
+ m : I18n.t(:"errors.format", options.merge(:message => m)))
end
end
end
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index d832027..7a292d3 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -161,34 +161,58 @@ 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 FullErrorMessage < String
+ attr_reader :message
+ def initialize(message)
+ super((@message = message).to_s) # in case of symbols...
+ end
+
+ 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. Pass +:full_message => true+ in the options for specifying
+ # it is the full message, so that the attribute is not prepended in <tt>full_messages</tt>.
+ #
# If +message+ is a Symbol, it will be translated, using the appropriate scope (see translate_error).
# If +message+ is a Proc, it will be called, allowing for things like Time.now to be used within an error
def add(attribute, message = nil, options = {})
message ||= :invalid
message = generate_message(attribute, message, options) if message.is_a?(Symbol)
message = message.call if message.is_a?(Proc)
+ message = FullErrorMessage.new message if options[:full_message]
self[attribute] << message
end
+ def generate_full_message(attribute, message)
+ message = message.message if message.respond_to? :message
+ return (message.is_a?(Symbol) ? I18n.t(message) : message) if attribute == :base or (predicate.full_message? rescue false)
+
+ attr_name = attribute.to_s.gsub('.', '_').humanize
+ attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
+ options = { :default => "{{attribute}} {{message}}", :attribute => attr_name }
+
+ I18n.t(:"errors.format", options.merge(:message => message))
+ end
+
# Will add an error message to each of the attributes in +attributes+ that is empty.
- def add_on_empty(attributes, custom_message = nil)
+ def add_on_empty(attributes, custom_message = nil, options = {})
[attributes].flatten.each do |attribute|
value = @base.send(:read_attribute_for_validation, attribute)
is_empty = value.respond_to?(:empty?) ? value.empty? : false
- add(attribute, :empty, :default => custom_message) unless !value.nil? && !is_empty
+ add(attribute, :empty, options.merge(:default => custom_message)) unless !value.nil? && !is_empty
end
end
# Will add an error message to each of the attributes in +attributes+ that is blank (using Object#blank?).
- def add_on_blank(attributes, custom_message = nil)
+ def add_on_blank(attributes, custom_message = nil, options = {})
[attributes].flatten.each do |attribute|
value = @base.send(:read_attribute_for_validation, attribute)
- add(attribute, :blank, :default => custom_message) if value.blank?
+ add(attribute, :blank, options.merge(:default => custom_message)) if value.blank?
end
end
@@ -206,20 +230,8 @@ module ActiveModel
full_messages = []
each do |attribute, messages|
- messages = Array(messages)
- next if messages.empty?
-
- if attribute == :base
- messages.each {|m| full_messages << m }
- else
- attr_name = attribute.to_s.gsub('.', '_').humanize
- attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
- options = { :default => "{{attribute}} {{message}}", :attribute => attr_name }
-
- messages.each do |m|
- full_messages << I18n.t(:"errors.format", options.merge(:message => m))
- end
- end
+ messages = Array.wrap(messages)
+ 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 0423fcd..dcb8ef5 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])
+ add_error(record, attribute, :accepted)
end
end
diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb
index 8041d4b..aa170f3 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])
+ add_error(record, attribute, :confirmation)
end
def setup(klass)
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index 7ee718c..ebb67d8 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)
+ add_error(record, attribute, :exclusion, :value => value)
end
end
diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb
index 9a9e7ec..b8dc3ff 100644
--- a/activemodel/lib/active_model/validations/format.rb
+++ b/activemodel/lib/active_model/validations/format.rb
@@ -2,11 +2,8 @@ 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
+ add_error(record, attribute, :invalid, :value => value) if
+ (options[:with] && value.to_s !~ options[:with]) or (options[:without] && value.to_s =~ options[:without])
end
def check_validity!
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index 0c1334f..9f19d36 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)
+ add_error(record, attribute, :inclusion, :value => value)
end
end
diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb
index 9ceb754..cdbae7f 100644
--- a/activemodel/lib/active_model/validations/length.rb
+++ b/activemodel/lib/active_model/validations/length.rb
@@ -37,7 +37,7 @@ module ActiveModel
CHECKS.each do |key, validity_check|
next unless check_value = options[key]
- custom_message = options[:message] || options[MESSAGES[key]]
+ custom_message = message || options[MESSAGES[key]]
valid_value = if key == :maximum
value.nil? || value.size.send(validity_check, check_value)
@@ -46,7 +46,7 @@ module ActiveModel
end
next if valid_value
- record.errors.add(attribute, MESSAGES[key], :default => custom_message, :count => check_value)
+ add_error(record, attribute, MESSAGES[key], :default => custom_message, :count => check_value)
end
end
end
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index c6d84c5..99448b3 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -26,7 +26,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])
+ add_error(record, attr_name, :not_a_number, :value => raw_value)
return
end
@@ -34,14 +34,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])
+ add_error(record, attr_name, option, :value => value)
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)
+ add_error(record, attr_name, option, :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 4a71cf7..624c88e 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])
+ add_on_blank_error record
end
end
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index b61f0cb..57452de 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -158,6 +158,21 @@ module ActiveModel #:nodoc:
# ArgumentError when invalid options are supplied.
def check_validity!
end
+
+ def message
+ options[:full_message] || options[:message]
+ end
+
+ def add_error(record, attribute, error_message, error_options={})
+ opts = {:default => message}
+ opts.merge! :full_message => !!options[:full_message] if options[:full_message]
+ record.errors.add(attribute, error_message, opts.merge(error_options))
+ end
+
+ def add_on_blank_error(record)
+ opts = options[:full_message] ? {:full_message => true} : {}
+ record.errors.add_on_blank attributes, message, opts
+ end
end
# BlockValidator is a special EachValidator which receives a block on initialization
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 1431228..96015d8 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -170,6 +170,16 @@ module ActiveModel
self
end
+ # Represents a full error message
+ class ErrorMessage < String
+ attr_reader :full_message
+
+ def initialize(message, full_message)
+ super(message || full_message)
+ @full_message = full_message
+ end
+ end
+
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
# <tt>on(attribute)</tt> for the same attribute. 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>.
@@ -178,15 +188,19 @@ module ActiveModel
# If +message+ is a symbol, it will be translated using the appropriate scope (see +translate_error+).
# If +message+ is a proc, it will be called, allowing for things like <tt>Time.now</tt> to be used within an error.
def add(attribute, message = nil, options = {})
- message ||= :invalid
-
- if message.is_a?(Symbol)
- message = generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
- elsif message.is_a?(Proc)
- message = message.call
+ messages = {:message => message || :invalid, :full_message => options[:full_message]}
+ options = options.except(*CALLBACKS_OPTIONS)
+
+ messages.each do |type, message|
+ if message.is_a? Symbol
+ messages[type] = generate_message(attribute, message, options)
+ elsif message.is_a? Proc
+ messages[type] = message.call
+ end
end
- self[attribute] << message
+ self[attribute] << (!messages[:full_message] ? messages[:message] :
+ ErrorMessage.new(messages[:message], messages[:full_message]))
end
# Will add an error message to each of the attributes in +attributes+ that is empty.
@@ -245,7 +259,8 @@ module ActiveModel
options = { :default => "%{attribute} %{message}", :attribute => attr_name }
messages.each do |m|
- full_messages << I18n.t(:"errors.format", options.merge(:message => m))
+ full_messages << ((m.respond_to?(:full_message) && m.full_message) ?
+ m.full_message : I18n.t(:"errors.format", options.merge(:message => m)))
end
end
end
@@ -280,6 +295,7 @@ module ActiveModel
# </ol>
def generate_message(attribute, type = :invalid, options = {})
type = options.delete(:message) if options[:message].is_a?(Symbol)
+ full_message = options.delete(:full_message)
if options[:default]
ActiveSupport::Deprecation.warn \
@@ -296,7 +312,7 @@ module ActiveModel
defaults << options.delete(:message)
defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}"
defaults << :"errors.attributes.#{attribute}.#{type}"
- defaults << :"errors.messages.#{type}"
+ defaults << :"errors.#{full_message ? 'full_' : ''}messages.#{type}"
defaults.compact!
defaults.flatten!
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 1431228..efc7eb5 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -170,23 +170,58 @@ module ActiveModel
self
end
+ # Represents a full error message. The full_message is available in <tt>full_message</tt>
+ # and <tt>to_s</tt> returns the +message+ given to constructor, or +full_message+ if +message+ is nil.
+ class ErrorMessage < String
+ attr_reader :full_message
+
+ def initialize(message, full_message)
+ super(message || full_message)
+ @full_message = full_message
+ end
+ end
+
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
# <tt>on(attribute)</tt> for the same attribute. 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 +message+ is supplied, <tt>:invalid</tt> is assumed.
#
+ # If a <tt>:full_message</tt> option is specified, an ErrorMessage instance will be added to errors,
+ # which will be processed by <tt>full_messages</tt> as a full message (no concatenation with attribute names).
+ #
# If +message+ is a symbol, it will be translated using the appropriate scope (see +translate_error+).
- # If +message+ is a proc, it will be called, allowing for things like <tt>Time.now</tt> to be used within an error.
+ # If +message+ or +options[:message/:full_message]+ is a proc, it will be called with optional parameters,
+ # depending on proc arity, in the following order: instance (@base), +attribute+, +options+.
def add(attribute, message = nil, options = {})
message ||= :invalid
+ proc_args = [@base, attribute, options]
- if message.is_a?(Symbol)
- message = generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
- elsif message.is_a?(Proc)
- message = message.call
+ if message.is_a? Proc
+ options = options.merge(:message => message)
+ ActiveSupport::Deprecation.warn \
+ "ActiveModel::Errors#add(attributes, proc, options) has been deprecated.\n" +
+ "Instead of passing the proc to message, add it to options[:message] or options[:full_message] instead."
+ end
+
+ message_options = options.except(:full_message, *CALLBACKS_OPTIONS)
+ messages = {}.tap{|m| m.merge! :message => message unless message.is_a? Symbol}
+ [:message, :full_message].each do |key|
+ messages[key] ||= options[key]
+ if (msg_proc = messages[key]).is_a?(Proc)
+ messages[key] = case msg_proc.arity
+ when 0: msg_proc.call
+ when -1: msg_proc.call(*proc_args)
+ else msg_proc.call(*proc_args[0..msg_proc.arity-1])
+ end
+ elsif message.is_a?(Symbol) && ((key == :message) || messages[:full_message])
+ opts = message_options
+ opts.merge! :message => messages[key] if messages[key]
+ messages[key] = generate_message attribute, message, opts #, key (temporarily disabled, to avoid changing the tests for now)
+ end
end
- self[attribute] << message
+ self[attribute] << (!messages[:full_message] ? messages[:message] :
+ ErrorMessage.new(messages[:message], messages[:full_message]))
end
# Will add an error message to each of the attributes in +attributes+ that is empty.
@@ -224,12 +259,12 @@ module ActiveModel
#
# class Company
# validates_presence_of :name, :address, :email
- # validates_length_of :name, :in => 5..30
+ # validates_length_of :name, :in => 5..30, :full_message => 'You should specify a name between 5 and 30 characters'
# end
#
# company = Company.create(:address => '123 First St.')
# company.errors.full_messages # =>
- # ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Address can't be blank"]
+ # ["You should specify a name between 5 and 30 characters", "Name can't be blank", "Address can't be blank"]
def full_messages
full_messages = []
@@ -245,7 +280,8 @@ module ActiveModel
options = { :default => "%{attribute} %{message}", :attribute => attr_name }
messages.each do |m|
- full_messages << I18n.t(:"errors.format", options.merge(:message => m))
+ full_messages << ((m.respond_to?(:full_message) && m.full_message) ?
+ m.full_message : I18n.t(:"errors.format", options.merge(:message => m)))
end
end
end
@@ -259,8 +295,9 @@ module ActiveModel
# Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
# if it's not there, it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not
# there also, it returns the translation of the default message
- # (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model name,
- # translated attribute name and the value are available for interpolation.
+ # (e.g. <tt>activemodel.errors.messages.MESSAGE</tt> or <tt>activemodel.errors.full_messages.MESSAGE</tt>,
+ # depending on the +translation_key+ param, which should be :message or :full_message).
+ # The translated model name, translated attribute name and the value are available for interpolation.
#
# When using inheritance in your models, it will check all the inherited
# models too, but only if the model itself hasn't been found. Say you have
@@ -278,7 +315,7 @@ module ActiveModel
# <li><tt>errors.attributes.title.blank</tt></li>
# <li><tt>errors.messages.blank</tt></li>
# </ol>
- def generate_message(attribute, type = :invalid, options = {})
+ def generate_message(attribute, type = :invalid, options = {}, translation_key = :message)
type = options.delete(:message) if options[:message].is_a?(Symbol)
if options[:default]
@@ -296,7 +333,7 @@ module ActiveModel
defaults << options.delete(:message)
defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}"
defaults << :"errors.attributes.#{attribute}.#{type}"
- defaults << :"errors.messages.#{type}"
+ defaults << :"errors.#{translation_key}s.#{type}"
defaults.compact!
defaults.flatten!
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 1431228..e2277fc 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -170,6 +170,17 @@ module ActiveModel
self
end
+ # Represents an error message. Contains the model instance (+base+), the +attribute+ and the +options+
+ # passed to validators.
+ class ErrorMessage < String
+ attr_reader :full_message
+
+ def initialize(message, full_message)
+ super(message || full_message)
+ @full_message = full_message
+ end
+ end
+
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
# <tt>on(attribute)</tt> for the same attribute. 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>.
@@ -179,13 +190,17 @@ module ActiveModel
# If +message+ is a proc, it will be called, allowing for things like <tt>Time.now</tt> to be used within an error.
def add(attribute, message = nil, options = {})
message ||= :invalid
+ full_message = options[:full_message]
if message.is_a?(Symbol)
- message = generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
+ options = options.except(*CALLBACKS_OPTIONS)
+ options[:message] = options.delete(:full_message) if full_message
+ message = generate_message(attribute, message, options)
elsif message.is_a?(Proc)
message = message.call
end
+ message = ErrorMessage.new(message, full_message) if message && full_message
self[attribute] << message
end
@@ -245,7 +260,8 @@ module ActiveModel
options = { :default => "%{attribute} %{message}", :attribute => attr_name }
messages.each do |m|
- full_messages << I18n.t(:"errors.format", options.merge(:message => m))
+ full_messages << ((m.respond_to?(:full_message) && m.full_message) ?
+ m : I18n.t(:"errors.format", options.merge(:message => m)))
end
end
end
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 1431228..8c71a5a 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -170,6 +170,17 @@ module ActiveModel
self
end
+ # Represents an error message. Contains the model instance (+base+), the +attribute+ and the +options+
+ # passed to validators.
+ class ErrorMessage < String
+ attr_reader :full_message
+
+ def initialize(message, full_message)
+ super(message || full_message)
+ @full_message = full_message
+ end
+ end
+
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
# <tt>on(attribute)</tt> for the same attribute. 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>.
@@ -179,13 +190,18 @@ module ActiveModel
# If +message+ is a proc, it will be called, allowing for things like <tt>Time.now</tt> to be used within an error.
def add(attribute, message = nil, options = {})
message ||= :invalid
-
+ full_message = options[:full_message]
+ message = full_message if full_message.is_a? Proc
+
if message.is_a?(Symbol)
- message = generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
+ options = options.except(*CALLBACKS_OPTIONS)
+ options[:message] = options.delete(:full_message) if full_message
+ message = generate_message(attribute, message, options)
elsif message.is_a?(Proc)
message = message.call
end
+ message = ErrorMessage.new(message, full_message) if message && full_message
self[attribute] << message
end
@@ -245,7 +261,8 @@ module ActiveModel
options = { :default => "%{attribute} %{message}", :attribute => attr_name }
messages.each do |m|
- full_messages << I18n.t(:"errors.format", options.merge(:message => m))
+ full_messages << ((m.respond_to?(:full_message) && m.full_message) ?
+ m : I18n.t(:"errors.format", options.merge(:message => m)))
end
end
end
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 1431228..ecedd79 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -170,6 +170,17 @@ module ActiveModel
self
end
+ # Represents an error message. Contains the model instance (+base+), the +attribute+ and the +options+
+ # passed to validators.
+ class ErrorMessage < String
+ attr_reader :full_message
+
+ def initialize(message, full_message)
+ super(message || full_message)
+ @full_message = full_message
+ end
+ end
+
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
# <tt>on(attribute)</tt> for the same attribute. 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>.
@@ -180,12 +191,19 @@ module ActiveModel
def add(attribute, message = nil, options = {})
message ||= :invalid
+ full_message = options[:full_message]
+ message = full_message if full_message.is_a? Proc
+
if message.is_a?(Symbol)
- message = generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
+ options = options.except(*(CALLBACKS_OPTIONS + [:full_message]))
+ full_message = generate_message(attribute, message, options.merge(:message => full_message)) if full_message
+ message = generate_message(attribute, message, options)
elsif message.is_a?(Proc)
message = message.call
+ full_message = message if full_message
end
+ message = ErrorMessage.new(message, full_message) if (message && full_message)
self[attribute] << message
end
@@ -245,7 +263,8 @@ module ActiveModel
options = { :default => "%{attribute} %{message}", :attribute => attr_name }
messages.each do |m|
- full_messages << I18n.t(:"errors.format", options.merge(:message => m))
+ full_messages << ((m.respond_to?(:full_message) && m.full_message) ?
+ m.full_message : I18n.t(:"errors.format", options.merge(:message => m)))
end
end
end
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 1431228..abcf15c 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -170,6 +170,17 @@ module ActiveModel
self
end
+ # Represents an error message. Contains the model instance (+base+), the +attribute+ and the +options+
+ # passed to validators.
+ class ErrorMessage < String
+ attr_reader :full_message
+
+ def initialize(message, full_message)
+ super(message || full_message)
+ @full_message = full_message
+ end
+ end
+
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
# <tt>on(attribute)</tt> for the same attribute. 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>.
@@ -180,12 +191,17 @@ module ActiveModel
def add(attribute, message = nil, options = {})
message ||= :invalid
+ full_message = options[:full_message]
+ message = full_message if full_message.is_a? Proc
+
if message.is_a?(Symbol)
- message = generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
+ message = generate_message(attribute, message, options.except(*(CALLBACKS_OPTIONS + [:full_message])))
elsif message.is_a?(Proc)
message = message.call
+ full_message &&= message
end
+ message &&= ErrorMessage.new(message, full_message) if full_message
self[attribute] << message
end
@@ -245,7 +261,8 @@ module ActiveModel
options = { :default => "%{attribute} %{message}", :attribute => attr_name }
messages.each do |m|
- full_messages << I18n.t(:"errors.format", options.merge(:message => m))
+ full_messages << ((m.respond_to?(:full_message) && m.full_message) ?
+ m.full_message : I18n.t(:"errors.format", options.merge(:message => m)))
end
end
end
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 1431228..46b6a15 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -170,6 +170,17 @@ module ActiveModel
self
end
+ # Represents an error message. Contains the model instance (+base+), the +attribute+ and the +options+
+ # passed to validators.
+ class ErrorMessage < String
+ attr_reader :full_message
+
+ def initialize(message, full_message)
+ super(message || full_message)
+ @full_message = full_message
+ end
+ end
+
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
# <tt>on(attribute)</tt> for the same attribute. 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>.
@@ -180,12 +191,17 @@ module ActiveModel
def add(attribute, message = nil, options = {})
message ||= :invalid
+ full_message = options[:full_message]
+ message = full_message if full_message.is_a? Proc
+
if message.is_a?(Symbol)
- message = generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
+ message = generate_message(attribute, message, options.except(:full_message, *CALLBACKS_OPTIONS))
elsif message.is_a?(Proc)
message = message.call
+ full_message &&= message
end
+ message &&= ErrorMessage.new(message, full_message) if full_message
self[attribute] << message
end
@@ -245,7 +261,8 @@ module ActiveModel
options = { :default => "%{attribute} %{message}", :attribute => attr_name }
messages.each do |m|
- full_messages << I18n.t(:"errors.format", options.merge(:message => m))
+ full_messages << ((m.respond_to?(:full_message) && m.full_message) ?
+ m.full_message : I18n.t(:"errors.format", options.merge(:message => m)))
end
end
end
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 1431228..304a010 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -170,6 +170,16 @@ module ActiveModel
self
end
+ # Represents a full error message
+ class ErrorMessage < String
+ attr_reader :full_message
+
+ def initialize(message, full_message)
+ super(message || full_message)
+ @full_message = full_message
+ end
+ end
+
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
# <tt>on(attribute)</tt> for the same attribute. 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>.
@@ -180,12 +190,17 @@ module ActiveModel
def add(attribute, message = nil, options = {})
message ||= :invalid
+ full_message = options[:full_message]
+ message = full_message if full_message.is_a? Proc
+
if message.is_a?(Symbol)
- message = generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
+ message = generate_message(attribute, message, options.except(:full_message, *CALLBACKS_OPTIONS))
elsif message.is_a?(Proc)
message = message.call
+ full_message &&= message
end
+ message &&= ErrorMessage.new(message, full_message) if full_message
self[attribute] << message
end
@@ -245,7 +260,8 @@ module ActiveModel
options = { :default => "%{attribute} %{message}", :attribute => attr_name }
messages.each do |m|
- full_messages << I18n.t(:"errors.format", options.merge(:message => m))
+ full_messages << ((m.respond_to?(:full_message) && m.full_message) ?
+ m.full_message : I18n.t(:"errors.format", options.merge(:message => m)))
end
end
end
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 1431228..d76fd2e 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -170,6 +170,16 @@ module ActiveModel
self
end
+ # Represents a full error message
+ class ErrorMessage < String
+ attr_reader :full_message
+
+ def initialize(message, full_message)
+ super(message || full_message)
+ @full_message = full_message
+ end
+ end
+
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
# <tt>on(attribute)</tt> for the same attribute. 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>.
@@ -180,12 +190,17 @@ module ActiveModel
def add(attribute, message = nil, options = {})
message ||= :invalid
+ full_message = options[:full_message]
+ message = full_message if full_message.is_a? Proc
+
if message.is_a?(Symbol)
message = generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
elsif message.is_a?(Proc)
message = message.call
+ full_message &&= message
end
+ message &&= ErrorMessage.new(message, full_message) if full_message
self[attribute] << message
end
@@ -245,7 +260,8 @@ module ActiveModel
options = { :default => "%{attribute} %{message}", :attribute => attr_name }
messages.each do |m|
- full_messages << I18n.t(:"errors.format", options.merge(:message => m))
+ full_messages << ((m.respond_to?(:full_message) && m.full_message) ?
+ m.full_message : I18n.t(:"errors.format", options.merge(:message => m)))
end
end
end
@@ -280,6 +296,7 @@ module ActiveModel
# </ol>
def generate_message(attribute, type = :invalid, options = {})
type = options.delete(:message) if options[:message].is_a?(Symbol)
+ full_message = options.delete(:full_message)
if options[:default]
ActiveSupport::Deprecation.warn \
@@ -296,7 +313,7 @@ module ActiveModel
defaults << options.delete(:message)
defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}"
defaults << :"errors.attributes.#{attribute}.#{type}"
- defaults << :"errors.messages.#{type}"
+ defaults << :"errors.#{full_message ? 'full_' : ''}messages.#{type}"
defaults.compact!
defaults.flatten!
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 1431228..d8c8588 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -170,6 +170,17 @@ module ActiveModel
self
end
+ # Represents an error message. Contains the model instance (+base+), the +attribute+ and the +options+
+ # passed to validators.
+ class ErrorMessage < String
+ attr_reader :full_message
+
+ def initialize(message, full_message)
+ super(message || full_message)
+ @full_message = full_message
+ end
+ end
+
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
# <tt>on(attribute)</tt> for the same attribute. 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>.
@@ -181,7 +192,10 @@ module ActiveModel
message ||= :invalid
if message.is_a?(Symbol)
- message = generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
+ options = options.except(*CALLBACKS_OPTIONS)
+ options[:message] = options.delete(:full_message) if full_message = options[:full_message]
+ message = generate_message(attribute, message, options)
+ message = ErrorMessage.new(message, full_message) if full_message
elsif message.is_a?(Proc)
message = message.call
end
@@ -245,7 +259,8 @@ module ActiveModel
options = { :default => "%{attribute} %{message}", :attribute => attr_name }
messages.each do |m|
- full_messages << I18n.t(:"errors.format", options.merge(:message => m))
+ full_messages << ((m.respond_to?(:full_message) && m.full_message) ?
+ m : I18n.t(:"errors.format", options.merge(:message => m)))
end
end
end
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 1431228..38549f5 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -170,6 +170,17 @@ module ActiveModel
self
end
+ # Represents an error message. Contains the model instance (+base+), the +attribute+ and the +options+
+ # passed to validators.
+ class ErrorMessage < String
+ attr_reader :full_message
+
+ def initialize(message, full_message)
+ super(message)
+ @full_message = full_message
+ end
+ end
+
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
# <tt>on(attribute)</tt> for the same attribute. 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>.
@@ -181,7 +192,10 @@ module ActiveModel
message ||= :invalid
if message.is_a?(Symbol)
- message = generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
+ options = options.except(*CALLBACKS_OPTIONS)
+ options[:message] = options.delete(:full_message) if full_message = options[:full_message]
+ message = generate_message(attribute, message, options)
+ message = ErrorMessage.new(message, full_message) if full_message
elsif message.is_a?(Proc)
message = message.call
end
@@ -245,7 +259,8 @@ module ActiveModel
options = { :default => "%{attribute} %{message}", :attribute => attr_name }
messages.each do |m|
- full_messages << I18n.t(:"errors.format", options.merge(:message => m))
+ full_messages << ((m.respond_to?(:full_message) && m.full_message) ?
+ m : I18n.t(:"errors.format", options.merge(:message => m)))
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment