jsonapi-deserializable 0.1.1.beta2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +116 -0
- data/lib/jsonapi/deserializable.rb +3 -0
- data/lib/jsonapi/deserializable/document_validator.rb +200 -0
- data/lib/jsonapi/deserializable/exceptions.rb +7 -0
- data/lib/jsonapi/deserializable/relationship.rb +38 -0
- data/lib/jsonapi/deserializable/relationship_dsl.rb +31 -0
- data/lib/jsonapi/deserializable/resource.rb +44 -0
- data/lib/jsonapi/deserializable/resource/dsl.rb +77 -0
- data/lib/jsonapi/deserializable/resource_dsl.rb +74 -0
- data/lib/jsonapi/deserializable/validations.rb +101 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 93ceb39a5f3d032a99748170ac818e096e691d05
|
4
|
+
data.tar.gz: adfdd698f25de948e225c87087f850376d900f09
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 52f05a26b475cf2afa912ab7e3e207f4d925c32e58c5ba99edfc8a52685c5c536ecdc8d8e0e7e2d06d44916be0b1af383ba035339c87754824a0988cf93ce48c
|
7
|
+
data.tar.gz: aa7bfa1f75f1ec8bd00dc69025dffbc7127874b86f946dd1489798837727630df4a15a9410cb8047a26c195b6b6082e185bb3d9770cc586668d9b52f274c5769
|
data/README.md
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
# jsonapi-deserializable
|
2
|
+
Ruby gem for validating and deserializing [JSON API](http://jsonapi.org)
|
3
|
+
payloads into custom hashes.
|
4
|
+
Built upon the [jsonapi-validations](https://github.com/beauby/jsonapi/tree/master/validations)
|
5
|
+
gem.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
```ruby
|
9
|
+
# In Gemfile
|
10
|
+
gem 'jsonapi-deserializable'
|
11
|
+
```
|
12
|
+
then
|
13
|
+
```
|
14
|
+
$ bundle
|
15
|
+
```
|
16
|
+
or manually via
|
17
|
+
```
|
18
|
+
$ gem install jsonapi-deserializable
|
19
|
+
```
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
First, require the gem:
|
24
|
+
```ruby
|
25
|
+
require 'jsonapi/deserializable'
|
26
|
+
```
|
27
|
+
|
28
|
+
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
|
+
|
66
|
+
# `field` is the standard method for defining a key on the result hash.
|
67
|
+
field :username do
|
68
|
+
@document.data.attributes.name
|
69
|
+
end
|
70
|
+
|
71
|
+
field :post_ids do
|
72
|
+
@relationships.posts.data.map(&:id)
|
73
|
+
end
|
74
|
+
|
75
|
+
field :sponsor_id do
|
76
|
+
@relationships.sponsor.data && @relationships.sponsor.data.id
|
77
|
+
end
|
78
|
+
end
|
79
|
+
```
|
80
|
+
Finally, build your hash from the deserializable resource:
|
81
|
+
```ruby
|
82
|
+
payload = {
|
83
|
+
'data' => {
|
84
|
+
'id' => '1',
|
85
|
+
'type' => 'users',
|
86
|
+
'attributes' => {
|
87
|
+
'name' => 'Name',
|
88
|
+
'address' => 'Address'
|
89
|
+
},
|
90
|
+
'relationships' => {
|
91
|
+
'sponsor' => {
|
92
|
+
'data' => { 'type' => 'users', 'id' => '1337' }
|
93
|
+
},
|
94
|
+
'posts' => {
|
95
|
+
'data' => [
|
96
|
+
{ 'type' => 'posts', 'id' => '123' },
|
97
|
+
{ 'type' => 'posts', 'id' => '234' },
|
98
|
+
{ 'type' => 'posts', 'id' => '345' }
|
99
|
+
]
|
100
|
+
}
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
DeserializableUser.new(payload).to_h
|
106
|
+
# => {
|
107
|
+
# username: 'Name',
|
108
|
+
# address: 'Address',
|
109
|
+
# sponsor_id: '1337',
|
110
|
+
# post_ids: ['123', '234', '345']
|
111
|
+
# }
|
112
|
+
```
|
113
|
+
|
114
|
+
## License
|
115
|
+
|
116
|
+
jsonapi-deserializable is released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
@@ -0,0 +1,200 @@
|
|
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
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'jsonapi/deserializable/exceptions'
|
2
|
+
require 'jsonapi/deserializable/relationship_dsl'
|
3
|
+
|
4
|
+
module JSONAPI
|
5
|
+
module Deserializable
|
6
|
+
class Relationship
|
7
|
+
include RelationshipDSL
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_accessor :field_blocks, :validations
|
11
|
+
end
|
12
|
+
|
13
|
+
self.field_blocks = {}
|
14
|
+
self.validations = {}
|
15
|
+
|
16
|
+
def self.inherited(klass)
|
17
|
+
klass.field_blocks = field_blocks.dup
|
18
|
+
klass.validations = Marshal.load(Marshal.dump(validations))
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(payload)
|
22
|
+
JSONAPI.validate_relationship!(payload, validations)
|
23
|
+
@document = payload
|
24
|
+
@data = payload['data']
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_h
|
28
|
+
return nil if @data.nil?
|
29
|
+
return @_hash if @_hash
|
30
|
+
@_hash = {}
|
31
|
+
self.class.field_blocks.each do |key, block|
|
32
|
+
@_hash[key] = instance_eval(&block)
|
33
|
+
end
|
34
|
+
@_hash
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module JSONAPI
|
2
|
+
module Deserializable
|
3
|
+
module RelationshipDSL
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def has_one
|
10
|
+
validations[:kind] = :has_one
|
11
|
+
end
|
12
|
+
|
13
|
+
def has_many
|
14
|
+
validations[:kind] = :has_many
|
15
|
+
end
|
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
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'jsonapi/validations'
|
2
|
+
require 'jsonapi/deserializable/exceptions'
|
3
|
+
require 'jsonapi/deserializable/resource_dsl'
|
4
|
+
|
5
|
+
module JSONAPI
|
6
|
+
module Deserializable
|
7
|
+
class Resource
|
8
|
+
include ResourceDSL
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :field_blocks, :validations
|
12
|
+
end
|
13
|
+
|
14
|
+
self.field_blocks = {}
|
15
|
+
self.validations = {}
|
16
|
+
|
17
|
+
def self.inherited(klass)
|
18
|
+
super
|
19
|
+
klass.field_blocks = field_blocks.dup
|
20
|
+
klass.validations = Marshal.load(Marshal.dump(validations))
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(payload)
|
24
|
+
JSONAPI.validate_resource!(payload, self.class.validations)
|
25
|
+
@document = payload
|
26
|
+
@data = @document['data']
|
27
|
+
@attributes = @data['attributes']
|
28
|
+
@relationships = @data['relationships']
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_h
|
32
|
+
return @_hash if @_hash
|
33
|
+
|
34
|
+
@_hash = {}
|
35
|
+
@_hash[:_payload] = @document
|
36
|
+
self.class.field_blocks.map do |k, v|
|
37
|
+
@_hash[k] = instance_eval(&v)
|
38
|
+
end
|
39
|
+
|
40
|
+
@_hash
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module JSONAPI
|
2
|
+
module Deserializable
|
3
|
+
module ResourceDSL
|
4
|
+
attr_accessor :field_blocks, :validations
|
5
|
+
|
6
|
+
def required(&block)
|
7
|
+
add_validations!(FieldList.new(:required, &block).to_h)
|
8
|
+
end
|
9
|
+
|
10
|
+
def optional(&block)
|
11
|
+
add_validations!(FieldList.new(:optional, &block).to_h)
|
12
|
+
end
|
13
|
+
|
14
|
+
def id
|
15
|
+
field_blocks[:id] = proc { @data['id'] }
|
16
|
+
end
|
17
|
+
|
18
|
+
def field(key, &block)
|
19
|
+
field_blocks[key] = block
|
20
|
+
end
|
21
|
+
|
22
|
+
def attribute(key, opts = {})
|
23
|
+
hash_key = (opts[:key] || key).to_s
|
24
|
+
field_blocks[key] = proc { @attributes[hash_key] }
|
25
|
+
end
|
26
|
+
|
27
|
+
def has_many_ids(key, opts = {})
|
28
|
+
hash_key = (opts[:key] || key).to_s
|
29
|
+
field_blocks[key] = proc do
|
30
|
+
@relationships[hash_key]['data'].map { |ri| ri['id'] }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def has_many_types(key, opts = {})
|
35
|
+
hash_key = (opts[:key] || key).to_s
|
36
|
+
field_blocks[key] = proc do
|
37
|
+
@relationships[hash_key]['data'].map { |ri| ri['type'] }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def has_many_ids(key, opts = {})
|
42
|
+
hash_key = (opts[:key] || key).to_s
|
43
|
+
@relationships[hash_key]['data']['id']
|
44
|
+
end
|
45
|
+
|
46
|
+
def has_many_ids(key, opts = {})
|
47
|
+
hash_key = (opts[:key] || key).to_s
|
48
|
+
@relationships[hash_key]['data']['type']
|
49
|
+
end
|
50
|
+
|
51
|
+
self.field_blocks = {}
|
52
|
+
self.validations = {}
|
53
|
+
|
54
|
+
def self.inherited(klass)
|
55
|
+
super
|
56
|
+
klass.field_blocks = field_blocks.dup
|
57
|
+
klass.validations = Marshal.load(Marshal.dump(validations))
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def add_validations!(hash)
|
63
|
+
validations[:permitted] = hash[:permitted] if hash[:permitted]
|
64
|
+
validations[:required] = hash[:required] if hash[:required]
|
65
|
+
return unless hash[:types]
|
66
|
+
validations[:types] ||= {}
|
67
|
+
if hash[:types][:primary]
|
68
|
+
validations[:types][:primary] = hash[:types][:primary]
|
69
|
+
end
|
70
|
+
return unless hash[:types][:relationships]
|
71
|
+
validations[:types][:relationships] ||= {}
|
72
|
+
validations[:types][:relationships]
|
73
|
+
.merge!(hash[:types][:relationships])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'jsonapi/deserializable/validations'
|
2
|
+
|
3
|
+
module JSONAPI
|
4
|
+
module Deserializable
|
5
|
+
module ResourceDSL
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def required(&block)
|
12
|
+
add_validations!(Validations.new(:required, &block).to_h)
|
13
|
+
end
|
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'] }
|
25
|
+
end
|
26
|
+
|
27
|
+
def attribute(key, opts = {})
|
28
|
+
hash_key = (opts[:key] || key).to_s
|
29
|
+
field(key) { @attributes[hash_key] }
|
30
|
+
end
|
31
|
+
|
32
|
+
def has_many_ids(key, opts = {})
|
33
|
+
hash_key = (opts[:key] || key).to_s
|
34
|
+
field(key) do
|
35
|
+
@relationships[hash_key]['data'].map { |ri| ri['id'] }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def has_many_types(key, opts = {})
|
40
|
+
hash_key = (opts[:key] || key).to_s
|
41
|
+
field(key) do
|
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'] }
|
49
|
+
end
|
50
|
+
|
51
|
+
def has_one_type(key, opts = {})
|
52
|
+
hash_key = (opts[:key] || key).to_s
|
53
|
+
field(key) { @relationships[hash_key]['data']['type'] }
|
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])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,101 @@
|
|
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
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jsonapi-deserializable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1.beta2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Lucas Hosseini
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-02 00:00:00.000000000 Z
|
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
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.9'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.9'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.4'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.4'
|
55
|
+
description: DSL for validating incoming JSON API payloads and building custom objects
|
56
|
+
out of them.
|
57
|
+
email: lucas.hosseini@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- README.md
|
63
|
+
- lib/jsonapi/deserializable.rb
|
64
|
+
- lib/jsonapi/deserializable/document_validator.rb
|
65
|
+
- lib/jsonapi/deserializable/exceptions.rb
|
66
|
+
- lib/jsonapi/deserializable/relationship.rb
|
67
|
+
- lib/jsonapi/deserializable/relationship_dsl.rb
|
68
|
+
- lib/jsonapi/deserializable/resource.rb
|
69
|
+
- lib/jsonapi/deserializable/resource/dsl.rb
|
70
|
+
- lib/jsonapi/deserializable/resource_dsl.rb
|
71
|
+
- lib/jsonapi/deserializable/validations.rb
|
72
|
+
homepage: https://github.com/beauby/jsonapi-deserializable
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.3.1
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.5.1
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: Deserialization of JSONAPI payloads.
|
96
|
+
test_files: []
|