From df63b5951222ef0ac47b85033bdc71c3e7724db1 Mon Sep 17 00:00:00 2001 From: Rodrigo Ra Date: Sun, 5 Jul 2015 19:47:58 -0300 Subject: [PATCH] Add key option to serializer associations --- README.md | 6 +++ lib/active_model/serializer.rb | 3 +- lib/active_model/serializer/adapter/json.rb | 10 ++--- .../serializer/adapter/json_api.rb | 14 +++--- test/adapter/json_api/json_api_test.rb | 38 ++++++++++++++++ test/adapter/json_test.rb | 16 ++++++- test/fixtures/poro.rb | 8 ++++ test/serializers/associations_test.rb | 43 ++++++++++++------- 8 files changed, 109 insertions(+), 29 deletions(-) create mode 100644 test/adapter/json_api/json_api_test.rb diff --git a/README.md b/README.md index e5f7bbe7..8b27002c 100644 --- a/README.md +++ b/README.md @@ -264,6 +264,12 @@ You may also use the `:serializer` option to specify a custom serializer class, has_many :comments, serializer: CommentPreviewSerializer ``` +And you can change the JSON key that the serializer should use for a particular association: + +```ruby + has_many :comments, key: :reviews +``` + The `url` declaration describes which named routes to use while generating URLs for your JSON. Not every adapter will require URLs. diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index 7e4eb0d6..8d5817ee 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -220,8 +220,9 @@ module ActiveModel association_options[:association_options][:virtual_value] = association_value end + association_key = association_options[:association_options][:key] || name if block_given? - block.call(name, serializer, association_options[:association_options]) + block.call(association_key, serializer, association_options[:association_options]) end end end diff --git a/lib/active_model/serializer/adapter/json.rb b/lib/active_model/serializer/adapter/json.rb index 0efd07d8..271c6959 100644 --- a/lib/active_model/serializer/adapter/json.rb +++ b/lib/active_model/serializer/adapter/json.rb @@ -15,23 +15,23 @@ module ActiveModel serializer.attributes(options) end - serializer.each_association do |name, association, opts| + serializer.each_association do |key, association, opts| if association.respond_to?(:each) array_serializer = association - @hash[name] = array_serializer.map do |item| + @hash[key] = array_serializer.map do |item| cache_check(item) do item.attributes(opts) end end else if association && association.object - @hash[name] = cache_check(association) do + @hash[key] = cache_check(association) do association.attributes(options) end elsif opts[:virtual_value] - @hash[name] = opts[:virtual_value] + @hash[key] = opts[:virtual_value] else - @hash[name] = nil + @hash[key] = nil end end end diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb index c72d822d..50dfb7eb 100644 --- a/lib/active_model/serializer/adapter/json_api.rb +++ b/lib/active_model/serializer/adapter/json_api.rb @@ -75,8 +75,8 @@ module ActiveModel end serializers.each do |serializer| - serializer.each_association do |name, association, opts| - add_included(name, association, resource_path) if association + serializer.each_association do |key, association, opts| + add_included(key, association, resource_path) if association end if include_nested_assoc? resource_path end end @@ -131,22 +131,22 @@ module ActiveModel def add_resource_relationships(attrs, serializer, options = {}) options[:add_included] = options.fetch(:add_included, true) - serializer.each_association do |name, association, opts| + serializer.each_association do |key, association, opts| attrs[:relationships] ||= {} if association.respond_to?(:each) - add_relationships(attrs, name, association) + add_relationships(attrs, key, association) else if opts[:virtual_value] - add_relationship(attrs, name, nil, opts[:virtual_value]) + add_relationship(attrs, key, nil, opts[:virtual_value]) else - add_relationship(attrs, name, association) + add_relationship(attrs, key, association) end end if options[:add_included] Array(association).each do |association| - add_included(name, association) + add_included(key, association) end end end diff --git a/test/adapter/json_api/json_api_test.rb b/test/adapter/json_api/json_api_test.rb new file mode 100644 index 00000000..5440811a --- /dev/null +++ b/test/adapter/json_api/json_api_test.rb @@ -0,0 +1,38 @@ +require 'test_helper' + +module ActiveModel + class Serializer + class Adapter + class JsonApiTest < Minitest::Test + def setup + ActionController::Base.cache_store.clear + @author = Author.new(id: 1, name: 'Steve K.') + @post = Post.new(id: 1, title: 'New Post', body: 'Body') + @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') + @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') + @post.comments = [@first_comment, @second_comment] + @first_comment.post = @post + @second_comment.post = @post + @post.author = @author + @blog = Blog.new(id: 1, name: "My Blog!!") + @post.blog = @blog + + end + + def test_custom_keys + serializer = PostWithCustomKeysSerializer.new(@post) + adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) + + assert_equal({ + reviews: { data: [ + {type: "comments", id: "1"}, + {type: "comments", id: "2"} + ]}, + writer: { data: {type: "authors", id: "1"} }, + site: { data: {type: "blogs", id: "1" } } + }, adapter.serializable_hash[:data][:relationships]) + end + end + end + end +end \ No newline at end of file diff --git a/test/adapter/json_test.rb b/test/adapter/json_test.rb index efc4c834..4aeb034f 100644 --- a/test/adapter/json_test.rb +++ b/test/adapter/json_test.rb @@ -7,7 +7,7 @@ module ActiveModel def setup ActionController::Base.cache_store.clear @author = Author.new(id: 1, name: 'Steve K.') - @post = Post.new(title: 'New Post', body: 'Body') + @post = Post.new(id: 1, title: 'New Post', body: 'Body') @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT') @post.comments = [@first_comment, @second_comment] @@ -27,6 +27,20 @@ module ActiveModel {id: 2, body: 'ZOMG ANOTHER COMMENT'} ], @adapter.serializable_hash[:post][:comments]) end + + def test_custom_keys + serializer = PostWithCustomKeysSerializer.new(@post) + adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) + + assert_equal({ + id: 1, + reviews: [{id: 1, body: "ZOMG A COMMENT"}, + {id: 2, body: "ZOMG ANOTHER COMMENT"} + ], + writer: {id: 1, name: "Steve K."}, + site: {id: 1, name: "My Blog!!"} + }, adapter.serializable_hash[:post_with_custom_keys]) + end end end end diff --git a/test/fixtures/poro.rb b/test/fixtures/poro.rb index 481cfb22..1a52dcec 100644 --- a/test/fixtures/poro.rb +++ b/test/fixtures/poro.rb @@ -228,6 +228,14 @@ PostWithTagsSerializer = Class.new(ActiveModel::Serializer) do has_many :tags end +PostWithCustomKeysSerializer = Class.new(ActiveModel::Serializer) do + attributes :id + + has_many :comments, key: :reviews + belongs_to :author, key: :writer + has_one :blog, key: :site +end + VirtualValueSerializer = Class.new(ActiveModel::Serializer) do attributes :id diff --git a/test/serializers/associations_test.rb b/test/serializers/associations_test.rb index 04681d2c..735311d6 100644 --- a/test/serializers/associations_test.rb +++ b/test/serializers/associations_test.rb @@ -51,33 +51,33 @@ module ActiveModel bio: { type: :has_one, association_options: {} } }, @author_serializer.class._associations ) - @author_serializer.each_association do |name, serializer, options| - if name == :posts + @author_serializer.each_association do |key, serializer, options| + if key == :posts assert_equal({embed: :ids}, options) assert_kind_of(ActiveModel::Serializer.config.array_serializer, serializer) - elsif name == :bio + elsif key == :bio assert_equal({}, options) assert_nil serializer - elsif name == :roles + elsif key == :roles assert_equal({embed: :ids}, options) assert_kind_of(ActiveModel::Serializer.config.array_serializer, serializer) else - flunk "Unknown association: #{name}" + flunk "Unknown association: #{key}" end end end def test_has_many_with_no_serializer - PostWithTagsSerializer.new(@post).each_association do |name, serializer, options| - assert_equal name, :tags + PostWithTagsSerializer.new(@post).each_association do |key, serializer, options| + assert_equal key, :tags assert_equal serializer, nil assert_equal [{ attributes: { name: "#hashtagged" }}].to_json, options[:virtual_value].to_json end end def test_serializer_options_are_passed_into_associations_serializers - @post_serializer.each_association do |name, association| - if name == :comments + @post_serializer.each_association do |key, association| + if key == :comments assert association.first.custom_options[:custom_options] end end @@ -89,15 +89,15 @@ module ActiveModel author: { type: :belongs_to, association_options: {} } }, @comment_serializer.class._associations ) - @comment_serializer.each_association do |name, serializer, options| - if name == :post + @comment_serializer.each_association do |key, serializer, options| + if key == :post assert_equal({}, options) assert_kind_of(PostSerializer, serializer) - elsif name == :author + elsif key == :author assert_equal({}, options) assert_nil serializer else - flunk "Unknown association: #{name}" + flunk "Unknown association: #{key}" end end end @@ -105,8 +105,8 @@ module ActiveModel def test_belongs_to_with_custom_method blog_is_present = false - @post_serializer.each_association do |name, serializer, options| - blog_is_present = true if name == :blog + @post_serializer.each_association do |key, serializer, options| + blog_is_present = true if key == :blog end assert blog_is_present @@ -132,6 +132,19 @@ module ActiveModel ) assert_equal(inherited_klass._associations, expected_associations) end + + def test_associations_custom_keys + serializer = PostWithCustomKeysSerializer.new(@post) + + expected_association_keys = [] + serializer.each_association do |key, serializer, options| + expected_association_keys << key + end + + assert expected_association_keys.include? :reviews + assert expected_association_keys.include? :writer + assert expected_association_keys.include? :site + end end end end