jsonapi-serializable 0.1.1.beta2 → 0.1.1.beta3
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 +246 -10
- data/lib/jsonapi/serializable.rb +1 -0
- data/lib/jsonapi/serializable/model.rb +0 -32
- data/lib/jsonapi/serializable/model_dsl.rb +6 -33
- data/lib/jsonapi/serializable/relationship.rb +7 -12
- data/lib/jsonapi/serializable/relationship_dsl.rb +47 -23
- data/lib/jsonapi/serializable/renderer.rb +72 -0
- data/lib/jsonapi/serializable/resource.rb +14 -12
- data/lib/jsonapi/serializable/resource_builder.rb +72 -0
- data/lib/jsonapi/serializable/resource_dsl.rb +3 -0
- metadata +16 -18
- data/lib/jsonapi/serializable_error.rb +0 -93
- data/lib/jsonapi/serializable_link.rb +0 -34
- data/lib/jsonapi/serializable_relationship.rb +0 -56
- data/lib/jsonapi/serializable_resource.rb +0 -114
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a382b6d5802f167908d0d6ee82605a7a52d43a0
|
4
|
+
data.tar.gz: 9c9211218919e4a05e74b010cbc9a5ad51c6533e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf14811ff7b926f43b45c9cf49032d3fc0c0fe507d4c446d2108fd948de2c5c782cba5b3e90bfe5e6893ee0ef18dcddaf6c91de9e7052445fae25f1b747f65e0
|
7
|
+
data.tar.gz: 14e585466ae7124d51c0d7562e52fbff0982f91025ed71ad9f6a01e1100379b241e1ccea9248d2ebe84a273bdf50d020671b41a80c672f819226c4cfdc0c9a69
|
data/README.md
CHANGED
@@ -1,11 +1,25 @@
|
|
1
1
|
# jsonapi-serializable
|
2
2
|
Ruby gem for building [JSON API](http://jsonapi.org) resources to be rendered by
|
3
|
-
the [jsonapi-renderer](https://github.com/
|
3
|
+
the [jsonapi-renderer](https://github.com/jsonapi-rb/renderer) gem.
|
4
4
|
|
5
5
|
## Status
|
6
6
|
|
7
7
|
[](https://badge.fury.io/rb/jsonapi-serializable)
|
8
|
-
[](http://travis-ci.org/jsonapi-rb/serializable?branch=master)
|
9
|
+
|
10
|
+
## Table of Contents
|
11
|
+
|
12
|
+
- [Installation](#installation)
|
13
|
+
- [Usage](#usage)
|
14
|
+
- [Example for Model-based Resources](#example-for-model-based-resources)
|
15
|
+
- [Example for General Resources](#example-for-general-resources)
|
16
|
+
- [Documentation](#documentation)
|
17
|
+
- [`JSONAPI::Serializable::Resource` DSL](#jsonapiserializableresource-dsl)
|
18
|
+
- [`JSONAPI::Serializable::Model` DSL](#jsonapiserializablemodel-dsl)
|
19
|
+
- [`JSONAPI::Serializable::Relationship` DSL](#jsonapiserializablerelationship-dsl)
|
20
|
+
- [`JSONAPI::Serializable::Link` DSL](#jsonapiserializablelink-dsl)
|
21
|
+
- [`JSONAPI::Serializable::Error` DSL](#jsonapiserializableerror-dsl)
|
22
|
+
- [License](#license)
|
9
23
|
|
10
24
|
## Installation
|
11
25
|
```ruby
|
@@ -30,23 +44,21 @@ require 'jsonapi/serializable'
|
|
30
44
|
|
31
45
|
Then, define some resource classes:
|
32
46
|
|
33
|
-
###
|
47
|
+
### Example for Model-based Resources
|
34
48
|
|
35
49
|
For resources that are simple representations of models, the DSL is simplified:
|
36
50
|
|
37
51
|
```ruby
|
38
|
-
class PostResource < JSONAPI::Serializable::
|
52
|
+
class PostResource < JSONAPI::Serializable::Model
|
39
53
|
type 'posts'
|
40
54
|
|
41
|
-
id
|
42
|
-
|
43
55
|
attribute :title
|
44
56
|
|
45
57
|
attribute :date do
|
46
58
|
@model.created_at
|
47
59
|
end
|
48
60
|
|
49
|
-
|
61
|
+
has_one :author, UserResource do
|
50
62
|
link(:self) do
|
51
63
|
href @url_helper.link_for_rel('posts', @model.id, 'author')
|
52
64
|
meta link_meta: 'some meta'
|
@@ -68,15 +80,16 @@ class PostResource < JSONAPI::Serializable::Resource
|
|
68
80
|
end
|
69
81
|
end
|
70
82
|
```
|
83
|
+
|
71
84
|
Then, build your resources from your models and render them:
|
72
85
|
```ruby
|
73
86
|
# post = some post model
|
74
87
|
# UrlHelper is some helper class
|
75
88
|
resource = PostResource.new(model: post, url_helper: UrlHelper)
|
76
|
-
document = JSONAPI.render(resource)
|
89
|
+
document = JSONAPI.render(data: resource)
|
77
90
|
```
|
78
91
|
|
79
|
-
###
|
92
|
+
### Example for General Resources
|
80
93
|
|
81
94
|
In case your resource is not a simple representation of one of your models,
|
82
95
|
the more general `JSONAPI::Serializable::Resource` class can be used.
|
@@ -129,9 +142,232 @@ Finally, build your resources from your models and render them:
|
|
129
142
|
# post = some post model
|
130
143
|
# UrlHelper is some helper class
|
131
144
|
resource = PostResource.new(post: post, url_helper: UrlHelper)
|
132
|
-
document = JSONAPI.render(resource)
|
145
|
+
document = JSONAPI.render(data: resource)
|
133
146
|
```
|
134
147
|
|
148
|
+
## Documentation
|
149
|
+
|
150
|
+
### `JSONAPI::Serializable::Resource` DSL
|
151
|
+
|
152
|
+
+ `#initialize(hash)`
|
153
|
+
|
154
|
+
All the values of the hash are made available during serialization as instance
|
155
|
+
variables within all DSLs.
|
156
|
+
|
157
|
+
Example:
|
158
|
+
```ruby
|
159
|
+
SerializablePost.new(post: post, url_helper: url_helper)
|
160
|
+
# => You can then use @post and @url_helper from within the DSL.
|
161
|
+
```
|
162
|
+
|
163
|
+
+ `::type(value = nil, &block)`
|
164
|
+
|
165
|
+
Define the type of the resource, either statically, or dynamically as the
|
166
|
+
return value of the block.
|
167
|
+
|
168
|
+
+ `::id(&block)`
|
169
|
+
|
170
|
+
Define the id of the resource.
|
171
|
+
|
172
|
+
Example:
|
173
|
+
```ruby
|
174
|
+
id { @post.id }
|
175
|
+
```
|
176
|
+
|
177
|
+
+ `::attribute(key, &block)`
|
178
|
+
|
179
|
+
Define an attribute of the resource.
|
180
|
+
|
181
|
+
Example:
|
182
|
+
```ruby
|
183
|
+
attribute(:title) { @post.title }
|
184
|
+
```
|
185
|
+
|
186
|
+
+ `::relationship(key, &block)`
|
187
|
+
|
188
|
+
Define a relationship of the resource. The block can contain any instruction of
|
189
|
+
the [`JSONAPI::Serializable::Relationship` DSL](#jsonapiserializablerelationship-dsl).
|
190
|
+
|
191
|
+
Example:
|
192
|
+
```ruby
|
193
|
+
relationship :comments do
|
194
|
+
data do
|
195
|
+
@post.comments.map do |c|
|
196
|
+
SerializableComment.new(comment: c, url_helper: @url_helper)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
link :self do
|
200
|
+
@url_helper.link_for_post_comments(post_id: @post.id)
|
201
|
+
end
|
202
|
+
meta do
|
203
|
+
{ count: @post.comments.count }
|
204
|
+
end
|
205
|
+
end
|
206
|
+
```
|
207
|
+
|
208
|
+
+ `::link(key, &block)`
|
209
|
+
|
210
|
+
Define a resource-level link. The block can either return a string or contain
|
211
|
+
any instruction of the [`JSONAPI::Serializable::Link` DSL](#jsonapiserializablelink-dsl).
|
212
|
+
|
213
|
+
Example:
|
214
|
+
```ruby
|
215
|
+
link :self do
|
216
|
+
"http://api.example.com/posts/#{@post.id}"
|
217
|
+
end
|
218
|
+
```
|
219
|
+
|
220
|
+
+ `::meta(value = nil, &block)`
|
221
|
+
|
222
|
+
Define a resource-level meta member. The value can either be provided
|
223
|
+
statically, or dynamically as the return value of a block.
|
224
|
+
|
225
|
+
Examples:
|
226
|
+
```ruby
|
227
|
+
meta(experimental: true)
|
228
|
+
```
|
229
|
+
```ruby
|
230
|
+
meta do
|
231
|
+
{ remaining_time: @post.remaining_time }
|
232
|
+
end
|
233
|
+
```
|
234
|
+
|
235
|
+
### `JSONAPI::Serializable::Model` DSL
|
236
|
+
|
237
|
+
This class is a subclass of `JSONAPI::Serializable::Resource` with a more
|
238
|
+
convenient DSL tailored for resources that are direct representation of some
|
239
|
+
business models.
|
240
|
+
|
241
|
+
+ `#initialize(hash)`
|
242
|
+
|
243
|
+
See `JSONAPI::Serializable::Resource` DSL.
|
244
|
+
|
245
|
+
The model is expected to be provided in the hash with the key `:model`.
|
246
|
+
|
247
|
+
+ `::type(value = nil, &block)`
|
248
|
+
|
249
|
+
See `JSONAPI::Serializable::Resource` DSL.
|
250
|
+
|
251
|
+
+ `::id(&block)`
|
252
|
+
|
253
|
+
See `JSONAPI::Serializable::Resource` DSL.
|
254
|
+
|
255
|
+
Defaults to:
|
256
|
+
```ruby
|
257
|
+
id { @model.id }
|
258
|
+
```
|
259
|
+
|
260
|
+
+ `::attribute(key, &block)`
|
261
|
+
|
262
|
+
See `JSONAPI::Serializable::Resource` DSL.
|
263
|
+
|
264
|
+
Defaults to the following when no block is provided:
|
265
|
+
```ruby
|
266
|
+
attribute key do
|
267
|
+
@model.public_send(key)
|
268
|
+
end
|
269
|
+
```
|
270
|
+
|
271
|
+
+ `::has_one(key, resource_klass = nil, &block)`
|
272
|
+
|
273
|
+
Define a `has_one` relationship on the resource.
|
274
|
+
|
275
|
+
The serializable class for the related resource can be explicitly stated as the
|
276
|
+
second parameter, but when omitted it will be infered from the related
|
277
|
+
resource's class name.
|
278
|
+
|
279
|
+
When no block is provided, the value of the relationship defaults to
|
280
|
+
`resource_klass.new(model: @model.public_send(key))`.
|
281
|
+
|
282
|
+
+ `::has_many(key, resource_klass = nil, &block)`
|
283
|
+
|
284
|
+
Define a `has_many` relationship on the resource.
|
285
|
+
|
286
|
+
The serializable class for the related resources can be explicitly stated as the
|
287
|
+
second parameter, but when omitted it will be infered from the related
|
288
|
+
resources' class names.
|
289
|
+
|
290
|
+
When no block is provided, the value of the relationship defaults to:
|
291
|
+
```ruby
|
292
|
+
@model.public_send(key).map do |r|
|
293
|
+
resource_klass.new(model: r)
|
294
|
+
end
|
295
|
+
```
|
296
|
+
|
297
|
+
+ `::relationship(key, &block)`
|
298
|
+
|
299
|
+
See `JSONAPI::Serializable::Resource` DSL.
|
300
|
+
|
301
|
+
+ `::link(key, &block)`
|
302
|
+
|
303
|
+
See `JSONAPI::Serializable::Resource` DSL.
|
304
|
+
|
305
|
+
+ `::meta(value = nil, &block)`
|
306
|
+
|
307
|
+
See `JSONAPI::Serializable::Resource` DSL.
|
308
|
+
|
309
|
+
### `JSONAPI::Serializable::Relationship` DSL
|
310
|
+
|
311
|
+
+ `::data(resource_class = nil, &block)`
|
312
|
+
|
313
|
+
NOTE: This section is outdated. It is still valid, but the data method is now
|
314
|
+
much more flexible.
|
315
|
+
|
316
|
+
Defines the related serializable resources for the relationship.
|
317
|
+
|
318
|
+
Example:
|
319
|
+
```ruby
|
320
|
+
data do
|
321
|
+
if @post.author.nil?
|
322
|
+
nil
|
323
|
+
else
|
324
|
+
SerializableUser.new(user: @post.author)
|
325
|
+
end
|
326
|
+
end
|
327
|
+
```
|
328
|
+
|
329
|
+
+ `::link(key, &block)`
|
330
|
+
|
331
|
+
Define a relationship-level link.
|
332
|
+
|
333
|
+
See `JSONAPI::Serializable::Resource` DSL.
|
334
|
+
|
335
|
+
+ `::meta(value = nil, &block)`
|
336
|
+
|
337
|
+
Define some relationship-level meta member.
|
338
|
+
|
339
|
+
See `JSONAPI::Serializable::Resource` DSL.
|
340
|
+
|
341
|
+
### `JSONAPI::Serializable::Link` DSL
|
342
|
+
|
343
|
+
+ `::href(value = nil, &block)`
|
344
|
+
|
345
|
+
Define the href member for the link, either directly, or dynamically as the
|
346
|
+
return value of a block.
|
347
|
+
|
348
|
+
+ `::meta(value = nil, &block)`
|
349
|
+
|
350
|
+
Define the meta member for the link, either directly, or dynamically as the
|
351
|
+
return value of a block.
|
352
|
+
|
353
|
+
### `JSONAPI::Serializable::Error` DSL
|
354
|
+
|
355
|
+
+ `::id(value = nil, &block)`
|
356
|
+
|
357
|
+
+ `::status(value = nil, &block)`
|
358
|
+
|
359
|
+
+ `::code(value = nil, &block)`
|
360
|
+
|
361
|
+
+ `::title(value = nil, &block)`
|
362
|
+
|
363
|
+
+ `::detail(value = nil, &block)`
|
364
|
+
|
365
|
+
+ `::meta(value = nil, &block)`
|
366
|
+
|
367
|
+
+ `::link(key, &block)`
|
368
|
+
|
369
|
+
+ `::source(&block)`
|
370
|
+
|
135
371
|
## License
|
136
372
|
|
137
373
|
jsonapi-serializable is released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
data/lib/jsonapi/serializable.rb
CHANGED
@@ -6,39 +6,7 @@ module JSONAPI
|
|
6
6
|
class Model < Resource
|
7
7
|
include ModelDSL
|
8
8
|
|
9
|
-
class << self
|
10
|
-
attr_accessor :api_version_val
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.inherited(klass)
|
14
|
-
super
|
15
|
-
klass.api_version_val = api_version_val
|
16
|
-
end
|
17
|
-
|
18
9
|
id { @model.public_send(:id).to_s }
|
19
|
-
|
20
|
-
def resource_klass_for(model_klass)
|
21
|
-
names = model_klass.name.split('::'.freeze)
|
22
|
-
model_klass_name = names.pop
|
23
|
-
namespace = names.join('::'.freeze)
|
24
|
-
version = self.class.api_version_val
|
25
|
-
|
26
|
-
klass_name = [namespace, version, "Serializable#{model_klass_name}"]
|
27
|
-
.reject(&:nil?)
|
28
|
-
.reject(&:empty?)
|
29
|
-
.join('::'.freeze)
|
30
|
-
|
31
|
-
Object.const_get(klass_name)
|
32
|
-
end
|
33
|
-
|
34
|
-
def nil?
|
35
|
-
@model.nil?
|
36
|
-
end
|
37
|
-
|
38
|
-
def as_jsonapi(params = {})
|
39
|
-
return nil if nil?
|
40
|
-
super(params)
|
41
|
-
end
|
42
10
|
end
|
43
11
|
end
|
44
12
|
end
|
@@ -6,48 +6,21 @@ module JSONAPI
|
|
6
6
|
end
|
7
7
|
|
8
8
|
module ClassMethods
|
9
|
-
def api_version(value)
|
10
|
-
self.api_version_val = value
|
11
|
-
end
|
12
|
-
|
13
|
-
def type(value = nil)
|
14
|
-
value ||= name
|
15
|
-
super(value)
|
16
|
-
end
|
17
|
-
|
18
9
|
def attribute(attr, &block)
|
19
10
|
block ||= proc { @model.public_send(attr) }
|
20
11
|
super(attr, &block)
|
21
12
|
end
|
22
13
|
|
23
|
-
def
|
24
|
-
rel_block = proc do
|
25
|
-
if resource_klass
|
26
|
-
data do
|
27
|
-
@model.public_send(rel).map do |related|
|
28
|
-
resource_klass ||= resource_klass_for(related.class)
|
29
|
-
resource_klass.new(model: related)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
instance_eval(&block) unless block.nil?
|
34
|
-
end
|
35
|
-
relationship(rel, &rel_block)
|
36
|
-
end
|
37
|
-
|
38
|
-
def has_one(rel, resource_klass = nil, &block)
|
14
|
+
def relationship(rel, resource_class = nil, &block)
|
39
15
|
rel_block = proc do
|
40
|
-
|
41
|
-
data do
|
42
|
-
related = @model.public_send(rel)
|
43
|
-
resource_klass ||= resource_klass_for(related.class)
|
44
|
-
resource_klass.new(model: related)
|
45
|
-
end
|
46
|
-
end
|
16
|
+
data(resource_class) { @model.public_send(rel) }
|
47
17
|
instance_eval(&block) unless block.nil?
|
48
18
|
end
|
49
|
-
|
19
|
+
super(rel, &rel_block)
|
50
20
|
end
|
21
|
+
alias has_many relationship
|
22
|
+
alias has_one relationship
|
23
|
+
alias belongs_to relationship
|
51
24
|
end
|
52
25
|
end
|
53
26
|
end
|
@@ -18,24 +18,19 @@ module JSONAPI
|
|
18
18
|
hash[:links] = @_links if @_links.any?
|
19
19
|
hash[:meta] = @_meta unless @_meta.nil?
|
20
20
|
return hash unless included || (!@_links.any? && @_meta.nil?)
|
21
|
-
hash[:data] =
|
21
|
+
hash[:data] = linkage_data
|
22
22
|
|
23
23
|
hash
|
24
24
|
end
|
25
25
|
|
26
26
|
private
|
27
27
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
elsif data.nil?
|
35
|
-
nil
|
36
|
-
else
|
37
|
-
{ type: data.jsonapi_type, id: data.jsonapi_id }
|
38
|
-
end
|
28
|
+
def linkage_data
|
29
|
+
linkage_data = Array(data).map do |res|
|
30
|
+
{ type: res.jsonapi_type, id: res.jsonapi_id }
|
31
|
+
end
|
32
|
+
|
33
|
+
data.respond_to?(:each) ? linkage_data : linkage_data.first
|
39
34
|
end
|
40
35
|
end
|
41
36
|
end
|
@@ -1,40 +1,55 @@
|
|
1
|
+
require 'jsonapi/serializable/resource_builder'
|
2
|
+
|
1
3
|
module JSONAPI
|
2
4
|
module Serializable
|
3
5
|
module RelationshipDSL
|
4
|
-
# Declare
|
5
|
-
#
|
6
|
-
# @yieldreturn
|
7
|
-
#
|
8
|
-
#
|
6
|
+
# Declare the data for this relationship.
|
7
|
+
# @param [String,Constant,Hash{Symbol=>String,Constant}] resource_class
|
8
|
+
# @yieldreturn The data for this relationship.
|
9
|
+
# If it is nil, an object implementing the Serializable::Resource
|
10
|
+
# interface, an empty array, or an array of objects implementing the
|
11
|
+
# Serializable::Resource interface, then it is used as is.
|
12
|
+
# Otherwise an appropriate Serializable::Model subclass is inferred
|
13
|
+
# from the object(s)' namespace/class, the resource_class parameter if
|
14
|
+
# provided, and the @_resource_inferer.
|
9
15
|
#
|
10
16
|
# @example
|
11
|
-
# data
|
17
|
+
# data do
|
12
18
|
# @user.posts.map { |p| PostResource.new(post: p) }
|
13
19
|
# end
|
14
20
|
#
|
15
21
|
# @example
|
16
|
-
# data
|
17
|
-
# @
|
22
|
+
# data do
|
23
|
+
# @post.author && UserResource.new(user: @user.author)
|
18
24
|
# end
|
19
|
-
def data(&block)
|
20
|
-
if block.nil?
|
21
|
-
@_data ||= (@_data_block && @_data_block.call)
|
22
|
-
else
|
23
|
-
@_data_block = block
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# Declare the linkage data for this relationship. Useful when linkage
|
28
|
-
# can be computed in a more efficient way than the data itself.
|
29
25
|
#
|
30
|
-
# @
|
26
|
+
# @example
|
27
|
+
# data do
|
28
|
+
# @user.posts
|
29
|
+
# end
|
30
|
+
# end
|
31
31
|
#
|
32
32
|
# @example
|
33
|
-
#
|
34
|
-
#
|
33
|
+
# data SerializablePost do
|
34
|
+
# @user.posts
|
35
35
|
# end
|
36
|
-
|
37
|
-
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# data "SerializableUser" do
|
39
|
+
# @post.author
|
40
|
+
# end
|
41
|
+
def data(resource_class = nil)
|
42
|
+
if block_given?
|
43
|
+
# NOTE(beauby): Lazify computation since it is only needed when
|
44
|
+
# the corresponding relationship is included.
|
45
|
+
@_data_block = proc do
|
46
|
+
_resources_for(yield, resource_class)
|
47
|
+
end
|
48
|
+
else
|
49
|
+
# NOTE(beauby): In the case of a computation heavy relationship with
|
50
|
+
# nil value, this block might be executed multiple times.
|
51
|
+
@_data ||= @_data_block.call
|
52
|
+
end
|
38
53
|
end
|
39
54
|
|
40
55
|
# @overload meta(value)
|
@@ -77,6 +92,15 @@ module JSONAPI
|
|
77
92
|
def link(name, &block)
|
78
93
|
@_links[name] = Link.as_jsonapi(@_param_hash, &block)
|
79
94
|
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
# @api private
|
99
|
+
def _resources_for(models, resource_class)
|
100
|
+
resource_class ||= @_resource_inferer
|
101
|
+
|
102
|
+
ResourceBuilder.build(models, @_param_hash, resource_class)
|
103
|
+
end
|
80
104
|
end
|
81
105
|
end
|
82
106
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'jsonapi/renderer'
|
2
|
+
|
3
|
+
module JSONAPI
|
4
|
+
module Serializable
|
5
|
+
class Renderer
|
6
|
+
def self.render(resources, options)
|
7
|
+
new(resources, options).render
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(resources, options)
|
11
|
+
@resources = resources
|
12
|
+
@options = options.dup
|
13
|
+
@klass = @options.delete(:class)
|
14
|
+
@namespace = @options.delete(:namespace)
|
15
|
+
@inferer = @options.delete(:inferer)
|
16
|
+
@exposures = @options.delete(:expose) || {}
|
17
|
+
@exposures[:_resource_inferer] = namespace_inferer || @inferer
|
18
|
+
end
|
19
|
+
|
20
|
+
def render
|
21
|
+
JSONAPI.render(jsonapi_params.merge(data: jsonapi_resources)).to_json
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def jsonapi_params
|
27
|
+
@options
|
28
|
+
end
|
29
|
+
|
30
|
+
def jsonapi_resources
|
31
|
+
toplevel_inferer = @klass || @inferer
|
32
|
+
JSONAPI::Serializable::ResourceBuilder.build(@resources,
|
33
|
+
@exposures,
|
34
|
+
toplevel_inferer)
|
35
|
+
end
|
36
|
+
|
37
|
+
def namespace_inferer
|
38
|
+
return nil unless @namespace
|
39
|
+
proc do |klass|
|
40
|
+
names = klass.name.split('::')
|
41
|
+
klass = names.pop
|
42
|
+
[@namespace, names, "Serializable#{klass}"].reject(&:nil?).join('::')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class ErrorRenderer
|
48
|
+
def self.render(errors, options)
|
49
|
+
new(errors, options).render
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize(errors, options)
|
53
|
+
@errors = errors
|
54
|
+
@options = options.dup
|
55
|
+
end
|
56
|
+
|
57
|
+
def render
|
58
|
+
JSONAPI.render(jsonapi_params.merge(errors: jsonapi_errors)).to_json
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def jsonapi_params
|
64
|
+
@options
|
65
|
+
end
|
66
|
+
|
67
|
+
def jsonapi_errors
|
68
|
+
@errors
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -34,19 +34,21 @@ module JSONAPI
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def as_jsonapi(params = {})
|
37
|
-
|
38
|
-
hash[:id] = jsonapi_id
|
39
|
-
hash[:type] = jsonapi_type
|
40
|
-
requested_attrs = params[:fields] || self.class.attribute_blocks.keys
|
41
|
-
attr = attributes(requested_attrs)
|
42
|
-
hash[:attributes] = attr if attr.any?
|
43
|
-
requested_rels = params[:fields] || self.class.relationship_blocks.keys
|
44
|
-
rels = relationships(requested_rels, params[:include] || [])
|
45
|
-
hash[:relationships] = rels if rels.any?
|
46
|
-
hash[:links] = links if links.any?
|
47
|
-
hash[:meta] = meta unless meta.nil?
|
37
|
+
return nil if nil?
|
48
38
|
|
49
|
-
hash
|
39
|
+
{}.tap do |hash|
|
40
|
+
hash[:id] = jsonapi_id
|
41
|
+
hash[:type] = jsonapi_type
|
42
|
+
requested_attrs = params[:fields] || self.class.attribute_blocks.keys
|
43
|
+
attrs = attributes(requested_attrs)
|
44
|
+
hash[:attributes] = attrs if attrs.any?
|
45
|
+
requested_rels = params[:fields] ||
|
46
|
+
self.class.relationship_blocks.keys
|
47
|
+
rels = relationships(requested_rels, params[:include] || [])
|
48
|
+
hash[:relationships] = rels if rels.any?
|
49
|
+
hash[:links] = links if links.any?
|
50
|
+
hash[:meta] = meta unless meta.nil?
|
51
|
+
end
|
50
52
|
end
|
51
53
|
|
52
54
|
def jsonapi_type
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module JSONAPI
|
2
|
+
module Serializable
|
3
|
+
class ResourceBuilder
|
4
|
+
DEFAULT_RESOURCE_INFERER = lambda do |model_klass_name|
|
5
|
+
names = model_klass_name.split('::'.freeze)
|
6
|
+
klass_name = names.pop
|
7
|
+
namespace = names.join('::'.freeze)
|
8
|
+
|
9
|
+
klass_name = [namespace, "Serializable#{klass_name}"]
|
10
|
+
.reject(&:nil?)
|
11
|
+
.reject(&:empty?)
|
12
|
+
.join('::'.freeze)
|
13
|
+
|
14
|
+
Object.const_get(klass_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.build(models, expose, klass)
|
18
|
+
return models if models.nil? ||
|
19
|
+
Array(models).first.respond_to?(:as_jsonapi)
|
20
|
+
|
21
|
+
resources =
|
22
|
+
Array(models).map { |model| new(model, expose, klass).resource }
|
23
|
+
|
24
|
+
models.respond_to?(:each) ? resources : resources.first
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :resource
|
28
|
+
|
29
|
+
def initialize(model, expose, klass)
|
30
|
+
@model = model
|
31
|
+
@expose = expose || {}
|
32
|
+
@klass = klass
|
33
|
+
@resource = serializable_class.new(serializable_params)
|
34
|
+
freeze
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def serializable_params
|
40
|
+
@expose.merge(model: @model)
|
41
|
+
end
|
42
|
+
|
43
|
+
# rubocop:disable Metrics/MethodLength
|
44
|
+
def serializable_class
|
45
|
+
klass =
|
46
|
+
if @klass.respond_to?(:call)
|
47
|
+
@klass.call(@model.class.name)
|
48
|
+
elsif @klass.is_a?(Hash)
|
49
|
+
@klass[@model.class.name.to_sym]
|
50
|
+
elsif @klass.nil?
|
51
|
+
DEFAULT_RESOURCE_INFERER.call(@model.class.name)
|
52
|
+
else
|
53
|
+
@klass
|
54
|
+
end
|
55
|
+
|
56
|
+
reify_class(klass)
|
57
|
+
end
|
58
|
+
# rubocop:enable Metrics/MethodLength
|
59
|
+
|
60
|
+
def reify_class(klass)
|
61
|
+
if klass.is_a?(Class)
|
62
|
+
klass
|
63
|
+
elsif klass.is_a?(String)
|
64
|
+
Object.const_get(klass)
|
65
|
+
else
|
66
|
+
# TODO(beauby): Raise meaningful exception.
|
67
|
+
raise
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -97,6 +97,9 @@ module JSONAPI
|
|
97
97
|
def relationship(name, &block)
|
98
98
|
relationship_blocks[name] = block
|
99
99
|
end
|
100
|
+
alias has_many relationship
|
101
|
+
alias has_one relationship
|
102
|
+
alias belongs_to relationship
|
100
103
|
|
101
104
|
# Declare a link for this resource. The properties of the link are set
|
102
105
|
# by providing a block in which the DSL methods of
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonapi-serializable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.1.
|
4
|
+
version: 0.1.1.beta3
|
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-
|
11
|
+
date: 2016-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jsonapi-renderer
|
@@ -16,43 +16,43 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.1.1.
|
20
|
-
type: :
|
19
|
+
version: 0.1.1.beta3
|
20
|
+
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.1.1.
|
26
|
+
version: 0.1.1.beta3
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '11.3'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '11.3'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '3.
|
47
|
+
version: '3.5'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '3.
|
55
|
-
description: DSL for building resource classes
|
54
|
+
version: '3.5'
|
55
|
+
description: Powerful DSL for building resource classes - efficient and flexible rendering.
|
56
56
|
email: lucas.hosseini@gmail.com
|
57
57
|
executables: []
|
58
58
|
extensions: []
|
@@ -67,13 +67,11 @@ files:
|
|
67
67
|
- lib/jsonapi/serializable/model_dsl.rb
|
68
68
|
- lib/jsonapi/serializable/relationship.rb
|
69
69
|
- lib/jsonapi/serializable/relationship_dsl.rb
|
70
|
+
- lib/jsonapi/serializable/renderer.rb
|
70
71
|
- lib/jsonapi/serializable/resource.rb
|
72
|
+
- lib/jsonapi/serializable/resource_builder.rb
|
71
73
|
- lib/jsonapi/serializable/resource_dsl.rb
|
72
|
-
|
73
|
-
- lib/jsonapi/serializable_link.rb
|
74
|
-
- lib/jsonapi/serializable_relationship.rb
|
75
|
-
- lib/jsonapi/serializable_resource.rb
|
76
|
-
homepage: https://github.com/beauby/jsonapi-serializable
|
74
|
+
homepage: https://github.com/jsonapi-rb/serializable
|
77
75
|
licenses:
|
78
76
|
- MIT
|
79
77
|
metadata: {}
|
@@ -96,5 +94,5 @@ rubyforge_project:
|
|
96
94
|
rubygems_version: 2.5.1
|
97
95
|
signing_key:
|
98
96
|
specification_version: 4
|
99
|
-
summary:
|
97
|
+
summary: Conveniently serialize JSON API resources.
|
100
98
|
test_files: []
|
@@ -1,93 +0,0 @@
|
|
1
|
-
require 'jsonapi/serializable_link'
|
2
|
-
|
3
|
-
module JSONAPI
|
4
|
-
class SerializableErrorSource
|
5
|
-
def self.as_jsonapi(params = {})
|
6
|
-
self.class.new(params).as_jsonapi
|
7
|
-
end
|
8
|
-
|
9
|
-
def initialize(params = {})
|
10
|
-
params.each { |k, v| instance_variable_set("@#{k}", v) }
|
11
|
-
@_data = {}
|
12
|
-
end
|
13
|
-
|
14
|
-
def as_jsonapi
|
15
|
-
@_data
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def method_missing(name, arg)
|
21
|
-
@_data[name] = arg
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class SerializableError
|
26
|
-
class << self
|
27
|
-
attr_accessor :id, :id_block, :status, :status_block, :code, :code_block,
|
28
|
-
:title, :title_block, :detail, :detail_block, :meta,
|
29
|
-
:meta_block, :source_block, :link_blocks
|
30
|
-
|
31
|
-
[:id, :status, :code, :title, :detail, :meta].each do |key|
|
32
|
-
define_method(key) do |*args, &block|
|
33
|
-
send("@#{key}=", args[0])
|
34
|
-
send("@#{key}_block=", block)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def link(name, &block)
|
39
|
-
link_blocks[name] = block
|
40
|
-
end
|
41
|
-
|
42
|
-
def source(&block)
|
43
|
-
self.source_block = block
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
self.link_blocks = {}
|
48
|
-
|
49
|
-
def self.inherited(klass)
|
50
|
-
super
|
51
|
-
klass.link_blocks = self.class.link_blocks.dup
|
52
|
-
end
|
53
|
-
|
54
|
-
def initialize(params = {})
|
55
|
-
@_param_hash = params
|
56
|
-
params.each { |k, v| instance_variable_set("@#{k}", v) }
|
57
|
-
end
|
58
|
-
|
59
|
-
def as_jsonapi
|
60
|
-
hash = links.any? ? { links: links } : {}
|
61
|
-
[:id, :status, :code, :title, :detail, :meta, :source]
|
62
|
-
.each_with_object(hash) do |key, h|
|
63
|
-
value = send(key)
|
64
|
-
h[key] = value unless value.nil?
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
private
|
69
|
-
|
70
|
-
def links
|
71
|
-
@_links ||= self.class.link_blocks.each_with_object({}) do |(k, v), h|
|
72
|
-
h[k] = JSONAPI::SerializableLink.as_jsonapi(@_param_hash, v)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def source
|
77
|
-
@_source ||=
|
78
|
-
JSONAPI::SerializableErrorSource.as_jsonapi(@_param_hash,
|
79
|
-
self.class.source_block)
|
80
|
-
end
|
81
|
-
|
82
|
-
[:id, :status, :code, :title, :detail, :meta].each do |key|
|
83
|
-
define_method(key) do
|
84
|
-
unless instance_variable_defined?("@#{key}")
|
85
|
-
instance_variable_set("@#{key}",
|
86
|
-
self.class.send(key) ||
|
87
|
-
instance_eval(self.class.send("#{key}_block")))
|
88
|
-
end
|
89
|
-
instance_variable_get("@#{key}")
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module JSONAPI
|
2
|
-
class SerializableLink
|
3
|
-
def self.as_jsonapi(param_hash = {}, &block)
|
4
|
-
new(param_hash, &block).as_jsonapi
|
5
|
-
end
|
6
|
-
|
7
|
-
def initialize(param_hash = {}, &block)
|
8
|
-
param_hash.each do |k, v|
|
9
|
-
instance_variable_set("@#{k}", v)
|
10
|
-
end
|
11
|
-
str_value = instance_eval(&block)
|
12
|
-
@_href ||= str_value
|
13
|
-
end
|
14
|
-
|
15
|
-
def as_jsonapi
|
16
|
-
@_hash ||=
|
17
|
-
if @_meta.nil?
|
18
|
-
@_href
|
19
|
-
else
|
20
|
-
{ href: @_href, meta: @_meta }
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def href(value = nil, &block)
|
27
|
-
@_href = block.nil? ? value : instance_eval(&block)
|
28
|
-
end
|
29
|
-
|
30
|
-
def meta(value = nil, &block)
|
31
|
-
@_meta = block.nil? ? value : instance_eval(&block)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
require 'jsonapi/serializable_link'
|
2
|
-
|
3
|
-
module JSONAPI
|
4
|
-
class SerializableRelationship
|
5
|
-
def initialize(param_hash = {}, &block)
|
6
|
-
@_param_hash = param_hash
|
7
|
-
@_param_hash.each { |k, v| instance_variable_set("@#{k}", v) }
|
8
|
-
@_links = {}
|
9
|
-
instance_eval(&block)
|
10
|
-
end
|
11
|
-
|
12
|
-
def data(&block)
|
13
|
-
if block.nil?
|
14
|
-
@_data ||= @_data_block.call
|
15
|
-
else
|
16
|
-
@_data_block = block
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def as_jsonapi(included)
|
21
|
-
hash = {}
|
22
|
-
hash[:links] = @_links if @_links.any?
|
23
|
-
hash[:meta] = @_meta unless @_meta.nil?
|
24
|
-
hash[:data] = eval_linkage_data if included
|
25
|
-
|
26
|
-
hash
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def eval_linkage_data
|
32
|
-
@_linkage_data ||=
|
33
|
-
if @_linkage_data_block
|
34
|
-
@_linkage_data_block.call
|
35
|
-
elsif data.respond_to?(:each)
|
36
|
-
data.map { |res| { type: res.jsonapi_type, id: res.jsonapi_id } }
|
37
|
-
elsif data.nil?
|
38
|
-
nil
|
39
|
-
else
|
40
|
-
{ type: data.jsonapi_type, id: data.jsonapi_id }
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def linkage_data(&block)
|
45
|
-
@_linkage_data_block = block
|
46
|
-
end
|
47
|
-
|
48
|
-
def meta(value = nil)
|
49
|
-
@_meta = value || yield
|
50
|
-
end
|
51
|
-
|
52
|
-
def link(name, &block)
|
53
|
-
@_links[name] = JSONAPI::SerializableLink.as_jsonapi(@_param_hash, &block)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,114 +0,0 @@
|
|
1
|
-
require 'jsonapi/serializable_link'
|
2
|
-
require 'jsonapi/serializable_relationship'
|
3
|
-
|
4
|
-
module JSONAPI
|
5
|
-
class SerializableResource
|
6
|
-
class << self
|
7
|
-
attr_accessor :type_val, :type_block, :id_block, :attribute_blocks,
|
8
|
-
:relationship_blocks, :link_blocks, :meta_val, :meta_block
|
9
|
-
|
10
|
-
def type(value = nil, &block)
|
11
|
-
self.type_val = value
|
12
|
-
self.type_block = block
|
13
|
-
end
|
14
|
-
|
15
|
-
def id(&block)
|
16
|
-
self.id_block = block
|
17
|
-
end
|
18
|
-
|
19
|
-
def meta(value = nil, &block)
|
20
|
-
self.meta_val = value
|
21
|
-
self.meta_block = block
|
22
|
-
end
|
23
|
-
|
24
|
-
def attribute(name, &block)
|
25
|
-
attribute_blocks[name] = block
|
26
|
-
end
|
27
|
-
|
28
|
-
def relationship(name, &block)
|
29
|
-
relationship_blocks[name] = block
|
30
|
-
end
|
31
|
-
|
32
|
-
def link(name, &block)
|
33
|
-
link_blocks[name] = block
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
self.attribute_blocks = {}
|
38
|
-
self.relationship_blocks = {}
|
39
|
-
self.link_blocks = {}
|
40
|
-
|
41
|
-
def self.inherited(klass)
|
42
|
-
super
|
43
|
-
klass.attribute_blocks = attribute_blocks.dup
|
44
|
-
klass.relationship_blocks = relationship_blocks.dup
|
45
|
-
klass.link_blocks = link_blocks.dup
|
46
|
-
end
|
47
|
-
|
48
|
-
def initialize(param_hash = {})
|
49
|
-
param_hash.each { |k, v| instance_variable_set("@#{k}", v) }
|
50
|
-
@_id = instance_eval(&self.class.id_block)
|
51
|
-
@_type = self.class.type_val || instance_eval(&self.class.type_block)
|
52
|
-
@_meta = if self.class.meta_val
|
53
|
-
self.class.meta_val
|
54
|
-
elsif self.class.meta_block
|
55
|
-
instance_eval(&self.class.meta_block)
|
56
|
-
end
|
57
|
-
@_attributes = {}
|
58
|
-
@_relationships = self.class.relationship_blocks
|
59
|
-
.each_with_object({}) do |(k, v), h|
|
60
|
-
h[k] = JSONAPI::SerializableRelationship.new(param_hash, &v)
|
61
|
-
end
|
62
|
-
@_links = self.class.link_blocks
|
63
|
-
.each_with_object({}) do |(k, v), h|
|
64
|
-
h[k] = JSONAPI::SerializableLink.as_jsonapi(param_hash, &v)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def as_jsonapi(params = {})
|
69
|
-
hash = {}
|
70
|
-
hash[:id] = @_id
|
71
|
-
hash[:type] = @_type
|
72
|
-
attr = attributes(params[:fields] || @_attributes.keys)
|
73
|
-
hash[:attributes] = attr if attr.any?
|
74
|
-
rels = relationships(params[:field] || @_relationships.keys,
|
75
|
-
params[:include] || [])
|
76
|
-
hash[:relationships] = rels if rels.any?
|
77
|
-
hash[:links] = @_links if @_links.any?
|
78
|
-
hash[:meta] = @_meta unless @_meta.nil?
|
79
|
-
|
80
|
-
hash
|
81
|
-
end
|
82
|
-
|
83
|
-
def jsonapi_type
|
84
|
-
@_type
|
85
|
-
end
|
86
|
-
|
87
|
-
def jsonapi_id
|
88
|
-
@_id
|
89
|
-
end
|
90
|
-
|
91
|
-
def jsonapi_related(include)
|
92
|
-
@_relationships
|
93
|
-
.select { |k, _| include.include?(k) }
|
94
|
-
.each_with_object({}) { |(k, v), h| h[k] = Array(v.data) }
|
95
|
-
end
|
96
|
-
|
97
|
-
private
|
98
|
-
|
99
|
-
def attributes(fields)
|
100
|
-
self.class.attribute_blocks
|
101
|
-
.select { |k, _| !@_attributes.key?(k) && fields.include?(k) }
|
102
|
-
.each { |k, v| @_attributes[k] = instance_eval(&v) }
|
103
|
-
@_attributes.select { |k, _| fields.include?(k) }
|
104
|
-
end
|
105
|
-
|
106
|
-
def relationships(fields, include)
|
107
|
-
@_relationships
|
108
|
-
.select { |k, _| fields.include?(k) }
|
109
|
-
.each_with_object({}) do |(k, v), h|
|
110
|
-
h[k] = v.as_jsonapi(include.include?(k))
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|