Skip to content

Commit 539542c

Browse files
authored
Add renderer option to render relationship only. (#24)
1 parent fd8923c commit 539542c

File tree

4 files changed

+155
-11
lines changed

4 files changed

+155
-11
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,18 @@ JSONAPI.render(data: resources,
8787

8888
This returns a JSON API compliant hash representing the described document.
8989

90+
#### Rendering a relationship
91+
```ruby
92+
JSONAPI.render(data: resource,
93+
relationship: :posts,
94+
include: include_string,
95+
fields: fields_hash,
96+
meta: meta_hash,
97+
links: links_hash)
98+
```
99+
100+
This returns a JSON API compliant hash representing the described document.
101+
90102
### Rendering errors
91103

92104
```ruby

lib/jsonapi/renderer/document.rb

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
module JSONAPI
66
class Renderer
7+
# @private
78
class Document
89
def initialize(params = {})
910
@data = params.fetch(:data, :no_data)
@@ -13,7 +14,8 @@ def initialize(params = {})
1314
@fields = _symbolize_fields(params[:fields] || {})
1415
@jsonapi = params[:jsonapi]
1516
@include = JSONAPI::IncludeDirective.new(params[:include] || {})
16-
@cache = params[:cache]
17+
@relationship = params[:relationship]
18+
@cache = params[:cache]
1719
end
1820

1921
def to_hash
@@ -23,18 +25,24 @@ def to_hash
2325

2426
private
2527

28+
# rubocop:disable Metrics/PerceivedComplexity, Metrics/MethodLength
29+
# rubocop:disable Metrics/CyclomaticComplexity
2630
def document_hash
2731
{}.tap do |hash|
28-
if @data != :no_data
32+
if @relationship
33+
hash.merge!(relationship_hash)
34+
elsif @data != :no_data
2935
hash.merge!(data_hash)
3036
elsif @errors.any?
3137
hash.merge!(errors_hash)
3238
end
33-
hash[:links] = @links if @links.any?
34-
hash[:meta] = @meta unless @meta.nil?
35-
hash[:jsonapi] = @jsonapi unless @jsonapi.nil?
39+
hash[:links] = @links if @links.any?
40+
hash[:meta] = @meta unless @meta.nil?
41+
hash[:jsonapi] = @jsonapi unless @jsonapi.nil?
3642
end
3743
end
44+
# rubocop:enable Metrics/PerceivedComplexity, Metrics/MethodLength
45+
# rubocop:enable Metrics/CyclomaticComplexity
3846

3947
def data_hash
4048
primary, included =
@@ -45,20 +53,44 @@ def data_hash
4553
end
4654
end
4755

48-
def resources_processor
49-
if @cache
50-
CachedResourcesProcessor.new(@cache)
51-
else
52-
SimpleResourcesProcessor.new
56+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
57+
def relationship_hash
58+
rel_name = @relationship.to_sym
59+
data = @data.jsonapi_related([rel_name])[rel_name]
60+
included =
61+
if @include.key?(rel_name)
62+
resources_processor.process(data, @include[rel_name], @fields)
63+
.flatten!
64+
else
65+
[]
66+
end
67+
68+
res = @data.as_jsonapi(fields: [rel_name], include: [rel_name])
69+
rel = res[:relationships][rel_name]
70+
@links = rel[:links].merge!(@links)
71+
@meta ||= rel[:meta]
72+
73+
{}.tap do |hash|
74+
hash[:data] = rel[:data]
75+
hash[:included] = included if included.any?
5376
end
5477
end
78+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
5579

5680
def errors_hash
5781
{}.tap do |hash|
5882
hash[:errors] = @errors.flat_map(&:as_jsonapi)
5983
end
6084
end
6185

86+
def resources_processor
87+
if @cache
88+
CachedResourcesProcessor.new(@cache)
89+
else
90+
SimpleResourcesProcessor.new
91+
end
92+
end
93+
6294
def _symbolize_fields(fields)
6395
fields.each_with_object({}) do |(k, v), h|
6496
h[k.to_sym] = v.map(&:to_sym)

lib/jsonapi/renderer/resources_processor.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
module JSONAPI
44
class Renderer
5-
# @api private
5+
# @private
66
class ResourcesProcessor
77
def process(resources, include, fields)
88
@resources = resources

spec/renderer_spec.rb

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,4 +336,104 @@ def as_jsonapi
336336

337337
expect(actual).to eq(expected)
338338
end
339+
340+
context 'when rendering a relationship' do
341+
it 'renders the linkage data only' do
342+
actual = subject.render(data: @users[0], relationship: :posts)
343+
expected = {
344+
data: [{ type: 'posts', id: '2' }],
345+
links: {
346+
self: 'http://api.example.com/users/1/relationships/posts',
347+
related: {
348+
href: 'http://api.example.com/users/1/posts',
349+
meta: {
350+
do_not_use: true
351+
}
352+
}
353+
},
354+
meta: {
355+
deleted_posts: 5
356+
}
357+
}
358+
359+
expect(actual).to eq(expected)
360+
end
361+
362+
it 'renders supports include parameter' do
363+
actual = subject.render(data: @users[0], relationship: :posts,
364+
include: 'posts.author')
365+
actual_included = actual.delete(:included)
366+
367+
expected = {
368+
data: [{ type: 'posts', id: '2' }],
369+
links: {
370+
self: 'http://api.example.com/users/1/relationships/posts',
371+
related: {
372+
href: 'http://api.example.com/users/1/posts',
373+
meta: {
374+
do_not_use: true
375+
}
376+
}
377+
},
378+
meta: {
379+
deleted_posts: 5
380+
}
381+
}
382+
expected_included = [
383+
{
384+
type: 'users',
385+
id: '1',
386+
attributes: {
387+
name: 'User 1',
388+
address: '123 Example st.'
389+
},
390+
relationships: {
391+
posts: {
392+
links: {
393+
self: 'http://api.example.com/users/1/relationships/posts',
394+
related: {
395+
href: 'http://api.example.com/users/1/posts',
396+
meta: {
397+
do_not_use: true
398+
}
399+
}
400+
},
401+
meta: {
402+
deleted_posts: 5
403+
}
404+
}
405+
},
406+
links: {
407+
self: 'http://api.example.com/users/1'
408+
},
409+
meta: {
410+
user_meta: 'is_meta'
411+
}
412+
},
413+
{
414+
type: 'posts',
415+
id: '2',
416+
attributes: {
417+
title: 'Post 2',
418+
date: 'today'
419+
},
420+
relationships: {
421+
author: {
422+
data: { type: 'users', id: '1' },
423+
links: {
424+
self: 'http://api.example.com/posts/2/relationships/author',
425+
related: 'http://api.example.com/posts/2/author'
426+
},
427+
meta: {
428+
author_active: true
429+
}
430+
}
431+
}
432+
}
433+
]
434+
435+
expect(actual).to eq(expected)
436+
expect(actual_included).to match_array(expected_included)
437+
end
438+
end
339439
end

0 commit comments

Comments
 (0)