Skip to content

Commit c0fea1d

Browse files
authored
Merge pull request #444 from seanpdoyle/validation-error-classes
Validations: Configure which status codes to rescue from
2 parents c71ca22 + 62bee80 commit c0fea1d

File tree

2 files changed

+54
-3
lines changed

2 files changed

+54
-3
lines changed

lib/active_resource/validations.rb

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,14 @@ def from_xml(xml, save_cache = false)
6767
end
6868

6969
# Module to support validation and errors with Active Resource objects. The module overrides
70-
# Base#save to rescue ActiveResource::ResourceInvalid exceptions and parse the errors returned
70+
# Base#save to rescue exceptions and parse the errors returned
7171
# in the web service response. The module also adds an +errors+ collection that mimics the interface
7272
# of the errors provided by ActiveModel::Errors.
7373
#
74+
# By default, Active Resource will raise, then rescue from ActiveResource::ResourceInvalid
75+
# exceptions for a response with a +422+ status code. Set the +remote_errors+
76+
# class attribute to rescue from other exceptions.
77+
#
7478
# ==== Example
7579
#
7680
# Consider a Person resource on the server requiring both a +first_name+ and a +last_name+ with a
@@ -133,6 +137,22 @@ module Validations
133137
included do
134138
alias_method :save_without_validation, :save
135139
alias_method :save, :save_with_validation
140+
class_attribute :_remote_errors, instance_accessor: false
141+
end
142+
143+
class_methods do
144+
# Sets the exception classes to rescue from during Base#save.
145+
def remote_errors=(errors)
146+
errors = Array.wrap(errors)
147+
errors.map! { |error| error.is_a?(String) ? error.constantize : error }
148+
self._remote_errors = errors
149+
end
150+
151+
# Returns the exception classes rescued from during Base#save. Defaults to
152+
# ActiveResource::ResourceInvalid.
153+
def remote_errors
154+
_remote_errors.presence || ResourceInvalid
155+
end
136156
end
137157

138158
# Validate a resource and save (POST) it to the remote web service.
@@ -150,7 +170,7 @@ def save_with_validation(options = {})
150170
else
151171
false
152172
end
153-
rescue ResourceInvalid => error
173+
rescue *self.class.remote_errors => error
154174
# cache the remote errors because every call to <tt>valid?</tt> clears
155175
# all errors. We must keep a copy to add these back after local
156176
# validations.

test/cases/base_errors_test.rb

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,19 +111,50 @@ def test_should_mark_as_invalid_when_content_type_is_unavailable_in_response_hea
111111
end
112112
end
113113

114+
def test_rescues_from_configured_exception_class_name
115+
ActiveResource::HttpMock.respond_to do |mock|
116+
mock.post "/people.xml", {}, %q(<?xml version="1.0" encoding="UTF-8"?><errors><error>Age can't be blank</error></errors>), 400, {}
117+
mock.post "/people.json", {}, %q({"errors":{"age":["can't be blank"]}}), 400, {}
118+
end
119+
120+
[ :json, :xml ].each do |format|
121+
invalid_user_using_format(format, rescue_from: "ActiveResource::BadRequest") do
122+
assert_not_predicate @person, :valid?
123+
assert_equal [ "can't be blank" ], @person.errors[:age]
124+
end
125+
end
126+
end
127+
128+
def test_rescues_from_configured_array_of_exception_classes
129+
[ :json, :xml ].product([ 400, 422 ]).each do |format, error_status|
130+
ActiveResource::HttpMock.respond_to do |mock|
131+
mock.post "/people.xml", {}, %q(<?xml version="1.0" encoding="UTF-8"?><errors><error>Age can't be blank</error></errors>), error_status, {}
132+
mock.post "/people.json", {}, %q({"errors":{"age":["can't be blank"]}}), error_status, {}
133+
end
134+
135+
invalid_user_using_format(format, rescue_from: [ ActiveResource::BadRequest, ActiveResource::ResourceInvalid ]) do
136+
assert_not_predicate @person, :valid?
137+
assert_equal [ "can't be blank" ], @person.errors[:age]
138+
end
139+
end
140+
end
141+
114142
private
115-
def invalid_user_using_format(mime_type_reference)
143+
def invalid_user_using_format(mime_type_reference, rescue_from: nil)
116144
previous_format = Person.format
117145
previous_schema = Person.schema
146+
previous_remote_errors = Person.remote_errors
118147

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

124154
yield
125155
ensure
126156
Person.format = previous_format
127157
Person.schema = previous_schema
158+
Person.remote_errors = previous_remote_errors
128159
end
129160
end

0 commit comments

Comments
 (0)