Skip to content
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

Closed
wants to merge 15 commits into from
Prev Previous commit
Next Next commit
Remove stale methods.
beauby committed Sep 21, 2015
commit ef6577d5e948ec7162ed543016e709a7d2ab815a
20 changes: 0 additions & 20 deletions lib/active_model/serializer.rb
Original file line number Diff line number Diff line change
@@ -170,25 +170,5 @@ def attributes(options = {})
ActiveModelSerializers.silence_warnings do
attr_accessor :instance_options
end

# Transforms an inclusion hash into an array of corresponding existing associations,
# and corresponding inclusion hashes.
# @param [Hash] includes
# @return [Array] an array of pairs [association, include_hash] for matching associations
def expand_includes(includes)
if includes.size == 1 && includes.each_key.first == :*
associations.map { |assoc| [assoc, includes.each_value.first] }
elsif includes.size == 1 && includes.each_key.first == :**
associations.map { |assoc| [assoc, { :** => nil }] }
else
expanded_associations = includes.map do |inc|
association = associations.find { |assoc| assoc.key == inc.first }
[association, inc.second] if association
end
expanded_associations.delete(nil)

expanded_associations
end
end
end
end
32 changes: 0 additions & 32 deletions lib/active_model/serializer/utils.rb
Original file line number Diff line number Diff line change
@@ -1,38 +1,6 @@
module ActiveModel::Serializer::Utils
module_function

# Translates a comma separated list of dot separated paths (JSONAPI format) into a Hash.
# Example: `'posts.author, posts.comments.upvotes, posts.comments.author'` would become `{ posts: { author: {}, comments: { author: {}, upvotes: {} } } }`.
#
# @param [String] included
# @return [Hash] a Hash representing the same tree structure
def include_string_to_hash(included)
included.delete(' ').split(',').inject({}) do |hash, path|
hash.deep_merge!(path.split('.').reverse_each.inject({}) { |a, e| { e.to_sym => a } })
end
end

# Translates the arguments passed to the include option into a Hash. The format can be either
# a String (see #include_string_to_hash), an Array of Symbols and Hashes, or a mix of both.
# Example: `posts: [:author, comments: [:author, :upvotes]]` would become `{ posts: { author: {}, comments: { author: {}, upvotes: {} } } }`.
#
# @param [Symbol, Hash, Array, String] included
# @return [Hash] a Hash representing the same tree structure
def include_args_to_hash(included)
case included
when Symbol
{ included => {} }
when Hash
included.each_with_object({}) { |(key, value), hash| hash[key] = include_args_to_hash(value) }
when Array
included.inject({}) { |a, e| a.merge!(include_args_to_hash(e)) }
when String
include_string_to_hash(included)
else
{}
end
end

def nested_lookup_paths(klass)
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done 👍

paths = klass.to_s.split('::').reverse.reduce([]) { |a, e| a + [[e] + Array(a.last)] }.reverse
Copy link
Member

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?

  1. TweetSerializer::AuthorSerializer.to_s.split('::').reverse #=> ['AuthorSerializer', 'TweetSerializer']
  2. _.reduce([]) {|result, element| result + [[element] + Array(result.last)] }.reverse
    1. paths = []
      1. paths = [] + [['AuthorSerializer'] + Array([]) ] #=> [['AuthorSerializer']]
      2. paths = [['AuthorSerializer']] + [ ['TweetSerializer'] + Array(['AuthorSerializer']) ] #=> [ ["AuthorSerializer"], ["TweetSerializer", "AuthorSerializer"] ]
    2. paths.reverse #=> ["TweetSerializer", "AuthorSerializer"], ["AuthorSerializer"]]
  3. ["TweetSerializer", "AuthorSerializer"], ["AuthorSerializer"]].map! {|path| path.join('::') } #=> ["TweetSerializer::AuthorSerializer", "AuthorSerializer"]
  4. [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?

Copy link
Contributor Author

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.

Copy link
Member

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]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yessir.

Copy link
Member

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 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe so.

Copy link
Member

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly.

Copy link
Member

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?

Copy link
Contributor Author

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.

paths.map! { |path| "::#{path.join('::')}" }