jsonapi-renderer 0.1.1.beta1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +16 -8
- data/lib/jsonapi/renderer/document.rb +59 -0
- data/lib/jsonapi/renderer/resources_processor.rb +87 -0
- data/lib/jsonapi/renderer.rb +14 -107
- metadata +52 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 07afefd2fe41039cb8c922578ba39e3aba41f5ec
|
|
4
|
+
data.tar.gz: e269e8a82ad892be4e7f9081e30487301c20d30d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2b79ebe7aa3d18209658d89042d8dfcc06d5a2a837688b57c9d3495b173aa50f65e6f35a0db0d4bb7c40b1fc0915db5b7733f016a4643e3b684b0878db9aac40
|
|
7
|
+
data.tar.gz: e320d995609cb5a3064533b08b01cf86133a513932ce22bfdc5eeede5c64d2b2c0899fcab2907d10251927910bb4ffd780f641377e722056aff80bb7a616253a
|
data/README.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# jsonapi-renderer
|
|
2
2
|
Ruby gem for rendering [JSON API](http://jsonapi.org) documents.
|
|
3
3
|
|
|
4
|
+
## Status
|
|
5
|
+
|
|
6
|
+
[](https://badge.fury.io/rb/jsonapi-renderer)
|
|
7
|
+
[](http://travis-ci.org/jsonapi-rb/renderer?branch=master)
|
|
8
|
+
[](https://codecov.io/gh/jsonapi-rb/renderer)
|
|
9
|
+
[](https://gitter.im/jsonapi-rb/Lobby)
|
|
10
|
+
|
|
4
11
|
## Installation
|
|
5
12
|
```ruby
|
|
6
13
|
# In Gemfile
|
|
@@ -35,25 +42,26 @@ class ResourceInterface
|
|
|
35
42
|
# @return [String]
|
|
36
43
|
def jsonapi_id; end
|
|
37
44
|
|
|
38
|
-
# Returns a hash containing, for each included relationship, the
|
|
39
|
-
# to be included from that one.
|
|
45
|
+
# Returns a hash containing, for each included relationship, an array of the
|
|
46
|
+
# resources to be included from that one.
|
|
40
47
|
# @param [Array<Symbol>] included_relationships The keys of the relationships
|
|
41
48
|
# to be included.
|
|
42
|
-
# @return [Hash{Symbol =>
|
|
49
|
+
# @return [Hash{Symbol => Array<#ResourceInterface>}]
|
|
43
50
|
def jsonapi_related(included_relationships); end
|
|
44
51
|
|
|
45
52
|
# Returns a JSON API-compliant representation of the resource as a hash.
|
|
46
53
|
# @param [Hash] options
|
|
47
54
|
# @option [Array<Symbol>, Nil] fields The requested fields, or nil.
|
|
48
|
-
# @option [Array<Symbol
|
|
49
|
-
#
|
|
55
|
+
# @option [Array<Symbol>] include The requested relationships to
|
|
56
|
+
# include (defaults to []).
|
|
50
57
|
# @return [Hash]
|
|
51
58
|
def as_jsonapi(options = {}); end
|
|
59
|
+
end
|
|
52
60
|
```
|
|
53
61
|
|
|
54
62
|
#### Rendering a single resource
|
|
55
63
|
```ruby
|
|
56
|
-
JSONAPI.render(resource,
|
|
64
|
+
JSONAPI.render(data: resource,
|
|
57
65
|
include: include_string,
|
|
58
66
|
fields: fields_hash,
|
|
59
67
|
meta: meta_hash,
|
|
@@ -64,7 +72,7 @@ This returns a JSON API compliant hash representing the described document.
|
|
|
64
72
|
|
|
65
73
|
#### Rendering a collection of resources
|
|
66
74
|
```ruby
|
|
67
|
-
JSONAPI.render(resources,
|
|
75
|
+
JSONAPI.render(data: resources,
|
|
68
76
|
include: include_string,
|
|
69
77
|
fields: fields_hash,
|
|
70
78
|
meta: meta_hash,
|
|
@@ -76,7 +84,7 @@ This returns a JSON API compliant hash representing the described document.
|
|
|
76
84
|
### Rendering errors
|
|
77
85
|
|
|
78
86
|
```ruby
|
|
79
|
-
JSONAPI.render_errors(errors,
|
|
87
|
+
JSONAPI.render_errors(errors: errors,
|
|
80
88
|
meta: meta_hash,
|
|
81
89
|
links: links_hash)
|
|
82
90
|
```
|
|
@@ -0,0 +1,59 @@
|
|
|
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 = _symbolize_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
|
+
|
|
52
|
+
def _symbolize_fields(fields)
|
|
53
|
+
fields.each_with_object({}) do |(k, v), h|
|
|
54
|
+
h[k.to_sym] = v.map(&:to_sym)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
|
|
3
|
+
module JSONAPI
|
|
4
|
+
module Renderer
|
|
5
|
+
class ResourcesProcessor
|
|
6
|
+
def initialize(resources, include, fields)
|
|
7
|
+
@resources = resources
|
|
8
|
+
@include = include
|
|
9
|
+
@fields = fields
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def process
|
|
13
|
+
traverse_resources
|
|
14
|
+
process_resources
|
|
15
|
+
|
|
16
|
+
[@primary, @included]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def traverse_resources
|
|
22
|
+
@traversed = Set.new # [type, id, prefix]
|
|
23
|
+
@include_rels = {} # [type, id => Set]
|
|
24
|
+
@queue = []
|
|
25
|
+
@primary = []
|
|
26
|
+
@included = []
|
|
27
|
+
|
|
28
|
+
initialize_queue
|
|
29
|
+
traverse_queue
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def initialize_queue
|
|
33
|
+
@resources.each do |res|
|
|
34
|
+
@traversed.add([res.jsonapi_type, res.jsonapi_id, ''])
|
|
35
|
+
traverse_resource(res, @include.keys, true)
|
|
36
|
+
enqueue_related_resources(res, '', @include)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def traverse_queue
|
|
41
|
+
until @queue.empty?
|
|
42
|
+
res, prefix, include_dir = @queue.shift
|
|
43
|
+
traverse_resource(res, include_dir.keys, false)
|
|
44
|
+
enqueue_related_resources(res, prefix, include_dir)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def traverse_resource(res, include_keys, primary)
|
|
49
|
+
ri = [res.jsonapi_type, res.jsonapi_id]
|
|
50
|
+
if @include_rels.include?(ri)
|
|
51
|
+
@include_rels[ri].merge!(include_keys)
|
|
52
|
+
else
|
|
53
|
+
@include_rels[ri] = Set.new(include_keys)
|
|
54
|
+
(primary ? @primary : @included) << res
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def enqueue_related_resources(res, prefix, include_dir)
|
|
59
|
+
res.jsonapi_related(include_dir.keys).each do |key, data|
|
|
60
|
+
data.each do |child_res|
|
|
61
|
+
next if child_res.nil?
|
|
62
|
+
child_prefix = "#{prefix}.#{key}"
|
|
63
|
+
enqueue_resource(child_res, child_prefix, include_dir[key])
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def enqueue_resource(res, prefix, include_dir)
|
|
69
|
+
return unless @traversed.add?([res.jsonapi_type,
|
|
70
|
+
res.jsonapi_id,
|
|
71
|
+
prefix])
|
|
72
|
+
@queue << [res, prefix, include_dir]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def process_resources
|
|
76
|
+
[@primary, @included].each do |resources|
|
|
77
|
+
resources.map! do |res|
|
|
78
|
+
ri = [res.jsonapi_type, res.jsonapi_id]
|
|
79
|
+
include_dir = @include_rels[ri]
|
|
80
|
+
fields = @fields[res.jsonapi_type.to_sym]
|
|
81
|
+
res.as_jsonapi(include: include_dir, fields: fields)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
data/lib/jsonapi/renderer.rb
CHANGED
|
@@ -1,118 +1,25 @@
|
|
|
1
|
-
require 'jsonapi/
|
|
1
|
+
require 'jsonapi/renderer/document'
|
|
2
2
|
|
|
3
3
|
module JSONAPI
|
|
4
|
-
class Renderer
|
|
5
|
-
def initialize(resources, options = {})
|
|
6
|
-
@resources = resources
|
|
7
|
-
@errors = options[:errors] || false
|
|
8
|
-
@meta = options[:meta] || nil
|
|
9
|
-
@links = options[:links] || {}
|
|
10
|
-
@fields = options[:fields] || {}
|
|
11
|
-
# NOTE(beauby): Room for some nifty defaults on those.
|
|
12
|
-
@jsonapi = options[:jsonapi_object] || nil
|
|
13
|
-
@include = JSONAPI::IncludeDirective.new(options[:include] || {})
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def as_json
|
|
17
|
-
return @json unless @json.nil?
|
|
18
|
-
|
|
19
|
-
process_resources
|
|
20
|
-
@json = {}
|
|
21
|
-
|
|
22
|
-
if @errors
|
|
23
|
-
@json[:errors] = @resources.map(&:as_jsonapi)
|
|
24
|
-
else
|
|
25
|
-
@json[:data] = @resources.respond_to?(:each) ? @primary : @primary[0]
|
|
26
|
-
@json[:included] = @included if @included.any?
|
|
27
|
-
end
|
|
28
|
-
@json[:links] = @links if @links.any?
|
|
29
|
-
@json[:meta] = @meta unless @meta.nil?
|
|
30
|
-
@json[:jsonapi] = @jsonapi unless @jsonapi.nil?
|
|
31
|
-
|
|
32
|
-
@json
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
private
|
|
36
|
-
|
|
37
|
-
def process_resources
|
|
38
|
-
@primary = []
|
|
39
|
-
@included = []
|
|
40
|
-
@hashes = {}
|
|
41
|
-
@processed = Set.new # NOTE(beauby): Set of [type, id, prefix].
|
|
42
|
-
@queue = []
|
|
43
|
-
|
|
44
|
-
Array(@resources).each do |res|
|
|
45
|
-
process_resource(res, '', @include, true)
|
|
46
|
-
@processed.add([res.jsonapi_type, res.jsonapi_id, ''])
|
|
47
|
-
end
|
|
48
|
-
until @queue.empty?
|
|
49
|
-
res, prefix, include_dir = @queue.pop
|
|
50
|
-
process_resource(res, prefix, include_dir, false)
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def merge_resources!(a, b)
|
|
55
|
-
b[:relationships].each do |name, rel|
|
|
56
|
-
a[:relationships][name][:data] ||= rel[:data] if rel.key?(:data)
|
|
57
|
-
(a[:relationships][name][:links] ||= {})
|
|
58
|
-
.merge!(rel[:links]) if rel.key?(:links)
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def process_resource(res, prefix, include_dir, is_primary)
|
|
63
|
-
ri = [res.jsonapi_type, res.jsonapi_id]
|
|
64
|
-
hash = res.as_jsonapi(fields: @fields[res.jsonapi_type.to_sym],
|
|
65
|
-
include: include_dir.keys)
|
|
66
|
-
if @hashes.key?(ri)
|
|
67
|
-
merge_resources!(@hashes[ri], hash)
|
|
68
|
-
else
|
|
69
|
-
(is_primary ? @primary : @included) << (@hashes[ri] = hash)
|
|
70
|
-
end
|
|
71
|
-
process_relationships(res, prefix, include_dir)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def process_relationships(res, prefix, include_dir)
|
|
75
|
-
res.jsonapi_related(include_dir.keys).each do |key, data|
|
|
76
|
-
Array(data).each do |child_res|
|
|
77
|
-
child_prefix = "#{prefix}.#{key}"
|
|
78
|
-
next unless @processed.add?([child_res.jsonapi_type,
|
|
79
|
-
child_res.jsonapi_id,
|
|
80
|
-
child_prefix])
|
|
81
|
-
@queue << [child_res, child_prefix, include_dir[key]]
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
4
|
module_function
|
|
88
5
|
|
|
89
|
-
# Render a
|
|
90
|
-
#
|
|
91
|
-
# @param [(#jsonapi_id, #jsonapi_type, #jsonapi_related, #as_jsonapi),
|
|
92
|
-
# Array<(#jsonapi_id, #jsonapi_type, #jsonapi_related, #as_jsonapi)>,
|
|
93
|
-
# nil] resources The primary resource(s) to be rendered.
|
|
94
|
-
# @param [Hash] options All optional.
|
|
95
|
-
# @option [String, Hash{Symbol => Hash}] include Relationships to be
|
|
96
|
-
# included.
|
|
97
|
-
# @option [Hash{Symbol, Array<Symbol>}] fields List of requested fields
|
|
98
|
-
# for some or all of the resource types.
|
|
99
|
-
# @option [Hash] meta Non-standard top-level meta information to be
|
|
100
|
-
# included.
|
|
101
|
-
# @option [Hash] links Top-level links to be included.
|
|
102
|
-
# @option [Hash] jsonapi_object JSON API object.
|
|
103
|
-
def render(resources, options = {})
|
|
104
|
-
Renderer.new(resources, options).as_json
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# Render an error JSON API document.
|
|
6
|
+
# Render a JSON API document.
|
|
108
7
|
#
|
|
109
|
-
# @param [
|
|
110
|
-
#
|
|
8
|
+
# @param [Hash] params
|
|
9
|
+
# @option [(#jsonapi_id, #jsonapi_type, #jsonapi_related, #as_jsonapi),
|
|
10
|
+
# Array<(#jsonapi_id, #jsonapi_type, #jsonapi_related,
|
|
11
|
+
# #as_jsonapi)>,
|
|
12
|
+
# nil] data Primary resource(s) to be rendered.
|
|
13
|
+
# @option [Array<#jsonapi_id>] errors Errors to be rendered.
|
|
14
|
+
# @option include Relationships to be included. See
|
|
15
|
+
# JSONAPI::IncludeDirective.
|
|
16
|
+
# @option [Hash{Symbol, Array<Symbol>}, Hash{String, Array<String>}] fields
|
|
17
|
+
# List of requested fields for some or all of the resource types.
|
|
111
18
|
# @option [Hash] meta Non-standard top-level meta information to be
|
|
112
19
|
# included.
|
|
113
20
|
# @option [Hash] links Top-level links to be included.
|
|
114
21
|
# @option [Hash] jsonapi_object JSON API object.
|
|
115
|
-
def
|
|
116
|
-
Renderer.new(
|
|
22
|
+
def render(params)
|
|
23
|
+
Renderer::Document.new(params).to_hash
|
|
117
24
|
end
|
|
118
25
|
end
|
metadata
CHANGED
|
@@ -1,16 +1,58 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jsonapi-renderer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.1
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lucas Hosseini
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2016-
|
|
12
|
-
dependencies:
|
|
13
|
-
|
|
11
|
+
date: 2016-12-02 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rake
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '11.3'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '11.3'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rspec
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '3.5'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '3.5'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: codecov
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0.1'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0.1'
|
|
55
|
+
description: Efficiently render JSON API documents.
|
|
14
56
|
email: lucas.hosseini@gmail.com
|
|
15
57
|
executables: []
|
|
16
58
|
extensions: []
|
|
@@ -20,7 +62,9 @@ files:
|
|
|
20
62
|
- lib/jsonapi/include_directive.rb
|
|
21
63
|
- lib/jsonapi/include_directive/parser.rb
|
|
22
64
|
- lib/jsonapi/renderer.rb
|
|
23
|
-
|
|
65
|
+
- lib/jsonapi/renderer/document.rb
|
|
66
|
+
- lib/jsonapi/renderer/resources_processor.rb
|
|
67
|
+
homepage: https://github.com/jsonapi-rb/jsonapi-renderer
|
|
24
68
|
licenses:
|
|
25
69
|
- MIT
|
|
26
70
|
metadata: {}
|
|
@@ -35,12 +79,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
35
79
|
version: '0'
|
|
36
80
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
37
81
|
requirements:
|
|
38
|
-
- - "
|
|
82
|
+
- - ">="
|
|
39
83
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
84
|
+
version: '0'
|
|
41
85
|
requirements: []
|
|
42
86
|
rubyforge_project:
|
|
43
|
-
rubygems_version: 2.
|
|
87
|
+
rubygems_version: 2.6.8
|
|
44
88
|
signing_key:
|
|
45
89
|
specification_version: 4
|
|
46
90
|
summary: Render JSONAPI documents.
|