Skip to content

Commit a363c90

Browse files
Erolshishirmk
authored andcommitted
Allow the serializer to return sparse fieldsets
1 parent 41c1e0a commit a363c90

5 files changed

Lines changed: 82 additions & 17 deletions

File tree

lib/fast_jsonapi/fieldset.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module FastJsonapi
2+
class Fieldset
3+
def initialize(fields)
4+
@fields = fields
5+
end
6+
end
7+
end

lib/fast_jsonapi/object_serializer.rb

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
require 'fast_jsonapi/relationship'
88
require 'fast_jsonapi/link'
99
require 'fast_jsonapi/serialization_core'
10+
require 'fast_jsonapi/fieldset'
1011

1112
module FastJsonapi
1213
module ObjectSerializer
@@ -41,8 +42,8 @@ def hash_for_one_record
4142

4243
return serializable_hash unless @resource
4344

44-
serializable_hash[:data] = self.class.record_hash(@resource, @params)
45-
serializable_hash[:included] = self.class.get_included_records(@resource, @includes, @known_included_objects, @params) if @includes.present?
45+
serializable_hash[:data] = self.class.record_hash(@resource, @fieldsets[self.class.reflected_record_type.to_sym], @params)
46+
serializable_hash[:included] = self.class.get_included_records(@resource, @includes, @known_included_objects, @fieldsets, @params) if @includes.present?
4647
serializable_hash
4748
end
4849

@@ -51,9 +52,10 @@ def hash_for_collection
5152

5253
data = []
5354
included = []
55+
fieldset = @fieldsets[self.class.reflected_record_type.to_sym]
5456
@resource.each do |record|
55-
data << self.class.record_hash(record, @params)
56-
included.concat self.class.get_included_records(record, @includes, @known_included_objects, @params) if @includes.present?
57+
data << self.class.record_hash(record, fieldset, @params)
58+
included.concat self.class.get_included_records(record, @includes, @known_included_objects, @fieldsets, @params) if @includes.present?
5759
end
5860

5961
serializable_hash[:data] = data
@@ -70,6 +72,8 @@ def serialized_json
7072
private
7173

7274
def process_options(options)
75+
@fieldsets = deep_symbolize(options[:fields].presence || {})
76+
7377
return if options.blank?
7478

7579
@known_included_objects = {}
@@ -85,6 +89,18 @@ def process_options(options)
8589
end
8690
end
8791

92+
def deep_symbolize(collection)
93+
if collection.is_a? Hash
94+
Hash[collection.map do |k, v|
95+
[k.to_sym, deep_symbolize(v)]
96+
end]
97+
elsif collection.is_a? Array
98+
collection.map { |i| deep_symbolize(i) }
99+
else
100+
collection.to_sym
101+
end
102+
end
103+
88104
def is_collection?(resource, force_is_collection = nil)
89105
return force_is_collection unless force_is_collection.nil?
90106

@@ -104,6 +120,7 @@ def inherited(subclass)
104120
subclass.race_condition_ttl = race_condition_ttl
105121
subclass.data_links = data_links
106122
subclass.cached = cached
123+
subclass.fieldset = fieldset.dup if fieldset.present?
107124
end
108125

109126
def reflected_record_type

lib/fast_jsonapi/serialization_core.rb

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ class << self
2121
:cache_length,
2222
:race_condition_ttl,
2323
:cached,
24-
:data_links
24+
:data_links,
25+
:fieldset
2526
end
2627
end
2728

@@ -40,36 +41,39 @@ def links_hash(record, params = {})
4041
end
4142
end
4243

43-
def attributes_hash(record, params = {})
44-
attributes_to_serialize.each_with_object({}) do |(_k, attribute), hash|
44+
def attributes_hash(record, fieldset = nil, params = {})
45+
attributes = attributes_to_serialize
46+
attributes = attributes.slice(*fieldset) if fieldset.present?
47+
attributes.each_with_object({}) do |(_k, attribute), hash|
4548
attribute.serialize(record, params, hash)
4649
end
4750
end
4851

49-
def relationships_hash(record, relationships = nil, params = {})
52+
def relationships_hash(record, relationships = nil, fieldset = nil, params = {})
5053
relationships = relationships_to_serialize if relationships.nil?
54+
relationships = relationships.slice(*fieldset) if fieldset.present?
5155

5256
relationships.each_with_object({}) do |(_k, relationship), hash|
5357
relationship.serialize(record, params, hash)
5458
end
5559
end
5660

57-
def record_hash(record, params = {})
61+
def record_hash(record, fieldset, params = {})
5862
if cached
5963
record_hash = Rails.cache.fetch(record.cache_key, expires_in: cache_length, race_condition_ttl: race_condition_ttl) do
6064
temp_hash = id_hash(id_from_record(record), record_type, true)
61-
temp_hash[:attributes] = attributes_hash(record, params) if attributes_to_serialize.present?
65+
temp_hash[:attributes] = attributes_hash(record, fieldset, params) if attributes_to_serialize.present?
6266
temp_hash[:relationships] = {}
63-
temp_hash[:relationships] = relationships_hash(record, cachable_relationships_to_serialize, params) if cachable_relationships_to_serialize.present?
67+
temp_hash[:relationships] = relationships_hash(record, cachable_relationships_to_serialize, fieldset, params) if cachable_relationships_to_serialize.present?
6468
temp_hash[:links] = links_hash(record, params) if data_links.present?
6569
temp_hash
6670
end
6771
record_hash[:relationships] = record_hash[:relationships].merge(relationships_hash(record, uncachable_relationships_to_serialize, params)) if uncachable_relationships_to_serialize.present?
6872
record_hash
6973
else
7074
record_hash = id_hash(id_from_record(record), record_type, true)
71-
record_hash[:attributes] = attributes_hash(record, params) if attributes_to_serialize.present?
72-
record_hash[:relationships] = relationships_hash(record, nil, params) if relationships_to_serialize.present?
75+
record_hash[:attributes] = attributes_hash(record, fieldset, params) if attributes_to_serialize.present?
76+
record_hash[:relationships] = relationships_hash(record, nil, fieldset, params) if relationships_to_serialize.present?
7377
record_hash[:links] = links_hash(record, params) if data_links.present?
7478
record_hash
7579
end
@@ -100,7 +104,7 @@ def remaining_items(items)
100104
end
101105

102106
# includes handler
103-
def get_included_records(record, includes_list, known_included_objects, params = {})
107+
def get_included_records(record, includes_list, known_included_objects, fieldsets, params = {})
104108
return unless includes_list.present?
105109

106110
includes_list.sort.each_with_object([]) do |include_item, included_records|
@@ -120,15 +124,16 @@ def get_included_records(record, includes_list, known_included_objects, params =
120124

121125
included_objects.each do |inc_obj|
122126
if remaining_items(items)
123-
serializer_records = serializer.get_included_records(inc_obj, remaining_items(items), known_included_objects)
127+
serializer_records = serializer.get_included_records(inc_obj, remaining_items(items), known_included_objects, fieldsets)
124128
included_records.concat(serializer_records) unless serializer_records.empty?
125129
end
126130

127131
code = "#{record_type}_#{inc_obj.id}"
128132
next if known_included_objects.key?(code)
129133

130134
known_included_objects[code] = inc_obj
131-
included_records << serializer.record_hash(inc_obj, params)
135+
136+
included_records << serializer.record_hash(inc_obj, fieldsets[serializer.reflected_record_type], params)
132137
end
133138
end
134139
end
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
require 'spec_helper'
2+
3+
describe FastJsonapi::ObjectSerializer do
4+
include_context 'movie class'
5+
6+
let(:fields) do
7+
{
8+
movie: %i[name actors],
9+
actor: %i[name agency]
10+
}
11+
end
12+
13+
it 'only returns specified fields' do
14+
hash = MovieSerializer.new(movie, fields: fields).serializable_hash
15+
16+
expect(hash[:data][:attributes].keys.sort).to eq %i[name]
17+
end
18+
19+
it 'only returns specified relationships' do
20+
hash = MovieSerializer.new(movie, fields: fields).serializable_hash
21+
22+
expect(hash[:data][:relationships].keys.sort).to eq %i[actors]
23+
end
24+
25+
it 'only returns specified fields for included relationships' do
26+
hash = MovieSerializer.new(movie, fields: fields, include: %i[actors]).serializable_hash
27+
28+
expect(hash[:included].first[:attributes].keys.sort).to eq %i[name]
29+
end
30+
31+
it 'only returns specified relationships for included relationships' do
32+
hash = MovieSerializer.new(movie, fields: fields, include: %i[actors]).serializable_hash
33+
34+
expect(hash[:included].first[:relationships].keys.sort).to eq %i[agency]
35+
end
36+
end

spec/lib/serialization_core_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
known_included_objects = {}
6565
included_records = []
6666
[movie, movie].each do |record|
67-
included_records.concat MovieSerializer.send(:get_included_records, record, includes_list, known_included_objects, nil)
67+
included_records.concat MovieSerializer.send(:get_included_records, record, includes_list, known_included_objects, {}, nil)
6868
end
6969
expect(included_records.size).to eq 3
7070
end

0 commit comments

Comments
 (0)