api_navigator 0.0.1

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.
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