diff --git a/lib/active_resource/validations.rb b/lib/active_resource/validations.rb
index e5ec962eef..be9bb31395 100644
--- a/lib/active_resource/validations.rb
+++ b/lib/active_resource/validations.rb
@@ -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
@@ -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.
@@ -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 valid? clears
# all errors. We must keep a copy to add these back after local
# validations.
diff --git a/test/cases/base_errors_test.rb b/test/cases/base_errors_test.rb
index d0ed1541aa..bfbff56ea3 100644
--- a/test/cases/base_errors_test.rb
+++ b/test/cases/base_errors_test.rb
@@ -111,13 +111,43 @@ 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(Age can't be blank), 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(Age can't be blank), 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
@@ -125,5 +155,6 @@ def invalid_user_using_format(mime_type_reference)
ensure
Person.format = previous_format
Person.schema = previous_schema
+ Person.remote_errors = previous_remote_errors
end
end