-
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. #1157
Conversation
a1da4c5
to
a922fc3
Compare
885bc1d
to
3d86c95
Compare
ya have a lot of chained PRs :-p |
d224918
to
777b5a1
Compare
@@ -9,40 +9,12 @@ def initialize(serializer, options = {}) | |||
|
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.
This whole file is not required for this PR.
28750fa
to
273f0a7
Compare
daf1334
to
ef6577d
Compare
module ActiveModel::Serializer::Utils | ||
module_function | ||
|
||
def nested_lookup_paths(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.
can you add some yardoc?
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.
Done 👍
belongs_to :author | ||
class AuthorSerializer < ActiveModel::Serializer | ||
attributes :id | ||
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.
How is this different from belongs_to: :author, serializer: TweetAuthorSerializer
?
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.
One thing is that the namespacing makes it so that you don't have to use funky adapter names that you then have to specify manually.
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.
So, this PR is more a syntactic sugar than a new feature?
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.
that's sorta how I see it as well. But for me, the biggest benefit would be to greatly reduce the number of top level serializers that actually have VERY specific purposes
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.
It is, indeed syntactic sugar. However, I could see in the future having, say the Json adapter default to including all nested relationships (default to **
instead of *
currently), in a kind of safe way since the user would provide all the subsequent serializers to make the graph explicit (maybe with a warning when there is an undefined serializer and possibly when using a fallback serializer).
# @param [Object] klass | ||
# @return [Array<Symbol>] | ||
def nested_lookup_paths(klass) | ||
paths = klass.to_s.split('::').reverse.reduce([]) { |a, e| a + [[e] + Array(a.last)] }.reverse |
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.
Would you mind helping me read this code?
TweetSerializer::AuthorSerializer.to_s.split('::').reverse #=> ['AuthorSerializer', 'TweetSerializer']
_.reduce([]) {|result, element| result + [[element] + Array(result.last)] }.reverse
paths = []
paths = [] + [['AuthorSerializer'] + Array([]) ] #=> [['AuthorSerializer']]
paths = [['AuthorSerializer']] + [ ['TweetSerializer'] + Array(['AuthorSerializer']) ] #=> [ ["AuthorSerializer"], ["TweetSerializer", "AuthorSerializer"] ]
paths.reverse #=> ["TweetSerializer", "AuthorSerializer"], ["AuthorSerializer"]]
["TweetSerializer", "AuthorSerializer"], ["AuthorSerializer"]].map! {|path| path.join('::') } #=> ["TweetSerializer::AuthorSerializer", "AuthorSerializer"]
[TweetSerializer::AuthorSerializer", "AuthorSerializer"].select! {|path| Object.const_defined?(path, false) }.map! {|path| Object.const_get(path) }.push(Object) #=> [TweetSerializer::AuthorSerializer, AuthorSerializer, Object]
Is that right?
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.
It is indeed. I admit that part was slightly cryptic.
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.
So that as the test says
Utils.nested_lookup_paths(TweetSerializer::ShareSerializer::AuthorSerializer) #=> [::TweetSerializer::ShareSerializer::AuthorSerializer, ::ShareSerializer::AuthorSerializer, ::AuthorSerializer, ::Object]
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.
Yessir.
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.
And
Utils.nested_lookup([::TweetSerializer, ::Object], 'ShareSerializer') #=> ::TweetSerializer::ShareSerializer
?
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 believe so.
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.
So that
@share1 = Share.new
TweetSerializer.serializer_for(@share1) #=> TweetSerializer::ShareSerializer
ActiveModel::Serializer.serializer_for(@share1) #=> ShareSerializer
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.
Exactly.
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.
So that you can build associations on tweet.author using serializer: TweetSerializer::AuthorSerializer
?
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.
Yes, exactly, if available, and safely fallback to any less specialized serializer that can handle an Author
.
def nested_lookup_paths(klass) | ||
paths = klass.to_s.split('::').reverse.reduce([]) { |a, e| a + [[e] + Array(a.last)] }.reverse | ||
paths.map! { |path| "#{path.join('::')}" } | ||
paths.map! do |path| |
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.
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'll comment the first line, which might be pretty cryptic. The rest is pretty much self-explanatory, no?
The tests currently fail because ruby <= 2.0.0 does not allow namespaced constants to be checked via |
how long is < ruby 2.0 supported for? |
@NullVoxPopuli Official support for 1.9.3 has ended in Feb 2015 (so we could probably drop support for 1.9.3 as well...). However, this issue still arises with ruby 2.0.0... I believe it was fixed in 2.0.1. |
Closing this in favor of #1193. |
Nice! #1193 looks awesome and way more mature |
Following discussion on #1113.
This adds support for defining serializers inside other serializers, so that when serializing a nested resource, the most nested serializer is used in priority.