jei 0.2.0 → 0.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7bf0d7bbd5077695a2e1219961576f894a5cd41f
4
- data.tar.gz: 34cb6f17efada6cd82e01d23e8e67a16f7f2414e
3
+ metadata.gz: 171c14fb585c7f29a0c740386776f278d423a72a
4
+ data.tar.gz: c8eeded9a3066460a0ff6946bac4d3b6e78d6adb
5
5
  SHA512:
6
- metadata.gz: 89a27cbfe0af409953efaf4da6d83f7a8f78659bf2ca58a40a22a1b148d8df1eb9d48a2b65e76f374b38598618a2fcf8310b425d001977ef0580a2a3186ed59b
7
- data.tar.gz: 5972dc16d4c2df09654730607ffbfa92165751a12fc3c6284923b4516af448c987e2b7747f99c4d39bf7266bfb09a5915b0bec0b2496685049bd5388c78764e3
6
+ metadata.gz: 1cc6431cfd6b4959b7af2fe826e5dec6a0f062b091f76aeff1bdcda88dbbc1926105c6f3bc89e62a5169021e617814e71988c6bb4057c3c65a010d299ad87793
7
+ data.tar.gz: 35d9daae8116b08430315435ae4b5f3e36e5781eb2fedca6531597c55192d38b0a9d21d1d687728903142a7914900c67accf6a9d0ef0beb9878548d8e3b37ebf
@@ -1,4 +1,5 @@
1
+ sudo: false
1
2
  language: ruby
2
3
  rvm:
3
- - 2.3.0
4
- before_install: gem install bundler -v 1.11.2
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.12.1
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## HEAD
4
4
 
5
+ ## 0.3.0 (2016-05-03)
6
+
7
+ * [CHANGE] Compound documents include full linkage. A relationship path
8
+ overrides a relationship's `:data` option.
9
+ * [ADD] Add `:errors` document build option to override primary data with an
10
+ array of error objects.
11
+ * [CHANGE] Directly build a document rather than building and evaluating a
12
+ parse tree. The tree ended up not being a very useful intermediate
13
+ representation, and removing it improves synthetic benchmarks by ~25%.
14
+
5
15
  ## 0.2.0 (2016-03-22)
6
16
 
7
17
  * [ADD] Add `:fields` document build option for sparse fieldsets.
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # Jei
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/jei.svg)](https://badge.fury.io/rb/jei)
4
+ [![Build Status](https://travis-ci.org/zaeleus/jei.svg?branch=master)](https://travis-ci.org/zaeleus/jei)
5
+
3
6
  **Jei** is a simple serializer for Ruby that formats a JSON document described
4
7
  by [JSON API][jsonapi].
5
8
 
@@ -71,6 +74,33 @@ class Artist; end
71
74
  class ArtistSerializer < Jei::Serializer; end
72
75
  ```
73
76
 
77
+ #### Resource Identifiers
78
+
79
+ A serializer wraps a resource, which has an id and type. By default, the
80
+ serializer assumes the resource responds to `id`. If not, override
81
+ `Jei::Serializer#id` and return a custom id as a string.
82
+
83
+ ```ruby
84
+ class ArtistSerializer < Jei::Serializer
85
+ def id
86
+ resource.uuid
87
+ end
88
+ end
89
+ ```
90
+
91
+ The default resource type is the resource's lowercased class name with an 's'
92
+ appended. For example, `Artist` becomes "artists" and `Person` becomes
93
+ "persons". For a custom type, override `Jei::Serializer#type` and return a
94
+ string.
95
+
96
+ ```ruby
97
+ class PersonSerializer < Jei::Serializer
98
+ def type
99
+ 'people'.freeze
100
+ end
101
+ end
102
+ ```
103
+
74
104
  #### Attributes
75
105
 
76
106
  Attributes represent model data. They are defined in a serializer by using the
@@ -181,6 +211,9 @@ Each relationship object can be modified with the following options.
181
211
  building a data object with resource identifiers. Note that doing so does not
182
212
  emit a valid JSON API document unless a links or meta object is present.
183
213
 
214
+ To ensure full linkage, this option is overridden to `true` when the resource
215
+ is on the included relationship path.
216
+
184
217
  ```ruby
185
218
  class ArtistSerializer < Jei::Serializer
186
219
  has_many :albums, data: false
@@ -287,6 +320,38 @@ string (`#to_json`).
287
320
 
288
321
  Top level objects can be added using the following options.
289
322
 
323
+ * `:errors`: (`Array<Hash<Symbol, Object>>`) An array of
324
+ [error objects][error-objects]. Setting this prevents the primary data
325
+ member from being added to the document.
326
+
327
+ ```ruby
328
+ artist = Artist.new(id: 1, name: '')
329
+
330
+ errors = [{
331
+ status: '422',
332
+ source: {
333
+ pointer: '/data/attributes/name'
334
+ },
335
+ detail: "Name can't be blank"
336
+ }]
337
+
338
+ Jei::Document.build(artist, errors: errors)
339
+ ```
340
+
341
+ ```json
342
+ {
343
+ "errors": [{
344
+ "status": "422",
345
+ "source": {
346
+ "pointer": "/data/attributes/name"
347
+ },
348
+ "detail": "Name can't be blank"
349
+ }]
350
+ }
351
+ ```
352
+
353
+ [error-objects]: http://jsonapi.org/format/1.0/#error-objects
354
+
290
355
  * `:fields`: (`Hash<String, String>`) A map of resource type-fields that define
291
356
  sparse fieldsets. Keys are resource types, and fields are a comma-separated
292
357
  list of field names. For example, `{ 'artists' => 'name,albums', 'albums' =>
data/lib/jei.rb CHANGED
@@ -3,34 +3,8 @@ require 'set'
3
3
 
4
4
  require 'jei/error'
5
5
 
6
- require 'jei/nodes/node'
7
-
8
- require 'jei/nodes/attribute_node'
9
- require 'jei/nodes/attributes_node'
10
- require 'jei/nodes/document_node'
11
- require 'jei/nodes/included_node'
12
- require 'jei/nodes/json_api_node'
13
- require 'jei/nodes/link_node'
14
- require 'jei/nodes/links_node'
15
- require 'jei/nodes/meta_node'
16
- require 'jei/nodes/relationship_node'
17
- require 'jei/nodes/relationships_node'
18
- require 'jei/nodes/resource_identifier_node'
19
- require 'jei/nodes/resource_node'
20
-
21
- require 'jei/nodes/data_node'
22
- require 'jei/nodes/collection_data_node'
23
-
24
- require 'jei/builders/attributes_node_builder'
25
- require 'jei/builders/data_node_builder'
26
- require 'jei/builders/document_builder'
27
- require 'jei/builders/included_node_builder'
28
- require 'jei/builders/links_node_builder'
29
- require 'jei/builders/relationship_node_builder'
30
- require 'jei/builders/relationships_node_builder'
31
- require 'jei/builders/resource_node_builder'
32
-
33
6
  require 'jei/document'
7
+ require 'jei/document/builder'
34
8
  require 'jei/fieldset'
35
9
  require 'jei/link'
36
10
  require 'jei/path'
@@ -1,15 +1,18 @@
1
1
  module Jei
2
2
  class Document
3
3
  # @return [String]
4
- VERSION = '1.0'
4
+ VERSION = '1.0'.freeze
5
5
 
6
- # @return [DocumentNode]
6
+ # @return [Hash<Symbol, Object>]
7
7
  attr_reader :root
8
+ alias_method :to_h, :root
8
9
 
9
10
  # Builds a document from a resource.
10
11
  #
11
12
  # @param [Object] resource
12
13
  # @param [Hash<Symbol, Object>] options
14
+ # @option options [Array<Hash<Symbol, Object>>] :errors override primary
15
+ # data with an array of error objects
13
16
  # @option options [Hash<String, String>] :fields restrict resource
14
17
  # attributes and relationships to a user-defined set of fields
15
18
  # @option options [String] :include a list of relationship paths
@@ -22,18 +25,11 @@ module Jei
22
25
  # @option options [Class] :serializer override the default serializer
23
26
  # @return [Document]
24
27
  def self.build(resource, options = {})
25
- Builders::DocumentBuilder.build(resource, options)
26
- end
27
-
28
- def initialize
29
- @root = Nodes::DocumentNode.new
28
+ Builder.build(resource, options)
30
29
  end
31
30
 
32
- # @return [Hash<Symbol, Object>]
33
- def to_h
34
- document = {}
35
- root.visit(document)
36
- document
31
+ def initialize(root = {})
32
+ @root = root
37
33
  end
38
34
 
39
35
  # @return [String]
@@ -0,0 +1,282 @@
1
+ module Jei
2
+ class Document
3
+ module Builder
4
+ EMPTY_COLLECTION = [].freeze
5
+ EMPTY_FIELDSETS = {}.freeze
6
+
7
+ module_function
8
+
9
+ # @param [Object] resource
10
+ # @param [Hash<Symbol, Object>] options
11
+ # @return [Document]
12
+ def build(resource, options = {})
13
+ document = Document.new
14
+ root = document.root
15
+
16
+ build_json_api(root) if options[:jsonapi]
17
+ build_meta(root, options[:meta]) if options[:meta]
18
+ build_links(root, options[:links]) if options[:links]
19
+
20
+ if options[:errors]
21
+ build_errors(root, options[:errors])
22
+ elsif resource.nil?
23
+ root[:data] = nil
24
+ elsif resource.is_a?(Enumerable)
25
+ build_collection(root, resource, options)
26
+ else
27
+ build_single(root, resource, options)
28
+ end
29
+
30
+ document
31
+ end
32
+
33
+ # @see http://jsonapi.org/format/1.0/#document-jsonapi-object
34
+ # @param [Hash<Symbol, Object>] context
35
+ def build_json_api(context)
36
+ context[:jsonapi] = { version: Document::VERSION }
37
+ end
38
+
39
+ # @see http://jsonapi.org/format/1.0/#document-meta
40
+ # @param [Hash<Symbol, Object>] context
41
+ # @param [Hash<Symbol, Object>] meta
42
+ def build_meta(context, meta)
43
+ context[:meta] = meta
44
+ end
45
+
46
+ # @see http://jsonapi.org/format/1.0/#document-links
47
+ # @param [Hash<Symbol, Object>] context
48
+ # @param [Array<Link>] links
49
+ def build_links(context, links)
50
+ root = {}
51
+ links.each { |link| build_link(root, link) }
52
+ context[:links] = root
53
+ end
54
+
55
+ # @see http://jsonapi.org/format/1.0/#document-links
56
+ # @param [Hash<Symbol, Object>] context
57
+ # @param [Link] link
58
+ def build_link(context, link)
59
+ context[link.name] =
60
+ if link.meta.any?
61
+ { href: link.href, meta: link.meta }
62
+ else
63
+ link.href
64
+ end
65
+ end
66
+
67
+ # @see http://jsonapi.org/format/1.0/#document-top-level
68
+ # @param [Hash<Symbol, Object>] context
69
+ # @param [Object] resource
70
+ # @param [Hash<Symbol, Object>] options
71
+ def build_single(context, resource, options)
72
+ fieldsets = options[:fields] ? Fieldset.parse(options[:fields]) : EMPTY_FIELDSETS
73
+
74
+ serializer = Serializer.factory(resource, options[:serializer])
75
+ fieldset = fieldsets[serializer.type]
76
+
77
+ if options[:include]
78
+ paths = Path.parse(options[:include])
79
+ serializers = Set.new
80
+ Path.find(paths, serializer, serializers)
81
+ end
82
+
83
+ root = {}
84
+ build_resource(root, serializer, fieldset)
85
+ context[:data] = root
86
+
87
+ if options[:include]
88
+ build_included(context, serializers, fieldsets)
89
+ end
90
+ end
91
+
92
+ # @see http://jsonapi.org/format/1.0/#document-top-level
93
+ # @param [Hash<Symbol, Object>] context
94
+ # @param [Enumerable] resources
95
+ # @param [Hash<Symbol, Object>] options
96
+ def build_collection(context, resources, options)
97
+ if resources.empty?
98
+ context[:data] = EMPTY_COLLECTION
99
+ return
100
+ end
101
+
102
+ fieldsets = options[:fields] ? Fieldset.parse(options[:fields]) : EMPTY_FIELDSETS
103
+
104
+ if options[:include]
105
+ paths = Path.parse(options[:include])
106
+ serializers = Set.new
107
+
108
+ context[:data] = resources.map do |resource|
109
+ serializer = Serializer.factory(resource, options[:serializer])
110
+
111
+ Path.find(paths, serializer, serializers)
112
+
113
+ root = {}
114
+ fieldset = fieldsets[serializer.type]
115
+
116
+ build_resource(root, serializer, fieldset)
117
+
118
+ root
119
+ end
120
+
121
+ build_included(context, serializers, fieldsets)
122
+ else
123
+ context[:data] = resources.map do |resource|
124
+ root = {}
125
+ serializer = Serializer.factory(resource, options[:serializer])
126
+ fieldset = fieldsets[serializer.type]
127
+
128
+ build_resource(root, serializer, fieldset)
129
+
130
+ root
131
+ end
132
+ end
133
+ end
134
+
135
+ # @see http://jsonapi.org/format/1.0/#document-resource-identifier-objects
136
+ # @param [Hash<Symbol, Object>] context
137
+ # @param [Serializer] serializer
138
+ def build_resource_identifier(context, serializer)
139
+ context[:id] = serializer.id
140
+ context[:type] = serializer.type
141
+ end
142
+
143
+ # @see http://jsonapi.org/format/1.0/#document-resource-objects
144
+ # @param [Hash<Symbol, Object>] context
145
+ # @param [Serializer] serializer
146
+ # @param [Array<Symbol>, nil] fieldset
147
+ def build_resource(context, serializer, fieldset)
148
+ build_resource_identifier(context, serializer)
149
+
150
+ attributes = serializer.attributes(fieldset).values
151
+
152
+ if attributes.any?
153
+ build_attributes(context, attributes, serializer)
154
+ end
155
+
156
+ relationships = serializer.relationships(fieldset).values
157
+
158
+ if relationships.any?
159
+ build_relationships(context, relationships, serializer)
160
+ end
161
+
162
+ links = serializer.links
163
+
164
+ if links
165
+ build_links(context, links)
166
+ end
167
+ end
168
+
169
+ # @see http://jsonapi.org/format/1.0/#document-resource-object-attributes
170
+ # @param [Hash<Symbol, Object>] context
171
+ # @param [Array<Attribute>] attributes
172
+ # @param [Serializer] serializer
173
+ def build_attributes(context, attributes, serializer)
174
+ root = {}
175
+
176
+ attributes.each do |attribute|
177
+ root[attribute.name] = attribute.evaluate(serializer)
178
+ end
179
+
180
+ context[:attributes] = root
181
+ end
182
+
183
+ # @see http://jsonapi.org/format/1.0/#document-resource-object-relationships
184
+ # @param [Hash<Symbol, Object>] context
185
+ # @param [Array<Relationship>] relationships
186
+ # @param [Serializer] serializer
187
+ def build_relationships(context, relationships, serializer)
188
+ root = {}
189
+
190
+ relationships.each do |relationship|
191
+ build_relationship(root, relationship, serializer)
192
+ end
193
+
194
+ context[:relationships] = root
195
+ end
196
+
197
+ # @see http://jsonapi.org/format/1.0/#document-resource-object-relationships
198
+ # @see http://jsonapi.org/format/1.0/#document-resource-object-linkage
199
+ # @param [Hash<Symbol, Object>] context
200
+ # @param [Relationship] relationship
201
+ # @param [Serializer] serializer
202
+ def build_relationship(context, relationship, serializer)
203
+ root = {}
204
+
205
+ if relationship.options[:data] ||
206
+ override_data?(serializer.options[:linkages], relationship)
207
+ case relationship
208
+ when BelongsToRelationship
209
+ build_belongs_to_relationship(root, relationship, serializer)
210
+ when HasManyRelationship
211
+ build_has_many_relationship(root, relationship, serializer)
212
+ else
213
+ raise ArgumentError, 'invalid relationship type'
214
+ end
215
+ end
216
+
217
+ if relationship.options[:links]
218
+ links = relationship.links(serializer)
219
+ build_links(root, links)
220
+ end
221
+
222
+ context[relationship.name] = root
223
+ end
224
+
225
+ def override_data?(linkages, relationship)
226
+ linkages && linkages.include?(relationship.name)
227
+ end
228
+
229
+ # @see http://jsonapi.org/format/1.0/#document-resource-object-linkage
230
+ # @param [Hash<Symbol, Object>] context
231
+ # @param [Relationship] relationship
232
+ # @param [Serializer] serializer
233
+ def build_belongs_to_relationship(context, relationship, serializer)
234
+ root = {}
235
+ resource = relationship.evaluate(serializer)
236
+ serializer = Serializer.factory(resource, relationship.options[:serializer])
237
+ build_resource_identifier(root, serializer)
238
+ context[:data] = root
239
+ end
240
+
241
+ # @see http://jsonapi.org/format/1.0/#document-resource-object-linkage
242
+ # @param [Hash<Symbol, Object>] context
243
+ # @param [Relationship] relationship
244
+ # @param [Serializer] serializer
245
+ def build_has_many_relationship(context, relationship, serializer)
246
+ resources = relationship.evaluate(serializer)
247
+
248
+ context[:data] =
249
+ if resources.empty?
250
+ EMPTY_COLLECTION
251
+ else
252
+ resources.map do |resource|
253
+ root = {}
254
+ serializer = Serializer.factory(resource, relationship.options[:serializer])
255
+ build_resource_identifier(root, serializer)
256
+ root
257
+ end
258
+ end
259
+ end
260
+
261
+ # @see http://jsonapi.org/format/1.0/#document-compound-documents
262
+ # @param [Hash<Symbol, Object>] context
263
+ # @param [Set<Serializer>] serializers
264
+ # @param [Hash<String, Array<Symbol>>] fieldsets
265
+ def build_included(context, serializers, fieldsets)
266
+ context[:included] = serializers.map do |serializer|
267
+ root = {}
268
+ fieldset = fieldsets[serializer.type]
269
+ build_resource(root, serializer, fieldset)
270
+ root
271
+ end
272
+ end
273
+
274
+ # @see http://jsonapi.org/format/1.0/#error-objects
275
+ # @param [Hash<Symbol, Object>] context
276
+ # @param [Array<Hash<Symbol, Object>>] errors
277
+ def build_errors(context, errors)
278
+ context[:errors] = errors
279
+ end
280
+ end
281
+ end
282
+ end
@@ -1,6 +1,6 @@
1
1
  module Jei
2
2
  class Fieldset
3
- NAME_SEPARATOR = ','
3
+ NAME_SEPARATOR = ','.freeze
4
4
 
5
5
  def self.parse(raw_fields)
6
6
  fields = {}
@@ -2,8 +2,8 @@ module Jei
2
2
  class Path
3
3
  class NameError < Jei::Error; end
4
4
 
5
- PATH_SEPARATOR = ','
6
- NAME_SEPARATOR = '.'
5
+ PATH_SEPARATOR = ','.freeze
6
+ NAME_SEPARATOR = '.'.freeze
7
7
 
8
8
  # @return [Array<Symbol>]
9
9
  attr_reader :names
@@ -37,8 +37,15 @@ module Jei
37
37
  return if level >= @names.length
38
38
 
39
39
  name = @names[level]
40
+
40
41
  relationship = serializer.relationships[name]
41
42
  raise NameError, "invalid relationship name `#{name}'" unless relationship
43
+
44
+ unless relationship.options[:data]
45
+ serializer.options[:linkages] = Set.new unless serializer.options[:linkages]
46
+ serializer.options[:linkages] << name
47
+ end
48
+
42
49
  resources = [*relationship.evaluate(serializer)]
43
50
 
44
51
  resources.each do |resource|
@@ -1,6 +1,6 @@
1
1
  module Jei
2
2
  class Serializer
3
- # @return [#id]
3
+ # @return [Object]
4
4
  attr_reader :resource
5
5
 
6
6
  # @return [Hash<Symbol, Attribute>]
@@ -62,8 +62,10 @@ module Jei
62
62
  end
63
63
 
64
64
  # @param [#id] resource
65
- def initialize(resource)
65
+ # @param [Hash<Symbol, Object>] options
66
+ def initialize(resource, options = nil)
66
67
  @resource = resource
68
+ @options = options
67
69
  end
68
70
 
69
71
  # @return [String]
@@ -93,6 +95,11 @@ module Jei
93
95
  nil
94
96
  end
95
97
 
98
+ # @return [Hash<Symbol, Object>]
99
+ def options
100
+ @options ||= {}
101
+ end
102
+
96
103
  # @return [Array<String>]
97
104
  def key
98
105
  [type, id]
@@ -100,13 +107,17 @@ module Jei
100
107
 
101
108
  # @return [Boolean]
102
109
  def ==(other)
103
- key == other.key
110
+ hash == other.hash
104
111
  end
105
112
  alias_method :eql?, :==
106
113
 
114
+ # Returns a digest for this serializer.
115
+ #
116
+ # The second element is shifted to preserve order.
117
+ #
107
118
  # @return [Fixnum]
108
119
  def hash
109
- key.hash
120
+ type.hash ^ (id.hash >> 1)
110
121
  end
111
122
 
112
123
  private
@@ -1,3 +1,3 @@
1
1
  module Jei
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jei
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Macias
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-03-22 00:00:00.000000000 Z
11
+ date: 2016-05-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -86,34 +86,12 @@ files:
86
86
  - jei.gemspec
87
87
  - lib/jei.rb
88
88
  - lib/jei/attribute.rb
89
- - lib/jei/builders/attributes_node_builder.rb
90
- - lib/jei/builders/data_node_builder.rb
91
- - lib/jei/builders/document_builder.rb
92
- - lib/jei/builders/included_node_builder.rb
93
- - lib/jei/builders/links_node_builder.rb
94
- - lib/jei/builders/relationship_node_builder.rb
95
- - lib/jei/builders/relationships_node_builder.rb
96
- - lib/jei/builders/resource_node_builder.rb
97
89
  - lib/jei/document.rb
90
+ - lib/jei/document/builder.rb
98
91
  - lib/jei/error.rb
99
92
  - lib/jei/field.rb
100
93
  - lib/jei/fieldset.rb
101
94
  - lib/jei/link.rb
102
- - lib/jei/nodes/attribute_node.rb
103
- - lib/jei/nodes/attributes_node.rb
104
- - lib/jei/nodes/collection_data_node.rb
105
- - lib/jei/nodes/data_node.rb
106
- - lib/jei/nodes/document_node.rb
107
- - lib/jei/nodes/included_node.rb
108
- - lib/jei/nodes/json_api_node.rb
109
- - lib/jei/nodes/link_node.rb
110
- - lib/jei/nodes/links_node.rb
111
- - lib/jei/nodes/meta_node.rb
112
- - lib/jei/nodes/node.rb
113
- - lib/jei/nodes/relationship_node.rb
114
- - lib/jei/nodes/relationships_node.rb
115
- - lib/jei/nodes/resource_identifier_node.rb
116
- - lib/jei/nodes/resource_node.rb
117
95
  - lib/jei/path.rb
118
96
  - lib/jei/relationship.rb
119
97
  - lib/jei/serializer.rb
@@ -1,19 +0,0 @@
1
- module Jei
2
- module Builders
3
- module AttributesNodeBuilder
4
- include Nodes
5
-
6
- # @param [Serializer] serializer
7
- # @return [AttributesNode]
8
- def self.build(attributes, serializer)
9
- node = AttributesNode.new
10
-
11
- attributes.each do |attribute|
12
- node.children << AttributeNode.new(serializer, attribute)
13
- end
14
-
15
- node
16
- end
17
- end
18
- end
19
- end
@@ -1,15 +0,0 @@
1
- module Jei
2
- module Builders
3
- module DataNodeBuilder
4
- include Nodes
5
-
6
- # @param [Serializer] serializer
7
- # @return [DataNode]
8
- def self.build(serializer)
9
- node = DataNode.new
10
- node.children << ResourceNodeBuilder.build(serializer)
11
- node
12
- end
13
- end
14
- end
15
- end
@@ -1,70 +0,0 @@
1
- module Jei
2
- module Builders
3
- module DocumentBuilder
4
- include Nodes
5
-
6
- # @param [Object] resource
7
- # @param [Hash<Symbol, Object>] options
8
- # @return [Document]
9
- def self.build(resource, options = {})
10
- document = Document.new
11
- root = document.root
12
-
13
- root.children << JSONAPINode.new if options[:jsonapi]
14
- root.children << MetaNode.new(options[:meta]) if options[:meta]
15
- root.children << LinksNodeBuilder.build(options[:links]) if options[:links]
16
-
17
- if resource.nil?
18
- root.children << DataNode.new
19
- return document
20
- end
21
-
22
- fieldsets = options[:fields] ? Fieldset.parse(options[:fields]) : {}
23
-
24
- if resource.is_a?(Enumerable)
25
- node = CollectionDataNode.new
26
-
27
- if options[:include]
28
- paths = Path.parse(options[:include])
29
- serializers = Set.new
30
-
31
- resource.each do |r|
32
- serializer = Serializer.factory(r, options[:serializer])
33
- Path.find(paths, serializer, serializers)
34
- fieldset = fieldsets[serializer.type]
35
- node.children << ResourceNodeBuilder.build(serializer, fieldset)
36
- end
37
-
38
- root.children << IncludedNodeBuilder.build(serializers, fieldsets)
39
- else
40
- resource.each do |r|
41
- serializer = Serializer.factory(r, options[:serializer])
42
- fieldset = fieldsets[serializer.type]
43
- node.children << ResourceNodeBuilder.build(serializer, fieldset)
44
- end
45
- end
46
-
47
- root.children << node
48
- else
49
- node = DataNode.new
50
-
51
- serializer = Serializer.factory(resource, options[:serializer])
52
- fieldset = fieldsets[serializer.type]
53
-
54
- node.children << ResourceNodeBuilder.build(serializer, fieldset)
55
-
56
- if options[:include]
57
- paths = Path.parse(options[:include])
58
- serializers = Set.new
59
- Path.find(paths, serializer, serializers)
60
- root.children << IncludedNodeBuilder.build(serializers, fieldsets)
61
- end
62
-
63
- root.children << node
64
- end
65
-
66
- document
67
- end
68
- end
69
- end
70
- end
@@ -1,21 +0,0 @@
1
- module Jei
2
- module Builders
3
- module IncludedNodeBuilder
4
- include Nodes
5
-
6
- # @param [Set<Serializer>] serializers
7
- # @param [Hash<String, String>] fieldset
8
- # @return [IncludedNode]
9
- def self.build(serializers, fieldsets = {})
10
- node = IncludedNode.new
11
-
12
- serializers.each do |serializer|
13
- fieldset = fieldsets[serializer.type]
14
- node.children << ResourceNodeBuilder.build(serializer, fieldset)
15
- end
16
-
17
- node
18
- end
19
- end
20
- end
21
- end
@@ -1,19 +0,0 @@
1
- module Jei
2
- module Builders
3
- module LinksNodeBuilder
4
- include Nodes
5
-
6
- # @param [Array<Link>] links
7
- # @return [LinksNode]
8
- def self.build(links)
9
- links_node = LinksNode.new
10
-
11
- links.each do |link|
12
- links_node.children << LinkNode.new(link)
13
- end
14
-
15
- links_node
16
- end
17
- end
18
- end
19
- end
@@ -1,68 +0,0 @@
1
- module Jei
2
- module Builders
3
- module RelationshipNodeBuilder
4
- include Nodes
5
-
6
- # @param [Relationship] relationship
7
- # @param [Serializer] serializer
8
- # @return [RelationshipNode]
9
- def self.build(relationship, serializer)
10
- node = RelationshipNode.new(relationship)
11
-
12
- if relationship.options[:data]
13
- node.children <<
14
- case relationship
15
- when BelongsToRelationship
16
- build_data_node(relationship, serializer)
17
- when HasManyRelationship
18
- build_collection_data_node(relationship, serializer)
19
- else
20
- raise ArgumentError, 'invalid relationship type'
21
- end
22
- end
23
-
24
- if relationship.options[:links]
25
- node.children << build_links_node(relationship, serializer)
26
- end
27
-
28
- node
29
- end
30
-
31
- # @param [Relationship] relationship
32
- # @param [Serializer] serializer
33
- # @return [DataNode]
34
- def self.build_data_node(relationship, serializer)
35
- node = DataNode.new
36
- resource = relationship.evaluate(serializer)
37
-
38
- serializer = Serializer.factory(resource, relationship.options[:serializer])
39
- node.children << ResourceIdentifierNode.new(serializer)
40
-
41
- node
42
- end
43
-
44
- # @param [Relationship] relationship
45
- # @param [Serializer] serializer
46
- # @return [CollectionDataNode]
47
- def self.build_collection_data_node(relationship, serializer)
48
- node = CollectionDataNode.new
49
- resources = relationship.evaluate(serializer)
50
-
51
- resources.each do |resource|
52
- serializer = Serializer.factory(resource, relationship.options[:serializer])
53
- node.children << ResourceIdentifierNode.new(serializer)
54
- end
55
-
56
- node
57
- end
58
-
59
- # @param [Relationship] relationship
60
- # @param [Serializer] serializer
61
- # @return [LinksNode]
62
- def self.build_links_node(relationship, serializer)
63
- links = relationship.links(serializer)
64
- LinksNodeBuilder.build(links)
65
- end
66
- end
67
- end
68
- end
@@ -1,19 +0,0 @@
1
- module Jei
2
- module Builders
3
- module RelationshipsNodeBuilder
4
- include Nodes
5
-
6
- # @param [Serializer] serializer
7
- # @return [RelationshipsNode]
8
- def self.build(relationships, serializer)
9
- node = RelationshipsNode.new
10
-
11
- relationships.each do |relationship|
12
- node.children << RelationshipNodeBuilder.build(relationship, serializer)
13
- end
14
-
15
- node
16
- end
17
- end
18
- end
19
- end
@@ -1,36 +0,0 @@
1
- module Jei
2
- module Builders
3
- module ResourceNodeBuilder
4
- include Nodes
5
-
6
- # @param [Serializer] serializer
7
- # @param [Array<Symbol>] fieldset
8
- # @return [ResourceNode]
9
- def self.build(serializer, fieldset = nil)
10
- node = ResourceNode.new
11
-
12
- node.children << ResourceIdentifierNode.new(serializer)
13
-
14
- attributes = serializer.attributes(fieldset).values
15
-
16
- if attributes.any?
17
- node.children << AttributesNodeBuilder.build(attributes, serializer)
18
- end
19
-
20
- relationships = serializer.relationships(fieldset).values
21
-
22
- if relationships.any?
23
- node.children << RelationshipsNodeBuilder.build(relationships, serializer)
24
- end
25
-
26
- links = serializer.links
27
-
28
- if links
29
- node.children << LinksNodeBuilder.build(links)
30
- end
31
-
32
- node
33
- end
34
- end
35
- end
36
- end
@@ -1,19 +0,0 @@
1
- module Jei
2
- module Nodes
3
- # @see http://jsonapi.org/format/1.0/#document-resource-object-attributes
4
- class AttributeNode < Node
5
- # @param [Serializer] serializer
6
- # @param [Attribute] attribute
7
- def initialize(serializer, attribute)
8
- super()
9
- @serializer = serializer
10
- @attribute = attribute
11
- end
12
-
13
- # @param [Hash<Symbol, Object>] context
14
- def visit(context)
15
- context[@attribute.name] = @attribute.evaluate(@serializer)
16
- end
17
- end
18
- end
19
- end
@@ -1,13 +0,0 @@
1
- module Jei
2
- module Nodes
3
- # @see http://jsonapi.org/format/1.0/#document-resource-object-attributes
4
- class AttributesNode < Node
5
- # @param [Hash<Symbol, Object>] context
6
- def visit(context)
7
- attributes = {}
8
- children.each { |child| child.visit(attributes) }
9
- context[:attributes] = attributes
10
- end
11
- end
12
- end
13
- end
@@ -1,15 +0,0 @@
1
- module Jei
2
- module Nodes
3
- # @see http://jsonapi.org/format/#document-top-level
4
- class CollectionDataNode < DataNode
5
- # @param [Hash<Symbol, Object>] context
6
- def visit(context)
7
- context[:data] = children.map do |child|
8
- data = {}
9
- child.visit(data)
10
- data
11
- end
12
- end
13
- end
14
- end
15
- end
@@ -1,18 +0,0 @@
1
- module Jei
2
- module Nodes
3
- # @see http://jsonapi.org/format/#document-top-level
4
- class DataNode < Node
5
- # @param [Hash<Symbol, Object>] context
6
- def visit(context)
7
- context[:data] =
8
- if children.empty?
9
- nil
10
- else
11
- data = {}
12
- children.each { |child| child.visit(data) }
13
- data
14
- end
15
- end
16
- end
17
- end
18
- end
@@ -1,11 +0,0 @@
1
- module Jei
2
- module Nodes
3
- # @see http://jsonapi.org/format/1.0/#document-top-level
4
- class DocumentNode < Node
5
- # @param [Hash<Symbol, Object>] context
6
- def visit(context)
7
- children.each { |child| child.visit(context) }
8
- end
9
- end
10
- end
11
- end
@@ -1,14 +0,0 @@
1
- module Jei
2
- module Nodes
3
- # @see http://jsonapi.org/format/1.0/#document-compound-documents
4
- class IncludedNode < Node
5
- def visit(context)
6
- context[:included] = children.map do |child|
7
- data = {}
8
- child.visit(data)
9
- data
10
- end
11
- end
12
- end
13
- end
14
- end
@@ -1,11 +0,0 @@
1
- module Jei
2
- module Nodes
3
- # @see http://jsonapi.org/format/1.0/#document-jsonapi-object
4
- class JSONAPINode < Node
5
- # @param [Hash<Symbol, Object>] context
6
- def visit(context)
7
- context[:jsonapi] = { version: Document::VERSION }
8
- end
9
- end
10
- end
11
- end
@@ -1,22 +0,0 @@
1
- module Jei
2
- module Nodes
3
- # @see http://jsonapi.org/format/1.0/#document-links
4
- class LinkNode < Node
5
- # @param [Link] link
6
- def initialize(link)
7
- super()
8
- @link = link
9
- end
10
-
11
- # @param [Hash<Symbol, Object>] context
12
- def visit(context)
13
- context[@link.name] =
14
- if @link.meta.any?
15
- { href: @link.href, meta: @link.meta }
16
- else
17
- @link.href
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,13 +0,0 @@
1
- module Jei
2
- module Nodes
3
- # @see http://jsonapi.org/format/1.0/#document-links
4
- class LinksNode < Node
5
- # @param [Hash<Symbol, Object>] context
6
- def visit(context)
7
- data = {}
8
- children.each { |child| child.visit(data) }
9
- context[:links] = data
10
- end
11
- end
12
- end
13
- end
@@ -1,17 +0,0 @@
1
- module Jei
2
- module Nodes
3
- # @see http://jsonapi.org/format/1.0/#document-meta
4
- class MetaNode < Node
5
- # param [Hash<Symbol, Object>] meta
6
- def initialize(meta)
7
- super()
8
- @meta = meta
9
- end
10
-
11
- # @param [Hash<Symbol, Object>] context
12
- def visit(context)
13
- context[:meta] = @meta
14
- end
15
- end
16
- end
17
- end
@@ -1,19 +0,0 @@
1
- module Jei
2
- module Nodes
3
- # @abstract
4
- class Node
5
- # @return [Array<Node>]
6
- attr_reader :children
7
-
8
- def initialize
9
- @children = []
10
- end
11
-
12
- # @abstract
13
- # @param [Hash<Symbol, Object>] _context
14
- def visit(_context)
15
- raise NotImplementedError
16
- end
17
- end
18
- end
19
- end
@@ -1,19 +0,0 @@
1
- module Jei
2
- module Nodes
3
- # @see http://jsonapi.org/format/1.0/#document-resource-object-relationships
4
- class RelationshipNode < Node
5
- # @param [Relationship] relationship
6
- def initialize(relationship)
7
- super()
8
- @relationship = relationship
9
- end
10
-
11
- # @param [Hash<Symbol, Object>] context
12
- def visit(context)
13
- data = {}
14
- children.each { |child| child.visit(data) }
15
- context[@relationship.name] = data
16
- end
17
- end
18
- end
19
- end
@@ -1,13 +0,0 @@
1
- module Jei
2
- module Nodes
3
- # @see http://jsonapi.org/format/1.0/#document-resource-object-relationships
4
- class RelationshipsNode < Node
5
- # @param [Hash<Symbol, Object>] context
6
- def visit(context)
7
- relationships = {}
8
- children.each { |child| child.visit(relationships) }
9
- context[:relationships] = relationships
10
- end
11
- end
12
- end
13
- end
@@ -1,18 +0,0 @@
1
- module Jei
2
- module Nodes
3
- # http://jsonapi.org/format/1.0/#document-resource-identifier-objects
4
- class ResourceIdentifierNode < Node
5
- # param [Serializer] serializer
6
- def initialize(serializer)
7
- super()
8
- @serializer = serializer
9
- end
10
-
11
- # @param [Hash<Symbol, Object>] context
12
- def visit(context)
13
- context[:id] = @serializer.id
14
- context[:type] = @serializer.type
15
- end
16
- end
17
- end
18
- end
@@ -1,11 +0,0 @@
1
- module Jei
2
- module Nodes
3
- # http://jsonapi.org/format/1.0/#document-resource-objects
4
- class ResourceNode < Node
5
- # @param [Hash<Symbol, Object>] context
6
- def visit(context)
7
- children.each { |child| child.visit(context) }
8
- end
9
- end
10
- end
11
- end