# Enables customization of validation error messages. # # Default validation error messages are always prefixed with the offending field name. # This plugin allows the developer to specify exactly where the field name should be # displayed in the message or whether it should be displayed at all. # # Tested with Rails 0.14.3 # # USAGE: # # Include %s in message to specify where the field name should be displayed: # validates_presence_of :rope, :on => :create, :message => "Can't skip %s" # results in: Can't skip Rope # # Messages may combine %d and %s: # validates_length_of :rope, :minimum=>10, :message => "Need at least %d feet of %s to hang yourself" # results in: Need at least 10 feet of Rope to hang yourself # # Wrap message in [ ] to avoid field name substition # validates_presence_of :joy, :on => :create, :message => "[Cheer up!]" # results in: Cheer up! # # %s may be specified more than once: # validates_presence_of :joy, :on => :create, :message => "Happy, happy, %s, %s!" # results in: Happy, happy, Joy, Joy! # # Default behaviour is preserved: # validates_presence_of :joy, :on => :create, :message => "is required" # results in: Joy is required module ActiveRecord #:nodoc: class Errors def add_on_boundary_breaking(attributes, range, too_long_msg = @@default_error_messages[:too_long], too_short_msg = @@default_error_messages[:too_short]) for attr in [attributes].flatten value = @base.respond_to?(attr.to_s) ? @base.send(attr.to_s) : @base[attr.to_s] # add(attr, too_short_msg % range.begin) if value && value.length < range.begin add(attr, too_short_msg.gsub(/(%d)/, range.begin.to_s)) if value && value.length < range.begin # add(attr, too_long_msg % range.end) if value && value.length > range.end add(attr, too_long_msg.gsub(/(%d)/, range.end.to_s)) if value && value.length > range.end end end # Returns all the full error messages in an array. # If message contains %s, it is replaced with field name # If message is wrapped in [ ] field name isn't included # If message doesn't contain %s, message is prefixed with field name (default rails behaviour) def full_messages full_messages = [] @errors.each_key do |attr| @errors[attr].each do |msg| next if msg.nil? if attr == "base" full_messages << msg else # full_messages << @base.class.human_attribute_name(attr) + " " + msg if msg =~ /^\[.*\]$/ full_messages << msg[1..-2] else if msg.include? '%s' full_messages << msg.gsub(/(%s)/, @base.class.human_attribute_name(attr)) else full_messages << @base.class.human_attribute_name(attr) + " " + msg end end end end end return full_messages end end module Validations module ClassMethods def validates_length_of(*attrs) # Merge given options with defaults. options = { :too_long => ActiveRecord::Errors.default_error_messages[:too_long], :too_short => ActiveRecord::Errors.default_error_messages[:too_short], :wrong_length => ActiveRecord::Errors.default_error_messages[:wrong_length] }.merge(DEFAULT_VALIDATION_OPTIONS) options.update(attrs.pop.symbolize_keys) if attrs.last.is_a?(Hash) # 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 # Get range option and value. option = range_options.first option_value = options[range_options.first] case option when :within, :in raise ArgumentError, ":#{option} must be a Range" unless option_value.is_a?(Range) too_short = options[:too_short] % option_value.begin too_long = options[:too_long] % option_value.end validates_each(attrs, options) do |record, attr, value| if value.nil? or value.size < option_value.begin record.errors.add(attr, too_short) elsif value.size > option_value.end record.errors.add(attr, too_long) end end 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 => "<=" } message_options = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long } # message = (options[:message] || options[message_options[option]]) % option_value message = (options[:message] || options[message_options[option]]).gsub(/(%d)/, option_value.to_s) validates_each(attrs, options) do |record, attr, value| record.errors.add(attr, message) unless !value.nil? and value.size.method(validity_checks[option])[option_value] end end end end end end