Validations: Decode response with format#441
Merged
Merged
Conversation
Member
|
Can you break the changes to |
Contributor
Author
Sure! That change is required for this behavior to work, but I'm happy to open a smaller PR to merge it ahead of reviewing this one. |
seanpdoyle
added a commit
to seanpdoyle/activeresource
that referenced
this pull request
Oct 23, 2025
Related to rails#441 This commit also adds the `remove_root = true` optional argument to the `JsonFormat` and `XmlFormat` modules' `#decode` method. The name and positional argument style draw direct inspiration from the `ActiveResource::Base#load` method's optional `remove_root = true` argument. The change is in support of replacing internal calls to `Hash.from_xml` and `ActiveSupport::JSON.decode`. Those method invocations are replaced with the appropriate format's `.decode` method.
Member
|
Yeah, let's break in two. I intended to merge both, but I don't think that change on its own makes sense. |
Contributor
Author
|
I've opened #446. |
472ae3f to
71dd0cb
Compare
First, extract the `ActiveResource::ErrorsParser` class along with the
internal `ActiveResource::ActiveModelErrorsParser` class (that inherits
from `ActiveResource::ErrorsParser`). Configure a `errors_parser`
resource class attribute to control how errors are extracted from
decoded payloads.
The `errors_parser` pattern and the `ErrorsParser` class are directly
inspired by the `collection_parser` and `ActiveResource::Collection`
class.
ActiveResource::ErrorsParser
---
`ActiveResource::ErrorsParser` is a wrapper to handle parsing responses in
response to invalid requests that do not directly map to Active Model
error conventions.
You can define a custom class that inherits from
`ActiveResource::ErrorsParser` in order to to set the elements instance.
The initialize method will receive the `ActiveResource::Formats` parsed
result and should set `@messages`.
Consider a `POST /posts.json` request that results in a `422
Unprocessable Content` response with the following `application/json`
body:
```json
{
"error": true,
"messages": ["Something went wrong", "Title can't be blank"]
}
```
A Post class can be setup to handle it with:
```ruby
class Post < ActiveResource::Base
self.errors_parser = PostErrorsParser
end
```
A custom `ActiveResource::ErrorsParser` instance's `messages` method
should return a mapping of attribute names (or `"base"`) to arrays of
error message strings:
```ruby
class PostErrorsParser < ActiveResource::ErrorsParser
def initialize(parsed)
@messages = Hash.new { |hash, attr_name| hash[attr_name] = [] }
parsed["messages"].each do |message|
if message.starts_with?("Title")
@messages["title"] << message
else
@messages["base"] << message
end
end
end
end
```
When the `POST /posts.json` request is submitted by calling `save`, the
errors are parsed from the body and assigned to the Post instance's
`errors` object:
```ruby
post = Post.new(title: "")
post.save # => false
post.valid? # => false
post.errors.messages_for(:base) # => ["Something went wrong"]
post.errors.messages_for(:title) # => ["Title can't be blank"]
```
If the custom `ActiveResource::ErrorsParser` instance's `messages`
method returns an array of error message strings, Active Resource will
try to infer the attribute name based on the contents of the error
message string. If an error starts with a known attribute name, Active
Resource will add the message to that attribute's error messages. If a
known attribute name cannot be inferred, the error messages will be
added to the `:base` errors:
```ruby
class PostErrorsParser < ActiveResource::ErrorsParser
def initialize(parsed)
@messages = parsed["messages"]
end
end
```
Changes to ActiveResource::Formats::JsonFormat and ActiveResource::Formats::XmlFormat
---
This commit changes the `ActiveResource::Errors#from_xml` and
`ActiveResource::Errors#from_json` methods to be implemented in terms of
a new `#from_body` method. The `#from_body` method is flexible enough to
support any application-side custom formats, while internally flexible
enough to rely on the built-in JSON and XML formats.
71dd0cb to
09e0d7e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First, extract the
ActiveResource::ErrorsParserclass along with the internalActiveResource::ActiveModelErrorsParserclass (that inherits fromActiveResource::ErrorsParser). Configure aerrors_parserresource class attribute to control how errors are extracted from decoded payloads.The
errors_parserpattern and theErrorsParserclass are directly inspired by thecollection_parserandActiveResource::Collectionclass.ActiveResource::ErrorsParser
ActiveResource::ErrorsParseris a wrapper to handle parsing responses in response to invalid requests that do not directly map to Active Model error conventions.You can define a custom class that inherits from
ActiveResource::ErrorsParserin order to to set the elements instance.The initialize method will receive the
ActiveResource::Formatsparsed result and should set@messages.Consider a
POST /posts.jsonrequest that results in a422 Unprocessable Contentresponse with the followingapplication/jsonbody:{ "error": true, "messages": ["Something went wrong", "Title can't be blank"] }A Post class can be setup to handle it with:
A custom
ActiveResource::ErrorsParserinstance'smessagesmethod should return a mapping of attribute names (or"base") to arrays of error message strings:When the
POST /posts.jsonrequest is submitted by callingsave, the errors are parsed from the body and assigned to the Post instance'serrorsobject:If the custom
ActiveResource::ErrorsParserinstance'smessagesmethod returns an array of error message strings, Active Resource will try to infer the attribute name based on the contents of the error message string. If an error starts with a known attribute name, Active Resource will add the message to that attribute's error messages. If a known attribute name cannot be inferred, the error messages will be added to the:baseerrors:Changes to ActiveResource::Formats::JsonFormat and ActiveResource::Formats::XmlFormat
This commit changes the
ActiveResource::Errors#from_xmlandActiveResource::Errors#from_jsonmethods to be implemented in terms of a new#from_bodymethod. The#from_bodymethod is flexible enough to support any application-side custom formats, while internally flexible enough to rely on the built-in JSON and XML formats.