well_rested 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ module WellRested
2
+ class CamelCaseFormatter
3
+ def initialize(lower = true)
4
+ raise "Upper case camelizing not supported yet" unless lower # TODO: Support upper-camel-casing
5
+ end
6
+
7
+ def encode(hash)
8
+ KeyTransformer.camelize_keys(hash)
9
+ end
10
+
11
+ def decode(hash)
12
+ KeyTransformer.underscore_keys(hash)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+
2
+ module WellRested
3
+ class JSONFormatter
4
+ def encode(obj)
5
+ obj.to_json
6
+ end
7
+
8
+ def decode(serialized_representation)
9
+ JSON.parse(serialized_representation)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,26 @@
1
+ require 'generic_utils'
2
+
3
+ module WellRested
4
+ module Utils
5
+ extend GenericUtils
6
+ extend self
7
+
8
+ # Turn any nested resources back into hashes before sending them
9
+ def objects_to_attributes(obj)
10
+ if obj.respond_to?(:attributes_for_api)
11
+ obj.attributes_for_api
12
+ elsif obj.kind_of?(Hash)
13
+ new_attributes = {}.with_indifferent_access
14
+ obj.each do |k, v|
15
+ new_attributes[k] = objects_to_attributes(v)
16
+ end
17
+ new_attributes
18
+ elsif obj.kind_of?(Array)
19
+ obj.map { |e| self.objects_to_attributes(e) }
20
+ else
21
+ obj
22
+ #raise "Attributes was not a Hash or Enumerable"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,619 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
4
+
5
+ describe API do
6
+ before(:each) do
7
+ @account_class = Class.new(Base) do
8
+ self.path = '/accounts'
9
+ end
10
+ @user_class = Class.new(Base) do
11
+ self.path = '/accounts/:account_id/users'
12
+ end
13
+ end
14
+
15
+ before do
16
+ @api = mock_api
17
+ FakeWeb.register_uri(:get, %r|/accounts$|, :body => '[{}]')
18
+ FakeWeb.register_uri(:get, %r|/accounts/1$|, :body => '{}')
19
+ FakeWeb.register_uri(:get, %r|/accounts/1/users|, :body => '[{}]', 'x_record_count' => '2')
20
+ FakeWeb.register_uri(:get, %r|/accounts/1/users/1|, :body => '{}')
21
+ end
22
+
23
+ after do
24
+ FakeWeb.clean_registry
25
+ end
26
+
27
+ describe "class methods" do
28
+ describe ".request_headers" do
29
+ subject { API.request_headers }
30
+ it { should be_a Hash }
31
+ end
32
+
33
+ describe '.fill_path' do
34
+ it "should return the path with filled parameters" do
35
+ API.fill_path('/something/:that/has/:parameters', :that => 'foo', :parameters => 'bar').should == '/something/foo/has/bar'
36
+ end
37
+
38
+ it "should raise an exception if there are unmatched parameters" do
39
+ expect { API.fill_path('/something/:that/lacks/:parameters', :that => 'foo') }.should raise_error ArgumentError
40
+ end
41
+
42
+ it "should raise an exception if there is a blank param" do
43
+ expect { API.fill_path('/something/:that/lacks/parameters', :that => '') }.should raise_error ArgumentError
44
+ end
45
+ end
46
+ end
47
+
48
+ describe '.request_headers' do
49
+ subject { @api.request_headers }
50
+ it { should be_a Hash }
51
+ end
52
+
53
+ describe ".url_for" do
54
+ it "should substitute path params" do
55
+ @api.url_for(@user_class, :account_id => 1).should match /accounts\/1\/users/
56
+ end
57
+
58
+ it "should append query params when passed a hash" do
59
+ @api.url_for(@account_class, {}, :count => 4).should match /accounts\?count=4$/
60
+ end
61
+
62
+ it "should append query params when passed a string" do
63
+ @api.url_for(@account_class, '/accounts', :count => 4).should match /accounts\?count=4$/
64
+ end
65
+
66
+ it "should attribute-encode query params" do
67
+ @account_class.attribute_formatter = CamelCaseFormatter.new
68
+ @api.url_for(@account_class, '/accounts', :my_count => 4).should match /accounts\?myCount=4$/
69
+ end
70
+
71
+ it "should use the specified extension" do
72
+ @account_class.extension = '.foobar'
73
+ @api.url_for(@account_class, '/accounts', :my_count => 4).should match /accounts.foobar\?myCount=4$/
74
+ end
75
+
76
+ context "with auth set" do
77
+ before do
78
+ @api.user = 'admin'
79
+ @api.password = 'password'
80
+ end
81
+
82
+ it "should generate a url with auth" do
83
+ @api.url_for(@account_class).should match /:\/\/admin:password@/
84
+ end
85
+ end
86
+ end
87
+
88
+ describe ".default_path_parameters" do
89
+ context "with empty initializer" do
90
+ subject { @api.default_path_parameters }
91
+
92
+ it { should be_empty }
93
+ end
94
+
95
+ context "with params in initializer" do
96
+ before { @api = API.new(:account_id => 7, :id => 2) }
97
+
98
+ it "should have the passed value" do
99
+ @api.default_path_parameters.should == { 'account_id' => 7, 'id' => 2 }
100
+ end
101
+
102
+ it "should use the default path params when generating URLs" do
103
+ @api.url_for(@user_class).should =~ /accounts\/7\/users\/2/
104
+ end
105
+ end
106
+ end
107
+
108
+ describe 'API Calls' do
109
+ describe ".request" do
110
+ it "should issue a PUT request" do
111
+ FakeWeb.register_uri(:put, "#{base_path}/request_put_test", :body => '')
112
+ @api.request(Base, :put, "/request_put_test")
113
+ FakeWeb.should have_requested(:put, "http://#{base_path}/request_put_test")
114
+ end
115
+
116
+ it "should issue a POST request" do
117
+ FakeWeb.register_uri(:post, "#{base_path}/request_post_test", :body => '{}')
118
+ @api.request(Base, :post, "/request_post_test")
119
+ FakeWeb.should have_requested(:post, "http://#{base_path}/request_post_test")
120
+ end
121
+
122
+ it "should issue a GET request" do
123
+ FakeWeb.register_uri(:get, "#{base_path}/request_get_test", :body => '{}')
124
+ @api.request(Base, :get, "/request_get_test")
125
+ FakeWeb.should have_requested(:get, "http://#{base_path}/request_get_test")
126
+ end
127
+
128
+ it "should issue a DELETE request" do
129
+ FakeWeb.register_uri(:delete, "#{base_path}/request_del_test", :body => '')
130
+ @api.request(Base, :delete, "/request_del_test")
131
+ FakeWeb.should have_requested(:delete, "http://#{base_path}/request_del_test")
132
+ end
133
+
134
+ context "when passed a fully qualified url" do
135
+ before { FakeWeb.register_uri(:get, "#{base_path}/request_get_url", :body => '{}') }
136
+
137
+ it "should use it directly" do
138
+ @api.request(Base, :get, "http://#{base_path}/request_get_url")
139
+ end
140
+ end
141
+
142
+ context "when passed a relative path (beginning with a slash)" do
143
+ before { FakeWeb.register_uri(:get, "#{base_path}/request_get_path", :body => '{}') }
144
+
145
+ it "should fill it out" do
146
+ @api.request(Base, :get, "/request_get_path")
147
+ end
148
+ end
149
+ end
150
+
151
+ describe '.find' do
152
+ context "when passed a url" do
153
+ before {
154
+ @path = "#{base_path}/my/weird/path"
155
+ FakeWeb.register_uri(:get, @path, :body => '{}')
156
+ @account = @api.find(@account_class, '/my/weird/path')
157
+ }
158
+ it "should use the URL" do
159
+ FakeWeb.should have_requested(:get, "http://#{@path}")
160
+ end
161
+
162
+ it "should return a resource of the requested type" do
163
+ @account.should be_an @account_class
164
+ end
165
+
166
+ it "should raise an exception if the server returns 404" do
167
+ expect { @api.find(@account_class, "/notfound") }.should raise_error
168
+ end
169
+ end
170
+
171
+ context "when passed path params" do
172
+ before { @account = @api.find(@account_class, :id => 1) }
173
+
174
+ it "should return a resource of the requested type" do
175
+ @account.should be_an @account_class
176
+ end
177
+
178
+ it "should handle query params" do
179
+ FakeWeb.register_uri(:get, %r|/accounts\/1\?test=true|, :body => '{}')
180
+ @api.find(@account_class, {:id => 1}, :test => 'true')
181
+ FakeWeb.should have_requested(:get, %r|/accounts\/1\?test=true|)
182
+ end
183
+
184
+ it "should create the resource using new_from_api" do
185
+ @account_class.should_receive(:new_from_api)
186
+ @api.find(@account_class, :id => 1)
187
+ end
188
+ end
189
+
190
+ context "it includes a camelized attribute name" do
191
+ before(:each) do
192
+ @api.client.should_receive(:get).and_return '{ "camelizedAttrName" : "value" }'
193
+ @user = @api.find(@account_class, :id => 1)
194
+ end
195
+
196
+ it "should not include camelize attribute names" do
197
+ @user.attributes.should_not include('camelizedAttrName')
198
+ end
199
+
200
+ it "should include decamelized attribute names" do
201
+ @user.attributes['camelized_attr_name'].should == "value"
202
+ end
203
+ end
204
+
205
+ context "when a resource is passed instead of a class and params" do
206
+ before do
207
+ FakeWeb.register_uri(:get, %r|/test/7/foo/4|, :body => '{}')
208
+ end
209
+ it "should use the path_parameters as path parameters" do
210
+ klass = Class.new(Base) do
211
+ self.path = '/test/:account_id/foo'
212
+ end
213
+ res = klass.new(:id => 4, :account_id => 7)
214
+ @api.find(res)
215
+ FakeWeb.should have_requested(:get, %r|/test/7/foo/4|)
216
+ end
217
+ end
218
+
219
+ end
220
+
221
+ describe '.find_many' do
222
+ it "should allow a URL override" do
223
+ FakeWeb.register_uri(:get, %r|\/alternate\/users\/path|, :body => '[]')
224
+ @api.find_many(@user_class, '/alternate/users/path')
225
+ FakeWeb.should have_requested(:get, %r|\/alternate\/users\/path|)
226
+ end
227
+
228
+ it "should set last_response on @api with headers" do
229
+ @api.last_response.should be_nil # sanity check
230
+ @users = @api.find_many(@user_class, :account_id => 1)
231
+ @api.last_response.headers[:'x_record_count'].should == '2'
232
+ end
233
+
234
+ context "with no parameters" do
235
+ before(:each) { @users = @api.find_many(@user_class, :account_id => 1) }
236
+
237
+ it "should return a collection of objects of the requested type" do
238
+ @users.should be_an Array
239
+ @users.size.should be > 0 # sanity check
240
+ @users.each { |a| a.should be_a @user_class }
241
+ end
242
+ end
243
+
244
+ context "with parameters" do
245
+ before { @users = @api.find_many(@user_class, {:account_id => 1}, :count => 1) }
246
+
247
+ it "should return the number of objects requested" do
248
+ @users.size.should == 1
249
+ end
250
+
251
+ it "should create the resources using new_from_api" do
252
+ @user_class.should_receive(:new_from_api).any_number_of_times
253
+ @api.find_many(@user_class, {:account_id => 1}, :count => 1)
254
+ end
255
+ end
256
+
257
+ end
258
+
259
+ describe ".create" do
260
+ before do
261
+ FakeWeb.register_uri(:post, %r||, :body => '{ "id": "test" }', :status => 200)
262
+ @klass = Class.new(Base) { self.path = '' }
263
+ @res = @klass.new
264
+ end
265
+
266
+ it "should allow a URL override" do
267
+ FakeWeb.clean_registry
268
+ FakeWeb.register_uri(:post, %r|/alternative/path|, :body => '{ "id": "test" }')
269
+ @api.create(@klass, {}, '/alternative/path')
270
+ FakeWeb.should have_requested(:post, %r|/alternative/path|)
271
+ end
272
+
273
+ it "should create the object using new" do
274
+ @klass.should_receive(:new).at_least(:once).and_return(@res)
275
+ @api.create(@klass, {})
276
+ end
277
+
278
+ it "should call attributes_for_api on the resource" do
279
+ @klass.stub!(:new).and_return(@res)
280
+ @res.should_receive(:attributes_for_api).at_least(:once).and_return({})
281
+ @api.create(@klass)
282
+ end
283
+ it "should not call attributes on the resource" do
284
+ @klass.stub!(:new_from_api).and_return(@res)
285
+ @res.should_not_receive(:attributes)
286
+ @api.create(@klass)
287
+ end
288
+
289
+ it "should camelize keys" do
290
+ attributes = { :underscored_key => 'foo' }
291
+ @api.client.should_receive(:post).with(anything, {:underscoredKey => 'foo'}.to_json, anything).and_return(double(:code => 200, :body => '{}'))
292
+ @api.create(@klass, attributes)
293
+ end
294
+
295
+ context "when the resource is valid" do
296
+ before(:each) do
297
+ @attrs = { :account_id => 1, :first_name => 'FirsTest', :last_name => 'LasTest', :email_address => 'a@b' }
298
+ @user_class.new(@attrs).should be_valid # sanity check that user is really valid
299
+ end
300
+
301
+ it "should POST to the resource path" do
302
+ user = @api.create(@user_class, @attrs)
303
+ user.should be_a @user_class
304
+ end
305
+ end
306
+
307
+ context "when an ID is set" do
308
+ before { @res = @klass.new(:id => 9) }
309
+
310
+ it "should not issue a POST" do
311
+ @api.client.stub(:put).and_return(double(:code => 200, :body => '{}'))
312
+ @api.client.should_not_receive(:post)
313
+ @api.save(@res)
314
+ end
315
+
316
+ it "should issue a PUT" do
317
+ @api.client.should_receive(:put).and_return(double(:code => 200, :body => '{}'))
318
+ @api.save(@res)
319
+ end
320
+ end
321
+
322
+ context "when the resource is invalid" do
323
+ it "should return false" do
324
+ klass = Class.new(Base) do
325
+ def valid?
326
+ false
327
+ end
328
+ end
329
+ klass.new.should_not be_valid
330
+
331
+ @api.create(klass, {}).should == false
332
+ end
333
+ end
334
+
335
+ context "when saving a new record" do
336
+ it "should mark the record persisted after save completes" do
337
+ @res.should be_new_record # sanity check
338
+ @api.save(@res)
339
+ @res.should_not be_new_record
340
+ end
341
+ end
342
+
343
+ context "when the resource is an array" do
344
+ before do
345
+ @klass = Class.new(Base) do
346
+ self.path = '/array/resource'
347
+ end
348
+ FakeWeb.clean_registry
349
+ FakeWeb.register_uri(:post, %r|/array\/resource$|, :body => '[{"name":"one"}, {"name":"two"}]')
350
+ end
351
+
352
+ it "should return an array when the resource is an array" do
353
+ res = @api.create(@klass)
354
+ res.should be_an Array
355
+ res.first.name.should == "one"
356
+ res.last.name.should == "two"
357
+ end
358
+ end
359
+
360
+ end
361
+
362
+ describe '.save' do
363
+ before do
364
+ @klass = Class.new(Base) { self.path = '' }
365
+ @res = @klass.new(:id => 4)
366
+ end
367
+
368
+ it "should set the correct content-type" do
369
+ @user = @user_class.new :first_name => 'First', :last_name => 'Last',
370
+ :account_id => 1, :email_address => 'user@foo', :password => 'foobar', :id => 77
371
+
372
+ @api.client.should_receive(:put).with(@api.url_for(@user_class, @user.attributes_for_api),
373
+ KeyTransformer.camelize_keys(@user.attributes_for_api).to_json,
374
+ @api.request_headers).and_return(double(:code => 200, :body => '{}'))
375
+ @api.save(@user)
376
+ end
377
+
378
+ it "should camelize keys" do
379
+ @res.load(:id => 4, :underscored_key => 'foo')
380
+ @api.client.should_receive(:put).with(anything, {:id => 4, :underscoredKey => 'foo'}.to_json, anything).and_return(
381
+ double(:code => 200, :body => '{}'))
382
+ @api.save(@res)
383
+ end
384
+
385
+ context "when attributes_for_api and path_params differ" do
386
+ before do
387
+ @klass = Class.new(Base) do
388
+ self.path = '/test/:special_attr/blah'
389
+ define_schema :id, :special_attr
390
+ end
391
+ @res = @klass.new
392
+ @res.id = 4
393
+ @res.stub!(:attributes).and_return({:id => 4, :special_attr => 'bad' }.with_indifferent_access)
394
+ @res.stub!(:attributes_for_api).and_return({:id => 4, :special_attr => 'bad' }.with_indifferent_access)
395
+ @res.stub!(:path_parameters).and_return({:id => 4, :special_attr => 'good'}.with_indifferent_access)
396
+ end
397
+
398
+ it "should use the path_parameters for path substitution" do
399
+ FakeWeb.register_uri(:put, %r|/test/good/blah/4$|, :body => '{}')
400
+ #FakeWeb.should have_requested(:put, %r|/test/foo/blah/4$|) # why doesn't this work?
401
+
402
+ @api.save(@res)
403
+ end
404
+
405
+ it "should use the attributes_for_api for the payload" do
406
+ @api.client.should_receive(:put).with(anything(), {:id => 4, :specialAttr => 'bad'}.to_json, anything()).and_return(
407
+ double(:body => '{}', :code => 200))
408
+ @api.save(@res)
409
+ end
410
+ end
411
+
412
+ context "when save succeeds" do
413
+ before do
414
+ FakeWeb.register_uri(:put, %r|.*|, :body => '{"newattr":"newval"}')
415
+ @api.save(@res)
416
+ end
417
+
418
+ it "should be updated in place" do
419
+ @res.newattr.should == 'newval'
420
+ end
421
+ end
422
+
423
+ context "when host returns a 422" do
424
+ before do
425
+ @res = @klass.new(:id => 4, :attr => 'val')
426
+ FakeWeb.register_uri(:put, %r||, :body => '{"errors":["first"]}', :status => 422)
427
+ end
428
+
429
+ it "should return false" do
430
+ ret = @api.save(@res)
431
+ ret.should == false
432
+ end
433
+
434
+ it "should not modify the resource, except to add errors" do
435
+ old_attrs = @res.attributes.clone
436
+ ret = @api.save(@res)
437
+ @res.attributes.should == old_attrs
438
+ @res.errors.to_hash.should == {:base => ['first']}
439
+ end
440
+ end
441
+
442
+ context "when host returns a 400" do
443
+ before do
444
+ @res = @klass.new(:id => 4, :attr => 'val')
445
+ FakeWeb.register_uri(:put, %r||, :body => '', :status => 400)
446
+ end
447
+
448
+ it "should raise an error" do
449
+ expect { @api.save(@res) }.should raise_error RestClient::BadRequest
450
+ end
451
+ end
452
+
453
+ context "when no ID is set" do
454
+ before { @res = @klass.new }
455
+ it "should issue a POST instead of a PUT" do
456
+ FakeWeb.register_uri(:post, %r||, :body => '{}')
457
+ @api.save(@res)
458
+ end
459
+ end
460
+
461
+ context "when the resource is invalid" do
462
+ it "should return false" do
463
+ klass = Class.new(Base) do
464
+ self.path = ''
465
+ end
466
+ r = klass.new :id => 1
467
+ r.should_receive('valid?').and_return(false)
468
+ @api.client.stub!(:put).and_return('{}')
469
+ @api.save(r).should == false
470
+ end
471
+ end
472
+ end
473
+
474
+ describe ".delete" do
475
+ context "when called on a class" do
476
+ it "should issue a delete to the specified ID" do
477
+ attrs = { :account_id => 1, :id => 7 }
478
+ FakeWeb.register_uri(:delete, @api.url_for(@user_class, attrs), :status => 200, :body => '')
479
+ @api.delete(@user_class, attrs)
480
+ FakeWeb.should have_requested(:delete, @api.url_for(@user_class, attrs))
481
+ end
482
+
483
+ it "should allow a URL override" do
484
+ FakeWeb.register_uri(:delete, %r|/alternate/delete/path|, :status => 200, :body => '')
485
+ @api.delete(@user_class, '/alternate/delete/path')
486
+ FakeWeb.should have_requested(:delete, %r|/alternate/delete/path|)
487
+ end
488
+ end
489
+ context "when called on a resource" do
490
+ before { @res = @user_class.new(:account_id => 1, :id => 7) }
491
+
492
+ it "should DELETE on the resource's url" do
493
+ FakeWeb.register_uri(:delete, @api.url_for(@user_class, @res.attributes), :status => 200, :body => '')
494
+ @api.delete(@res)
495
+ FakeWeb.should have_requested(:delete, @api.url_for(@user_class, @res.attributes))
496
+ end
497
+ end
498
+ end
499
+
500
+ describe ".get" do
501
+ context "when the response is an XML array" do
502
+ before do
503
+ FakeWeb.register_uri(:get, %r|/array\/resource$|, :body => '<?xml version="1.0" encoding="UTF-8"?><hash></hash>')
504
+ @response = @api.get("#{base_path}/array/resource", XMLFormatter.new)
505
+ end
506
+ it "should return an array" do
507
+ pending
508
+ @response.should be_an Array
509
+ end
510
+ end
511
+
512
+ context "when the response is a json array" do
513
+ before do
514
+ FakeWeb.register_uri(:get, %r|/array\/resource$|, :body => '[{"name":"one"}, {"name":"two"}]')
515
+ @response = @api.get("#{base_path}/array/resource")
516
+ end
517
+ it "should return an array" do
518
+ @response.should be_an Array
519
+ end
520
+ end
521
+
522
+ context "when the response is a hash" do
523
+ before do
524
+ FakeWeb.register_uri(:get, %r|/hash\/resource$|, :body => '{"name":"one"}')
525
+ @response = @api.get("#{base_path}/hash/resource")
526
+ end
527
+ it "should return a hash" do
528
+ @response.should be_a Hash
529
+ end
530
+ end
531
+
532
+ # TODO: Make this format-agnostic
533
+ context "when the response isn't valid JSON" do
534
+ before { FakeWeb.register_uri(:get, %r|/invalid/json$|, :body => 'bogus') }
535
+ context "we're expecting json" do
536
+ it "should raise an error" do
537
+ expect { @api.get("#{base_path}/invalid/json") }.should raise_error
538
+ end
539
+ end
540
+ context "we're not expecting json" do
541
+ it "should return the string" do
542
+ @api.get("#{base_path}/invalid/json", false).should == 'bogus'
543
+ end
544
+ end
545
+ end
546
+ end
547
+
548
+ describe ".post" do
549
+ # TODO: Make this format-agnostic
550
+ context "when parsing JSON" do
551
+ it "should issue a post to the given URL" do
552
+ FakeWeb.register_uri(:post, %r|/custom/post/url|, :body => '{}')
553
+ res = @api.post("#{base_path}/custom/post/url", 'asdf')
554
+ FakeWeb.should have_requested(:post, %r|/custom/post/url|)
555
+ end
556
+
557
+ it "should return a hash" do
558
+ FakeWeb.register_uri(:post, %r|/custom/post/url|, :body => '{}')
559
+ res = @api.post("#{base_path}/custom/post/url", 'asdf')
560
+ res.should == {}
561
+ end
562
+ end
563
+
564
+ # TODO: Make this format-agnostic
565
+ context "When not parsing JSON" do
566
+ it "should return a string" do
567
+ FakeWeb.register_uri(:post, %r|/custom/post/url|, :body => '{}')
568
+ res = @api.post("#{base_path}/custom/post/url", 'asdf', false)
569
+ res.should == '{}'
570
+ end
571
+ end
572
+ end
573
+
574
+ describe ".put" do
575
+ it "should issue a PUT to the given URL" do
576
+ FakeWeb.register_uri(:put, %r|/custom/put/url|, :body => '{}')
577
+ @api.put("#{base_path}/custom/put/url", [{:a => :b}, {:b => :c}])
578
+ end
579
+
580
+ it "should raise an error if we get a 400" do
581
+ FakeWeb.register_uri(:put, %r|/custom/put/url|, :status => 400, :body => '{}')
582
+ expect { @api.put("#{base_path}/custom/put/url", [{:a => :b}, {:b => :c}]) }.should raise_error RestClient::BadRequest
583
+ end
584
+
585
+ context "when the server returns a list of resources" do
586
+ before { FakeWeb.register_uri(:put, %r|/custom/put/url|, :status => 200, :body => '[{"a":"b"}]') }
587
+ it "should return an array of hashes" do
588
+ ar = @api.put("#{base_path}/custom/put/url", {})
589
+ ar.should be_an Array
590
+ end
591
+ end
592
+
593
+ context "when the server returns a single resource" do
594
+ before { FakeWeb.register_uri(:put, %r|/custom/put/url|, :status => 200, :body => '{"a":"b"}') }
595
+ it "should return an array of hashes" do
596
+ ar = @api.put("#{base_path}/custom/put/url", {})
597
+ ar.should be_a Hash
598
+ end
599
+ end
600
+
601
+ context "when the server returns an empty response" do
602
+ before { FakeWeb.register_uri(:put, %r|/custom/put/url|, :status => 200, :body => '') }
603
+ it "should return an empty string" do
604
+ ar = @api.put("#{base_path}/custom/put/url", {})
605
+ ar.should == ""
606
+ end
607
+ end
608
+
609
+ context "when passed :json => false" do
610
+ before { FakeWeb.register_uri(:put, %r|/custom/put/url|, :status => 200, :body => '{"a":"b"}') }
611
+ it "should return a string" do
612
+ ar = @api.put("#{base_path}/custom/put/url", {}, :json => false)
613
+ ar.should be_a String
614
+ end
615
+ end
616
+ end
617
+ end
618
+ end
619
+