-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for nested serializers #1225
Add support for nested serializers #1225
Conversation
450744a
to
9da2061
Compare
@@ -112,10 +112,25 @@ def self.digest_caller_file(caller_line) | |||
Digest::MD5.hexdigest(serializer_file_contents) | |||
end | |||
|
|||
# TODO(beauby): Cache this. | |||
def self.serializer_lookup_chain_for(klass) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this extraction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some documentation on how this works (when to use, expected input/output, etc) would be fantabulous
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty much self-explanatory to me. What kind of comment would you add that would not be direct paraphrasing of the code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
me too, but I would like to prefer to over document than to under document. Like, look at how rails documents everything with plenty of examples: https://github.com/rails/rails/blob/master/activemodel/lib/active_model/errors.rb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I guess that is more or less direct paraphrasing in some cases though) :-\
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah but serializer_lookup_chain_for
is part of the private API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fair point
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
b47d5e7
to
745cd82
Compare
def test_serializer_for_nested_resource | ||
comment = ResourceNamespace::Comment.new | ||
serializer = ResourceNamespace::PostSerializer.serializer_for(comment) | ||
assert_equal(ResourceNamespace::PostSerializer::CommentSerializer, serializer) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's missing from the tests is when ResourceNamespace::PostSerializer.serializer_for
is called. the tests don't confirm this is hooked up anywhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand. ResourceNamespace::PostSerializer.serializer_for
is called just above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I see is:
ActiveModel::Serializer.serializer_for(post) #=> ResourceNamespace::PostSerializer
ResourceNamespace::PostSerializer.serializer_for(comment) #=> ResourceNamespace::PostSerializer::CommentSerializer
which tests serialization from an AM::S subclass, but not that the subclass is ever used.. maybe my comment just means there should be another test somewhere else
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to add a test to test/serializers/associations_test.rb
to make sure the corresponding serializer gets used. This will be doable in a decent way once the test infrastructure refactor has been done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added tests.
Nice and slim! good encapsulation! |
223e597
to
8692e94
Compare
chain.push(serializer_class_name) | ||
|
||
chain | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bf4 So if you were referring to this part of code saying it was not easily understandable, then I agree. Suggestions? I could add comments, but I don't see how to make the code itself clearer.
96d4b22
to
d913dbf
Compare
Squashed. |
d913dbf
to
de2c834
Compare
ActiveModel::Serializer.serializer_for(resource) | ||
end | ||
serializer_context_class = options.fetch(:serializer_context_class, ActiveModel::Serializer) | ||
serializer_class = options.fetch(:serializer) { serializer_context_class.serializer_for(resource) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential 🐛 serializer_context_class
is a required option. Would you mind adding a comment to the initializer to that effect? and if it's absent, it'll throw a KeyError. How do we want to handle that?
Maybe just say "don't use ArraySerializer directly, Just use SerializableResource" :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous line(serializer_context_class = options.fetch(:serializer_context_class, ActiveModel::Serializer)
) ensures it is not required. Am I misunderstanding?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nope, I'm 👊 (facepalm?) oops never mind me
46d0915
to
9147469
Compare
chain | ||
end | ||
|
||
# @api private |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
giving us the license to change it :) 👍
The build failed on appveyor, not sure why as I can't consult the logs. |
Actually, I have the answer:
|
Yikes. I'll write a timeout max for ci B mobile phone
|
hmm, I don't really know how to do around hooks Minitest without hacking it |
oh weird, it succeeded |
try |
67c72e7
to
9147469
Compare
👍 LGTM |
Add support for nested serializers
Adds support for finding serializers based on the "current" serializers namespace to support things like serializer versioning. Without this you have to be explicit in naming which serializer to use in all of your serializer associations. That's OK usually (albeit annoying) but it breaks polymorphism since you can't really specify the right serializer ahead of time. I came across a bunch of PRs such as rails-api#1225 that talk about different versions of namespacing based on modules or controllers or whatever and some of them even talk about supporting serializer namespaces but I have been unable to find one that actually does this part of it. If there is already work being done for this, feel free to ignore this PR but we needed this for our current work. This PR builds on top of the work done by @beauby in rails-api#1225 to add support for namespaced serializer lookups. @beauby's work only allowed for actual nesting of serializers withing a namespaced serializer (e.g. for overrides) but did not actually walk up the tree to siblings or serializers farther up the inheritance/namespace tree. It simply modifies `Serializer#serializer_lookup_chain_for` to add all of the possible levels of namespaces based on the current serializer. With this patch for example if we have: ```ruby class Public::V1::PostSerializer belongs_to :author end class Public::V1::AuthorSerializer end ``` we will wind up with the following items in the lookup chain for an author when serializing a Post with Public::V1::PostSerializer: ```ruby ['Public::V1::PostSerializer::AuthorSerializer', 'Public::V1::AuthorSerializer', 'Public::AuthorSerializer', '::AuthorSerializer'] ```
Adds support for finding serializers based on the "current" serializers namespace to support things like serializer versioning. Without this you have to be explicit in naming which serializer to use in all of your serializer associations. That's OK usually (albeit annoying) but it breaks polymorphism since you can't really specify the right serializer ahead of time. I came across a bunch of PRs such as rails-api#1225 that talk about different versions of namespacing based on modules or controllers or whatever and some of them even talk about supporting serializer namespaces but I have been unable to find one that actually does this part of it. If there is already work being done for this, feel free to ignore this PR but we needed this for our current work. This PR builds on top of the work done by @beauby in rails-api#1225 to add support for namespaced serializer lookups. @beauby's work only allowed for actual nesting of serializers withing a namespaced serializer (e.g. for overrides) but did not actually walk up the tree to siblings or serializers farther up the inheritance/namespace tree. It simply modifies `Serializer#serializer_lookup_chain_for` to add all of the possible levels of namespaces based on the current serializer. With this patch for example if we have: ```ruby class Public::V1::PostSerializer belongs_to :author end class Public::V1::AuthorSerializer end ``` we will wind up with the following items in the lookup chain for an author when serializing a Post with Public::V1::PostSerializer: ```ruby ['Public::V1::PostSerializer::AuthorSerializer', 'Public::V1::AuthorSerializer', 'Public::AuthorSerializer', '::AuthorSerializer'] ```
Adds support for finding serializers based on the "current" serializers namespace to support things like serializer versioning. Without this you have to be explicit in naming which serializer to use in all of your serializer associations. That's OK usually (albeit annoying) but it breaks polymorphism since you can't really specify the right serializer ahead of time. I came across a bunch of PRs such as rails-api#1225 that talk about different versions of namespacing based on modules or controllers or whatever and some of them even talk about supporting serializer namespaces but I have been unable to find one that actually does this part of it. If there is already work being done for this, feel free to ignore this PR but we needed this for our current work. This PR builds on top of the work done by @beauby in rails-api#1225 to add support for namespaced serializer lookups. @beauby's work only allowed for actual nesting of serializers withing a namespaced serializer (e.g. for overrides) but did not actually walk up the tree to siblings or serializers farther up the inheritance/namespace tree. It simply modifies `Serializer#serializer_lookup_chain_for` to add all of the possible levels of namespaces based on the current serializer. With this patch for example if we have: ```ruby class Public::V1::PostSerializer belongs_to :author end class Public::V1::AuthorSerializer end ``` we will wind up with the following items in the lookup chain for an author when serializing a Post with Public::V1::PostSerializer: ```ruby ['Public::V1::PostSerializer::AuthorSerializer', 'Public::V1::AuthorSerializer', 'Public::AuthorSerializer', '::AuthorSerializer'] ```
Match current API endpoints (/links, /characters) There is a pull request that is currently active (within the last day) for Active Model Serializers that will allow for nested and inline Serializers. This will allow `CardSerializer` to go from the complicated monstrosity that it is to a much simpler representation. For example ``` has_one :character ``` instead of: ``` def character Api::V1::CharacterSerializer.new(object.character) end ``` follow this pull request at rails-api/active_model_serializers#1225 See merge request !1
This is half of #1193. This PR brings lookup for serializer classes in different namespaces (namely that of the parent serializer, that of the resource), so that the following is possible:
(this is a way to make single-purpose nested serializers contained in one class instead of polluting the global namespace and having to call them
PostCommentSerializer
).Cases not handled yet:
Post
is serialized with aNamespace::PostSerializer
, we might want to look forNamespace::AuthorSerializer
),Namespace::PostController
serializes aPost
, we might want to look forNamespace::PostSerializer
).