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

Support for Nested Relationships for the Json Adapter #1114

Closed

Conversation

NullVoxPopuli
Copy link
Contributor

This PR is based off of #1111, cause I needed the refactoring.

Discussion of idea here: #1113

Basically:

BlogSerializer < #...
  has_many :posts, include: { author: [:bio], comments: [:author]] }
end

resulting json may look like:

{
  blog: {
    posts: [
      {
        author: {
          bio: {...}
        },
        comments: [
          {  author: {...} }
        ]
      }
    ]
  }
}

See the included tests for more examples.

@NullVoxPopuli NullVoxPopuli changed the title Support for Nested Relationships Support for Nested Relationships for the Json Adapter Sep 1, 2015
@@ -47,6 +26,151 @@ def fragment_cache(cached_hash, non_cached_hash)
Json::FragmentCache.new().fragment_cache(cached_hash, non_cached_hash)
end

private
Copy link
Member

Choose a reason for hiding this comment

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

👍

end
end

def resource_object_for(serializer)
Copy link
Member

Choose a reason for hiding this comment

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

Nice abstraction! 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks!

@first_comment.author = @author
@second_comment.author = @author

serializer = ComplexNestedAuthorSerializer.new(@author)
Copy link
Member

Choose a reason for hiding this comment

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

loving the name

# have the include array normalized
nested_associations = include_array_to_hash(nested_associations)

included_associations = if nested_associations.present?
Copy link
Member

Choose a reason for hiding this comment

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

so nested_associations should be an normalized array with the nested objects you need.

Eg.
BlogSerializer with has_many :posts include: [:author]

so nested_associations will tell you that you need author but inside this if you check if it is included iinside serializer.associations with .select. But serializer.associations will have only posts.

Initially I thought this was to check if it's an already rendered relationship and avoid render it again. but it seems it is used here to loop the associations.

What I presume is the the code inside this if might never be executed unless you have something like (probably):

Eg.
BlogSerializer with has_many :posts include: [:author.posts]

then you will find the posts inside nested_associations as a member of serializer.associations.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The if body actually decides what nested association to render.

Cause, lets say a post has a ton of associations, but you only want the author.
by the time you get to rendering the posts, [:author] will be passed in as nested_associations and the select filters for :author among all of a post's associations.

(The tests also cover this scenario)

# it would normally be silly to have this in production code, cause a post's
# author in this case is always going to be your root object
has_many :posts, include: [:author, comments: [:author]]
end
Copy link
Member

Choose a reason for hiding this comment

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

Would be nice to cover scenarios using multiple association declarations as well 👍

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'll add a test for that real quick, just to be sure

@joaomdmoura
Copy link
Member

We made a nice and big review here, this is complex but solid, something we aim to have on 0.10.x.
I think it's almost there, let's check the existing comments and then get back to it.

hashes = include_array.select{|a| a.is_a?(Hash)}
non_hashes = include_array - hashes

hashes += non_hashes.map{ |association_name| { association_name => [] } }
Copy link
Member

Choose a reason for hiding this comment

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

better to mutate hashes than generate a new object, I think

Copy link
Contributor Author

Choose a reason for hiding this comment

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

how come?

Copy link
Member

Choose a reason for hiding this comment

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

this seems more like a function than a (private) property of the JSON Adapter.. What would you think about adding a

module ActiveModel::Serializer::Utils
  module_function

        def include_array_to_hash(include_array)
          # still don't trust input
          # but this also allows
          #  include: :author syntax
          include_array = Array[*include_array].compact

          result = {}

          hashes = include_array.select{|a| a.is_a?(Hash)}
          non_hashes = include_array - hashes

          hashes += non_hashes.map{ |association_name| { association_name => [] } }

          # now merge all the hashes
          hashes.each{|hash| result.merge!(hash) }

          result
        end
end

and then Utils.include_array_to_hash or mix it in

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 like that. Calling it as a class metho would be my preference.

@joaomdmoura
Copy link
Member

Just waiting to merge #1017 before going back to this

@NullVoxPopuli
Copy link
Contributor Author

woo!

@joaomdmoura
Copy link
Member

related links:
#921
#1073

NullVoxPopuli and others added 5 commits September 7, 2015 11:10
…izers into nested-relationships

Conflicts:
	lib/active_model/serializer/adapter/json.rb
…sociation declaration in the JSON adapter

initial refactor to make way for easy feature adding

refactored a bit more, and matched some method names to that of json_api

rename add_relationship methods to be less awkward

tests added

all tests tpass

remove binding.pry require

fixed app warnings

removed unused method

changed restrict_to to nested_associations and changed nested_relationships to nested_associations_to_include

removed unneeded parameter from add_resource_relationships

simplify key check

added another test, showing multiple relationship serializers

refactored include_array_to_hash to a utils module - this also simplified the test for it. yay

fixed merge conflicts
Conflicts:
	test/action_controller/serialization_test.rb
@NullVoxPopuli
Copy link
Contributor Author

Well, I accidentally committed a merge instead of a rebase. tried to checkout my last commit, and rebase that then force push to this branch... didn't work out so well. haha

@NullVoxPopuli
Copy link
Contributor Author

Closed in favor of #1124 (Cleaner git history - but same changes)

@joaomdmoura
Copy link
Member

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants