From ae6805eacd53b1af1f976af494b039d7d023cd24 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Wed, 30 Mar 2016 11:03:38 -0500 Subject: [PATCH 1/2] Add serializer to association block context --- lib/active_model/serializer/reflection.rb | 4 +++- test/adapter/json_api/relationships_test.rb | 11 +++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/active_model/serializer/reflection.rb b/lib/active_model/serializer/reflection.rb index 701b1b92e..5257a9058 100644 --- a/lib/active_model/serializer/reflection.rb +++ b/lib/active_model/serializer/reflection.rb @@ -59,6 +59,8 @@ def include_data(value = true) def value(serializer) @object = serializer.object @scope = serializer.scope + # Add '@serializer' to binding for use in association block as 'serializer' + @serializer = serializer if block block_value = instance_eval(&block) @@ -117,7 +119,7 @@ def build_association(subject, parent_serializer_options) protected - attr_accessor :object, :scope + attr_accessor :object, :scope, :serializer private diff --git a/test/adapter/json_api/relationships_test.rb b/test/adapter/json_api/relationships_test.rb index b612a9809..bf47a4e5e 100644 --- a/test/adapter/json_api/relationships_test.rb +++ b/test/adapter/json_api/relationships_test.rb @@ -40,6 +40,7 @@ class RelationshipAuthorSerializer < ActiveModel::Serializer has_many :roles do meta count: object.posts.count + serializer.cached_roles end has_one :blog do @@ -60,6 +61,12 @@ class RelationshipAuthorSerializer < ActiveModel::Serializer end meta liked: object.likes.any? end + + def cached_roles + [ + Role.new(id: 'from-serializer-method') + ] + end end def setup @@ -67,7 +74,7 @@ def setup @blog = Blog.new(id: 1337, name: 'extra') @bio = Bio.new(id: 1337) @like = Like.new(id: 1337) - @role = Role.new(id: 1337) + @role = Role.new(id: 'from-record') @profile = Profile.new(id: 1337) @location = Location.new(id: 1337) @reviewer = Author.new(id: 1337) @@ -144,7 +151,7 @@ def test_relationship_block_link_meta def test_relationship_meta expected = { - data: [{ id: '1337', type: 'roles' }], + data: [{ id: 'from-serializer-method', type: 'roles' }], meta: { count: 1 } } assert_relationship(:roles, expected) From fa7b3afbfd27fe40038413896d02a3754cee1e01 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Wed, 30 Mar 2016 13:35:17 -0500 Subject: [PATCH 2/2] Prefer explicitly yielding the serializer, per groyoh --- CHANGELOG.md | 1 + docs/general/serializers.md | 12 ++++++++++++ lib/active_model/serializer/reflection.rb | 19 +++++++++++++++---- test/adapter/json_api/relationships_test.rb | 2 +- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f03aa5b2d..6c0cc134d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Breaking changes: Features: +- [#1633](https://github.com/rails-api/active_model_serializers/pull/1633) Yield 'serializer' to serializer association blocks. (@bf4) - [#1616](https://github.com/rails-api/active_model_serializers/pull/1616) SerializableResource handles no serializer like controller. (@bf4) - [#1618](https://github.com/rails-api/active_model_serializers/issues/1618) Get collection root key for empty collection from explicit serializer option, when possible. (@bf4) diff --git a/docs/general/serializers.md b/docs/general/serializers.md index 23f707206..8c0fca050 100644 --- a/docs/general/serializers.md +++ b/docs/general/serializers.md @@ -48,6 +48,18 @@ has_one :blog, key: :site has_one :maker, virtual_value: { id: 1 } ``` +``ruby +has_one :blog do |serializer| + serializer.cached_blog +end + +def cached_blog + cache_store.fetch("cached_blog:#{object.updated_at}") do + Blog.find(object.blog_id) + end +end +``` + #### ::has_many e.g. diff --git a/lib/active_model/serializer/reflection.rb b/lib/active_model/serializer/reflection.rb index 5257a9058..aba75a359 100644 --- a/lib/active_model/serializer/reflection.rb +++ b/lib/active_model/serializer/reflection.rb @@ -56,14 +56,25 @@ def include_data(value = true) :nil end + # @param serializer [ActiveModel::Serializer] + # @yield [ActiveModel::Serializer] + # @return [:nil, associated resource or resource collection] + # @example + # has_one :blog do |serializer| + # serializer.cached_blog + # end + # + # def cached_blog + # cache_store.fetch("cached_blog:#{object.updated_at}") do + # Blog.find(object.blog_id) + # end + # end def value(serializer) @object = serializer.object @scope = serializer.scope - # Add '@serializer' to binding for use in association block as 'serializer' - @serializer = serializer if block - block_value = instance_eval(&block) + block_value = instance_exec(serializer, &block) if block_value == :nil serializer.read_attribute_for_serialization(name) else @@ -119,7 +130,7 @@ def build_association(subject, parent_serializer_options) protected - attr_accessor :object, :scope, :serializer + attr_accessor :object, :scope private diff --git a/test/adapter/json_api/relationships_test.rb b/test/adapter/json_api/relationships_test.rb index bf47a4e5e..5fa0de8df 100644 --- a/test/adapter/json_api/relationships_test.rb +++ b/test/adapter/json_api/relationships_test.rb @@ -38,7 +38,7 @@ class RelationshipAuthorSerializer < ActiveModel::Serializer end end - has_many :roles do + has_many :roles do |serializer| meta count: object.posts.count serializer.cached_roles end