diff --git a/lib/action_controller/serialization.rb b/lib/action_controller/serialization.rb index edb5bc0e..90897a70 100644 --- a/lib/action_controller/serialization.rb +++ b/lib/action_controller/serialization.rb @@ -6,7 +6,7 @@ module ActionController include ActionController::Renderers - ADAPTER_OPTION_KEYS = [:include, :root, :adapter] + ADAPTER_OPTION_KEYS = [:include, :fields, :root, :adapter] def get_serializer(resource) @_serializer ||= @_serializer_opts.delete(:serializer) diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index a98b092c..f105ab5a 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -132,7 +132,14 @@ module ActiveModel end def attributes(options = {}) - self.class._attributes.dup.each_with_object({}) do |name, hash| + attributes = + if options[:fields] + self.class._attributes & options[:fields] + else + self.class._attributes.dup + end + + attributes.each_with_object({}) do |name, hash| hash[name] = send(name) end end diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb index f02152c7..d018d5e0 100644 --- a/lib/active_model/serializer/adapter/json_api.rb +++ b/lib/active_model/serializer/adapter/json_api.rb @@ -7,6 +7,12 @@ module ActiveModel serializer.root = true @hash = {} @top = @options.fetch(:top) { @hash } + + if fields = options.delete(:fields) + @fieldset = ActiveModel::Serializer::Fieldset.new(fields, serializer.json_key) + else + @fieldset = options[:fieldset] + end end def serializable_hash(options = {}) @@ -14,7 +20,7 @@ module ActiveModel if serializer.respond_to?(:each) @hash[@root] = serializer.map do |s| - self.class.new(s, @options.merge(top: @top)).serializable_hash[@root] + self.class.new(s, @options.merge(top: @top, fieldset: @fieldset)).serializable_hash[@root] end else @hash[@root] = attributes_for_serializer(serializer, @options) @@ -84,15 +90,18 @@ module ActiveModel end end + def attributes_for_serializer(serializer, options) if serializer.respond_to?(:each) result = [] serializer.each do |object| + options[:fields] = @fieldset && @fieldset.fields_for(serializer) attributes = object.attributes(options) attributes[:id] = attributes[:id].to_s if attributes[:id] result << attributes end else + options[:fields] = @fieldset && @fieldset.fields_for(serializer) result = serializer.attributes(options) result[:id] = result[:id].to_s if result[:id] end diff --git a/lib/active_model/serializer/fieldset.rb b/lib/active_model/serializer/fieldset.rb new file mode 100644 index 00000000..a09fad18 --- /dev/null +++ b/lib/active_model/serializer/fieldset.rb @@ -0,0 +1,40 @@ +module ActiveModel + class Serializer + class Fieldset + + def initialize(fields, root = nil) + @root = root + @raw_fields = fields + end + + def fields + @fields ||= parsed_fields + end + + def fields_for(serializer) + key = serializer.json_key || serializer.class.root_name + fields[key.to_sym] + end + + private + + attr_reader :raw_fields, :root + + def parsed_fields + if raw_fields.is_a?(Hash) + raw_fields.inject({}) { |h,(k,v)| h[k.to_sym] = v.map(&:to_sym); h} + elsif raw_fields.is_a?(Array) + if root.nil? + raise ArgumentError, 'The root argument must be specified if the fileds argument is an array.' + end + hash = {} + hash[root.to_sym] = raw_fields.map(&:to_sym) + hash + else + {} + end + end + + end + end +end \ No newline at end of file diff --git a/lib/active_model_serializers.rb b/lib/active_model_serializers.rb index c96b90a9..5b0ed1b1 100644 --- a/lib/active_model_serializers.rb +++ b/lib/active_model_serializers.rb @@ -1,6 +1,7 @@ require "active_model" require "active_model/serializer/version" require "active_model/serializer" +require "active_model/serializer/fieldset" begin require 'action_controller' diff --git a/test/adapter/json_api/belongs_to_test.rb b/test/adapter/json_api/belongs_to_test.rb index e641429e..578896b1 100644 --- a/test/adapter/json_api/belongs_to_test.rb +++ b/test/adapter/json_api/belongs_to_test.rb @@ -45,6 +45,18 @@ module ActiveModel assert_equal expected, @adapter.serializable_hash[:linked][:posts] end + def test_limiting_linked_post_fields + @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'post', fields: {post: [:title]}) + expected = [{ + title: 'New Post', + links: { + comments: ["1"], + author: "1" + } + }] + assert_equal expected, @adapter.serializable_hash[:linked][:posts] + end + def test_include_nil_author serializer = PostSerializer.new(@anonymous_post) adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) diff --git a/test/adapter/json_api/collection_test.rb b/test/adapter/json_api/collection_test.rb index a48e66b0..205eaf21 100644 --- a/test/adapter/json_api/collection_test.rb +++ b/test/adapter/json_api/collection_test.rb @@ -26,6 +26,15 @@ module ActiveModel { title: "New Post", body: "Body", id: "2", links: { comments: [], author: "1" } } ], @adapter.serializable_hash[:posts]) end + + def test_limiting_fields + @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, fields: ['title']) + assert_equal([ + { title: "Hello!!", links: { comments: [], author: "1" } }, + { title: "New Post", links: { comments: [], author: "1" } } + ], @adapter.serializable_hash[:posts]) + end + end end end diff --git a/test/adapter/json_api/has_many_test.rb b/test/adapter/json_api/has_many_test.rb index e6a511fb..c824d847 100644 --- a/test/adapter/json_api/has_many_test.rb +++ b/test/adapter/json_api/has_many_test.rb @@ -32,7 +32,7 @@ module ActiveModel def test_includes_comment_ids assert_equal(["1", "2"], @adapter.serializable_hash[:posts][:links][:comments]) end - + def test_includes_linked_comments @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'comments') expected = [{ @@ -53,6 +53,24 @@ module ActiveModel assert_equal expected, @adapter.serializable_hash[:linked][:comments] end + def test_limit_fields_of_linked_comments + @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'comments', fields: {comment: [:id]}) + expected = [{ + id: "1", + links: { + post: "1", + author: nil + } + }, { + id: "2", + links: { + post: "1", + author: nil + } + }] + assert_equal expected, @adapter.serializable_hash[:linked][:comments] + end + def test_no_include_linked_if_comments_is_empty serializer = PostSerializer.new(@post_without_comments) adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) diff --git a/test/serializers/attributes_test.rb b/test/serializers/attributes_test.rb index f9467b18..7c4bbc8b 100644 --- a/test/serializers/attributes_test.rb +++ b/test/serializers/attributes_test.rb @@ -12,7 +12,11 @@ module ActiveModel assert_equal([:name, :description], @profile_serializer.class._attributes) end + + def test_attributes_with_fields_option + assert_equal({name: 'Name 1'}, + @profile_serializer.attributes( { fields: [:name] } ) ) + end end end end - diff --git a/test/serializers/fieldset_test.rb b/test/serializers/fieldset_test.rb new file mode 100644 index 00000000..05439177 --- /dev/null +++ b/test/serializers/fieldset_test.rb @@ -0,0 +1,26 @@ +require 'test_helper' + +module ActiveModel + class Serializer + class FieldsetTest < Minitest::Test + + def test_fieldset_with_hash + fieldset = ActiveModel::Serializer::Fieldset.new({'post' => ['id', 'title'], 'coment' => ['body']}) + + assert_equal( + {:post=>[:id, :title], :coment=>[:body]}, + fieldset.fields + ) + end + + def test_fieldset_with_array_of_fields_and_root_name + fieldset = ActiveModel::Serializer::Fieldset.new(['title'], 'post') + + assert_equal( + {:post => [:title]}, + fieldset.fields + ) + end + end + end +end \ No newline at end of file