jsonapi-deserializable 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 +51 -62
- data/lib/jsonapi/deserializable.rb +0 -1
- data/lib/jsonapi/deserializable/relationship.rb +27 -14
- data/lib/jsonapi/deserializable/relationship_dsl.rb +6 -16
- data/lib/jsonapi/deserializable/resource.rb +82 -16
- data/lib/jsonapi/deserializable/resource_dsl.rb +17 -52
- metadata +2 -19
- data/lib/jsonapi/deserializable/document_validator.rb +0 -200
- data/lib/jsonapi/deserializable/exceptions.rb +0 -7
- data/lib/jsonapi/deserializable/validations.rb +0 -101
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3004871b4ea0f17caaeab2c1f000c632337c486
|
4
|
+
data.tar.gz: 4eb06414674e45c45fc3d276ffa966bc97313ae7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fdf14b9d40b30ea331a01b10212be3b5582785e45634ab7e49e2d81957a4408af6ce838d05dc27e9d86828a704cef1a2eb23e5331d2c13d4fa131829c6a4c03e
|
7
|
+
data.tar.gz: ee5342f0d5444e68773e504353000090dc71117fba3eee3a0af21399c32836085eab9f8076dd70a2cfe54385a85f9140c692007199f12b70ef60315ca76cad64
|
data/README.md
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
# jsonapi-deserializable
|
2
|
-
Ruby gem for
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
Ruby gem for deserializing [JSON API](http://jsonapi.org) payloads into custom
|
3
|
+
hashes.
|
4
|
+
|
5
|
+
## Status
|
6
|
+
|
7
|
+
[](https://badge.fury.io/rb/jsonapi-deserializable)
|
8
|
+
[](http://travis-ci.org/beauby/jsonapi-deserializable?branch=master)
|
6
9
|
|
7
10
|
## Installation
|
8
11
|
```ruby
|
@@ -26,54 +29,23 @@ require 'jsonapi/deserializable'
|
|
26
29
|
```
|
27
30
|
|
28
31
|
Then, define some resource/relationship classes:
|
29
|
-
```ruby
|
30
|
-
class DeserializableUser < JSONAPI::Deserializable::Resource
|
31
|
-
# List of required attributes / has_many/has_one relationships.
|
32
|
-
# This directive is not mandatory. If not declared, no field
|
33
|
-
# will be required.
|
34
|
-
required do
|
35
|
-
id # Optional, require an id for the primary resource.
|
36
|
-
|
37
|
-
type :users # Optional, force a type for the primary resource.
|
38
|
-
# or, still optional, force a set of allowed types for the primary resource:
|
39
|
-
types [:users, :superusers] # Optional,
|
40
|
-
|
41
|
-
attribute :name
|
42
|
-
has_one :sponsor
|
43
|
-
# or, optionally, spcecify a type for the relationship target:
|
44
|
-
has_one :sponsor, :users
|
45
|
-
end
|
46
|
-
|
47
|
-
# List of optional attributes / has_many/has_one relationships.
|
48
|
-
# This directive is not mandatory. If not declared, all fields
|
49
|
-
# will be allowed. If declared, all fields that are not within
|
50
|
-
# eitheroptional or required will be rejected.
|
51
|
-
optional do
|
52
|
-
attribute :address
|
53
|
-
has_many :posts
|
54
|
-
# or, optionally, specify a set of allowed types for the primary resource:
|
55
|
-
has_many :posts, [:posts, :blogs]
|
56
|
-
end
|
57
|
-
|
58
|
-
## The actual fields of the generated hash.
|
59
|
-
# `attribute` is a shorthand for `field(key) { @attributes.send(key) }`.
|
60
|
-
attribute :address
|
61
|
-
|
62
|
-
field :id do
|
63
|
-
@data.id
|
64
|
-
end
|
65
32
|
|
66
|
-
|
67
|
-
field :username do
|
68
|
-
@document.data.attributes.name
|
69
|
-
end
|
33
|
+
### Resources
|
70
34
|
|
71
|
-
|
72
|
-
|
35
|
+
```ruby
|
36
|
+
class DeserializableCreatePost < JSONAPI::Deserializable::Resource
|
37
|
+
type
|
38
|
+
attribute :title
|
39
|
+
attribute :date { |date| field date: DateTime.parse(date) }
|
40
|
+
has_one :author do |rel, id, type|
|
41
|
+
field author_id: id
|
42
|
+
field author_type: type
|
73
43
|
end
|
74
|
-
|
75
|
-
|
76
|
-
|
44
|
+
has_many :comments do |rel, ids, types|
|
45
|
+
field comment_ids: ids
|
46
|
+
field comment_types: types.map do |type|
|
47
|
+
camelize(singularize(type))
|
48
|
+
end
|
77
49
|
end
|
78
50
|
end
|
79
51
|
```
|
@@ -82,35 +54,52 @@ Finally, build your hash from the deserializable resource:
|
|
82
54
|
payload = {
|
83
55
|
'data' => {
|
84
56
|
'id' => '1',
|
85
|
-
'type' => '
|
57
|
+
'type' => 'posts',
|
86
58
|
'attributes' => {
|
87
|
-
'
|
88
|
-
'
|
59
|
+
'title' => 'Title',
|
60
|
+
'date' => '2016-01-10 02:30:00'
|
89
61
|
},
|
90
62
|
'relationships' => {
|
91
|
-
'
|
63
|
+
'author' => {
|
92
64
|
'data' => { 'type' => 'users', 'id' => '1337' }
|
93
65
|
},
|
94
|
-
'
|
66
|
+
'comments' => {
|
95
67
|
'data' => [
|
96
|
-
{ 'type' => '
|
97
|
-
{ 'type' => '
|
98
|
-
{ 'type' => '
|
68
|
+
{ 'type' => 'comments', 'id' => '123' },
|
69
|
+
{ 'type' => 'comments', 'id' => '234' },
|
70
|
+
{ 'type' => 'comments', 'id' => '345' }
|
99
71
|
]
|
100
72
|
}
|
101
73
|
}
|
102
74
|
}
|
103
75
|
}
|
104
76
|
|
105
|
-
|
77
|
+
DeserializableCreateUser.(payload)
|
106
78
|
# => {
|
107
|
-
#
|
108
|
-
#
|
109
|
-
#
|
110
|
-
#
|
79
|
+
# id: '1',
|
80
|
+
# title: 'Title',
|
81
|
+
# date: #<DateTime: 2016-01-10T02:30:00+00:00 ((2457398j,9000s,0n),+0s,2299161j)>,
|
82
|
+
# author_id: '1337',
|
83
|
+
# author_type: 'users',
|
84
|
+
# comment_ids: ['123', '234', '345']
|
85
|
+
# comment_types: ['Comment', 'Comment', 'Comment']
|
111
86
|
# }
|
112
87
|
```
|
113
88
|
|
89
|
+
### Relationships
|
90
|
+
|
91
|
+
```
|
92
|
+
class DeserializablePostComments < JSONAPI::Deserializable::Relationship
|
93
|
+
has_many do |rel, ids, types|
|
94
|
+
field comment_ids: ids
|
95
|
+
field comment_types: types.map do |ri|
|
96
|
+
camelize(singularize(type))
|
97
|
+
end
|
98
|
+
field comments_meta: rel['meta']
|
99
|
+
end
|
100
|
+
end
|
101
|
+
```
|
102
|
+
|
114
103
|
## License
|
115
104
|
|
116
105
|
jsonapi-deserializable is released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'jsonapi/deserializable/exceptions'
|
2
1
|
require 'jsonapi/deserializable/relationship_dsl'
|
3
2
|
|
4
3
|
module JSONAPI
|
@@ -7,31 +6,45 @@ module JSONAPI
|
|
7
6
|
include RelationshipDSL
|
8
7
|
|
9
8
|
class << self
|
10
|
-
attr_accessor :
|
9
|
+
attr_accessor :has_one_block, :has_many_block
|
11
10
|
end
|
12
11
|
|
13
|
-
self.field_blocks = {}
|
14
|
-
self.validations = {}
|
15
|
-
|
16
12
|
def self.inherited(klass)
|
17
|
-
klass.
|
18
|
-
klass.
|
13
|
+
klass.has_one_block = has_one_block
|
14
|
+
klass.has_many_block = has_many_block
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.call(payload)
|
18
|
+
new(payload).to_h
|
19
19
|
end
|
20
20
|
|
21
21
|
def initialize(payload)
|
22
|
-
JSONAPI.validate_relationship!(payload, validations)
|
23
22
|
@document = payload
|
24
23
|
@data = payload['data']
|
24
|
+
deserialize!
|
25
25
|
end
|
26
26
|
|
27
27
|
def to_h
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
@hash
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def deserialize!
|
34
|
+
@hash = {}
|
35
|
+
if @data.is_a?(Array)
|
36
|
+
ids = @data.map { |ri| ri['id'] }
|
37
|
+
types = @data.map { |ri| ri['type'] }
|
38
|
+
instance_exec(@document, ids, types, &self.class.has_many_block)
|
39
|
+
else
|
40
|
+
id = @data && @data['id']
|
41
|
+
type = @data && @data['type']
|
42
|
+
instance_exec(@document, id, type, &self.class.has_one_block)
|
33
43
|
end
|
34
|
-
|
44
|
+
end
|
45
|
+
|
46
|
+
def field(hash)
|
47
|
+
@hash.merge!(hash)
|
35
48
|
end
|
36
49
|
end
|
37
50
|
end
|
@@ -6,24 +6,14 @@ module JSONAPI
|
|
6
6
|
end
|
7
7
|
|
8
8
|
module ClassMethods
|
9
|
-
def has_one
|
10
|
-
|
9
|
+
def has_one(&block)
|
10
|
+
block ||= proc { |rel| field key.to_sym => rel }
|
11
|
+
self.has_one_block = block
|
11
12
|
end
|
12
13
|
|
13
|
-
def has_many
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
def type(value)
|
18
|
-
(validations[:types] ||= []) << value
|
19
|
-
end
|
20
|
-
|
21
|
-
def types(values)
|
22
|
-
(validations[:types] ||= []).concat(values)
|
23
|
-
end
|
24
|
-
|
25
|
-
def field(key, &block)
|
26
|
-
field_blocks[key] = block
|
14
|
+
def has_many(&block)
|
15
|
+
block ||= proc { |rel| field key.to_sym => rel }
|
16
|
+
self.has_many_block = block
|
27
17
|
end
|
28
18
|
end
|
29
19
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'jsonapi/validations'
|
2
|
-
require 'jsonapi/deserializable/exceptions'
|
3
1
|
require 'jsonapi/deserializable/resource_dsl'
|
4
2
|
|
5
3
|
module JSONAPI
|
@@ -8,36 +6,104 @@ module JSONAPI
|
|
8
6
|
include ResourceDSL
|
9
7
|
|
10
8
|
class << self
|
11
|
-
attr_accessor :
|
9
|
+
attr_accessor :type_block, :id_block
|
10
|
+
attr_accessor :attr_blocks
|
11
|
+
attr_accessor :has_one_rel_blocks, :has_many_rel_blocks
|
12
12
|
end
|
13
13
|
|
14
|
-
self.
|
15
|
-
self.
|
14
|
+
self.attr_blocks = {}
|
15
|
+
self.has_one_rel_blocks = {}
|
16
|
+
self.has_many_rel_blocks = {}
|
16
17
|
|
17
18
|
def self.inherited(klass)
|
18
19
|
super
|
19
|
-
klass.
|
20
|
-
klass.
|
20
|
+
klass.type_block = type_block
|
21
|
+
klass.id_block = id_block
|
22
|
+
klass.attr_blocks = attr_blocks.dup
|
23
|
+
klass.has_one_rel_blocks = has_one_rel_blocks.dup
|
24
|
+
klass.has_many_rel_blocks = has_many_rel_blocks.dup
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.call(payload)
|
28
|
+
new(payload).to_h
|
21
29
|
end
|
22
30
|
|
23
31
|
def initialize(payload)
|
24
|
-
JSONAPI.validate_resource!(payload, self.class.validations)
|
25
32
|
@document = payload
|
26
33
|
@data = @document['data']
|
27
|
-
@
|
28
|
-
@
|
34
|
+
@type = @data['type']
|
35
|
+
@id = @data['id']
|
36
|
+
@attributes = @data['attributes'] || {}
|
37
|
+
@relationships = @data['relationships'] || {}
|
38
|
+
deserialize!
|
29
39
|
end
|
30
40
|
|
31
41
|
def to_h
|
32
|
-
|
42
|
+
@hash
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def deserialize!
|
48
|
+
@hash = {}
|
49
|
+
deserialize_type!
|
50
|
+
deserialize_id!
|
51
|
+
deserialize_attrs!
|
52
|
+
deserialize_rels!
|
53
|
+
end
|
33
54
|
|
34
|
-
|
35
|
-
@
|
36
|
-
self.class.
|
37
|
-
|
55
|
+
def deserialize_type!
|
56
|
+
return unless @type && self.class.type_block
|
57
|
+
instance_exec(@type, &self.class.type_block)
|
58
|
+
end
|
59
|
+
|
60
|
+
def deserialize_id!
|
61
|
+
return unless @id && self.class.id_block
|
62
|
+
instance_exec(@id, &self.class.id_block)
|
63
|
+
end
|
64
|
+
|
65
|
+
def deserialize_attrs!
|
66
|
+
self.class.attr_blocks.each do |attr, block|
|
67
|
+
next unless @attributes.key?(attr)
|
68
|
+
instance_exec(@attributes[attr], &block)
|
38
69
|
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def deserialize_rels!
|
73
|
+
deserialize_has_one_rels!
|
74
|
+
deserialize_has_many_rels!
|
75
|
+
end
|
76
|
+
|
77
|
+
def deserialize_has_one_rels!
|
78
|
+
self.class.has_one_rel_blocks.each do |key, block|
|
79
|
+
rel = @relationships[key]
|
80
|
+
next unless rel && (rel['data'].nil? || rel['data'].is_a?(Hash))
|
81
|
+
deserialize_has_one_rel!(rel, &block)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def deserialize_has_one_rel!(rel, &block)
|
86
|
+
id = rel['data'] && rel['data']['id']
|
87
|
+
type = rel['data'] && rel['data']['type']
|
88
|
+
instance_exec(rel, id, type, &block)
|
89
|
+
end
|
90
|
+
|
91
|
+
def deserialize_has_many_rels!
|
92
|
+
self.class.has_many_rel_blocks.each do |key, block|
|
93
|
+
rel = @relationships[key]
|
94
|
+
next unless rel && rel['data'].is_a?(Array)
|
95
|
+
deserialize_has_many_rel!(rel, &block)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def deserialize_has_many_rel!(rel, &block)
|
100
|
+
ids = rel['data'].map { |ri| ri['id'] }
|
101
|
+
types = rel['data'].map { |ri| ri['type'] }
|
102
|
+
instance_exec(rel, ids, types, &block)
|
103
|
+
end
|
39
104
|
|
40
|
-
|
105
|
+
def field(hash)
|
106
|
+
@hash.merge!(hash)
|
41
107
|
end
|
42
108
|
end
|
43
109
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'jsonapi/deserializable/validations'
|
2
|
-
|
3
1
|
module JSONAPI
|
4
2
|
module Deserializable
|
5
3
|
module ResourceDSL
|
@@ -8,65 +6,32 @@ module JSONAPI
|
|
8
6
|
end
|
9
7
|
|
10
8
|
module ClassMethods
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
def optional(&block)
|
16
|
-
add_validations!(Validations.new(:optional, &block).to_h)
|
17
|
-
end
|
18
|
-
|
19
|
-
def field(key, &block)
|
20
|
-
field_blocks[key] = block
|
21
|
-
end
|
22
|
-
|
23
|
-
def id
|
24
|
-
field(:id) { @data['id'] }
|
9
|
+
def type(&block)
|
10
|
+
block ||= proc { |type| field type: type }
|
11
|
+
self.type_block = block
|
25
12
|
end
|
26
13
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
14
|
+
def id(&block)
|
15
|
+
block ||= proc { |id| field id: id }
|
16
|
+
self.id_block = block
|
30
17
|
end
|
31
18
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
19
|
+
def attribute(key, options = {}, &block)
|
20
|
+
unless block
|
21
|
+
options[:key] ||= key.to_sym
|
22
|
+
block = proc { |attr| field key => attr }
|
36
23
|
end
|
24
|
+
attr_blocks[key.to_s] = block
|
37
25
|
end
|
38
26
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
@relationships[hash_key]['data'].map { |ri| ri['type'] }
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def has_one_id(key, opts = {})
|
47
|
-
hash_key = (opts[:key] || key).to_s
|
48
|
-
field(key) { @relationships[hash_key]['data']['id'] }
|
27
|
+
def has_one(key, &block)
|
28
|
+
block ||= proc { |rel| field key.to_sym => rel }
|
29
|
+
has_one_rel_blocks[key.to_s] = block
|
49
30
|
end
|
50
31
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
private
|
57
|
-
|
58
|
-
def add_validations!(hash)
|
59
|
-
validations[:permitted] = hash[:permitted] if hash[:permitted]
|
60
|
-
validations[:required] = hash[:required] if hash[:required]
|
61
|
-
return unless hash[:types]
|
62
|
-
validations[:types] ||= {}
|
63
|
-
if hash[:types][:primary]
|
64
|
-
validations[:types][:primary] = hash[:types][:primary]
|
65
|
-
end
|
66
|
-
return unless hash[:types][:relationships]
|
67
|
-
validations[:types][:relationships] ||= {}
|
68
|
-
validations[:types][:relationships]
|
69
|
-
.merge!(hash[:types][:relationships])
|
32
|
+
def has_many(key, &block)
|
33
|
+
block ||= proc { |rel| field key.to_sym => rel }
|
34
|
+
has_many_rel_blocks[key.to_s] = block
|
70
35
|
end
|
71
36
|
end
|
72
37
|
end
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonapi-deserializable
|
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-10-
|
11
|
+
date: 2016-10-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: jsonapi-validations
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0.1'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0.1'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: rake
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -61,14 +47,11 @@ extra_rdoc_files: []
|
|
61
47
|
files:
|
62
48
|
- README.md
|
63
49
|
- lib/jsonapi/deserializable.rb
|
64
|
-
- lib/jsonapi/deserializable/document_validator.rb
|
65
|
-
- lib/jsonapi/deserializable/exceptions.rb
|
66
50
|
- lib/jsonapi/deserializable/relationship.rb
|
67
51
|
- lib/jsonapi/deserializable/relationship_dsl.rb
|
68
52
|
- lib/jsonapi/deserializable/resource.rb
|
69
53
|
- lib/jsonapi/deserializable/resource/dsl.rb
|
70
54
|
- lib/jsonapi/deserializable/resource_dsl.rb
|
71
|
-
- lib/jsonapi/deserializable/validations.rb
|
72
55
|
homepage: https://github.com/beauby/jsonapi-deserializable
|
73
56
|
licenses:
|
74
57
|
- MIT
|
@@ -1,200 +0,0 @@
|
|
1
|
-
require 'jsonapi/deserializable/field_list'
|
2
|
-
|
3
|
-
module JSONAPI
|
4
|
-
module Deserializable
|
5
|
-
class DocumentValidator
|
6
|
-
def self.validate!(payload, required_list, optional_list)
|
7
|
-
new(payload, required_list, optional_list).validate!
|
8
|
-
end
|
9
|
-
|
10
|
-
def initialize(document, required, optional)
|
11
|
-
whitelist = {
|
12
|
-
id: :optional,
|
13
|
-
types: [],
|
14
|
-
attributes: {
|
15
|
-
foo: :required,
|
16
|
-
bar: :optional
|
17
|
-
},
|
18
|
-
relationships: {
|
19
|
-
foobar: :required,
|
20
|
-
barfoo: {
|
21
|
-
required: true,
|
22
|
-
types: [:baz],
|
23
|
-
arity: :to_many
|
24
|
-
}
|
25
|
-
}
|
26
|
-
}
|
27
|
-
|
28
|
-
|
29
|
-
arities = {
|
30
|
-
foobar: :to_many,
|
31
|
-
barfoo: :to_one
|
32
|
-
}
|
33
|
-
types = {
|
34
|
-
primary: [],
|
35
|
-
relationships: {
|
36
|
-
foobar: [:baz],
|
37
|
-
barfoo: [:bazbaz]
|
38
|
-
}
|
39
|
-
}
|
40
|
-
required: true,
|
41
|
-
types: [:baz],
|
42
|
-
arity: :to_many
|
43
|
-
}
|
44
|
-
}
|
45
|
-
}
|
46
|
-
@document = document
|
47
|
-
@required = required || {}
|
48
|
-
@optional = optional
|
49
|
-
@fields = {}
|
50
|
-
@fields[:primary_id] = true if @required[:primary_id]
|
51
|
-
primary_types = @required[:primary_types]
|
52
|
-
@fields[:primary_types] = primary_types if primary_types
|
53
|
-
@fields[:attributes] = @required[:attributes].dup
|
54
|
-
@fields[:relationships] = @required[:relationships].dup
|
55
|
-
if @optional
|
56
|
-
@fields[:primary_id] = true if @optional[:primary_id]
|
57
|
-
primary_types = @optional[:primary_types]
|
58
|
-
@fields[:primary_types] = primary_types if primary_types
|
59
|
-
@fields[:attributes].merge
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def validate!
|
64
|
-
raise INVALID_DOCUMENT unless @document.data
|
65
|
-
@data = @document.data
|
66
|
-
if @data.respond_to?(:each)
|
67
|
-
raise INVALID_DOCUMENT, 'The request MUST include a single resource' \
|
68
|
-
' object as primary data'
|
69
|
-
end
|
70
|
-
@attributes = @data.attributes
|
71
|
-
@relationships = @data.relationships
|
72
|
-
validate_primary!
|
73
|
-
validate_fields!
|
74
|
-
end
|
75
|
-
|
76
|
-
def validate_primary!
|
77
|
-
validate_id!
|
78
|
-
validate_type!
|
79
|
-
end
|
80
|
-
|
81
|
-
def id_required?
|
82
|
-
@required[:primary_id]
|
83
|
-
end
|
84
|
-
|
85
|
-
def id_permitted?
|
86
|
-
@fields[:primary_id]
|
87
|
-
end
|
88
|
-
|
89
|
-
def validate_id!
|
90
|
-
if @data.id.nil?
|
91
|
-
raise INVALID_DOCUMENT,
|
92
|
-
'Expected id for primary resource' if id_required?
|
93
|
-
else
|
94
|
-
raise INVALID_DOCUMENT,
|
95
|
-
'Unexpected id for primary resource' unless id_permitted?
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def type_valid?
|
100
|
-
return false if @required &&
|
101
|
-
@required[:primary_types] &&
|
102
|
-
!@required[:primary_types].include?(@data.type.to_sym)
|
103
|
-
return false if @optional &&
|
104
|
-
@optional[:primary_types] &&
|
105
|
-
!@optional[:primary_types].include?(@data.type.to_sym)
|
106
|
-
true
|
107
|
-
end
|
108
|
-
|
109
|
-
def validate_type!
|
110
|
-
return if type_valid?
|
111
|
-
raise INVALID_DOCUMENT,
|
112
|
-
"Unexpected type #{@data.type} for primary resource"
|
113
|
-
end
|
114
|
-
|
115
|
-
def validate_fields!
|
116
|
-
validate_attributes!
|
117
|
-
validate_relationships!
|
118
|
-
end
|
119
|
-
|
120
|
-
def attr_permitted?(attr_key)
|
121
|
-
return true unless @optional
|
122
|
-
return true if @optional[:attributes].include?(attr_key.to_sym)
|
123
|
-
@required && @required[:attributes].include?(attr_key.to_sym)
|
124
|
-
end
|
125
|
-
|
126
|
-
def validate_attributes!
|
127
|
-
@attributes.keys.each do |attr_key|
|
128
|
-
next if attr_permitted?(attr_key)
|
129
|
-
raise INVALID_DOCUMENT, "Unexpected attribute #{attr_key}"
|
130
|
-
end
|
131
|
-
return unless @required
|
132
|
-
@required[:attributes].each do |attr_key|
|
133
|
-
next if @attributes.defined?(attr_key)
|
134
|
-
raise INVALID_DOCUMENT, "Expected attribute #{attr_key}"
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
def rel_permitted?(rel_key)
|
139
|
-
return true unless @optional
|
140
|
-
return true if @optional[:relationships].key?(rel_key.to_sym)
|
141
|
-
@required && @required[:relationships].key?(rel_key.to_sym)
|
142
|
-
end
|
143
|
-
|
144
|
-
def validate_relationships!
|
145
|
-
@relationships.keys.each do |rel_key|
|
146
|
-
next if rel_permitted?(rel_key)
|
147
|
-
raise INVALID_DOCUMENT, "Unexpected relationship #{rel_key}"
|
148
|
-
end
|
149
|
-
return unless @required
|
150
|
-
@required[:relationships].keys.each do |rel_key|
|
151
|
-
next if @relationships.defined?(rel_key)
|
152
|
-
raise INVALID_DOCUMENT, "Expected relationship #{rel_key}"
|
153
|
-
end
|
154
|
-
validate_relationship_types!
|
155
|
-
end
|
156
|
-
|
157
|
-
def validate_relationship_types!
|
158
|
-
rels = {}
|
159
|
-
rels.merge!(@required.relationships) if @required
|
160
|
-
rels.merge(@optional.relationships) if @optional
|
161
|
-
rels.each do |key, hash|
|
162
|
-
rel = @relationships[key.to_s]
|
163
|
-
unless rel.data
|
164
|
-
raise INVALID_DOCUMENT, "Expected data for relationship #{key}"
|
165
|
-
end
|
166
|
-
|
167
|
-
if hash[:arity] == :to_one
|
168
|
-
validate_to_one_relationship_type!(rel, hash[:types])
|
169
|
-
else
|
170
|
-
validate_to_many_relationship_type!(rel, hash[:types])
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
def validate_to_one_relationship_type!(rel, types)
|
176
|
-
if rel.collection?
|
177
|
-
raise INVALID_DOCUMENT,
|
178
|
-
"Expected relationship #{key} to be has_one"
|
179
|
-
end
|
180
|
-
return unless types && !types.include?(rel.data.type.to_sym)
|
181
|
-
raise INVALID_DOCUMENT, "Unexpected type: #{rel.data.type} for " \
|
182
|
-
"relationship #{key}"
|
183
|
-
end
|
184
|
-
|
185
|
-
def validate_to_many_relationship_type!(rel, types)
|
186
|
-
unless rel.collection?
|
187
|
-
raise INVALID_DOCUMENT,
|
188
|
-
"Expected relationship #{key} to be has_many"
|
189
|
-
end
|
190
|
-
return unless types
|
191
|
-
rel.data.each do |ri|
|
192
|
-
unless types.include?(ri.type.to_sym)
|
193
|
-
raise INVALID_DOCUMENT, "Unexpected type: #{ri.type} for " \
|
194
|
-
"relationship #{key}"
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
199
|
-
end
|
200
|
-
end
|
@@ -1,101 +0,0 @@
|
|
1
|
-
module JSONAPI
|
2
|
-
module Deserializable
|
3
|
-
class Validations
|
4
|
-
def initialize(validation_type, &block)
|
5
|
-
@validation_type = validation_type
|
6
|
-
@hash = {
|
7
|
-
@validation_type => {
|
8
|
-
attributes: [],
|
9
|
-
relationships: []
|
10
|
-
},
|
11
|
-
types: {
|
12
|
-
relationships: {}
|
13
|
-
}
|
14
|
-
}
|
15
|
-
instance_eval(&block)
|
16
|
-
end
|
17
|
-
|
18
|
-
def to_h
|
19
|
-
@hash
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
# Define whether the +id+ of the primary resource should be part of
|
25
|
-
# this list.
|
26
|
-
def id
|
27
|
-
validations_hash[:id] = true
|
28
|
-
end
|
29
|
-
|
30
|
-
# Define the allowed type for the primary resource.
|
31
|
-
# @param [Symbol] value The value of the type.
|
32
|
-
def type(value)
|
33
|
-
types_hash[:primary] = Array(value)
|
34
|
-
end
|
35
|
-
|
36
|
-
# Define the allowed type for the primary resource.
|
37
|
-
# @param [Array<Symbol>] values List of allowed values of the type.
|
38
|
-
def types(values)
|
39
|
-
types_hash[:primary] = values
|
40
|
-
end
|
41
|
-
|
42
|
-
# Define an attribute with given key.
|
43
|
-
# @param [Symbol] key The key of the attribute in the payload.
|
44
|
-
def attribute(key)
|
45
|
-
validations_hash[:attributes] << key
|
46
|
-
end
|
47
|
-
|
48
|
-
# TODO(beauby): Decide whether type: 'users' / types: [...] is better.
|
49
|
-
#
|
50
|
-
# @overload has_one(key)
|
51
|
-
# Define a has_one relationship with given key.
|
52
|
-
# @param [Symbol] key The key of the relationship in the payload.
|
53
|
-
#
|
54
|
-
# @overload has_one(key, type)
|
55
|
-
# Define a has_one relationship with given key.
|
56
|
-
# @param [Symbol] key The key of the relationship in the payload.
|
57
|
-
# @param [Symbol] type The expected type of the relationship value.
|
58
|
-
#
|
59
|
-
# @overload has_one(key, types)
|
60
|
-
# Define a has_one relationship with given key.
|
61
|
-
# @param [Symbol] key The key of the relationship in the payload.
|
62
|
-
# @param [Array<Symbol>] type List of acceptable types for the
|
63
|
-
# relationship value.
|
64
|
-
def has_many(key, types = nil)
|
65
|
-
validations_hash[:relationships] << key
|
66
|
-
types_hash[:relationships][key] = { kind: :has_many }
|
67
|
-
return unless types
|
68
|
-
types_hash[:relationships][key][:types] = Array(types)
|
69
|
-
end
|
70
|
-
|
71
|
-
# @overload has_one(key)
|
72
|
-
# Define a has_one relationship with given key.
|
73
|
-
# @param [Symbol] key The key of the relationship in the payload.
|
74
|
-
#
|
75
|
-
# @overload has_one(key, type)
|
76
|
-
# Define a has_one relationship with given key.
|
77
|
-
# @param [Symbol] key The key of the relationship in the payload.
|
78
|
-
# @param [Symbol] type The expected type of the relationship value.
|
79
|
-
#
|
80
|
-
# @overload has_one(key, types)
|
81
|
-
# Define a has_one relationship with given key.
|
82
|
-
# @param [Symbol] key The key of the relationship in the payload.
|
83
|
-
# @param [Array<Symbol>] type List of acceptable types for the
|
84
|
-
# relationship value.
|
85
|
-
def has_one(key, types = nil)
|
86
|
-
validations_hash[:relationships] << key
|
87
|
-
types_hash[:relationships][key] = { kind: :has_one }
|
88
|
-
return unless types
|
89
|
-
types_hash[:relationships][key][:types] = Array(types)
|
90
|
-
end
|
91
|
-
|
92
|
-
def validations_hash
|
93
|
-
@hash[@validation_type]
|
94
|
-
end
|
95
|
-
|
96
|
-
def types_hash
|
97
|
-
@hash[:types]
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|