roar-jsonapi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,91 @@
1
+ # encoding: utf-8
2
+
3
+ require 'test_helper'
4
+ require 'roar/json/json_api'
5
+ require 'json'
6
+ require 'jsonapi/representer'
7
+
8
+ class MemberNameTest < MiniTest::Spec
9
+ MemberName = Roar::JSON::JSONAPI::MemberName
10
+
11
+ # http://jsonapi.org/format/#document-member-names-reserved-characters
12
+ UNICODE_RESERVED_CHARACTERS = [
13
+ "\u002B",
14
+ "\u002C",
15
+ "\u002E",
16
+ "\u005B",
17
+ "\u005D",
18
+ "\u0021",
19
+ "\u0022",
20
+ "\u0023",
21
+ "\u0024",
22
+ "\u0025",
23
+ "\u0026",
24
+ "\u0027",
25
+ "\u0028",
26
+ "\u0029",
27
+ "\u002A",
28
+ "\u002F",
29
+ "\u003A",
30
+ "\u003B",
31
+ "\u003C",
32
+ "\u003D",
33
+ "\u003E",
34
+ "\u003F",
35
+ "\u0040",
36
+ "\u005C",
37
+ "\u005E",
38
+ "\u0060",
39
+ "\u007B",
40
+ "\u007C",
41
+ "\u007D",
42
+ "\u007E"
43
+ ].freeze
44
+
45
+ describe 'strict (default)' do
46
+ it 'permits alphanumeric ASCII characters, hyphens' do
47
+ MemberName.('99 Luftballons').must_equal '99luftballons'
48
+ MemberName.('Artist').must_equal 'artist'
49
+ MemberName.('Актер').must_equal ''
50
+ MemberName.('おまかせ').must_equal ''
51
+ MemberName.('auf-der-bühne').must_equal 'auf-der-bhne'
52
+ MemberName.('nouvelle_interprétation').must_equal 'nouvelle-interprtation'
53
+ end
54
+
55
+ it 'does not permit any reserved characters' do
56
+ MemberName.(UNICODE_RESERVED_CHARACTERS.join).must_equal ''
57
+ end
58
+
59
+ it 'hyphenates underscored words' do
60
+ MemberName.('playtime_report').must_equal 'playtime-report'
61
+ end
62
+ end
63
+
64
+ describe 'non-strict' do
65
+ it 'permits alphanumeric unicode characters, hyphens, underscores and spaces' do
66
+ MemberName.('99 Luftballons', strict: false).must_equal '99 Luftballons'
67
+ MemberName.('Artist', strict: false).must_equal 'Artist'
68
+ MemberName.('Актер', strict: false).must_equal 'Актер'
69
+ MemberName.('おまかせ', strict: false).must_equal 'おまかせ'
70
+ MemberName.('auf-der-bühne', strict: false).must_equal 'auf-der-bühne'
71
+ MemberName.('nouvelle_interprétation', strict: false).must_equal 'nouvelle_interprétation'
72
+ end
73
+
74
+ it 'does not permit any reserved characters' do
75
+ MemberName.(UNICODE_RESERVED_CHARACTERS.join, strict: false).must_equal ''
76
+ end
77
+
78
+ it 'does not permit hyphens, underscores or spaces at beginning or end' do
79
+ MemberName.(' 99 Luftballons ', strict: false).must_equal '99 Luftballons'
80
+ MemberName.('-Artist_', strict: false).must_equal 'Artist'
81
+ MemberName.('_Актер', strict: false).must_equal 'Актер'
82
+ MemberName.(' おまかせ', strict: false).must_equal 'おまかせ'
83
+ MemberName.('-auf-der-bühne', strict: false).must_equal 'auf-der-bühne'
84
+ MemberName.('nouvelle_interprétation_', strict: false).must_equal 'nouvelle_interprétation'
85
+ end
86
+
87
+ it 'preserves underscored words' do
88
+ MemberName.('playtime_report', strict: false).must_equal 'playtime_report'
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,78 @@
1
+ require 'test_helper'
2
+ require 'roar/json/json_api'
3
+ require 'json'
4
+
5
+ class JsonapiPostTest < MiniTest::Spec
6
+ describe 'Parse' do
7
+ let(:post_article) {
8
+ %({
9
+ "data": {
10
+ "type": "articles",
11
+ "attributes": {
12
+ "title": "Ember Hamster"
13
+ },
14
+ "relationships": {
15
+ "author": {
16
+ "data": {
17
+ "type": "people",
18
+ "id": "9",
19
+ "name": "Celsito"
20
+ }
21
+ },
22
+ "comments": {
23
+ "data": [{
24
+ "type": "comment",
25
+ "id": "2"
26
+ }, {
27
+ "type": "comment",
28
+ "id": "3"
29
+ }]
30
+ }
31
+ }
32
+ }
33
+ })
34
+ }
35
+
36
+ subject { ArticleDecorator.new(Article.new(nil, nil, nil, nil, [])).from_json(post_article) }
37
+
38
+ it do
39
+ subject.title.must_equal 'Ember Hamster'
40
+ subject.author.id.must_equal '9'
41
+ subject.author.email.must_equal '9@nine.to'
42
+ # subject.author.name.must_be_nil
43
+
44
+ subject.comments.must_equal [Comment.new('2'), Comment.new('3')]
45
+ end
46
+ end
47
+
48
+ describe 'Parse Simple' do
49
+ let(:post_article) {
50
+ %({
51
+ "data": {
52
+ "type": "articles",
53
+ "attributes": {
54
+ "title": "Ember Hamster"
55
+ }
56
+ }
57
+ })
58
+ }
59
+
60
+ subject { ArticleDecorator.new(Article.new(nil, nil, nil, nil, [])).from_json(post_article) }
61
+
62
+ it do
63
+ subject.title.must_equal 'Ember Hamster'
64
+ end
65
+ end
66
+
67
+ describe 'Parse Badly Formed Document' do
68
+ let(:post_article) {
69
+ %({"title":"Ember Hamster"})
70
+ }
71
+
72
+ subject { ArticleDecorator.new(Article.new(nil, nil, nil, nil, [])).from_json(post_article) }
73
+
74
+ it do
75
+ subject.title.must_be_nil
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,281 @@
1
+ require 'test_helper'
2
+ require 'roar/json/json_api'
3
+ require 'json'
4
+
5
+ class JsonapiRenderTest < MiniTest::Spec
6
+ let(:article) { Article.new(1, 'Health walk', Author.new(2), Author.new('editor:1'), [Comment.new('comment:1', 'Ice and Snow'), Comment.new('comment:2', 'Red Stripe Skank')]) }
7
+ let(:decorator) { ArticleDecorator.new(article) }
8
+
9
+ it 'renders full document' do
10
+ json = decorator.to_json
11
+ json.must_equal_json(%({
12
+ "data": {
13
+ "id": "1",
14
+ "relationships": {
15
+ "author": {
16
+ "data": {
17
+ "id": "2",
18
+ "type": "authors"
19
+ },
20
+ "links": {
21
+ "self": "/articles/1/relationships/author",
22
+ "related": "/articles/1/author"
23
+ }
24
+ },
25
+ "editor": {
26
+ "data": {
27
+ "id": "editor:1",
28
+ "type": "editors"
29
+ },
30
+ "meta": {
31
+ "peer-reviewed": false
32
+ }
33
+ },
34
+ "comments": {
35
+ "data": [{
36
+ "id": "comment:1",
37
+ "type": "comments"
38
+ }, {
39
+ "id": "comment:2",
40
+ "type": "comments"
41
+ }],
42
+ "links": {
43
+ "self": "/articles/1/relationships/comments",
44
+ "related": "/articles/1/comments"
45
+ },
46
+ "meta": {
47
+ "comment-count": 5
48
+ }
49
+ }
50
+ },
51
+ "attributes": {
52
+ "title": "Health walk"
53
+ },
54
+ "type": "articles",
55
+ "links": {
56
+ "self": "http://Article/1"
57
+ }
58
+ },
59
+ "included": [{
60
+ "id": "2",
61
+ "type": "authors",
62
+ "links": {
63
+ "self": "http://authors/2"
64
+ }
65
+ }, {
66
+ "id": "editor:1",
67
+ "type": "editors"
68
+ }, {
69
+ "id": "comment:1",
70
+ "attributes": {
71
+ "body": "Ice and Snow"
72
+ },
73
+ "type": "comments",
74
+ "links": {
75
+ "self": "http://comments/comment:1"
76
+ }
77
+ }, {
78
+ "id": "comment:2",
79
+ "attributes": {
80
+ "body": "Red Stripe Skank"
81
+ },
82
+ "type": "comments",
83
+ "links": {
84
+ "self": "http://comments/comment:2"
85
+ }
86
+ }],
87
+ "meta": {
88
+ "reviewers": ["Christian Bernstein"],
89
+ "reviewer-initials": "C.B."
90
+ }
91
+ }))
92
+ end
93
+
94
+ it 'included: false suppresses compound docs' do
95
+ json = decorator.to_json(included: false)
96
+ json.must_equal_json(%({
97
+ "data": {
98
+ "id": "1",
99
+ "relationships": {
100
+ "author": {
101
+ "data": {
102
+ "id": "2",
103
+ "type": "authors"
104
+ },
105
+ "links": {
106
+ "self": "/articles/1/relationships/author",
107
+ "related": "/articles/1/author"
108
+ }
109
+ },
110
+ "editor": {
111
+ "data": {
112
+ "id": "editor:1",
113
+ "type": "editors"
114
+ },
115
+ "meta": {
116
+ "peer-reviewed": false
117
+ }
118
+ },
119
+ "comments": {
120
+ "data": [{
121
+ "id": "comment:1",
122
+ "type": "comments"
123
+ }, {
124
+ "id": "comment:2",
125
+ "type": "comments"
126
+ }],
127
+ "links": {
128
+ "self": "/articles/1/relationships/comments",
129
+ "related": "/articles/1/comments"
130
+ },
131
+ "meta": {
132
+ "comment-count": 5
133
+ }
134
+ }
135
+ },
136
+ "attributes": {
137
+ "title": "Health walk"
138
+ },
139
+ "type": "articles",
140
+ "links": {
141
+ "self": "http://Article/1"
142
+ }
143
+ },
144
+ "meta": {
145
+ "reviewers": ["Christian Bernstein"],
146
+ "reviewer-initials": "C.B."
147
+ }
148
+ }))
149
+ end
150
+
151
+ it 'renders additional meta information if meta option supplied' do
152
+ hash = decorator.to_hash(meta: {
153
+ 'copyright' => 'Nick Sutterer', 'reviewers' => []
154
+ })
155
+ hash['meta']['copyright'].must_equal('Nick Sutterer')
156
+ hash['meta']['reviewers'].must_equal([])
157
+ hash['meta']['reviewer-initials'].must_equal('C.B.')
158
+ end
159
+
160
+ it 'does not render additonal meta information if meta option is empty' do
161
+ hash = decorator.to_hash(meta: {})
162
+ hash['meta']['copyright'].must_be_nil
163
+ hash['meta']['reviewers'].must_equal(['Christian Bernstein'])
164
+ hash['meta']['reviewer-initials'].must_equal('C.B.')
165
+ end
166
+
167
+ describe 'Single Resource Object with simple attributes' do
168
+ class DocumentSingleResourceObjectDecorator < Roar::Decorator
169
+ include Roar::JSON::JSONAPI.resource :articles
170
+
171
+ attributes do
172
+ property :title
173
+ end
174
+ end
175
+
176
+ let(:document) {
177
+ %({
178
+ "data": {
179
+ "id": "1",
180
+ "attributes": {
181
+ "title": "My Article"
182
+ },
183
+ "type": "articles"
184
+ }
185
+ })
186
+ }
187
+
188
+ let(:collection_document) {
189
+ %({
190
+ "data": [
191
+ {
192
+ "type": "articles",
193
+ "id": "1",
194
+ "attributes": {
195
+ "title": "My Article"
196
+ }
197
+ }
198
+ ]
199
+ })
200
+ }
201
+
202
+ it { DocumentSingleResourceObjectDecorator.new(Article.new(1, 'My Article')).to_json.must_equal_json document }
203
+ it { DocumentSingleResourceObjectDecorator.for_collection.new([Article.new(1, 'My Article')]).to_json.must_equal_json collection_document }
204
+ end
205
+
206
+ describe 'Single Resource Object with complex attributes' do
207
+ class VisualArtistDecorator < Roar::Decorator
208
+ include Roar::JSON::JSONAPI.resource :visual_artists
209
+
210
+ attributes do
211
+ property :name
212
+ collection :known_aliases
213
+ property :movement
214
+ collection :noteable_works
215
+ end
216
+
217
+ link(:self) { "http://visual_artists/#{represented.id}" }
218
+ link(:wikipedia_page) { "https://en.wikipedia.org/wiki/#{represented.name}" }
219
+ end
220
+
221
+ Painter = Struct.new(:id, :name, :known_aliases, :movement, :noteable_works)
222
+
223
+ let(:document) {
224
+ %({
225
+ "data": {
226
+ "type": "visual-artists",
227
+ "id": "p1",
228
+ "attributes": {
229
+ "name": "Pablo Picasso",
230
+ "known-aliases": [
231
+ "Pablo Ruiz Picasso"
232
+ ],
233
+ "movement": "Cubism",
234
+ "noteable-works": [
235
+ "Kahnweiler",
236
+ "Guernica"
237
+ ]
238
+ },
239
+ "links": {
240
+ "self": "http://visual_artists/p1",
241
+ "wikipedia-page": "https://en.wikipedia.org/wiki/Pablo Picasso"
242
+ }
243
+ }
244
+ })
245
+ }
246
+
247
+ let(:collection_document) {
248
+ %({
249
+ "data": [
250
+ {
251
+ "type": "visual-artists",
252
+ "id": "p1",
253
+ "attributes": {
254
+ "name": "Pablo Picasso",
255
+ "known-aliases": [
256
+ "Pablo Ruiz Picasso"
257
+ ],
258
+ "movement": "Cubism",
259
+ "noteable-works": [
260
+ "Kahnweiler",
261
+ "Guernica"
262
+ ]
263
+ },
264
+ "links": {
265
+ "self": "http://visual_artists/p1",
266
+ "wikipedia-page": "https://en.wikipedia.org/wiki/Pablo Picasso"
267
+ }
268
+ }
269
+ ]
270
+ })
271
+ }
272
+
273
+ let(:painter) {
274
+ Painter.new('p1', 'Pablo Picasso', ['Pablo Ruiz Picasso'], 'Cubism',
275
+ %w(Kahnweiler Guernica))
276
+ }
277
+
278
+ it { VisualArtistDecorator.new(painter).to_json.must_equal_json document }
279
+ it { VisualArtistDecorator.for_collection.new([painter]).to_json.must_equal_json collection_document }
280
+ end
281
+ end