Skip to content

Commit 74f27cc

Browse files
jshowshishirmk
authored andcommitted
Links within data (#161)
1 parent b090391 commit 74f27cc

6 files changed

Lines changed: 143 additions & 7 deletions

File tree

lib/fast_jsonapi/object_serializer.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def hash_for_one_record
3838

3939
return serializable_hash unless @resource
4040

41-
serializable_hash[:data] = self.class.record_hash(@resource, @params)
41+
serializable_hash[:data] = self.class.record_hash(@resource, @params, self)
4242
serializable_hash[:included] = self.class.get_included_records(@resource, @includes, @known_included_objects, @params) if @includes.present?
4343
serializable_hash
4444
end
@@ -49,7 +49,7 @@ def hash_for_collection
4949
data = []
5050
included = []
5151
@resource.each do |record|
52-
data << self.class.record_hash(record, @params)
52+
data << self.class.record_hash(record, @params, self)
5353
included.concat self.class.get_included_records(record, @includes, @known_included_objects, @params) if @includes.present?
5454
end
5555

@@ -72,6 +72,7 @@ def process_options(options)
7272
@known_included_objects = {}
7373
@meta = options[:meta]
7474
@links = options[:links]
75+
@data_links = {}
7576
@params = options[:params] || {}
7677
raise ArgumentError.new("`params` option passed to serializer must be a hash") unless @params.is_a?(Hash)
7778

@@ -228,6 +229,11 @@ def fetch_polymorphic_option(options)
228229
{}
229230
end
230231

232+
def link(name, value = nil, &block)
233+
self.data_links = {} unless data_links
234+
self.data_links[name] = block || value
235+
end
236+
231237
def validate_includes!(includes)
232238
return if includes.blank?
233239

lib/fast_jsonapi/serialization_core.rb

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ class << self
2020
:record_id,
2121
:cache_length,
2222
:race_condition_ttl,
23-
:cached
23+
:cached,
24+
:data_links
2425
end
2526
end
2627

@@ -61,6 +62,12 @@ def ids_hash_from_record_and_relationship(record, relationship, params = {})
6162
id_hash_from_record associated_object, polymorphic
6263
end
6364

65+
def links_hash(record, serializer_instance)
66+
@data_links.each_with_object({}) do |(key, method), link_hash|
67+
link_hash[key] = method.is_a?(Proc) ? serializer_instance.instance_exec(record, &method) : record.public_send(method)
68+
end
69+
end
70+
6471
def attributes_hash(record, params = {})
6572
attributes_to_serialize.each_with_object({}) do |(key, method), attr_hash|
6673
attr_hash[key] = if method.is_a?(Proc)
@@ -83,13 +90,17 @@ def relationships_hash(record, relationships = nil, params = {})
8390
end
8491
end
8592

86-
def record_hash(record, params = {})
93+
def record_hash(record, params = {}, serializer_instance)
8794
if cached
8895
record_hash = Rails.cache.fetch(record.cache_key, expires_in: cache_length, race_condition_ttl: race_condition_ttl) do
8996
temp_hash = id_hash(id_from_record(record), record_type, true)
9097
temp_hash[:attributes] = attributes_hash(record, params) if attributes_to_serialize.present?
9198
temp_hash[:relationships] = {}
9299
temp_hash[:relationships] = relationships_hash(record, cachable_relationships_to_serialize, params) if cachable_relationships_to_serialize.present?
100+
if @data_links.present?
101+
temp_links_hash = links_hash(record, serializer_instance)
102+
temp_hash[:links] = temp_links_hash if temp_links_hash
103+
end
93104
temp_hash
94105
end
95106
record_hash[:relationships] = record_hash[:relationships].merge(relationships_hash(record, uncachable_relationships_to_serialize, params)) if uncachable_relationships_to_serialize.present?
@@ -98,6 +109,10 @@ def record_hash(record, params = {})
98109
record_hash = id_hash(id_from_record(record), record_type, true)
99110
record_hash[:attributes] = attributes_hash(record, params) if attributes_to_serialize.present?
100111
record_hash[:relationships] = relationships_hash(record, nil, params) if relationships_to_serialize.present?
112+
if @data_links.present?
113+
temp_links_hash = links_hash(record, serializer_instance)
114+
record_hash[:links] = temp_links_hash if temp_links_hash
115+
end
101116
record_hash
102117
end
103118
end
@@ -153,7 +168,7 @@ def get_included_records(record, includes_list, known_included_objects, params =
153168
next if known_included_objects.key?(code)
154169

155170
known_included_objects[code] = inc_obj
156-
included_records << serializer.record_hash(inc_obj, params)
171+
included_records << serializer.record_hash(inc_obj, params, serializer.new(record))
157172
end
158173
end
159174
end

spec/lib/object_serializer_class_methods_spec.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,51 @@
232232
end
233233
end
234234

235+
describe '#link' do
236+
subject(:serializable_hash) { MovieSerializer.new(movie).serializable_hash }
237+
238+
after do
239+
MovieSerializer.data_links = {}
240+
ActorSerializer.data_links = {}
241+
end
242+
243+
context 'with block calling instance method on serializer' do
244+
before do
245+
MovieSerializer.link(:self) do |movie_object|
246+
movie_url(movie_object)
247+
end
248+
end
249+
let(:url) { "http://movies.com/#{movie.id}" }
250+
251+
it 'returns correct hash when serializable_hash is called' do
252+
expect(serializable_hash[:data][:links][:self]).to eq url
253+
end
254+
end
255+
256+
context 'with block and param' do
257+
before do
258+
MovieSerializer.link(:public_url) do |movie_object|
259+
"http://movies.com/#{movie_object.id}"
260+
end
261+
end
262+
let(:url) { "http://movies.com/#{movie.id}" }
263+
264+
it 'returns correct hash when serializable_hash is called' do
265+
expect(serializable_hash[:data][:links][:public_url]).to eq url
266+
end
267+
end
268+
269+
context 'with method' do
270+
before do
271+
MovieSerializer.link(:object_id, :id)
272+
end
273+
274+
it 'returns correct hash when serializable_hash is called' do
275+
expect(serializable_hash[:data][:links][:object_id]).to eq movie.id
276+
end
277+
end
278+
end
279+
235280
describe '#key_transform' do
236281
subject(:hash) { movie_serializer_class.new([movie, movie], include: [:movie_type]).serializable_hash }
237282

spec/lib/object_serializer_spec.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,30 @@
3131
expect(serializable_hash[:included]).to be nil
3232
end
3333

34+
it 'returns correct nested includes when serializable_hash is called' do
35+
# 3 actors, 3 agencies
36+
include_object_total = 6
37+
38+
options = {}
39+
options[:include] = [:actors, :'actors.agency']
40+
serializable_hash = MovieSerializer.new([movie], options).serializable_hash
41+
42+
expect(serializable_hash[:included]).to be_instance_of(Array)
43+
expect(serializable_hash[:included].length).to eq include_object_total
44+
(0..include_object_total-1).each do |include|
45+
expect(serializable_hash[:included][include]).to be_instance_of(Hash)
46+
end
47+
48+
options[:include] = [:'actors.agency']
49+
serializable_hash = MovieSerializer.new([movie], options).serializable_hash
50+
51+
expect(serializable_hash[:included]).to be_instance_of(Array)
52+
expect(serializable_hash[:included].length).to eq include_object_total
53+
(0..include_object_total-1).each do |include|
54+
expect(serializable_hash[:included][include]).to be_instance_of(Hash)
55+
end
56+
end
57+
3458
it 'returns correct number of records when serialized_json is called for an array' do
3559
options = {}
3660
options[:meta] = { total: 2 }
@@ -259,4 +283,23 @@ class BlahSerializer
259283
expect(V1::BlahSerializer.record_type).to be :blah
260284
end
261285
end
286+
287+
context 'when serializing included, serialize any links' do
288+
before do
289+
ActorSerializer.link(:self) do |actor_object|
290+
actor_url(actor_object)
291+
end
292+
end
293+
subject(:serializable_hash) do
294+
options = {}
295+
options[:include] = [:actors]
296+
MovieSerializer.new(movie, options).serializable_hash
297+
end
298+
let(:actor) { movie.actors.first }
299+
let(:url) { "http://movies.com/actors/#{actor.id}" }
300+
301+
it 'returns correct hash when serializable_hash is called' do
302+
expect(serializable_hash[:included][0][:links][:self]).to eq url
303+
end
304+
end
262305
end

spec/lib/serialization_core_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
end
7171

7272
it 'returns correct hash when record_hash is called' do
73-
record_hash = MovieSerializer.send(:record_hash, movie)
73+
record_hash = MovieSerializer.send(:record_hash, movie, nil)
7474
expect(record_hash[:id]).to eq movie.id.to_s
7575
expect(record_hash[:type]).to eq MovieSerializer.record_type
7676
expect(record_hash).to have_key(:attributes) if MovieSerializer.attributes_to_serialize.present?
@@ -82,7 +82,7 @@
8282
known_included_objects = {}
8383
included_records = []
8484
[movie, movie].each do |record|
85-
included_records.concat MovieSerializer.send(:get_included_records, record, includes_list, known_included_objects)
85+
included_records.concat MovieSerializer.send(:get_included_records, record, includes_list, known_included_objects, nil)
8686
end
8787
expect(included_records.size).to eq 3
8888
end

spec/shared/contexts/movie_context.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ class Agency
114114
attr_accessor :id, :name, :actor_ids
115115
end
116116

117+
class Agency
118+
attr_accessor :id, :name, :actor_ids
119+
end
120+
117121
class Supplier
118122
attr_accessor :id, :account_id
119123

@@ -140,6 +144,10 @@ class MovieSerializer
140144
belongs_to :owner, record_type: :user
141145
belongs_to :movie_type
142146
has_one :advertising_campaign
147+
148+
def movie_url(movie)
149+
"http://movies.com/#{movie.id}"
150+
end
143151
end
144152

145153
class MovieWithoutIdStructSerializer
@@ -176,6 +184,10 @@ class ActorSerializer
176184
belongs_to :agency
177185
has_many :awards
178186
belongs_to :agency
187+
188+
def actor_url(actor)
189+
"http://movies.com/actors/#{actor.id}"
190+
end
179191
end
180192

181193
class AgencySerializer
@@ -219,6 +231,21 @@ class MovieSerializerWithAttributeBlock
219231
end
220232
end
221233

234+
class MovieSerializerWithAttributeBlock
235+
include FastJsonapi::ObjectSerializer
236+
set_type :movie
237+
attributes :name, :release_year
238+
attribute :title_with_year do |record|
239+
"#{record.name} (#{record.release_year})"
240+
end
241+
end
242+
243+
class AgencySerializer
244+
include FastJsonapi::ObjectSerializer
245+
attributes :id, :name
246+
has_many :actors
247+
end
248+
222249
class SupplierSerializer
223250
include FastJsonapi::ObjectSerializer
224251
set_type :supplier

0 commit comments

Comments
 (0)