Skip to content

Commit 100f850

Browse files
authored
Merge branch 'dev' into add-links-option-to-relationship-serializer
2 parents 8eef7a0 + a5414c6 commit 100f850

7 files changed

Lines changed: 125 additions & 14 deletions

File tree

README.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,18 @@ This will create a `self` reference for the relationship, and a `related` link f
275275
end
276276
```
277277

278+
### Meta Per Resource
279+
280+
For every resource in the collection, you can include a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.
281+
```ruby
282+
meta do |movie|
283+
{
284+
years_since_release: Date.current.year - movie.year
285+
}
286+
end
287+
end
288+
```
289+
278290
### Compound Document
279291

280292
Support for top-level and nested included associations through ` options[:include] `.
@@ -381,15 +393,15 @@ class MovieSerializer
381393
include FastJsonapi::ObjectSerializer
382394

383395
attributes :name, :year
384-
attribute :release_year, if: Proc.new do |record|
396+
attribute :release_year, if: Proc.new { |record|
385397
# Release year will only be serialized if it's greater than 1990
386398
record.release_year > 1990
387-
end
399+
}
388400

389-
attribute :director, if: Proc.new do |record, params|
401+
attribute :director, if: Proc.new { |record, params|
390402
# The director will be serialized only if the :admin key of params is true
391403
params && params[:admin] == true
392-
end
404+
}
393405
end
394406

395407
# ...

lib/fast_jsonapi/object_serializer.rb

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ def inherited(subclass)
120120
subclass.data_links = data_links
121121
subclass.cached = cached
122122
subclass.set_type(subclass.reflected_record_type) if subclass.reflected_record_type
123+
subclass.meta_to_serialize = meta_to_serialize
123124
end
124125

125126
def reflected_record_type
@@ -218,6 +219,10 @@ def belongs_to(relationship_name, options = {}, &block)
218219
add_relationship(relationship)
219220
end
220221

222+
def meta(&block)
223+
self.meta_to_serialize = block
224+
end
225+
221226
def create_relationship(base_key, relationship_type, options, block)
222227
name = base_key.to_sym
223228
if relationship_type == :has_many
@@ -232,7 +237,11 @@ def create_relationship(base_key, relationship_type, options, block)
232237
Relationship.new(
233238
key: options[:key] || run_key_transform(base_key),
234239
name: name,
235-
id_method_name: options[:id_method_name] || "#{base_serialization_key}#{id_postfix}".to_sym,
240+
id_method_name: compute_id_method_name(
241+
options[:id_method_name],
242+
"#{base_serialization_key}#{id_postfix}".to_sym,
243+
block
244+
),
236245
record_type: options[:record_type] || run_key_transform(base_key_sym),
237246
object_method_name: options[:object_method_name] || name,
238247
object_block: block,
@@ -245,6 +254,14 @@ def create_relationship(base_key, relationship_type, options, block)
245254
)
246255
end
247256

257+
def compute_id_method_name(custom_id_method_name, id_method_name_from_relationship, block)
258+
if block.present?
259+
custom_id_method_name || :id
260+
else
261+
custom_id_method_name || id_method_name_from_relationship
262+
end
263+
end
264+
248265
def compute_serializer_name(serializer_key)
249266
return serializer_key unless serializer_key.is_a? Symbol
250267
namespace = self.name.gsub(/()?\w+Serializer$/, '')

lib/fast_jsonapi/relationship.rb

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,11 @@ def id_hash(id, record_type, default_return=false)
8989
end
9090

9191
def fetch_id(record, params)
92-
unless object_block.nil?
92+
if object_block.present?
9393
object = object_block.call(record, params)
94-
95-
return object.map(&:id) if object.respond_to? :map
96-
return object.try(:id)
94+
return object.map { |item| item.public_send(id_method_name) } if object.respond_to? :map
95+
return object.try(id_method_name)
9796
end
98-
9997
record.public_send(id_method_name)
10098
end
10199

lib/fast_jsonapi/serialization_core.rb

Lines changed: 10 additions & 3 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+
:meta_to_serialize
2526
end
2627
end
2728

@@ -57,6 +58,10 @@ def relationships_hash(record, relationships = nil, fieldset = nil, params = {})
5758
end
5859
end
5960

61+
def meta_hash(record, params = {})
62+
meta_to_serialize.call(record, params)
63+
end
64+
6065
def record_hash(record, fieldset, params = {})
6166
if cached
6267
record_hash = Rails.cache.fetch(record.cache_key, expires_in: cache_length, race_condition_ttl: race_condition_ttl) do
@@ -67,13 +72,15 @@ def record_hash(record, fieldset, params = {})
6772
temp_hash[:links] = links_hash(record, params) if data_links.present?
6873
temp_hash
6974
end
70-
record_hash[:relationships] = record_hash[:relationships].merge(relationships_hash(record, uncachable_relationships_to_serialize, params)) if uncachable_relationships_to_serialize.present?
75+
record_hash[:relationships] = record_hash[:relationships].merge(relationships_hash(record, uncachable_relationships_to_serialize, fieldset, params)) if uncachable_relationships_to_serialize.present?
76+
record_hash[:meta] = meta_hash(record, params) if meta_to_serialize.present?
7177
record_hash
7278
else
7379
record_hash = id_hash(id_from_record(record), record_type, true)
7480
record_hash[:attributes] = attributes_hash(record, fieldset, params) if attributes_to_serialize.present?
7581
record_hash[:relationships] = relationships_hash(record, nil, fieldset, params) if relationships_to_serialize.present?
7682
record_hash[:links] = links_hash(record, params) if data_links.present?
83+
record_hash[:meta] = meta_hash(record, params) if meta_to_serialize.present?
7784
record_hash
7885
end
7986
end
@@ -123,7 +130,7 @@ def get_included_records(record, includes_list, known_included_objects, fieldset
123130

124131
included_objects.each do |inc_obj|
125132
if remaining_items(items)
126-
serializer_records = serializer.get_included_records(inc_obj, remaining_items(items), known_included_objects, fieldsets)
133+
serializer_records = serializer.get_included_records(inc_obj, remaining_items(items), known_included_objects, fieldsets, params)
127134
included_records.concat(serializer_records) unless serializer_records.empty?
128135
end
129136

spec/lib/object_serializer_class_methods_spec.rb

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,31 @@
8787
end
8888
end
8989

90+
describe '#has_many with block and id_method_name' do
91+
before do
92+
MovieSerializer.has_many(:awards, id_method_name: :imdb_award_id) do |movie|
93+
movie.actors.map(&:awards).flatten
94+
end
95+
end
96+
97+
after do
98+
MovieSerializer.relationships_to_serialize.delete(:awards)
99+
end
100+
101+
context 'awards is not included' do
102+
subject(:hash) { MovieSerializer.new(movie).serializable_hash }
103+
104+
it 'returns correct hash where id is obtained from the method specified via `id_method_name`' do
105+
expected_award_data = movie.actors.map(&:awards).flatten.map do |actor|
106+
{ id: actor.imdb_award_id.to_s, type: actor.class.name.downcase.to_sym }
107+
end
108+
serialized_award_data = hash[:data][:relationships][:awards][:data]
109+
110+
expect(serialized_award_data).to eq(expected_award_data)
111+
end
112+
end
113+
end
114+
90115
describe '#belongs_to' do
91116
subject(:relationship) { MovieSerializer.relationships_to_serialize[:area] }
92117

@@ -249,6 +274,34 @@
249274
end
250275
end
251276

277+
describe '#meta' do
278+
subject(:serializable_hash) { MovieSerializer.new(movie).serializable_hash }
279+
280+
before do
281+
movie.release_year = 2008
282+
MovieSerializer.meta do |movie|
283+
{
284+
years_since_release: year_since_release_calculator(movie.release_year)
285+
}
286+
end
287+
end
288+
289+
after do
290+
movie.release_year = nil
291+
MovieSerializer.meta_to_serialize = nil
292+
end
293+
294+
it 'returns correct hash when serializable_hash is called' do
295+
expect(serializable_hash[:data][:meta]).to eq ({ years_since_release: year_since_release_calculator(movie.release_year) })
296+
end
297+
298+
private
299+
300+
def year_since_release_calculator(release_year)
301+
Date.current.year - release_year
302+
end
303+
end
304+
252305
describe '#link' do
253306
subject(:serializable_hash) { MovieSerializer.new(movie).serializable_hash }
254307

spec/lib/object_serializer_spec.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,23 @@ class BlahSerializer
310310
end
311311
end
312312

313+
context 'when serializing included, params should be available in any serializer' do
314+
subject(:serializable_hash) do
315+
options = {}
316+
options[:include] = [:"actors.awards"]
317+
options[:params] = { include_award_year: true }
318+
MovieSerializer.new(movie, options).serializable_hash
319+
end
320+
let(:actor) { movie.actors.first }
321+
let(:award) { actor.awards.first }
322+
let(:year) { award.year }
323+
324+
it 'passes params to deeply nested includes' do
325+
expect(year).to_not be_blank
326+
expect(serializable_hash[:included][0][:attributes][:year]).to eq year
327+
end
328+
end
329+
313330
context 'when is_collection option present' do
314331
subject { MovieSerializer.new(resource, is_collection_options).serializable_hash }
315332

spec/shared/contexts/movie_context.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ def awards
8484
a.id = i
8585
a.title = "Test Award #{i}"
8686
a.actor_id = id
87+
a.imdb_award_id = i * 10
88+
a.year = 1990 + i
8789
end
8890
end
8991
end
@@ -114,7 +116,7 @@ def state
114116
end
115117

116118
class Award
117-
attr_accessor :id, :title, :actor_id
119+
attr_accessor :id, :title, :actor_id, :year, :imdb_award_id
118120
end
119121

120122
class State
@@ -229,6 +231,11 @@ class AgencySerializer
229231
class AwardSerializer
230232
include FastJsonapi::ObjectSerializer
231233
attributes :id, :title
234+
attribute :year, if: Proc.new { |record, params|
235+
params[:include_award_year].present? ?
236+
params[:include_award_year] :
237+
false
238+
}
232239
belongs_to :actor
233240
end
234241

0 commit comments

Comments
 (0)