From 39bee48ae63c78cd11b72ec9d1543d6c9c3dd22f Mon Sep 17 00:00:00 2001 From: Aaron Renoir Date: Sun, 26 Oct 2014 13:04:14 -0700 Subject: [PATCH] implement sparse fieldsets http://jsonapi.org/format/#fetching-sparse-fieldsets --- lib/active_model/serializer.rb | 9 ++- lib/active_model/serializer/adapter.rb | 1 + .../serializer/adapter/json_api.rb | 10 +++- lib/active_model/serializer/fieldset.rb | 33 +++++++++++ lib/active_model_serializers.rb | 1 + test/adapter/json_api/fieldset_test.rb | 56 +++++++++++++++++++ test/serializers/attributes_test.rb | 6 +- 7 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 lib/active_model/serializer/fieldset.rb create mode 100644 test/adapter/json_api/fieldset_test.rb diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index 7050dc38..0f1fc2eb 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -124,7 +124,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.rb b/lib/active_model/serializer/adapter.rb index f0a82573..b8a8b9df 100644 --- a/lib/active_model/serializer/adapter.rb +++ b/lib/active_model/serializer/adapter.rb @@ -17,6 +17,7 @@ module ActiveModel end def to_json(options = {}) + options[:fieldset] = ActiveModel::Serializer::Fieldset.new(serializer, options[:fields]) serializable_hash(options).to_json end end diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb index 71327b3c..8f09b82b 100644 --- a/lib/active_model/serializer/adapter/json_api.rb +++ b/lib/active_model/serializer/adapter/json_api.rb @@ -10,9 +10,12 @@ module ActiveModel def serializable_hash(options = {}) @root = (options[:root] || serializer.json_key).to_s.pluralize.to_sym @hash = {} + @fieldset = options[:fieldset] if serializer.respond_to?(:each) - @hash[@root] = serializer.map{|s| self.class.new(s).serializable_hash[@root] } + opt = @fieldset ? {fieldset: @fieldset} : {} + + @hash[@root] = serializer.map{|s| self.class.new(s).serializable_hash(opt)[@root] } else @hash[@root] = attributes_for_serializer(serializer, {}) @@ -57,6 +60,11 @@ module ActiveModel private def attributes_for_serializer(serializer, options) + + if fields = @fieldset && @fieldset.fields_for(serializer) + options[:fields] = fields + end + attributes = serializer.attributes(options) attributes[:id] = attributes[:id].to_s if attributes[:id] attributes diff --git a/lib/active_model/serializer/fieldset.rb b/lib/active_model/serializer/fieldset.rb new file mode 100644 index 00000000..e7d288f7 --- /dev/null +++ b/lib/active_model/serializer/fieldset.rb @@ -0,0 +1,33 @@ +module ActiveModel + class Serializer + class Fieldset + + attr_accessor :fields, :root + + def initialize(serializer, fields = {}) + @root = serializer.json_key + @fields = parse(fields) + end + + def fields_for(serializer) + key = serializer.json_key || serializer.class.root_name + fields[key] + end + + private + + def parse(fields) + if fields.is_a?(Hash) + fields.inject({}) { |h,(k,v)| h[k.to_s] = v.map(&:to_sym); h} + elsif fields.is_a?(Array) + hash = {} + hash[root.to_s] = 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/fieldset_test.rb b/test/adapter/json_api/fieldset_test.rb new file mode 100644 index 00000000..03619724 --- /dev/null +++ b/test/adapter/json_api/fieldset_test.rb @@ -0,0 +1,56 @@ +require 'test_helper' + +module ActiveModel + class Serializer + class Adapter + class JsonApi + class FieldsetTest < Minitest::Test + def setup + @post = Post.new(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 + + @serializer = PostSerializer.new(@post) + @adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer) + end + + def teardown + @serializer = nil + @adapter = nil + end + + def test_fieldset_with_fields_array + fieldset = ActiveModel::Serializer::Fieldset.new(@serializer, ['title']) + + assert_equal( + {:title=>"New Post", :links=>{:comments=>["1", "2"]}}, + @adapter.serializable_hash({fieldset: fieldset})[:posts] + ) + end + + def test_fieldset_with_hash + fieldset = ActiveModel::Serializer::Fieldset.new(@serializer, {post: [:body]}) + + assert_equal( + {:body=>"Body", :links=>{:comments=>["1", "2"]}}, + @adapter.serializable_hash({fieldset: fieldset})[:posts] + ) + end + + def test_fieldset_with_multiple_hashes + fieldset = ActiveModel::Serializer::Fieldset.new(@serializer, {post: [:title], comment: [:body]}) + + assert_equal( + [{:body=>"ZOMG A COMMENT" }, {:body=>"ZOMG ANOTHER COMMENT"}], + @adapter.serializable_hash({fieldset: fieldset})[:linked][:comments] + ) + end + + end + end + end + end +end \ No newline at end of file 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 -