lib/mutations/errors.rb
module Mutations
# Offers a non-localized, english only, non configurable way to get error messages. This probably isnt good enough for users as-is.
class DefaultErrorMessageCreator
MESSAGES = Hash.new("is invalid").tap do |h|
h.merge!(
# General
:nils => "can't be nil",
:required => "is required",
# Datatypes
:string => "isn't a string",
:integer => "isn't an integer",
:boolean => "isn't a boolean",
:hash => "isn't a hash",
:array => "isn't an array",
:model => "isn't the right class",
# Date
:date => "date doesn't exist",
:before => "isn't before given date",
:after => "isn't after given date",
# String
:empty => "can't be blank",
:max_length => "is too long",
:min_length => "is too short",
:matches => "isn't in the right format",
:in => "isn't an option",
# Array
:class => "isn't the right class",
# Integer
:min => "is too small",
:max => "is too big",
# Model
:new_records => "isn't a saved model"
)
end
# key: the name of the field, eg, :email. Could be nil if it's an array element
# error_symbol: the validation symbol, eg, :matches or :required
# options:
# :index -- index of error if it's in an array
def message(key, error_symbol, options = {})
if options[:index]
"#{titleize(key || 'array')}[#{options[:index]}] #{MESSAGES[error_symbol]}"
else
"#{titleize(key)} #{MESSAGES[error_symbol]}"
end
end
def titleize(key)
key = key.to_s.downcase
if key == "id"
"ID"
elsif key.end_with?("_id")
"#{key.titleize} ID"
else
key.titleize
end
end
end
class ErrorAtom
# NOTE: in the future, could also pass in:
# - error type
# - value (eg, string :name, length: 5 # value=5)
# ErrorAtom.new(:name, :too_short)
# ErrorAtom.new(:name, :too_short, message: "is too short")
def initialize(key, error_symbol, options = {})
@key = key
@symbol = error_symbol
@message = options[:message]
@index = options[:index]
end
def symbolic
@symbol
end
def message
@message ||= Mutations.error_message_creator.message(@key, @symbol, :index => @index)
end
def message_list
Array(message)
end
end
# mutation.errors is an ErrorHash instance like this:
# {
# email: ErrorAtom(:matches),
# name: ErrorAtom(:too_weird, message: "is too weird"),
# adddress: { # Nested ErrorHash object
# city: ErrorAtom(:not_found, message: "That's not a city, silly!"),
# state: ErrorAtom(:in)
# }
# }
class ErrorHash < Hash
# Returns a nested HashWithIndifferentAccess where the values are symbols. Eg:
# {
# email: :matches,
# name: :too_weird,
# adddress: {
# city: :not_found,
# state: :in
# }
# }
def symbolic
HashWithIndifferentAccess.new.tap do |hash|
each do |k, v|
hash[k] = v.symbolic
end
end
end
# Returns a nested HashWithIndifferentAccess where the values are messages. Eg:
# {
# email: "isn't in the right format",
# name: "is too weird",
# adddress: {
# city: "is not a city",
# state: "isn't a valid option"
# }
# }
def message
HashWithIndifferentAccess.new.tap do |hash|
each do |k, v|
hash[k] = v.message
end
end
end
# Returns a flat array where each element is a full sentence. Eg:
# [
# "Email isn't in the right format.",
# "Name is too weird",
# "That's not a city, silly!",
# "State isn't a valid option."
# ]
def message_list
list = []
each do |k, v|
list.concat(v.message_list)
end
list
end
end
class ErrorArray < Array
def symbolic
map {|e| e && e.symbolic }
end
def message
map {|e| e && e.message }
end
def message_list
compact.map {|e| e.message_list }.flatten
end
end
end