1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/activeresource/lib/active_resource/validations.rb
2007-01-16 06:34:10 +00:00

161 lines
5.2 KiB
Ruby

module ActiveResource
class ResourceInvalid < ClientError #:nodoc:
end
# Active Resource validation is reported to and from this object, which is used by Base#save
# to determine whether the object in a valid state to be saved. See usage example in Validations.
class Errors
include Enumerable
attr_reader :errors
delegate :empty?, :to => :errors
def initialize(base) # :nodoc:
@base, @errors = base, {}
end
def add_to_base(msg)
add(:base, msg)
end
def add(attribute, msg)
@errors[attribute.to_s] = [] if @errors[attribute.to_s].nil?
@errors[attribute.to_s] << msg
end
# Returns true if the specified +attribute+ has errors associated with it.
def invalid?(attribute)
!@errors[attribute.to_s].nil?
end
# * Returns nil, if no errors are associated with the specified +attribute+.
# * Returns the error message, if one error is associated with the specified +attribute+.
# * Returns an array of error messages, if more than one error is associated with the specified +attribute+.
def on(attribute)
errors = @errors[attribute.to_s]
return nil if errors.nil?
errors.size == 1 ? errors.first : errors
end
alias :[] :on
# Returns errors assigned to base object through add_to_base according to the normal rules of on(attribute).
def on_base
on(:base)
end
# Yields each attribute and associated message per error added.
def each
@errors.each_key { |attr| @errors[attr].each { |msg| yield attr, msg } }
end
# Yields each full error message added. So Person.errors.add("first_name", "can't be empty") will be returned
# through iteration as "First name can't be empty".
def each_full
full_messages.each { |msg| yield msg }
end
# Returns all the full error messages in an array.
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 << [attr.humanize, msg].join(' ')
end
end
end
full_messages
end
def clear
@errors = {}
end
# Returns the total number of errors added. Two errors added to the same attribute will be counted as such
# with this as well.
def size
@errors.values.inject(0) { |error_count, attribute| error_count + attribute.size }
end
alias_method :count, :size
alias_method :length, :size
def from_xml(xml)
clear
humanized_attributes = @base.attributes.keys.inject({}) { |h, attr_name| h.update(attr_name.humanize => attr_name) }
messages = Hash.from_xml(xml)['errors']['error'] rescue []
messages.each do |message|
attr_message = humanized_attributes.keys.detect do |attr_name|
if message[0, attr_name.size + 1] == "#{attr_name} "
add humanized_attributes[attr_name], message[(attr_name.size + 1)..-1]
end
end
add_to_base message if attr_message.nil?
end
end
end
# Module to allow validation of ActiveResource objects, which are implemented by overriding +Base#validate+ or its variants.
# Each of these methods can inspect the state of the object, which usually means ensuring that a number of
# attributes have a certain value (such as not empty, within a given range, matching a certain regular expression). For example:
#
# class Person < ActiveResource::Base
# self.site = "http://www.localhost.com:3000/"
# protected
# def validate
# errors.add_on_empty %w( first_name last_name )
# errors.add("phone_number", "has invalid format") unless phone_number =~ /[0-9]*/
# end
#
# def validate_on_create # is only run the first time a new object is saved
# unless valid_member?(self)
# errors.add("membership_discount", "has expired")
# end
# end
#
# def validate_on_update
# errors.add_to_base("No changes have occurred") if unchanged_attributes?
# end
# end
#
# person = Person.new("first_name" => "Jim", "phone_number" => "I will not tell you.")
# person.save # => false (and doesn't do the save)
# person.errors.empty? # => false
# person.errors.count # => 2
# person.errors.on "last_name" # => "can't be empty"
# person.attributes = { "last_name" => "Halpert", "phone_number" => "555-5555" }
# person.save # => true (and person is now saved to the remote service)
#
# An Errors object is automatically created for every resource.
module Validations
def self.included(base) # :nodoc:
base.class_eval do
alias_method_chain :save, :validation
end
end
def save_with_validation
save_without_validation
true
rescue ResourceInvalid => error
errors.from_xml(error.response.body)
false
end
def valid?
errors.empty?
end
# Returns the Errors object that holds all information about attribute error messages.
def errors
@errors ||= Errors.new(self)
end
end
end