diff options
Diffstat (limited to 'activemodel/lib/active_model/errors.rb')
-rw-r--r-- | activemodel/lib/active_model/errors.rb | 96 |
1 files changed, 63 insertions, 33 deletions
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 0e2a2fd5e2..aafd1c8a74 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -49,7 +49,7 @@ module ActiveModel # # The last three methods are required in your object for Errors to be # able to generate error messages correctly and also handle multiple - # languages. Of course, if you extend your object with ActiveModel::Translations + # languages. Of course, if you extend your object with ActiveModel::Translation # you will not need to implement the last two. Likewise, using # ActiveModel::Validations will handle the validation related methods # for you. @@ -63,7 +63,7 @@ module ActiveModel class Errors include Enumerable - CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank] + CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict] attr_reader :messages @@ -86,8 +86,9 @@ module ActiveModel # Do the error messages include an error with key +error+? def include?(error) - messages.include? error + (v = messages[error]) && v.any? end + alias :has_key? :include? # Get messages for +key+ def get(key) @@ -174,11 +175,13 @@ module ActiveModel to_a.size end - # Returns true if there are any errors, false if not. + # Returns true if no errors are found, false otherwise. + # If the error message is a string it can be empty. def empty? - all? { |k, v| v && v.empty? } + all? { |k, v| v && v.empty? && !v.is_a?(String) } end alias_method :blank?, :empty? + # Returns an xml formatted representation of the Errors hash. # # p.errors.add(:name, "can't be blank") @@ -203,20 +206,16 @@ module ActiveModel messages.dup 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>. + # Adds +message+ to the error messages on +attribute+. More than one error can be added to the same + # +attribute+. # If no +message+ is supplied, <tt>:invalid</tt> 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 <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 + message = normalize_message(attribute, message, options) + if options[:strict] + raise ActiveModel::StrictValidationFailed, message end self[attribute] << message @@ -239,6 +238,15 @@ module ActiveModel end end + # Returns true if an error on the attribute with the given message is present, false otherwise. + # +message+ is treated the same as for +add+. + # p.errors.add :name, :blank + # p.errors.added? :name, :blank # => true + def added?(attribute, message = nil, options = {}) + message = normalize_message(attribute, message, options) + self[attribute].include? message + end + # Returns all the full error messages in an array. # # class Company @@ -248,22 +256,24 @@ module ActiveModel # # 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"] + # ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Email can't be blank"] def full_messages - map { |attribute, message| - if attribute == :base - message - else - attr_name = attribute.to_s.gsub('.', '_').humanize - attr_name = @base.class.human_attribute_name(attribute, :default => attr_name) - - I18n.t(:"errors.format", { - :default => "%{attribute} %{message}", - :attribute => attr_name, - :message => message - }) - end - } + map { |attribute, message| full_message(attribute, message) } + end + + # Returns a full message for a given attribute. + # + # company.errors.full_message(:name, "is invalid") # => + # "Name is invalid" + def full_message(attribute, message) + return message if attribute == :base + attr_name = attribute.to_s.gsub('.', '_').humanize + attr_name = @base.class.human_attribute_name(attribute, :default => attr_name) + I18n.t(:"errors.format", { + :default => "%{attribute} %{message}", + :attribute => attr_name, + :message => message + }) end # Translates an error message in its default scope @@ -293,13 +303,17 @@ module ActiveModel def generate_message(attribute, type = :invalid, options = {}) type = options.delete(:message) if options[:message].is_a?(Symbol) - defaults = @base.class.lookup_ancestors.map do |klass| - [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}", - :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ] + if @base.class.respond_to?(:i18n_scope) + defaults = @base.class.lookup_ancestors.map do |klass| + [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}", + :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ] + end + else + defaults = [] end defaults << options.delete(:message) - defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}" + defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}" if @base.class.respond_to?(:i18n_scope) defaults << :"errors.attributes.#{attribute}.#{type}" defaults << :"errors.messages.#{type}" @@ -318,5 +332,21 @@ module ActiveModel I18n.translate(key, options) end + + private + def normalize_message(attribute, message, options) + message ||= :invalid + + if message.is_a?(Symbol) + generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS)) + elsif message.is_a?(Proc) + message.call + else + message + end + end + end + + class StrictValidationFailed < StandardError end end |