Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions lib/active_resource/validations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,14 @@ def from_xml(xml, save_cache = false)
end

# Module to support validation and errors with Active Resource objects. The module overrides
# Base#save to rescue ActiveResource::ResourceInvalid exceptions and parse the errors returned
# Base#save to rescue exceptions and parse the errors returned
# in the web service response. The module also adds an +errors+ collection that mimics the interface
# of the errors provided by ActiveModel::Errors.
#
# By default, Active Resource will raise, then rescue from ActiveResource::ResourceInvalid
# exceptions for a response with a +422+ status code. Set the +remote_errors+
# class attribute to rescue from other exceptions.
#
# ==== Example
#
# Consider a Person resource on the server requiring both a +first_name+ and a +last_name+ with a
Expand Down Expand Up @@ -133,6 +137,22 @@ module Validations
included do
alias_method :save_without_validation, :save
alias_method :save, :save_with_validation
class_attribute :_remote_errors, instance_accessor: false
end

class_methods do
# Sets the exception classes to rescue from during Base#save.
def remote_errors=(errors)
errors = Array.wrap(errors)
errors.map! { |error| error.is_a?(String) ? error.constantize : error }
self._remote_errors = errors
end

# Returns the exception classes rescued from during Base#save. Defaults to
# ActiveResource::ResourceInvalid.
def remote_errors
_remote_errors.presence || ResourceInvalid
end
end

# Validate a resource and save (POST) it to the remote web service.
Expand All @@ -150,7 +170,7 @@ def save_with_validation(options = {})
else
false
end
rescue ResourceInvalid => error
rescue *self.class.remote_errors => error
# cache the remote errors because every call to <tt>valid?</tt> clears
# all errors. We must keep a copy to add these back after local
# validations.
Expand Down
33 changes: 32 additions & 1 deletion test/cases/base_errors_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,19 +111,50 @@ def test_should_mark_as_invalid_when_content_type_is_unavailable_in_response_hea
end
end

def test_rescues_from_configured_exception_class_name
ActiveResource::HttpMock.respond_to do |mock|
mock.post "/people.xml", {}, %q(<?xml version="1.0" encoding="UTF-8"?><errors><error>Age can't be blank</error></errors>), 400, {}
mock.post "/people.json", {}, %q({"errors":{"age":["can't be blank"]}}), 400, {}
end

[ :json, :xml ].each do |format|
invalid_user_using_format(format, rescue_from: "ActiveResource::BadRequest") do
assert_not_predicate @person, :valid?
assert_equal [ "can't be blank" ], @person.errors[:age]
end
end
end

def test_rescues_from_configured_array_of_exception_classes
[ :json, :xml ].product([ 400, 422 ]).each do |format, error_status|
ActiveResource::HttpMock.respond_to do |mock|
mock.post "/people.xml", {}, %q(<?xml version="1.0" encoding="UTF-8"?><errors><error>Age can't be blank</error></errors>), error_status, {}
mock.post "/people.json", {}, %q({"errors":{"age":["can't be blank"]}}), error_status, {}
end

invalid_user_using_format(format, rescue_from: [ ActiveResource::BadRequest, ActiveResource::ResourceInvalid ]) do
assert_not_predicate @person, :valid?
assert_equal [ "can't be blank" ], @person.errors[:age]
end
end
end

private
def invalid_user_using_format(mime_type_reference)
def invalid_user_using_format(mime_type_reference, rescue_from: nil)
previous_format = Person.format
previous_schema = Person.schema
previous_remote_errors = Person.remote_errors

Person.format = mime_type_reference
Person.schema = { "known_attribute" => "string" }
Person.remote_errors = rescue_from
@person = Person.new(name: "", age: "", phone: "", phone_work: "")
assert_equal false, @person.save

yield
ensure
Person.format = previous_format
Person.schema = previous_schema
Person.remote_errors = previous_remote_errors
end
end