api_navigator 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +26 -0
  7. data/.yardopts +8 -0
  8. data/CHANGELOG.md +3 -0
  9. data/CONTRIBUTING.md +4 -0
  10. data/Dangerfile +2 -0
  11. data/Gemfile +23 -0
  12. data/Guardfile +5 -0
  13. data/LICENSE +22 -0
  14. data/README.md +5 -0
  15. data/RELEASING.md +4 -0
  16. data/Rakefile +34 -0
  17. data/UPGRADING.md +4 -0
  18. data/api_navigator.gemspec +23 -0
  19. data/bin/console +15 -0
  20. data/bin/setup +8 -0
  21. data/fixtures_2.rb +253 -0
  22. data/lib/api_navigator.rb +45 -0
  23. data/lib/api_navigator/attributes.rb +20 -0
  24. data/lib/api_navigator/collection_hash.rb +90 -0
  25. data/lib/api_navigator/curie.rb +47 -0
  26. data/lib/api_navigator/entry_point.rb +157 -0
  27. data/lib/api_navigator/link.rb +160 -0
  28. data/lib/api_navigator/link_collection.rb +63 -0
  29. data/lib/api_navigator/resource.rb +130 -0
  30. data/lib/api_navigator/resources/collection_resource.rb +41 -0
  31. data/lib/api_navigator/resources/member_resource.rb +63 -0
  32. data/lib/api_navigator/version.rb +3 -0
  33. data/lib/faraday/connection.rb +17 -0
  34. data/spec/fixtures/requests.rb +157 -0
  35. data/spec/fixtures/sample.json +108 -0
  36. data/spec/lib/api_navigator/attribute_spec.rb +36 -0
  37. data/spec/lib/api_navigator/collection_hash_spec.rb +71 -0
  38. data/spec/lib/api_navigator/entry_point_spec.rb +185 -0
  39. data/spec/lib/api_navigator/link_collection_spec.rb +77 -0
  40. data/spec/lib/api_navigator/link_spec.rb +343 -0
  41. data/spec/lib/api_navigator/resource_spec.rb +368 -0
  42. data/spec/spec_helper.rb +112 -0
  43. data/spec/support/book_resource.rb +10 -0
  44. data/spec/support/request_helper.rb +8 -0
  45. metadata +172 -0
@@ -0,0 +1,343 @@
1
+ require 'spec_helper'
2
+
3
+ module ApiNavigator
4
+ describe Link do
5
+ let(:entry_point) do
6
+ EntryPoint.new('http://api.example.org/')
7
+ end
8
+
9
+ %w[type deprecation name profile title hreflang].each do |prop|
10
+ describe prop do
11
+ it 'returns the property value' do
12
+ link = Link.new('key', { prop => 'value' }, entry_point)
13
+ expect(link.send("_#{prop}")).to be == 'value'
14
+ end
15
+
16
+ it 'returns nil if the property is not present' do
17
+ link = Link.new('key', {}, entry_point)
18
+ expect(link.send("_#{prop}")).to be_nil
19
+ end
20
+ end
21
+ end
22
+
23
+ describe '_templated?' do
24
+ it 'returns true if the link is templated' do
25
+ link = Link.new('key', { 'templated' => true }, entry_point)
26
+
27
+ expect(link._templated?).to be_truthy
28
+ end
29
+
30
+ it 'returns false if the link is not templated' do
31
+ link = Link.new('key', {}, entry_point)
32
+
33
+ expect(link._templated?).to be_falsey
34
+ end
35
+ end
36
+
37
+ describe '_variables' do
38
+ it 'returns a list of required variables' do
39
+ link = Link.new('key', { 'href' => '/orders{?id,owner}', 'templated' => true }, entry_point)
40
+
41
+ expect(link._variables).to match_array %w[id owner]
42
+ end
43
+
44
+ it 'returns an empty array for untemplated links' do
45
+ link = Link.new('key', { 'href' => '/orders' }, entry_point)
46
+
47
+ expect(link._variables).to be_empty
48
+ end
49
+ end
50
+
51
+ context '_expand templated links' do
52
+ describe 'required argument' do
53
+ it 'builds a Link with the templated URI representation' do
54
+ link = Link.new('key', { 'href' => '/orders/{id}', 'templated' => true }, entry_point)
55
+ expect(link._expand(id: '1')._url).to be == '/orders/1'
56
+ end
57
+
58
+ it 'expands an uri template without variables' do
59
+ link = Link.new('key', { 'href' => '/orders/{id}', 'templated' => true }, entry_point)
60
+ expect(link._expand._url).to be == '/orders/'
61
+ expect(link._url).to be == '/orders/'
62
+ end
63
+ end
64
+
65
+ describe 'query string argument' do
66
+ it 'builds a Link with the templated URI representation' do
67
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point)
68
+ expect(link._expand(id: '1')._url).to be == '/orders?id=1'
69
+ end
70
+
71
+ it 'expands an uri template without variables' do
72
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point)
73
+ expect(link._expand._url).to be == '/orders'
74
+ expect(link._url).to be == '/orders'
75
+ end
76
+
77
+ it 'does not expand unknown variables' do
78
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point)
79
+ expect(link._expand(unknown: '1')._url).to be == '/orders'
80
+ end
81
+
82
+ it 'only expands known variables' do
83
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point)
84
+ expect(link._expand(unknown: '1', id: '2')._url).to be == '/orders?id=2'
85
+ end
86
+
87
+ it 'only expands templated links' do
88
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => false }, entry_point)
89
+ expect(link._expand(id: '1')._url).to be == '/orders{?id}'
90
+ end
91
+ end
92
+ end
93
+
94
+ describe '_url' do
95
+ it 'expands an uri template without variables' do
96
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point)
97
+
98
+ expect(link._url).to be == '/orders'
99
+ end
100
+
101
+ it 'expands an uri template with variables' do
102
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point, id: 1)
103
+
104
+ expect(link._url).to be == '/orders?id=1'
105
+ end
106
+
107
+ it 'does not expand an uri template with unknown variables' do
108
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point, unknown: 1)
109
+
110
+ expect(link._url).to be == '/orders'
111
+ end
112
+
113
+ it 'only expands known variables in a uri template' do
114
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point, unknown: 1, id: 2)
115
+
116
+ expect(link._url).to be == '/orders?id=2'
117
+ end
118
+
119
+ it 'returns the link when no uri template' do
120
+ link = Link.new('key', { 'href' => '/orders' }, entry_point)
121
+
122
+ expect(link._url).to be == '/orders'
123
+ end
124
+
125
+ it 'aliases to_s to _url' do
126
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point, id: 1)
127
+
128
+ expect(link.to_s).to be == '/orders?id=1'
129
+ end
130
+ end
131
+
132
+ describe '_resource' do
133
+ it 'builds a resource with the link href representation' do
134
+ expect(Resource).to receive(:new)
135
+
136
+ link = Link.new('key', { 'href' => '/' }, entry_point)
137
+
138
+ stub_request(entry_point.connection) do |stub|
139
+ stub.get('http://api.example.org/') { [200, {}, nil] }
140
+ end
141
+
142
+ link._resource
143
+ end
144
+ end
145
+
146
+ describe 'get' do
147
+ it 'sends a GET request with the link url' do
148
+ link = Link.new('key', { 'href' => '/productions/1' }, entry_point)
149
+
150
+ stub_request(entry_point.connection) do |stub|
151
+ stub.get('http://api.example.org/productions/1') { [200, {}, nil] }
152
+ end
153
+
154
+ expect(link._get).to be_kind_of Resource
155
+ end
156
+
157
+ it 'raises exceptions by default' do
158
+ link = Link.new('key', { 'href' => '/productions/1' }, entry_point)
159
+
160
+ stub_request(entry_point.connection) do |stub|
161
+ stub.get('http://api.example.org/productions/1') { [400, {}, nil] }
162
+ end
163
+
164
+ expect { link._get }.to raise_error Faraday::ClientError
165
+ end
166
+ end
167
+
168
+ describe '_options' do
169
+ it 'sends a OPTIONS request with the link url' do
170
+ link = Link.new('key', { 'href' => '/productions/1' }, entry_point)
171
+
172
+ stub_request(entry_point.connection) do |stub|
173
+ stub.options('http://api.example.org/productions/1') { [200, {}, nil] }
174
+ end
175
+
176
+ expect(link._options).to be_kind_of Resource
177
+ end
178
+ end
179
+
180
+ describe '_head' do
181
+ it 'sends a HEAD request with the link url' do
182
+ link = Link.new('key', { 'href' => '/productions/1' }, entry_point)
183
+
184
+ stub_request(entry_point.connection) do |stub|
185
+ stub.head('http://api.example.org/productions/1') { [200, {}, nil] }
186
+ end
187
+
188
+ expect(link._head).to be_kind_of Resource
189
+ end
190
+ end
191
+
192
+ describe '_delete' do
193
+ it 'sends a DELETE request with the link url' do
194
+ link = Link.new('key', { 'href' => '/productions/1' }, entry_point)
195
+
196
+ stub_request(entry_point.connection) do |stub|
197
+ stub.delete('http://api.example.org/productions/1') { [200, {}, nil] }
198
+ end
199
+
200
+ expect(link._delete).to be_kind_of Resource
201
+ end
202
+ end
203
+
204
+ describe '_post' do
205
+ let(:link) { Link.new('key', { 'href' => '/productions/1' }, entry_point) }
206
+
207
+ it 'sends a POST request with the link url and params' do
208
+ stub_request(entry_point.connection) do |stub|
209
+ stub.post('http://api.example.org/productions/1') { [200, {}, nil] }
210
+ end
211
+
212
+ expect(link._post('foo' => 'bar')).to be_kind_of Resource
213
+ end
214
+
215
+ it 'defaults params to an empty hash' do
216
+ stub_request(entry_point.connection) do |stub|
217
+ stub.post('http://api.example.org/productions/1') { [200, {}, nil] }
218
+ end
219
+
220
+ expect(link._post).to be_kind_of Resource
221
+ end
222
+ end
223
+
224
+ describe '_put' do
225
+ let(:link) { Link.new('key', { 'href' => '/productions/1' }, entry_point) }
226
+
227
+ it 'sends a PUT request with the link url and params' do
228
+ stub_request(entry_point.connection) do |stub|
229
+ stub.put('http://api.example.org/productions/1', '{"foo":"bar"}') { [200, {}, nil] }
230
+ end
231
+
232
+ expect(link._put('foo' => 'bar')).to be_kind_of Resource
233
+ end
234
+
235
+ it 'defaults params to an empty hash' do
236
+ stub_request(entry_point.connection) do |stub|
237
+ stub.put('http://api.example.org/productions/1') { [200, {}, nil] }
238
+ end
239
+
240
+ expect(link._put).to be_kind_of Resource
241
+ end
242
+ end
243
+
244
+ describe '_patch' do
245
+ let(:link) { Link.new('key', { 'href' => '/productions/1' }, entry_point) }
246
+
247
+ it 'sends a PATCH request with the link url and params' do
248
+ stub_request(entry_point.connection) do |stub|
249
+ stub.patch('http://api.example.org/productions/1', '{"foo":"bar"}') { [200, {}, nil] }
250
+ end
251
+
252
+ expect(link._patch('foo' => 'bar')).to be_kind_of Resource
253
+ end
254
+
255
+ it 'defaults params to an empty hash' do
256
+ stub_request(entry_point.connection) do |stub|
257
+ stub.patch('http://api.example.org/productions/1') { [200, {}, nil] }
258
+ end
259
+
260
+ expect(link._patch).to be_kind_of Resource
261
+ end
262
+ end
263
+
264
+ describe 'inspect' do
265
+ it 'outputs a custom-friendly output' do
266
+ link = Link.new('key', { 'href' => '/productions/1' }, 'foo')
267
+
268
+ expect(link.inspect).to include 'Link'
269
+ expect(link.inspect).to include '"href"=>"/productions/1"'
270
+ end
271
+ end
272
+
273
+
274
+ describe 'method_missing' do
275
+ describe 'delegation' do
276
+ it 'delegates when link key matches' do
277
+ resource = Resource.from_representation({ '_links' => { 'orders' => { 'href' => '/orders' } } }, entry_point)
278
+
279
+ stub_request(entry_point.connection) do |stub|
280
+ stub.get('http://api.example.org/orders') { [200, {}, { 'data' => [{'data' => {'id' => 1 }}] }] }
281
+ end
282
+
283
+ expect(resource.orders.first.id).to be == 1
284
+ end
285
+
286
+ it 'can handle false values in the response' do
287
+ resource = Resource.from_representation({ '_links' => { 'orders' => { 'href' => '/orders' } } }, entry_point)
288
+
289
+ stub_request(entry_point.connection) do |stub|
290
+ stub.get('http://api.example.org/orders') { [200, {}, { 'data' => {'any' => false }}] }
291
+ end
292
+
293
+ expect(resource.orders.any).to be_falsey
294
+ end
295
+
296
+ it "doesn't delegate when link key doesn't match" do
297
+ resource = Resource.from_representation({ '_links' => { 'foos' => { 'href' => '/orders' } } }, entry_point)
298
+
299
+ stub_request(entry_point.connection) do |stub|
300
+ stub.get('http://api.example.org/orders') { [200, {}, { 'data' => [{ 'data' => { 'id' => 1 }}] }] }
301
+ end
302
+
303
+ expect(resource.foos.first.id).to be == 1
304
+ end
305
+ end
306
+
307
+ describe 'resource' do
308
+ before do
309
+ stub_request(entry_point.connection) do |stub|
310
+ stub.get('http://myapi.org/orders') { [200, {}, { 'data' => {'any' => false }}] }
311
+ end
312
+
313
+ Resource.stub(:new) { resource }
314
+ end
315
+
316
+ let(:resource) { double('Resource') }
317
+ let(:link) { Link.new('orders', { 'href' => 'http://myapi.org/orders' }, entry_point) }
318
+
319
+ it 'delegates unkown methods to the resource' do
320
+ expect(Resource).to receive(:new) {resource }.at_least(1)
321
+ expect(resource).to receive(:unknown_method)
322
+
323
+ link.unknown_method
324
+ end
325
+
326
+ it 'raises an error when the method does not exist in the resource' do
327
+ expect { link.this_method_does_not_exist }.to raise_error NoMethodError
328
+ end
329
+
330
+ # it 'responds to missing methods' do
331
+ # expect(resource).to receive(:respond_to?).with('orders') { false }
332
+
333
+ # expect(link.respond_to?(:hui)).to be_falsey
334
+ # end
335
+
336
+ it 'does not delegate to_ary to resource' do
337
+ expect(resource).to receive(:to_ary).never
338
+ expect([[link, link]].flatten).to be == [link, link]
339
+ end
340
+ end
341
+ end
342
+ end
343
+ end
@@ -0,0 +1,368 @@
1
+ require 'spec_helper'
2
+
3
+ module ApiNavigator
4
+ describe Resource do
5
+ let(:entry_point) do
6
+ EntryPoint.new('http://api.example.org/', 'sample_client')
7
+ end
8
+
9
+ %w[type deprecation name profile title hreflang].each do |prop|
10
+ describe prop do
11
+ it 'returns the property value' do
12
+ link = Link.new('key', { prop => 'value' }, entry_point)
13
+ expect(link.send("_#{prop}")).to be == 'value'
14
+ end
15
+
16
+ it 'returns nil if the property is not present' do
17
+ link = Link.new('key', {}, entry_point)
18
+ expect(link.send("_#{prop}")).to be_nil
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ describe '_templated?' do
25
+ it 'returns true if the link is templated' do
26
+ link = Link.new('key', { 'templated' => true }, entry_point)
27
+
28
+ expect(link._templated?).to be_truthy
29
+ end
30
+
31
+ it 'returns false if the link is not templated' do
32
+ link = Link.new('key', {}, entry_point)
33
+
34
+ expect(link._templated?).to be_falsey
35
+ end
36
+ end
37
+
38
+ describe '_variables' do
39
+ it 'returns a list of required variables' do
40
+ link = Link.new('key', { 'href' => '/orders{?id,owner}', 'templated' => true }, entry_point)
41
+
42
+ expect(link._variables).to match_array %w[id owner]
43
+ end
44
+
45
+ it 'returns an empty array for untemplated links' do
46
+ link = Link.new('key', { 'href' => '/orders' }, entry_point)
47
+
48
+ expect(link._variables).to be_empty
49
+ end
50
+ end
51
+
52
+ context '_expand templated links' do
53
+ describe 'required argument' do
54
+ it 'builds a Link with the templated URI representation' do
55
+ link = Link.new('key', { 'href' => '/orders/{id}', 'templated' => true }, entry_point)
56
+ expect(link._expand(id: '1')._url).to be == '/orders/1'
57
+ end
58
+
59
+ it 'expands an uri template without variables' do
60
+ link = Link.new('key', { 'href' => '/orders/{id}', 'templated' => true }, entry_point)
61
+ expect(link._expand._url).to be == '/orders/'
62
+ expect(link._url).to be == '/orders/'
63
+ end
64
+ end
65
+
66
+ describe 'query string argument' do
67
+ it 'builds a Link with the templated URI representation' do
68
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point)
69
+ expect(link._expand(id: '1')._url).to be == '/orders?id=1'
70
+ end
71
+
72
+ it 'expands an uri template without variables' do
73
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point)
74
+ expect(link._expand._url).to be == '/orders'
75
+ expect(link._url).to be == '/orders'
76
+ end
77
+
78
+ it 'does not expand unknown variables' do
79
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point)
80
+ expect(link._expand(unknown: '1')._url).to be == '/orders'
81
+ end
82
+
83
+ it 'only expands known variables' do
84
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point)
85
+ expect(link._expand(unknown: '1', id: '2')._url).to be == '/orders?id=2'
86
+ end
87
+
88
+ it 'only expands templated links' do
89
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => false }, entry_point)
90
+ expect(link._expand(id: '1')._url).to be == '/orders{?id}'
91
+ end
92
+ end
93
+ end
94
+
95
+ describe '_url' do
96
+ it 'expands an uri template without variables' do
97
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point)
98
+
99
+ expect(link._url).to be == '/orders'
100
+ end
101
+
102
+ it 'expands an uri template with variables' do
103
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point, id: 1)
104
+
105
+ expect(link._url).to be == '/orders?id=1'
106
+ end
107
+
108
+ it 'does not expand an uri template with unknown variables' do
109
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point, unknown: 1)
110
+
111
+ expect(link._url).to be == '/orders'
112
+ end
113
+
114
+ it 'only expands known variables in a uri template' do
115
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point, unknown: 1, id: 2)
116
+
117
+ expect(link._url).to be == '/orders?id=2'
118
+ end
119
+
120
+ it 'returns the link when no uri template' do
121
+ link = Link.new('key', { 'href' => '/orders' }, entry_point)
122
+
123
+ expect(link._url).to be == '/orders'
124
+ end
125
+
126
+ it 'aliases to_s to _url' do
127
+ link = Link.new('key', { 'href' => '/orders{?id}', 'templated' => true }, entry_point, id: 1)
128
+
129
+ expect(link.to_s).to be == '/orders?id=1'
130
+ end
131
+ end
132
+
133
+ describe '_resource' do
134
+ it 'builds a resource with the link href representation' do
135
+ expect(Resource).to receive(:new)
136
+
137
+ link = Link.new('key', { 'href' => '/' }, entry_point)
138
+
139
+ stub_request(entry_point.connection) do |stub|
140
+ stub.get('http://api.example.org/') { [200, {}, nil] }
141
+ end
142
+
143
+ link._resource
144
+ end
145
+ end
146
+
147
+ describe 'get' do
148
+ it 'sends a GET request with the link url' do
149
+ link = Link.new('key', { 'href' => '/productions/1' }, entry_point)
150
+
151
+ stub_request(entry_point.connection) do |stub|
152
+ stub.get('http://api.example.org/productions/1') { [200, {}, nil] }
153
+ end
154
+
155
+ expect(link._get).to be_kind_of Resource
156
+ end
157
+
158
+ it 'raises exceptions by default' do
159
+ link = Link.new('key', { 'href' => '/productions/1' }, entry_point)
160
+
161
+ stub_request(entry_point.connection) do |stub|
162
+ stub.get('http://api.example.org/productions/1') { [400, {}, nil] }
163
+ end
164
+
165
+ expect { link._get }.to raise_error Faraday::ClientError
166
+ end
167
+ end
168
+
169
+ describe '_options' do
170
+ it 'sends a OPTIONS request with the link url' do
171
+ link = Link.new('key', { 'href' => '/productions/1' }, entry_point)
172
+
173
+ stub_request(entry_point.connection) do |stub|
174
+ stub.options('http://api.example.org/productions/1') { [200, {}, nil] }
175
+ end
176
+
177
+ expect(link._options).to be_kind_of Resource
178
+ end
179
+ end
180
+
181
+ describe '_head' do
182
+ it 'sends a HEAD request with the link url' do
183
+ link = Link.new('key', { 'href' => '/productions/1' }, entry_point)
184
+
185
+ stub_request(entry_point.connection) do |stub|
186
+ stub.head('http://api.example.org/productions/1') { [200, {}, nil] }
187
+ end
188
+
189
+ expect(link._head).to be_kind_of Resource
190
+ end
191
+ end
192
+
193
+ describe '_delete' do
194
+ it 'sends a DELETE request with the link url' do
195
+ link = Link.new('key', { 'href' => '/productions/1' }, entry_point)
196
+
197
+ stub_request(entry_point.connection) do |stub|
198
+ stub.delete('http://api.example.org/productions/1') { [200, {}, nil] }
199
+ end
200
+
201
+ expect(link._delete).to be_kind_of Resource
202
+ end
203
+ end
204
+
205
+ describe '_post' do
206
+ let(:link) { Link.new('key', { 'href' => '/productions/1' }, entry_point) }
207
+
208
+ it 'sends a POST request with the link url and params' do
209
+ stub_request(entry_point.connection) do |stub|
210
+ stub.post('http://api.example.org/productions/1') { [200, {}, nil] }
211
+ end
212
+
213
+ expect(link._post('foo' => 'bar')).to be_kind_of Resource
214
+ end
215
+
216
+ it 'defaults params to an empty hash' do
217
+ stub_request(entry_point.connection) do |stub|
218
+ stub.post('http://api.example.org/productions/1') { [200, {}, nil] }
219
+ end
220
+
221
+ expect(link._post).to be_kind_of Resource
222
+ end
223
+ end
224
+
225
+ describe '_put' do
226
+ let(:link) { Link.new('key', { 'href' => '/productions/1' }, entry_point) }
227
+
228
+ it 'sends a PUT request with the link url and params' do
229
+ stub_request(entry_point.connection) do |stub|
230
+ stub.put('http://api.example.org/productions/1', '{"foo":"bar"}') { [200, {}, nil] }
231
+ end
232
+
233
+ expect(link._put('foo' => 'bar')).to be_kind_of Resource
234
+ end
235
+
236
+ it 'defaults params to an empty hash' do
237
+ stub_request(entry_point.connection) do |stub|
238
+ stub.put('http://api.example.org/productions/1') { [200, {}, nil] }
239
+ end
240
+
241
+ expect(link._put).to be_kind_of Resource
242
+ end
243
+ end
244
+
245
+ describe '_patch' do
246
+ let(:link) { Link.new('key', { 'href' => '/productions/1' }, entry_point) }
247
+
248
+ it 'sends a PATCH request with the link url and params' do
249
+ stub_request(entry_point.connection) do |stub|
250
+ stub.patch('http://api.example.org/productions/1', '{"foo":"bar"}') { [200, {}, nil] }
251
+ end
252
+
253
+ expect(link._patch('foo' => 'bar')).to be_kind_of Resource
254
+ end
255
+
256
+ it 'defaults params to an empty hash' do
257
+ stub_request(entry_point.connection) do |stub|
258
+ stub.patch('http://api.example.org/productions/1') { [200, {}, nil] }
259
+ end
260
+
261
+ expect(link._patch).to be_kind_of Resource
262
+ end
263
+ end
264
+
265
+ describe 'inspect' do
266
+ it 'outputs a custom-friendly output' do
267
+ link = Link.new('key', { 'href' => '/productions/1' }, 'foo')
268
+
269
+ expect(link.inspect).to include 'Link'
270
+ expect(link.inspect).to include '"href"=>"/productions/1"'
271
+ end
272
+ end
273
+
274
+
275
+ describe 'method_missing' do
276
+ describe 'delegation' do
277
+ it 'delegates when link key matches' do
278
+ resource = Resource.from_representation({ '_links' => { 'orders' => { 'href' => '/orders' } } }, entry_point)
279
+
280
+ stub_request(entry_point.connection) do |stub|
281
+ stub.get('http://api.example.org/orders') { [200, {}, { 'data' => [{'data' => {'id' => 1 }}] }] }
282
+ end
283
+
284
+ expect(resource.orders.first.id).to be == 1
285
+ end
286
+
287
+ it 'delegates to data when data and link identifier is present' do
288
+ resource = Resource.from_representation({
289
+ 'data' => { 'orders' => [ 'data' => { 'id' => 9 }] },
290
+ '_links' => { 'orders' => { 'href' => '/orders' } }
291
+ }, entry_point)
292
+
293
+ stub_request(entry_point.connection) do |stub|
294
+ stub.get('http://api.example.org/orders') { [200, {}, { 'data' => [{'data' => {'id' => 1 }}] }] }
295
+ end
296
+
297
+ expect(resource.orders.first.id).to be == 9
298
+ end
299
+
300
+ it 'can handle false values in the response' do
301
+ resource = Resource.from_representation({ '_links' => { 'orders' => { 'href' => '/orders' } } }, entry_point)
302
+
303
+ stub_request(entry_point.connection) do |stub|
304
+ stub.get('http://api.example.org/orders') { [200, {}, { 'data' => {'any' => false }}] }
305
+ end
306
+
307
+ expect(resource.orders.any).to be_falsey
308
+ end
309
+
310
+ it "doesn't delegate when link key doesn't match" do
311
+ resource = Resource.from_representation({ '_links' => { 'foos' => { 'href' => '/orders' } } }, entry_point)
312
+
313
+ stub_request(entry_point.connection) do |stub|
314
+ stub.get('http://api.example.org/orders') { [200, {}, { 'data' => [{ 'data' => { 'id' => 1 }}] }] }
315
+ end
316
+
317
+ expect(resource.foos.first.id).to be == 1
318
+ end
319
+ end
320
+
321
+ describe "MemberResource instance creation" do
322
+ it "builds a resource of specific class if registered" do
323
+ ApiNavigator.register('sample_client')
324
+ ApiNavigator.register_resource('book', BookResource, client_identifier: 'sample_client')
325
+
326
+ resource = Resource.from_representation({
327
+ 'data' => { 'id' => 1 },
328
+ '_meta' => { 'type' => 'book'} ,
329
+ }, entry_point)
330
+
331
+ expect(resource).to be_kind_of(BookResource)
332
+ expect(resource.foo).to be == 'bar'
333
+ expect(resource.id).to be == 1
334
+ end
335
+ end
336
+
337
+ describe 'resource' do
338
+ before do
339
+ stub_request(entry_point.connection) do |stub|
340
+ stub.get('http://myapi.org/orders') { [200, {}, { 'data' => {'any' => false }}] }
341
+ end
342
+
343
+ Resource.stub(:new) { resource }
344
+ end
345
+
346
+
347
+ let(:resource) { double('Resource') }
348
+ let(:link) { Link.new('orders', { 'href' => 'http://myapi.org/orders' }, entry_point) }
349
+
350
+ it 'delegates unkown methods to the resource' do
351
+ expect(Resource).to receive(:new) {resource }.at_least(1)
352
+ expect(resource).to receive(:unknown_method)
353
+
354
+ link.unknown_method
355
+ end
356
+
357
+ it 'raises an error when the method does not exist in the resource' do
358
+ expect { link.this_method_does_not_exist }.to raise_error NoMethodError
359
+ end
360
+
361
+ it 'does not delegate to_ary to resource' do
362
+ expect(resource).to receive(:to_ary).never
363
+ expect([[link, link]].flatten).to be == [link, link]
364
+ end
365
+ end
366
+ end
367
+ end
368
+ end