timriley-httparty 0.3.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 (56) hide show
  1. data/History +108 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Manifest +55 -0
  4. data/README +35 -0
  5. data/Rakefile +47 -0
  6. data/bin/httparty +98 -0
  7. data/cucumber.yml +1 -0
  8. data/examples/aaws.rb +32 -0
  9. data/examples/basic.rb +11 -0
  10. data/examples/delicious.rb +37 -0
  11. data/examples/google.rb +16 -0
  12. data/examples/rubyurl.rb +14 -0
  13. data/examples/twitter.rb +31 -0
  14. data/examples/whoismyrep.rb +10 -0
  15. data/features/basic_authentication.feature +20 -0
  16. data/features/command_line.feature +7 -0
  17. data/features/deals_with_http_error_codes.feature +26 -0
  18. data/features/handles_multiple_formats.feature +34 -0
  19. data/features/steps/env.rb +15 -0
  20. data/features/steps/httparty_response_steps.rb +26 -0
  21. data/features/steps/httparty_steps.rb +15 -0
  22. data/features/steps/mongrel_helper.rb +55 -0
  23. data/features/steps/remote_service_steps.rb +47 -0
  24. data/features/supports_redirection.feature +22 -0
  25. data/httparty.gemspec +37 -0
  26. data/lib/core_extensions.rb +175 -0
  27. data/lib/httparty/cookie_hash.rb +9 -0
  28. data/lib/httparty/exceptions.rb +7 -0
  29. data/lib/httparty/module_inheritable_attributes.rb +25 -0
  30. data/lib/httparty/parsers/json.rb +74 -0
  31. data/lib/httparty/parsers/xml.rb +209 -0
  32. data/lib/httparty/parsers.rb +4 -0
  33. data/lib/httparty/request.rb +139 -0
  34. data/lib/httparty/response.rb +17 -0
  35. data/lib/httparty/version.rb +3 -0
  36. data/lib/httparty.rb +201 -0
  37. data/setup.rb +1585 -0
  38. data/spec/fixtures/delicious.xml +23 -0
  39. data/spec/fixtures/empty.xml +0 -0
  40. data/spec/fixtures/google.html +3 -0
  41. data/spec/fixtures/twitter.json +1 -0
  42. data/spec/fixtures/twitter.xml +403 -0
  43. data/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
  44. data/spec/hash_spec.rb +49 -0
  45. data/spec/httparty/cookie_hash_spec.rb +38 -0
  46. data/spec/httparty/parsers/json_spec.rb +42 -0
  47. data/spec/httparty/parsers/xml_spec.rb +445 -0
  48. data/spec/httparty/request_spec.rb +196 -0
  49. data/spec/httparty/response_spec.rb +53 -0
  50. data/spec/httparty_spec.rb +259 -0
  51. data/spec/spec.opts +3 -0
  52. data/spec/spec_helper.rb +21 -0
  53. data/spec/string_spec.rb +27 -0
  54. data/website/css/common.css +47 -0
  55. data/website/index.html +74 -0
  56. metadata +132 -0
@@ -0,0 +1,42 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
+
3
+ describe HTTParty::Parsers::JSON do
4
+ TESTS = {
5
+ %q({"data": "G\u00fcnter"}) => {"data" => "Günter"},
6
+ %q({"returnTo":{"\/categories":"\/"}}) => {"returnTo" => {"/categories" => "/"}},
7
+ %q({returnTo:{"\/categories":"\/"}}) => {"returnTo" => {"/categories" => "/"}},
8
+ %q({"return\\"To\\":":{"\/categories":"\/"}}) => {"return\"To\":" => {"/categories" => "/"}},
9
+ %q({"returnTo":{"\/categories":1}}) => {"returnTo" => {"/categories" => 1}},
10
+ %({"returnTo":[1,"a"]}) => {"returnTo" => [1, "a"]},
11
+ %({"returnTo":[1,"\\"a\\",", "b"]}) => {"returnTo" => [1, "\"a\",", "b"]},
12
+ %({a: "'", "b": "5,000"}) => {"a" => "'", "b" => "5,000"},
13
+ %({a: "a's, b's and c's", "b": "5,000"}) => {"a" => "a's, b's and c's", "b" => "5,000"},
14
+ %({a: "2007-01-01"}) => {'a' => Date.new(2007, 1, 1)},
15
+ %({a: "2007-01-01 01:12:34 Z"}) => {'a' => Time.utc(2007, 1, 1, 1, 12, 34)},
16
+ # no time zone
17
+ %({a: "2007-01-01 01:12:34"}) => {'a' => "2007-01-01 01:12:34"},
18
+ %([]) => [],
19
+ %({}) => {},
20
+ %(1) => 1,
21
+ %("") => "",
22
+ %("\\"") => "\"",
23
+ %(null) => nil,
24
+ %(true) => true,
25
+ %(false) => false,
26
+ %q("http:\/\/test.host\/posts\/1") => "http://test.host/posts/1"
27
+ }
28
+
29
+ TESTS.each do |json, expected|
30
+ it "should decode json (#{json})" do
31
+ lambda {
32
+ HTTParty::Parsers::JSON.decode(json).should == expected
33
+ }.should_not raise_error
34
+ end
35
+ end
36
+
37
+ it "should raise error for failed decoding" do
38
+ lambda {
39
+ HTTParty::Parsers::JSON.decode(%({: 1}))
40
+ }.should raise_error(HTTParty::Parsers::JSON::ParseError)
41
+ end
42
+ end
@@ -0,0 +1,445 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
+
3
+ require "date"
4
+ require 'bigdecimal'
5
+
6
+ describe HTTParty::Parsers::XML, "#parse" do
7
+ it "should transform a simple tag with content" do
8
+ xml = "<tag>This is the contents</tag>"
9
+ HTTParty::Parsers::XML.parse(xml).should == { 'tag' => 'This is the contents' }
10
+ end
11
+
12
+ it "should work with cdata tags" do
13
+ xml = <<-END
14
+ <tag>
15
+ <![CDATA[
16
+ text inside cdata
17
+ ]]>
18
+ </tag>
19
+ END
20
+ HTTParty::Parsers::XML.parse(xml)["tag"].strip.should == "text inside cdata"
21
+ end
22
+
23
+ it "should transform a simple tag with attributes" do
24
+ xml = "<tag attr1='1' attr2='2'></tag>"
25
+ hash = { 'tag' => { 'attr1' => '1', 'attr2' => '2' } }
26
+ HTTParty::Parsers::XML.parse(xml).should == hash
27
+ end
28
+
29
+ it "should transform repeating siblings into an array" do
30
+ xml =<<-XML
31
+ <opt>
32
+ <user login="grep" fullname="Gary R Epstein" />
33
+ <user login="stty" fullname="Simon T Tyson" />
34
+ </opt>
35
+ XML
36
+
37
+ HTTParty::Parsers::XML.parse(xml)['opt']['user'].should be_an_instance_of(Array)
38
+
39
+ hash = {
40
+ 'opt' => {
41
+ 'user' => [{
42
+ 'login' => 'grep',
43
+ 'fullname' => 'Gary R Epstein'
44
+ },{
45
+ 'login' => 'stty',
46
+ 'fullname' => 'Simon T Tyson'
47
+ }]
48
+ }
49
+ }
50
+
51
+ HTTParty::Parsers::XML.parse(xml).should == hash
52
+ end
53
+
54
+ it "should not transform non-repeating siblings into an array" do
55
+ xml =<<-XML
56
+ <opt>
57
+ <user login="grep" fullname="Gary R Epstein" />
58
+ </opt>
59
+ XML
60
+
61
+ HTTParty::Parsers::XML.parse(xml)['opt']['user'].should be_an_instance_of(Hash)
62
+
63
+ hash = {
64
+ 'opt' => {
65
+ 'user' => {
66
+ 'login' => 'grep',
67
+ 'fullname' => 'Gary R Epstein'
68
+ }
69
+ }
70
+ }
71
+
72
+ HTTParty::Parsers::XML.parse(xml).should == hash
73
+ end
74
+
75
+ it "should typecast an integer" do
76
+ xml = "<tag type='integer'>10</tag>"
77
+ HTTParty::Parsers::XML.parse(xml)['tag'].should == 10
78
+ end
79
+
80
+ it "should typecast a true boolean" do
81
+ xml = "<tag type='boolean'>true</tag>"
82
+ HTTParty::Parsers::XML.parse(xml)['tag'].should be_true
83
+ end
84
+
85
+ it "should typecast a false boolean" do
86
+ ["false"].each do |w|
87
+ HTTParty::Parsers::XML.parse("<tag type='boolean'>#{w}</tag>")['tag'].should be_false
88
+ end
89
+ end
90
+
91
+ it "should typecast a datetime" do
92
+ xml = "<tag type='datetime'>2007-12-31 10:32</tag>"
93
+ HTTParty::Parsers::XML.parse(xml)['tag'].should == Time.parse( '2007-12-31 10:32' ).utc
94
+ end
95
+
96
+ it "should typecast a date" do
97
+ xml = "<tag type='date'>2007-12-31</tag>"
98
+ HTTParty::Parsers::XML.parse(xml)['tag'].should == Date.parse('2007-12-31')
99
+ end
100
+
101
+ it "should unescape html entities" do
102
+ values = {
103
+ "<" => "&lt;",
104
+ ">" => "&gt;",
105
+ '"' => "&quot;",
106
+ "'" => "&apos;",
107
+ "&" => "&amp;"
108
+ }
109
+ values.each do |k,v|
110
+ xml = "<tag>Some content #{v}</tag>"
111
+ HTTParty::Parsers::XML.parse(xml)['tag'].should match(Regexp.new(k))
112
+ end
113
+ end
114
+
115
+ it "should undasherize keys as tags" do
116
+ xml = "<tag-1>Stuff</tag-1>"
117
+ HTTParty::Parsers::XML.parse(xml).keys.should include( 'tag_1' )
118
+ end
119
+
120
+ it "should undasherize keys as attributes" do
121
+ xml = "<tag1 attr-1='1'></tag1>"
122
+ HTTParty::Parsers::XML.parse(xml)['tag1'].keys.should include( 'attr_1')
123
+ end
124
+
125
+ it "should undasherize keys as tags and attributes" do
126
+ xml = "<tag-1 attr-1='1'></tag-1>"
127
+ HTTParty::Parsers::XML.parse(xml).keys.should include( 'tag_1' )
128
+ HTTParty::Parsers::XML.parse(xml)['tag_1'].keys.should include( 'attr_1')
129
+ end
130
+
131
+ it "should render nested content correctly" do
132
+ xml = "<root><tag1>Tag1 Content <em><strong>This is strong</strong></em></tag1></root>"
133
+ HTTParty::Parsers::XML.parse(xml)['root']['tag1'].should == "Tag1 Content <em><strong>This is strong</strong></em>"
134
+ end
135
+
136
+ it "should render nested content with split text nodes correctly" do
137
+ xml = "<root>Tag1 Content<em>Stuff</em> Hi There</root>"
138
+ HTTParty::Parsers::XML.parse(xml)['root'].should == "Tag1 Content<em>Stuff</em> Hi There"
139
+ end
140
+
141
+ it "should ignore attributes when a child is a text node" do
142
+ xml = "<root attr1='1'>Stuff</root>"
143
+ HTTParty::Parsers::XML.parse(xml).should == { "root" => "Stuff" }
144
+ end
145
+
146
+ it "should ignore attributes when any child is a text node" do
147
+ xml = "<root attr1='1'>Stuff <em>in italics</em></root>"
148
+ HTTParty::Parsers::XML.parse(xml).should == { "root" => "Stuff <em>in italics</em>" }
149
+ end
150
+
151
+ it "should correctly transform multiple children" do
152
+ xml = <<-XML
153
+ <user gender='m'>
154
+ <age type='integer'>35</age>
155
+ <name>Home Simpson</name>
156
+ <dob type='date'>1988-01-01</dob>
157
+ <joined-at type='datetime'>2000-04-28 23:01</joined-at>
158
+ <is-cool type='boolean'>true</is-cool>
159
+ </user>
160
+ XML
161
+
162
+ hash = {
163
+ "user" => {
164
+ "gender" => "m",
165
+ "age" => 35,
166
+ "name" => "Home Simpson",
167
+ "dob" => Date.parse('1988-01-01'),
168
+ "joined_at" => Time.parse("2000-04-28 23:01"),
169
+ "is_cool" => true
170
+ }
171
+ }
172
+
173
+ HTTParty::Parsers::XML.parse(xml).should == hash
174
+ end
175
+
176
+ it "should properly handle nil values (ActiveSupport Compatible)" do
177
+ topic_xml = <<-EOT
178
+ <topic>
179
+ <title></title>
180
+ <id type="integer"></id>
181
+ <approved type="boolean"></approved>
182
+ <written-on type="date"></written-on>
183
+ <viewed-at type="datetime"></viewed-at>
184
+ <content type="yaml"></content>
185
+ <parent-id></parent-id>
186
+ </topic>
187
+ EOT
188
+
189
+ expected_topic_hash = {
190
+ 'title' => nil,
191
+ 'id' => nil,
192
+ 'approved' => nil,
193
+ 'written_on' => nil,
194
+ 'viewed_at' => nil,
195
+ 'content' => nil,
196
+ 'parent_id' => nil
197
+ }
198
+ HTTParty::Parsers::XML.parse(topic_xml)["topic"].should == expected_topic_hash
199
+ end
200
+
201
+ it "should handle a single record from xml (ActiveSupport Compatible)" do
202
+ topic_xml = <<-EOT
203
+ <topic>
204
+ <title>The First Topic</title>
205
+ <author-name>David</author-name>
206
+ <id type="integer">1</id>
207
+ <approved type="boolean"> true </approved>
208
+ <replies-count type="integer">0</replies-count>
209
+ <replies-close-in type="integer">2592000000</replies-close-in>
210
+ <written-on type="date">2003-07-16</written-on>
211
+ <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
212
+ <content type="yaml">--- \n1: should be an integer\n:message: Have a nice day\narray: \n- should-have-dashes: true\n should_have_underscores: true\n</content>
213
+ <author-email-address>david@loudthinking.com</author-email-address>
214
+ <parent-id></parent-id>
215
+ <ad-revenue type="decimal">1.5</ad-revenue>
216
+ <optimum-viewing-angle type="float">135</optimum-viewing-angle>
217
+ <resident type="symbol">yes</resident>
218
+ </topic>
219
+ EOT
220
+
221
+ expected_topic_hash = {
222
+ 'title' => "The First Topic",
223
+ 'author_name' => "David",
224
+ 'id' => 1,
225
+ 'approved' => true,
226
+ 'replies_count' => 0,
227
+ 'replies_close_in' => 2592000000,
228
+ 'written_on' => Date.new(2003, 7, 16),
229
+ 'viewed_at' => Time.utc(2003, 7, 16, 9, 28),
230
+ # Changed this line where the key is :message. The yaml specifies this as a symbol, and who am I to change what you specify
231
+ # The line in ActiveSupport is
232
+ # 'content' => { 'message' => "Have a nice day", 1 => "should be an integer", "array" => [{ "should-have-dashes" => true, "should_have_underscores" => true }] },
233
+ 'content' => { :message => "Have a nice day", 1 => "should be an integer", "array" => [{ "should-have-dashes" => true, "should_have_underscores" => true }] },
234
+ 'author_email_address' => "david@loudthinking.com",
235
+ 'parent_id' => nil,
236
+ 'ad_revenue' => BigDecimal("1.50"),
237
+ 'optimum_viewing_angle' => 135.0,
238
+ 'resident' => :yes
239
+ }
240
+
241
+ HTTParty::Parsers::XML.parse(topic_xml)["topic"].each do |k,v|
242
+ v.should == expected_topic_hash[k]
243
+ end
244
+ end
245
+
246
+ it "should handle multiple records (ActiveSupport Compatible)" do
247
+ topics_xml = <<-EOT
248
+ <topics type="array">
249
+ <topic>
250
+ <title>The First Topic</title>
251
+ <author-name>David</author-name>
252
+ <id type="integer">1</id>
253
+ <approved type="boolean">false</approved>
254
+ <replies-count type="integer">0</replies-count>
255
+ <replies-close-in type="integer">2592000000</replies-close-in>
256
+ <written-on type="date">2003-07-16</written-on>
257
+ <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
258
+ <content>Have a nice day</content>
259
+ <author-email-address>david@loudthinking.com</author-email-address>
260
+ <parent-id nil="true"></parent-id>
261
+ </topic>
262
+ <topic>
263
+ <title>The Second Topic</title>
264
+ <author-name>Jason</author-name>
265
+ <id type="integer">1</id>
266
+ <approved type="boolean">false</approved>
267
+ <replies-count type="integer">0</replies-count>
268
+ <replies-close-in type="integer">2592000000</replies-close-in>
269
+ <written-on type="date">2003-07-16</written-on>
270
+ <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
271
+ <content>Have a nice day</content>
272
+ <author-email-address>david@loudthinking.com</author-email-address>
273
+ <parent-id></parent-id>
274
+ </topic>
275
+ </topics>
276
+ EOT
277
+
278
+ expected_topic_hash = {
279
+ 'title' => "The First Topic",
280
+ 'author_name' => "David",
281
+ 'id' => 1,
282
+ 'approved' => false,
283
+ 'replies_count' => 0,
284
+ 'replies_close_in' => 2592000000,
285
+ 'written_on' => Date.new(2003, 7, 16),
286
+ 'viewed_at' => Time.utc(2003, 7, 16, 9, 28),
287
+ 'content' => "Have a nice day",
288
+ 'author_email_address' => "david@loudthinking.com",
289
+ 'parent_id' => nil
290
+ }
291
+ # puts HTTParty::Parsers::XML.parse(topics_xml)['topics'].first.inspect
292
+ HTTParty::Parsers::XML.parse(topics_xml)["topics"].first.each do |k,v|
293
+ v.should == expected_topic_hash[k]
294
+ end
295
+ end
296
+
297
+ it "should handle a single record from_xml with attributes other than type (ActiveSupport Compatible)" do
298
+ topic_xml = <<-EOT
299
+ <rsp stat="ok">
300
+ <photos page="1" pages="1" perpage="100" total="16">
301
+ <photo id="175756086" owner="55569174@N00" secret="0279bf37a1" server="76" title="Colored Pencil PhotoBooth Fun" ispublic="1" isfriend="0" isfamily="0"/>
302
+ </photos>
303
+ </rsp>
304
+ EOT
305
+
306
+ expected_topic_hash = {
307
+ 'id' => "175756086",
308
+ 'owner' => "55569174@N00",
309
+ 'secret' => "0279bf37a1",
310
+ 'server' => "76",
311
+ 'title' => "Colored Pencil PhotoBooth Fun",
312
+ 'ispublic' => "1",
313
+ 'isfriend' => "0",
314
+ 'isfamily' => "0",
315
+ }
316
+ HTTParty::Parsers::XML.parse(topic_xml)["rsp"]["photos"]["photo"].each do |k,v|
317
+ v.should == expected_topic_hash[k]
318
+ end
319
+ end
320
+
321
+ it "should handle an emtpy array (ActiveSupport Compatible)" do
322
+ blog_xml = <<-XML
323
+ <blog>
324
+ <posts type="array"></posts>
325
+ </blog>
326
+ XML
327
+ expected_blog_hash = {"blog" => {"posts" => []}}
328
+ HTTParty::Parsers::XML.parse(blog_xml).should == expected_blog_hash
329
+ end
330
+
331
+ it "should handle empty array with whitespace from xml (ActiveSupport Compatible)" do
332
+ blog_xml = <<-XML
333
+ <blog>
334
+ <posts type="array">
335
+ </posts>
336
+ </blog>
337
+ XML
338
+ expected_blog_hash = {"blog" => {"posts" => []}}
339
+ HTTParty::Parsers::XML.parse(blog_xml).should == expected_blog_hash
340
+ end
341
+
342
+ it "should handle array with one entry from_xml (ActiveSupport Compatible)" do
343
+ blog_xml = <<-XML
344
+ <blog>
345
+ <posts type="array">
346
+ <post>a post</post>
347
+ </posts>
348
+ </blog>
349
+ XML
350
+ expected_blog_hash = {"blog" => {"posts" => ["a post"]}}
351
+ HTTParty::Parsers::XML.parse(blog_xml).should == expected_blog_hash
352
+ end
353
+
354
+ it "should handle array with multiple entries from xml (ActiveSupport Compatible)" do
355
+ blog_xml = <<-XML
356
+ <blog>
357
+ <posts type="array">
358
+ <post>a post</post>
359
+ <post>another post</post>
360
+ </posts>
361
+ </blog>
362
+ XML
363
+ expected_blog_hash = {"blog" => {"posts" => ["a post", "another post"]}}
364
+ HTTParty::Parsers::XML.parse(blog_xml).should == expected_blog_hash
365
+ end
366
+
367
+ it "should handle file types (ActiveSupport Compatible)" do
368
+ blog_xml = <<-XML
369
+ <blog>
370
+ <logo type="file" name="logo.png" content_type="image/png">
371
+ </logo>
372
+ </blog>
373
+ XML
374
+ hash = HTTParty::Parsers::XML.parse(blog_xml)
375
+ hash.should have_key('blog')
376
+ hash['blog'].should have_key('logo')
377
+
378
+ file = hash['blog']['logo']
379
+ file.original_filename.should == 'logo.png'
380
+ file.content_type.should == 'image/png'
381
+ end
382
+
383
+ it "should handle file from xml with defaults (ActiveSupport Compatible)" do
384
+ blog_xml = <<-XML
385
+ <blog>
386
+ <logo type="file">
387
+ </logo>
388
+ </blog>
389
+ XML
390
+ file = HTTParty::Parsers::XML.parse(blog_xml)['blog']['logo']
391
+ file.original_filename.should == 'untitled'
392
+ file.content_type.should == 'application/octet-stream'
393
+ end
394
+
395
+ it "should handle xsd like types from xml (ActiveSupport Compatible)" do
396
+ bacon_xml = <<-EOT
397
+ <bacon>
398
+ <weight type="double">0.5</weight>
399
+ <price type="decimal">12.50</price>
400
+ <chunky type="boolean"> 1 </chunky>
401
+ <expires-at type="dateTime">2007-12-25T12:34:56+0000</expires-at>
402
+ <notes type="string"></notes>
403
+ <illustration type="base64Binary">YmFiZS5wbmc=</illustration>
404
+ </bacon>
405
+ EOT
406
+
407
+ expected_bacon_hash = {
408
+ 'weight' => 0.5,
409
+ 'chunky' => true,
410
+ 'price' => BigDecimal("12.50"),
411
+ 'expires_at' => Time.utc(2007,12,25,12,34,56),
412
+ 'notes' => "",
413
+ 'illustration' => "babe.png"
414
+ }
415
+
416
+ HTTParty::Parsers::XML.parse(bacon_xml)["bacon"].should == expected_bacon_hash
417
+ end
418
+
419
+ it "should let type trickle through when unknown (ActiveSupport Compatible)" do
420
+ product_xml = <<-EOT
421
+ <product>
422
+ <weight type="double">0.5</weight>
423
+ <image type="ProductImage"><filename>image.gif</filename></image>
424
+
425
+ </product>
426
+ EOT
427
+
428
+ expected_product_hash = {
429
+ 'weight' => 0.5,
430
+ 'image' => {'type' => 'ProductImage', 'filename' => 'image.gif' },
431
+ }
432
+
433
+ HTTParty::Parsers::XML.parse(product_xml)["product"].should == expected_product_hash
434
+ end
435
+
436
+ it "should handle unescaping from xml (ActiveResource Compatible)" do
437
+ xml_string = '<person><bare-string>First &amp; Last Name</bare-string><pre-escaped-string>First &amp;amp; Last Name</pre-escaped-string></person>'
438
+ expected_hash = {
439
+ 'bare_string' => 'First & Last Name',
440
+ 'pre_escaped_string' => 'First &amp; Last Name'
441
+ }
442
+
443
+ HTTParty::Parsers::XML.parse(xml_string)['person'].should == expected_hash
444
+ end
445
+ end
@@ -0,0 +1,196 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe HTTParty::Request do
4
+ def stub_response(body, code = 200)
5
+ unless @http
6
+ @http = Net::HTTP.new('localhost', 80)
7
+ @request.stub!(:http).and_return(@http)
8
+ @request.stub!(:uri).and_return(URI.parse("http://foo.com/foobar"))
9
+ end
10
+
11
+ response = Net::HTTPResponse::CODE_TO_OBJ[code.to_s].new("1.1", code, body)
12
+ response.stub!(:body).and_return(body)
13
+
14
+ @http.stub!(:request).and_return(response)
15
+ response
16
+ end
17
+
18
+ before do
19
+ @request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', :format => :xml)
20
+ end
21
+
22
+ describe "#format" do
23
+ it "should return the correct parsing format" do
24
+ @request.format.should == :xml
25
+ end
26
+ end
27
+
28
+ describe 'http' do
29
+ it "should use ssl for port 443" do
30
+ request = HTTParty::Request.new(Net::HTTP::Get, 'https://api.foo.com/v1:443')
31
+ request.send(:http).use_ssl?.should == true
32
+ end
33
+
34
+ it 'should not use ssl for port 80' do
35
+ request = HTTParty::Request.new(Net::HTTP::Get, 'http://foobar.com')
36
+ @request.send(:http).use_ssl?.should == false
37
+ end
38
+
39
+ it "should use basic auth when configured" do
40
+ @request.options[:basic_auth] = {:username => 'foobar', :password => 'secret'}
41
+ @request.send(:setup_raw_request)
42
+ @request.instance_variable_get(:@raw_request)['authorization'].should_not be_nil
43
+ end
44
+ end
45
+
46
+ describe '#format_from_mimetype' do
47
+ it 'should handle text/xml' do
48
+ ["text/xml", "text/xml; charset=iso8859-1"].each do |ct|
49
+ @request.send(:format_from_mimetype, ct).should == :xml
50
+ end
51
+ end
52
+
53
+ it 'should handle application/xml' do
54
+ ["application/xml", "application/xml; charset=iso8859-1"].each do |ct|
55
+ @request.send(:format_from_mimetype, ct).should == :xml
56
+ end
57
+ end
58
+
59
+ it 'should handle text/json' do
60
+ ["text/json", "text/json; charset=iso8859-1"].each do |ct|
61
+ @request.send(:format_from_mimetype, ct).should == :json
62
+ end
63
+ end
64
+
65
+ it 'should handle application/json' do
66
+ ["application/json", "application/json; charset=iso8859-1"].each do |ct|
67
+ @request.send(:format_from_mimetype, ct).should == :json
68
+ end
69
+ end
70
+
71
+ it 'should handle text/javascript' do
72
+ ["text/javascript", "text/javascript; charset=iso8859-1"].each do |ct|
73
+ @request.send(:format_from_mimetype, ct).should == :json
74
+ end
75
+ end
76
+
77
+ it 'should handle application/javascript' do
78
+ ["application/javascript", "application/javascript; charset=iso8859-1"].each do |ct|
79
+ @request.send(:format_from_mimetype, ct).should == :json
80
+ end
81
+ end
82
+ end
83
+
84
+ describe 'parsing responses' do
85
+ it 'should handle xml automatically' do
86
+ xml = %q[<books><book><id>1234</id><name>Foo Bar!</name></book></books>]
87
+ @request.options[:format] = :xml
88
+ @request.send(:parse_response, xml).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
89
+ end
90
+
91
+ it 'should handle json automatically' do
92
+ json = %q[{"books": {"book": {"name": "Foo Bar!", "id": "1234"}}}]
93
+ @request.options[:format] = :json
94
+ @request.send(:parse_response, json).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
95
+ end
96
+
97
+ it 'should handle yaml automatically' do
98
+ yaml = "books: \n book: \n name: Foo Bar!\n id: \"1234\"\n"
99
+ @request.options[:format] = :yaml
100
+ @request.send(:parse_response, yaml).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
101
+ end
102
+
103
+ it "should include any HTTP headers in the returned response" do
104
+ @request.options[:format] = :html
105
+ response = stub_response "Content"
106
+ response.initialize_http_header("key" => "value")
107
+
108
+ @request.perform.headers.should == { "key" => ["value"] }
109
+ end
110
+
111
+ describe 'with non-200 responses' do
112
+
113
+ it 'should return a valid object for 4xx response' do
114
+ stub_response '<foo><bar>yes</bar></foo>', 401
115
+ resp = @request.perform
116
+ resp.code.should == 401
117
+ resp.body.should == "<foo><bar>yes</bar></foo>"
118
+ resp['foo']['bar'].should == "yes"
119
+ end
120
+
121
+ it 'should return a valid object for 5xx response' do
122
+ stub_response '<foo><bar>error</bar></foo>', 500
123
+ resp = @request.perform
124
+ resp.code.should == 500
125
+ resp.body.should == "<foo><bar>error</bar></foo>"
126
+ resp['foo']['bar'].should == "error"
127
+ end
128
+
129
+ end
130
+ end
131
+
132
+ it "should not attempt to parse empty responses" do
133
+ stub_response "", 204
134
+
135
+ @request.options[:format] = :xml
136
+ @request.perform.should be_nil
137
+ end
138
+
139
+ it "should not fail for missing mime type" do
140
+ stub_response "Content for you"
141
+ @request.options[:format] = :html
142
+ @request.perform.should == 'Content for you'
143
+ end
144
+
145
+ describe "a request that redirects" do
146
+ before(:each) do
147
+ @redirect = stub_response("", 302)
148
+ @redirect['location'] = '/foo'
149
+
150
+ @ok = stub_response('<hash><foo>bar</foo></hash>', 200)
151
+ end
152
+
153
+ describe "once" do
154
+ before(:each) do
155
+ @http.stub!(:request).and_return(@redirect, @ok)
156
+ end
157
+
158
+ it "should be handled by GET transparently" do
159
+ @request.perform.should == {"hash" => {"foo" => "bar"}}
160
+ end
161
+
162
+ it "should be handled by POST transparently" do
163
+ @request.http_method = Net::HTTP::Post
164
+ @request.perform.should == {"hash" => {"foo" => "bar"}}
165
+ end
166
+
167
+ it "should be handled by DELETE transparently" do
168
+ @request.http_method = Net::HTTP::Delete
169
+ @request.perform.should == {"hash" => {"foo" => "bar"}}
170
+ end
171
+
172
+ it "should be handled by PUT transparently" do
173
+ @request.http_method = Net::HTTP::Put
174
+ @request.perform.should == {"hash" => {"foo" => "bar"}}
175
+ end
176
+ end
177
+
178
+ describe "infinitely" do
179
+ before(:each) do
180
+ @http.stub!(:request).and_return(@redirect)
181
+ end
182
+
183
+ it "should raise an exception" do
184
+ lambda { @request.perform }.should raise_error(HTTParty::RedirectionTooDeep)
185
+ end
186
+ end
187
+ end
188
+ end
189
+
190
+ describe HTTParty::Request, "with POST http method" do
191
+ it "should raise argument error if query is not a hash" do
192
+ lambda {
193
+ HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :format => :xml, :query => 'astring').perform
194
+ }.should raise_error(ArgumentError)
195
+ end
196
+ end