Skip to content

Commit a6b08bf

Browse files
authored
Refactor renderer + change JSONAPI.render API. (#2)
1 parent a6d2098 commit a6b08bf

5 files changed

Lines changed: 98 additions & 93 deletions

File tree

lib/jsonapi/renderer.rb

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
require 'jsonapi/renderer/error_document'
2-
require 'jsonapi/renderer/success_document'
1+
require 'jsonapi/renderer/document'
32

43
module JSONAPI
54
module_function
65

7-
# Render a success JSON API document.
6+
# Render a JSON API document.
87
#
9-
# @param [(#jsonapi_id, #jsonapi_type, #jsonapi_related, #as_jsonapi),
10-
# Array<(#jsonapi_id, #jsonapi_type, #jsonapi_related, #as_jsonapi)>,
11-
# nil] resources The primary resource(s) to be rendered.
12-
# @param [Hash] options All optional.
8+
# @param [Hash] params
9+
# @option [(#jsonapi_id, #jsonapi_type, #jsonapi_related, #as_jsonapi),
10+
# Array<(#jsonapi_id, #jsonapi_type, #jsonapi_related, #as_jsonapi)>,
11+
# nil] data Primary resource(s) to be rendered.
12+
# @option [Array<#jsonapi_id>] errors Errors to be rendered.
1313
# @option [String, Hash{Symbol => Hash}] include Relationships to be
1414
# included.
1515
# @option [Hash{Symbol, Array<Symbol>}] fields List of requested fields
@@ -18,19 +18,7 @@ module JSONAPI
1818
# included.
1919
# @option [Hash] links Top-level links to be included.
2020
# @option [Hash] jsonapi_object JSON API object.
21-
def render(resources, options = {})
22-
Renderer::SuccessDocument.new(resources, options).as_json
23-
end
24-
25-
# Render an error JSON API document.
26-
#
27-
# @param [Array<#jsonapi_id>] errors Errors to be rendered.
28-
# @param [Hash] options All optional.
29-
# @option [Hash] meta Non-standard top-level meta information to be
30-
# included.
31-
# @option [Hash] links Top-level links to be included.
32-
# @option [Hash] jsonapi_object JSON API object.
33-
def render_errors(errors)
34-
Renderer::ErrorDocument.new(errors).as_json
21+
def render(params)
22+
Renderer::Document.new(params).to_hash
3523
end
3624
end

lib/jsonapi/renderer/document.rb

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
require 'jsonapi/include_directive'
2+
require 'jsonapi/renderer/resources_processor'
3+
4+
module JSONAPI
5+
module Renderer
6+
class Document
7+
def initialize(params = {})
8+
@data = params.fetch(:data, :no_data)
9+
@errors = params.fetch(:errors, [])
10+
@meta = params.fetch(:meta, nil)
11+
@links = params.fetch(:links, {})
12+
@fields = params.fetch(:fields, {})
13+
@jsonapi = params.fetch(:jsonapi, nil)
14+
@include = JSONAPI::IncludeDirective.new(params.fetch(:include, {}))
15+
end
16+
17+
def to_hash
18+
@hash ||= document_hash
19+
end
20+
alias to_h to_hash
21+
22+
private
23+
24+
def document_hash
25+
{}.tap do |hash|
26+
if @data != :no_data
27+
hash.merge!(data_hash)
28+
elsif @errors.any?
29+
hash.merge!(errors_hash)
30+
end
31+
hash[:links] = @links if @links.any?
32+
hash[:meta] = @meta unless @meta.nil?
33+
hash[:jsonapi] = @jsonapi unless @jsonapi.nil?
34+
end
35+
end
36+
37+
def data_hash
38+
primary, included =
39+
ResourcesProcessor.new(Array(@data), @include, @fields).process
40+
{}.tap do |hash|
41+
hash[:data] = @data.respond_to?(:each) ? primary : primary[0]
42+
hash[:included] = included if included.any?
43+
end
44+
end
45+
46+
def errors_hash
47+
{}.tap do |hash|
48+
hash[:errors] = @errors.map(&:as_jsonapi)
49+
end
50+
end
51+
end
52+
end
53+
end

lib/jsonapi/renderer/error_document.rb

Lines changed: 0 additions & 24 deletions
This file was deleted.

lib/jsonapi/renderer/success_document.rb renamed to lib/jsonapi/renderer/resources_processor.rb

Lines changed: 18 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,40 @@
11
require 'set'
22

3-
require 'jsonapi/include_directive'
4-
53
module JSONAPI
64
module Renderer
7-
class SuccessDocument
8-
def initialize(resources, options = {})
5+
class ResourcesProcessor
6+
def initialize(resources, include, fields)
97
@resources = resources
10-
@meta = options[:meta] || nil
11-
@links = options[:links] || {}
12-
@fields = options[:fields] || {}
13-
# NOTE(beauby): Room for some nifty defaults on those.
14-
@jsonapi = options[:jsonapi_object] || nil
15-
@include = JSONAPI::IncludeDirective.new(options[:include] || {})
16-
end
17-
18-
def as_json
19-
return @json unless @json.nil?
20-
21-
process_resources
22-
23-
@json = {}
24-
@json[:data] = @resources.respond_to?(:each) ? @primary : @primary[0]
25-
@json[:included] = @included if @included.any?
26-
@json[:links] = @links if @links.any?
27-
@json[:meta] = @meta unless @meta.nil?
28-
@json[:jsonapi] = @jsonapi unless @jsonapi.nil?
29-
30-
@json
31-
end
32-
33-
private
34-
35-
def process_resources
36-
@primary = []
37-
@included = []
38-
@hashes = {}
8+
@include = include
9+
@fields = fields
10+
@primary = []
11+
@included = []
12+
@hashes = {}
13+
@queue = []
3914
@processed = Set.new # NOTE(beauby): Set of [type, id, prefix].
40-
@queue = []
15+
end
4116

42-
Array(@resources).each do |res|
17+
def process
18+
@resources.each do |res|
4319
process_resource(res, '', @include, true)
4420
@processed.add([res.jsonapi_type, res.jsonapi_id, ''])
4521
end
4622
until @queue.empty?
4723
res, prefix, include_dir = @queue.pop
4824
process_resource(res, prefix, include_dir, false)
4925
end
26+
27+
[@primary, @included]
5028
end
5129

30+
private
31+
5232
def merge_resources!(a, b)
5333
b[:relationships].each do |name, rel|
5434
a[:relationships][name][:data] ||= rel[:data] if rel.key?(:data)
55-
(a[:relationships][name][:links] ||= {})
56-
.merge!(rel[:links]) if rel.key?(:links)
35+
if rel.key?(:links)
36+
(a[:relationships][name][:links] ||= {}).merge!(rel[:links])
37+
end
5738
end
5839
end
5940

spec/renderer_spec.rb

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def as_jsonapi(options = {})
141141
end
142142

143143
it 'renders nil' do
144-
actual = JSONAPI.render(nil)
144+
actual = JSONAPI.render(data: nil)
145145
expected = {
146146
data: nil
147147
}
@@ -150,7 +150,7 @@ def as_jsonapi(options = {})
150150
end
151151

152152
it 'renders an empty array' do
153-
actual = JSONAPI.render([])
153+
actual = JSONAPI.render(data: [])
154154
expected = {
155155
data: []
156156
}
@@ -159,7 +159,7 @@ def as_jsonapi(options = {})
159159
end
160160

161161
it 'renders a single resource' do
162-
actual = JSONAPI.render(UserResource.new(@users[0]))
162+
actual = JSONAPI.render(data: UserResource.new(@users[0]))
163163
expected = {
164164
data: {
165165
type: 'users',
@@ -197,8 +197,8 @@ def as_jsonapi(options = {})
197197
end
198198

199199
it 'renders a collection of resources' do
200-
actual = JSONAPI.render([UserResource.new(@users[0]),
201-
UserResource.new(@users[1])])
200+
actual = JSONAPI.render(data: [UserResource.new(@users[0]),
201+
UserResource.new(@users[1])])
202202
expected = {
203203
data: [
204204
{
@@ -268,7 +268,7 @@ def as_jsonapi(options = {})
268268
end
269269

270270
it 'renders included relationships' do
271-
actual = JSONAPI.render(UserResource.new(@users[0]),
271+
actual = JSONAPI.render(data: UserResource.new(@users[0]),
272272
include: 'posts')
273273
expected = {
274274
data: {
@@ -329,7 +329,7 @@ def as_jsonapi(options = {})
329329
end
330330

331331
it 'filters out fields' do
332-
actual = JSONAPI.render(UserResource.new(@users[0]),
332+
actual = JSONAPI.render(data: UserResource.new(@users[0]),
333333
fields: { users: [:name] })
334334
expected = {
335335
data: {
@@ -351,7 +351,7 @@ def as_jsonapi(options = {})
351351
end
352352

353353
it 'renders a toplevel meta' do
354-
actual = JSONAPI.render(nil,
354+
actual = JSONAPI.render(data: nil,
355355
meta: { this: 'is_meta' })
356356
expected = {
357357
data: nil,
@@ -362,7 +362,7 @@ def as_jsonapi(options = {})
362362
end
363363

364364
it 'renders toplevel links' do
365-
actual = JSONAPI.render(nil,
365+
actual = JSONAPI.render(data: nil,
366366
links: { self: 'http://api.example.com/users' })
367367
expected = {
368368
data: nil,
@@ -373,8 +373,8 @@ def as_jsonapi(options = {})
373373
end
374374

375375
it 'renders a toplevel jsonapi object' do
376-
actual = JSONAPI.render(nil,
377-
jsonapi_object: {
376+
actual = JSONAPI.render(data: nil,
377+
jsonapi: {
378378
version: '1.0',
379379
meta: 'For real'
380380
})
@@ -388,4 +388,11 @@ def as_jsonapi(options = {})
388388

389389
expect(actual).to eq(expected)
390390
end
391+
392+
it 'renders an empty hash if neither errors nor data provided' do
393+
actual = JSONAPI.render({})
394+
expected = {}
395+
396+
expect(actual).to eq(expected)
397+
end
391398
end

0 commit comments

Comments
 (0)