Skip to content

Commit 87f8179

Browse files
committed
Merge pull request #700 from arenoir/sparse_fieldsets
sparse fieldsets
2 parents 3a60633 + 2ed52f9 commit 87f8179

File tree

10 files changed

+131
-5
lines changed

10 files changed

+131
-5
lines changed

lib/action_controller/serialization.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ module Serialization
66

77
include ActionController::Renderers
88

9-
ADAPTER_OPTION_KEYS = [:include, :root, :adapter]
9+
ADAPTER_OPTION_KEYS = [:include, :fields, :root, :adapter]
1010

1111
def get_serializer(resource)
1212
@_serializer ||= @_serializer_opts.delete(:serializer)

lib/active_model/serializer.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,14 @@ def json_key
132132
end
133133

134134
def attributes(options = {})
135-
self.class._attributes.dup.each_with_object({}) do |name, hash|
135+
attributes =
136+
if options[:fields]
137+
self.class._attributes & options[:fields]
138+
else
139+
self.class._attributes.dup
140+
end
141+
142+
attributes.each_with_object({}) do |name, hash|
136143
hash[name] = send(name)
137144
end
138145
end

lib/active_model/serializer/adapter/json_api.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,20 @@ def initialize(serializer, options = {})
77
serializer.root = true
88
@hash = {}
99
@top = @options.fetch(:top) { @hash }
10+
11+
if fields = options.delete(:fields)
12+
@fieldset = ActiveModel::Serializer::Fieldset.new(fields, serializer.json_key)
13+
else
14+
@fieldset = options[:fieldset]
15+
end
1016
end
1117

1218
def serializable_hash(options = {})
1319
@root = (@options[:root] || serializer.json_key.to_s.pluralize).to_sym
1420

1521
if serializer.respond_to?(:each)
1622
@hash[@root] = serializer.map do |s|
17-
self.class.new(s, @options.merge(top: @top)).serializable_hash[@root]
23+
self.class.new(s, @options.merge(top: @top, fieldset: @fieldset)).serializable_hash[@root]
1824
end
1925
else
2026
@hash[@root] = attributes_for_serializer(serializer, @options)
@@ -84,15 +90,18 @@ def add_linked(resource_name, serializers, parent = nil)
8490
end
8591
end
8692

93+
8794
def attributes_for_serializer(serializer, options)
8895
if serializer.respond_to?(:each)
8996
result = []
9097
serializer.each do |object|
98+
options[:fields] = @fieldset && @fieldset.fields_for(serializer)
9199
attributes = object.attributes(options)
92100
attributes[:id] = attributes[:id].to_s if attributes[:id]
93101
result << attributes
94102
end
95103
else
104+
options[:fields] = @fieldset && @fieldset.fields_for(serializer)
96105
result = serializer.attributes(options)
97106
result[:id] = result[:id].to_s if result[:id]
98107
end
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
module ActiveModel
2+
class Serializer
3+
class Fieldset
4+
5+
def initialize(fields, root = nil)
6+
@root = root
7+
@raw_fields = fields
8+
end
9+
10+
def fields
11+
@fields ||= parsed_fields
12+
end
13+
14+
def fields_for(serializer)
15+
key = serializer.json_key || serializer.class.root_name
16+
fields[key.to_sym]
17+
end
18+
19+
private
20+
21+
attr_reader :raw_fields, :root
22+
23+
def parsed_fields
24+
if raw_fields.is_a?(Hash)
25+
raw_fields.inject({}) { |h,(k,v)| h[k.to_sym] = v.map(&:to_sym); h}
26+
elsif raw_fields.is_a?(Array)
27+
if root.nil?
28+
raise ArgumentError, 'The root argument must be specified if the fileds argument is an array.'
29+
end
30+
hash = {}
31+
hash[root.to_sym] = raw_fields.map(&:to_sym)
32+
hash
33+
else
34+
{}
35+
end
36+
end
37+
38+
end
39+
end
40+
end

lib/active_model_serializers.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require "active_model"
22
require "active_model/serializer/version"
33
require "active_model/serializer"
4+
require "active_model/serializer/fieldset"
45

56
begin
67
require 'action_controller'

test/adapter/json_api/belongs_to_test.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ def test_includes_linked_post
4545
assert_equal expected, @adapter.serializable_hash[:linked][:posts]
4646
end
4747

48+
def test_limiting_linked_post_fields
49+
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'post', fields: {post: [:title]})
50+
expected = [{
51+
title: 'New Post',
52+
links: {
53+
comments: ["1"],
54+
author: "1"
55+
}
56+
}]
57+
assert_equal expected, @adapter.serializable_hash[:linked][:posts]
58+
end
59+
4860
def test_include_nil_author
4961
serializer = PostSerializer.new(@anonymous_post)
5062
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)

test/adapter/json_api/collection_test.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ def test_include_multiple_posts
2626
{ title: "New Post", body: "Body", id: "2", links: { comments: [], author: "1" } }
2727
], @adapter.serializable_hash[:posts])
2828
end
29+
30+
def test_limiting_fields
31+
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, fields: ['title'])
32+
assert_equal([
33+
{ title: "Hello!!", links: { comments: [], author: "1" } },
34+
{ title: "New Post", links: { comments: [], author: "1" } }
35+
], @adapter.serializable_hash[:posts])
36+
end
37+
2938
end
3039
end
3140
end

test/adapter/json_api/has_many_test.rb

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def setup
3232
def test_includes_comment_ids
3333
assert_equal(["1", "2"], @adapter.serializable_hash[:posts][:links][:comments])
3434
end
35-
35+
3636
def test_includes_linked_comments
3737
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'comments')
3838
expected = [{
@@ -53,6 +53,24 @@ def test_includes_linked_comments
5353
assert_equal expected, @adapter.serializable_hash[:linked][:comments]
5454
end
5555

56+
def test_limit_fields_of_linked_comments
57+
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer, include: 'comments', fields: {comment: [:id]})
58+
expected = [{
59+
id: "1",
60+
links: {
61+
post: "1",
62+
author: nil
63+
}
64+
}, {
65+
id: "2",
66+
links: {
67+
post: "1",
68+
author: nil
69+
}
70+
}]
71+
assert_equal expected, @adapter.serializable_hash[:linked][:comments]
72+
end
73+
5674
def test_no_include_linked_if_comments_is_empty
5775
serializer = PostSerializer.new(@post_without_comments)
5876
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)

test/serializers/attributes_test.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ def test_attributes_definition
1212
assert_equal([:name, :description],
1313
@profile_serializer.class._attributes)
1414
end
15+
16+
def test_attributes_with_fields_option
17+
assert_equal({name: 'Name 1'},
18+
@profile_serializer.attributes( { fields: [:name] } ) )
19+
end
1520
end
1621
end
1722
end
18-

test/serializers/fieldset_test.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
require 'test_helper'
2+
3+
module ActiveModel
4+
class Serializer
5+
class FieldsetTest < Minitest::Test
6+
7+
def test_fieldset_with_hash
8+
fieldset = ActiveModel::Serializer::Fieldset.new({'post' => ['id', 'title'], 'coment' => ['body']})
9+
10+
assert_equal(
11+
{:post=>[:id, :title], :coment=>[:body]},
12+
fieldset.fields
13+
)
14+
end
15+
16+
def test_fieldset_with_array_of_fields_and_root_name
17+
fieldset = ActiveModel::Serializer::Fieldset.new(['title'], 'post')
18+
19+
assert_equal(
20+
{:post => [:title]},
21+
fieldset.fields
22+
)
23+
end
24+
end
25+
end
26+
end

0 commit comments

Comments
 (0)