httsoiree 0.13.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 (94) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.travis.yml +7 -0
  4. data/Gemfile +14 -0
  5. data/Guardfile +16 -0
  6. data/History +303 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.md +77 -0
  9. data/Rakefile +12 -0
  10. data/bin/httparty +117 -0
  11. data/cucumber.yml +1 -0
  12. data/examples/aaws.rb +32 -0
  13. data/examples/basic.rb +28 -0
  14. data/examples/crack.rb +19 -0
  15. data/examples/custom_parsers.rb +67 -0
  16. data/examples/delicious.rb +37 -0
  17. data/examples/google.rb +16 -0
  18. data/examples/headers_and_user_agents.rb +6 -0
  19. data/examples/logging.rb +38 -0
  20. data/examples/nokogiri_html_parser.rb +22 -0
  21. data/examples/rubyurl.rb +14 -0
  22. data/examples/stackexchange.rb +24 -0
  23. data/examples/tripit_sign_in.rb +33 -0
  24. data/examples/twitter.rb +31 -0
  25. data/examples/whoismyrep.rb +10 -0
  26. data/features/basic_authentication.feature +20 -0
  27. data/features/command_line.feature +7 -0
  28. data/features/deals_with_http_error_codes.feature +26 -0
  29. data/features/digest_authentication.feature +20 -0
  30. data/features/handles_compressed_responses.feature +27 -0
  31. data/features/handles_multiple_formats.feature +57 -0
  32. data/features/steps/env.rb +22 -0
  33. data/features/steps/httparty_response_steps.rb +52 -0
  34. data/features/steps/httparty_steps.rb +43 -0
  35. data/features/steps/mongrel_helper.rb +94 -0
  36. data/features/steps/remote_service_steps.rb +74 -0
  37. data/features/supports_read_timeout_option.feature +13 -0
  38. data/features/supports_redirection.feature +22 -0
  39. data/features/supports_timeout_option.feature +13 -0
  40. data/httparty.gemspec +25 -0
  41. data/lib/httparty/connection_adapter.rb +188 -0
  42. data/lib/httparty/cookie_hash.rb +22 -0
  43. data/lib/httparty/core_extensions.rb +32 -0
  44. data/lib/httparty/exceptions.rb +29 -0
  45. data/lib/httparty/hash_conversions.rb +51 -0
  46. data/lib/httparty/logger/apache_logger.rb +22 -0
  47. data/lib/httparty/logger/curl_logger.rb +48 -0
  48. data/lib/httparty/logger/logger.rb +18 -0
  49. data/lib/httparty/module_inheritable_attributes.rb +56 -0
  50. data/lib/httparty/net_digest_auth.rb +84 -0
  51. data/lib/httparty/parser.rb +141 -0
  52. data/lib/httparty/request.rb +339 -0
  53. data/lib/httparty/response/headers.rb +31 -0
  54. data/lib/httparty/response.rb +72 -0
  55. data/lib/httparty/version.rb +3 -0
  56. data/lib/httparty.rb +618 -0
  57. data/lib/httsoiree.rb +3 -0
  58. data/script/release +42 -0
  59. data/spec/fixtures/delicious.xml +23 -0
  60. data/spec/fixtures/empty.xml +0 -0
  61. data/spec/fixtures/google.html +3 -0
  62. data/spec/fixtures/ssl/generate.sh +29 -0
  63. data/spec/fixtures/ssl/generated/1fe462c2.0 +16 -0
  64. data/spec/fixtures/ssl/generated/bogushost.crt +13 -0
  65. data/spec/fixtures/ssl/generated/ca.crt +16 -0
  66. data/spec/fixtures/ssl/generated/ca.key +15 -0
  67. data/spec/fixtures/ssl/generated/selfsigned.crt +14 -0
  68. data/spec/fixtures/ssl/generated/server.crt +13 -0
  69. data/spec/fixtures/ssl/generated/server.key +15 -0
  70. data/spec/fixtures/ssl/openssl-exts.cnf +9 -0
  71. data/spec/fixtures/twitter.csv +2 -0
  72. data/spec/fixtures/twitter.json +1 -0
  73. data/spec/fixtures/twitter.xml +403 -0
  74. data/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
  75. data/spec/httparty/connection_adapter_spec.rb +370 -0
  76. data/spec/httparty/cookie_hash_spec.rb +83 -0
  77. data/spec/httparty/exception_spec.rb +23 -0
  78. data/spec/httparty/logger/apache_logger_spec.rb +41 -0
  79. data/spec/httparty/logger/curl_logger_spec.rb +18 -0
  80. data/spec/httparty/logger/logger_spec.rb +22 -0
  81. data/spec/httparty/net_digest_auth_spec.rb +152 -0
  82. data/spec/httparty/parser_spec.rb +165 -0
  83. data/spec/httparty/request_spec.rb +774 -0
  84. data/spec/httparty/response_spec.rb +221 -0
  85. data/spec/httparty/ssl_spec.rb +74 -0
  86. data/spec/httparty_spec.rb +783 -0
  87. data/spec/spec.opts +2 -0
  88. data/spec/spec_helper.rb +37 -0
  89. data/spec/support/ssl_test_helper.rb +47 -0
  90. data/spec/support/ssl_test_server.rb +80 -0
  91. data/spec/support/stub_response.rb +43 -0
  92. data/website/css/common.css +47 -0
  93. data/website/index.html +73 -0
  94. metadata +215 -0
@@ -0,0 +1,783 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe HTTParty do
4
+ before(:each) do
5
+ @klass = Class.new
6
+ @klass.instance_eval { include HTTParty }
7
+ end
8
+
9
+ describe "AllowedFormats deprecated" do
10
+ before do
11
+ Kernel.stub(:warn)
12
+ end
13
+
14
+ it "warns with a deprecation message" do
15
+ Kernel.should_receive(:warn).with("Deprecated: Use HTTParty::Parser::SupportedFormats")
16
+ HTTParty::AllowedFormats
17
+ end
18
+
19
+ it "returns HTTPart::Parser::SupportedFormats" do
20
+ HTTParty::AllowedFormats.should == HTTParty::Parser::SupportedFormats
21
+ end
22
+ end
23
+
24
+ describe "pem" do
25
+ it 'should set the pem content' do
26
+ @klass.pem 'PEM-CONTENT'
27
+ @klass.default_options[:pem].should == 'PEM-CONTENT'
28
+ end
29
+
30
+ it "should set the password to nil if it's not provided" do
31
+ @klass.pem 'PEM-CONTENT'
32
+ @klass.default_options[:pem_password].should be_nil
33
+ end
34
+
35
+ it 'should set the password' do
36
+ @klass.pem 'PEM-CONTENT', 'PASSWORD'
37
+ @klass.default_options[:pem_password].should == 'PASSWORD'
38
+ end
39
+ end
40
+
41
+ describe "pkcs12" do
42
+ it 'should set the p12 content' do
43
+ @klass.pkcs12 'P12-CONTENT', 'PASSWORD'
44
+ @klass.default_options[:p12].should == 'P12-CONTENT'
45
+ end
46
+
47
+ it 'should set the password' do
48
+ @klass.pkcs12 'P12-CONTENT', 'PASSWORD'
49
+ @klass.default_options[:p12_password].should == 'PASSWORD'
50
+ end
51
+ end
52
+
53
+ describe 'ssl_version' do
54
+ it 'should set the ssl_version content' do
55
+ @klass.ssl_version :SSLv3
56
+ @klass.default_options[:ssl_version].should == :SSLv3
57
+ end
58
+ end
59
+
60
+ describe 'ciphers' do
61
+ it 'should set the ciphers content' do
62
+ @klass.default_options[:ciphers].should be_nil
63
+ @klass.ciphers 'RC4-SHA'
64
+ @klass.default_options[:ciphers].should == 'RC4-SHA'
65
+ end
66
+ end
67
+
68
+ describe 'http_proxy' do
69
+ it 'should set the address' do
70
+ @klass.http_proxy 'proxy.foo.com', 80
71
+ options = @klass.default_options
72
+ options[:http_proxyaddr].should == 'proxy.foo.com'
73
+ options[:http_proxyport].should == 80
74
+ end
75
+
76
+ it 'should set the proxy user and pass when they are provided' do
77
+ @klass.http_proxy 'proxy.foo.com', 80, 'user', 'pass'
78
+ options = @klass.default_options
79
+ options[:http_proxyuser].should == 'user'
80
+ options[:http_proxypass].should == 'pass'
81
+ end
82
+ end
83
+
84
+ describe "base uri" do
85
+ before(:each) do
86
+ @klass.base_uri('api.foo.com/v1')
87
+ end
88
+
89
+ it "should have reader" do
90
+ @klass.base_uri.should == 'http://api.foo.com/v1'
91
+ end
92
+
93
+ it 'should have writer' do
94
+ @klass.base_uri('http://api.foobar.com')
95
+ @klass.base_uri.should == 'http://api.foobar.com'
96
+ end
97
+
98
+ it 'should not modify the parameter during assignment' do
99
+ uri = 'http://api.foobar.com'
100
+ @klass.base_uri(uri)
101
+ uri.should == 'http://api.foobar.com'
102
+ end
103
+ end
104
+
105
+ describe ".disable_rails_query_string_format" do
106
+ it "sets the query string normalizer to HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER" do
107
+ @klass.disable_rails_query_string_format
108
+ @klass.default_options[:query_string_normalizer].should == HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER
109
+ end
110
+ end
111
+
112
+ describe ".normalize_base_uri" do
113
+ it "should add http if not present for non ssl requests" do
114
+ uri = HTTParty.normalize_base_uri('api.foobar.com')
115
+ uri.should == 'http://api.foobar.com'
116
+ end
117
+
118
+ it "should add https if not present for ssl requests" do
119
+ uri = HTTParty.normalize_base_uri('api.foo.com/v1:443')
120
+ uri.should == 'https://api.foo.com/v1:443'
121
+ end
122
+
123
+ it "should not remove https for ssl requests" do
124
+ uri = HTTParty.normalize_base_uri('https://api.foo.com/v1:443')
125
+ uri.should == 'https://api.foo.com/v1:443'
126
+ end
127
+
128
+ it 'should not modify the parameter' do
129
+ uri = 'http://api.foobar.com'
130
+ HTTParty.normalize_base_uri(uri)
131
+ uri.should == 'http://api.foobar.com'
132
+ end
133
+
134
+ it "should not treat uri's with a port of 4430 as ssl" do
135
+ uri = HTTParty.normalize_base_uri('http://api.foo.com:4430/v1')
136
+ uri.should == 'http://api.foo.com:4430/v1'
137
+ end
138
+ end
139
+
140
+ describe "headers" do
141
+ def expect_headers(header={})
142
+ HTTParty::Request.should_receive(:new) \
143
+ .with(anything, anything, hash_including({ headers: header })) \
144
+ .and_return(mock("mock response", perform: nil))
145
+ end
146
+
147
+ it "should default to empty hash" do
148
+ @klass.headers.should == {}
149
+ end
150
+
151
+ it "should be able to be updated" do
152
+ init_headers = {foo: 'bar', baz: 'spax'}
153
+ @klass.headers init_headers
154
+ @klass.headers.should == init_headers
155
+ end
156
+
157
+ it "uses the class headers when sending a request" do
158
+ expect_headers(foo: 'bar')
159
+ @klass.headers(foo: 'bar')
160
+ @klass.get('')
161
+ end
162
+
163
+ it "merges class headers with request headers" do
164
+ expect_headers(baz: 'spax', foo: 'bar')
165
+ @klass.headers(foo: 'bar')
166
+ @klass.get('', headers: {baz: 'spax'})
167
+ end
168
+
169
+ it 'overrides class headers with request headers' do
170
+ expect_headers(baz: 'spax', foo: 'baz')
171
+ @klass.headers(foo: 'bar')
172
+ @klass.get('', headers: {baz: 'spax', foo: 'baz'})
173
+ end
174
+
175
+ context "with cookies" do
176
+ it 'utilizes the class-level cookies' do
177
+ expect_headers(foo: 'bar', 'cookie' => 'type=snickerdoodle')
178
+ @klass.headers(foo: 'bar')
179
+ @klass.cookies(type: 'snickerdoodle')
180
+ @klass.get('')
181
+ end
182
+
183
+ it 'adds cookies to the headers' do
184
+ expect_headers(foo: 'bar', 'cookie' => 'type=snickerdoodle')
185
+ @klass.headers(foo: 'bar')
186
+ @klass.get('', cookies: {type: 'snickerdoodle'})
187
+ end
188
+
189
+ it 'doesnt modify default_options' do
190
+ @klass.headers.should == {}
191
+ expect_headers('cookie' => 'type=snickerdoodle')
192
+ @klass.get('', cookies: {type: 'snickerdoodle'})
193
+ @klass.default_options[:headers].should == {}
194
+ end
195
+
196
+ it 'adds optional cookies to the optional headers' do
197
+ expect_headers(baz: 'spax', 'cookie' => 'type=snickerdoodle')
198
+ @klass.get('', cookies: {type: 'snickerdoodle'}, headers: {baz: 'spax'})
199
+ end
200
+ end
201
+ end
202
+
203
+ describe "cookies" do
204
+ def expect_cookie_header(s)
205
+ HTTParty::Request.should_receive(:new) \
206
+ .with(anything, anything, hash_including({ headers: { "cookie" => s } })) \
207
+ .and_return(mock("mock response", perform: nil))
208
+ end
209
+
210
+ it "should not be in the headers by default" do
211
+ HTTParty::Request.stub!(:new).and_return(stub(nil, perform: nil))
212
+ @klass.get("")
213
+ @klass.headers.keys.should_not include("cookie")
214
+ end
215
+
216
+ it "should raise an ArgumentError if passed a non-Hash" do
217
+ lambda do
218
+ @klass.cookies("nonsense")
219
+ end.should raise_error(ArgumentError)
220
+ end
221
+
222
+ it "should allow a cookie to be specified with a one-off request" do
223
+ expect_cookie_header "type=snickerdoodle"
224
+ @klass.get("", cookies: { type: "snickerdoodle" })
225
+ end
226
+
227
+ describe "when a cookie is set at the class level" do
228
+ before(:each) do
229
+ @klass.cookies({ type: "snickerdoodle" })
230
+ end
231
+
232
+ it "should include that cookie in the request" do
233
+ expect_cookie_header "type=snickerdoodle"
234
+ @klass.get("")
235
+ end
236
+
237
+ it "should pass the proper cookies when requested multiple times" do
238
+ 2.times do
239
+ expect_cookie_header "type=snickerdoodle"
240
+ @klass.get("")
241
+ end
242
+ end
243
+
244
+ it "should allow the class defaults to be overridden" do
245
+ expect_cookie_header "type=chocolate_chip"
246
+
247
+ @klass.get("", cookies: { type: "chocolate_chip" })
248
+ end
249
+ end
250
+
251
+ describe "in a class with multiple methods that use different cookies" do
252
+ before(:each) do
253
+ @klass.instance_eval do
254
+ def first_method
255
+ get("first_method", cookies: { first_method_cookie: "foo" })
256
+ end
257
+
258
+ def second_method
259
+ get("second_method", cookies: { second_method_cookie: "foo" })
260
+ end
261
+ end
262
+ end
263
+
264
+ it "should not allow cookies used in one method to carry over into other methods" do
265
+ expect_cookie_header "first_method_cookie=foo"
266
+ @klass.first_method
267
+
268
+ expect_cookie_header "second_method_cookie=foo"
269
+ @klass.second_method
270
+ end
271
+ end
272
+ end
273
+
274
+ describe "default params" do
275
+ it "should default to empty hash" do
276
+ @klass.default_params.should == {}
277
+ end
278
+
279
+ it "should be able to be updated" do
280
+ new_defaults = {foo: 'bar', baz: 'spax'}
281
+ @klass.default_params new_defaults
282
+ @klass.default_params.should == new_defaults
283
+ end
284
+ end
285
+
286
+ describe "default timeout" do
287
+ it "should default to nil" do
288
+ @klass.default_options[:timeout].should == nil
289
+ end
290
+
291
+ it "should support updating" do
292
+ @klass.default_timeout 10
293
+ @klass.default_options[:timeout].should == 10
294
+ end
295
+
296
+ it "should support floats" do
297
+ @klass.default_timeout 0.5
298
+ @klass.default_options[:timeout].should == 0.5
299
+ end
300
+ end
301
+
302
+ describe "debug_output" do
303
+ it "stores the given stream as a default_option" do
304
+ @klass.debug_output $stdout
305
+ @klass.default_options[:debug_output].should == $stdout
306
+ end
307
+
308
+ it "stores the $stderr stream by default" do
309
+ @klass.debug_output
310
+ @klass.default_options[:debug_output].should == $stderr
311
+ end
312
+ end
313
+
314
+ describe "basic http authentication" do
315
+ it "should work" do
316
+ @klass.basic_auth 'foobar', 'secret'
317
+ @klass.default_options[:basic_auth].should == {username: 'foobar', password: 'secret'}
318
+ end
319
+ end
320
+
321
+ describe "digest http authentication" do
322
+ it "should work" do
323
+ @klass.digest_auth 'foobar', 'secret'
324
+ @klass.default_options[:digest_auth].should == {username: 'foobar', password: 'secret'}
325
+ end
326
+ end
327
+
328
+ describe "parser" do
329
+ class CustomParser
330
+ def self.parse(body)
331
+ return {sexy: true}
332
+ end
333
+ end
334
+
335
+ let(:parser) do
336
+ Proc.new{ |data, format| CustomParser.parse(data) }
337
+ end
338
+
339
+ it "should set parser options" do
340
+ @klass.parser parser
341
+ @klass.default_options[:parser].should == parser
342
+ end
343
+
344
+ it "should be able parse response with custom parser" do
345
+ @klass.parser parser
346
+ FakeWeb.register_uri(:get, 'http://twitter.com/statuses/public_timeline.xml', body: 'tweets')
347
+ custom_parsed_response = @klass.get('http://twitter.com/statuses/public_timeline.xml')
348
+ custom_parsed_response[:sexy].should == true
349
+ end
350
+
351
+ it "raises UnsupportedFormat when the parser cannot handle the format" do
352
+ @klass.format :json
353
+ class MyParser < HTTParty::Parser
354
+ SupportedFormats = {}
355
+ end unless defined?(MyParser)
356
+ expect do
357
+ @klass.parser MyParser
358
+ end.to raise_error(HTTParty::UnsupportedFormat)
359
+ end
360
+
361
+ it 'does not validate format whe custom parser is a proc' do
362
+ expect do
363
+ @klass.format :json
364
+ @klass.parser lambda {|body, format|}
365
+ end.to_not raise_error(HTTParty::UnsupportedFormat)
366
+ end
367
+ end
368
+
369
+ describe "connection_adapter" do
370
+ let(:uri) { 'http://google.com/api.json' }
371
+ let(:connection_adapter) { mock('CustomConnectionAdapter') }
372
+
373
+ it "should set the connection_adapter" do
374
+ @klass.connection_adapter connection_adapter
375
+ @klass.default_options[:connection_adapter].should be connection_adapter
376
+ end
377
+
378
+ it "should set the connection_adapter_options when provided" do
379
+ options = {foo: :bar}
380
+ @klass.connection_adapter connection_adapter, options
381
+ @klass.default_options[:connection_adapter_options].should be options
382
+ end
383
+
384
+ it "should not set the connection_adapter_options when not provided" do
385
+ @klass.connection_adapter connection_adapter
386
+ @klass.default_options[:connection_adapter_options].should be_nil
387
+ end
388
+
389
+ it "should process a request with a connection from the adapter" do
390
+ connection_adapter_options = {foo: :bar}
391
+ connection_adapter.should_receive(:call) do |u,o|
392
+ o[:connection_adapter_options].should == connection_adapter_options
393
+ HTTParty::ConnectionAdapter.call(u,o)
394
+ end.with(URI.parse(uri), kind_of(Hash))
395
+ FakeWeb.register_uri(:get, uri, body: 'stuff')
396
+ @klass.connection_adapter connection_adapter, connection_adapter_options
397
+ @klass.get(uri).should == 'stuff'
398
+ end
399
+ end
400
+
401
+ describe "format" do
402
+ it "should allow xml" do
403
+ @klass.format :xml
404
+ @klass.default_options[:format].should == :xml
405
+ end
406
+
407
+ it "should allow csv" do
408
+ @klass.format :csv
409
+ @klass.default_options[:format].should == :csv
410
+ end
411
+
412
+ it "should allow json" do
413
+ @klass.format :json
414
+ @klass.default_options[:format].should == :json
415
+ end
416
+
417
+ it "should allow plain" do
418
+ @klass.format :plain
419
+ @klass.default_options[:format].should == :plain
420
+ end
421
+
422
+ it 'should not allow funky format' do
423
+ lambda do
424
+ @klass.format :foobar
425
+ end.should raise_error(HTTParty::UnsupportedFormat)
426
+ end
427
+
428
+ it 'should only print each format once with an exception' do
429
+ lambda do
430
+ @klass.format :foobar
431
+ end.should raise_error(HTTParty::UnsupportedFormat, "':foobar' Must be one of: csv, html, json, plain, xml")
432
+ end
433
+
434
+ it 'sets the default parser' do
435
+ @klass.default_options[:parser].should be_nil
436
+ @klass.format :json
437
+ @klass.default_options[:parser].should == HTTParty::Parser
438
+ end
439
+
440
+ it 'does not reset parser to the default parser' do
441
+ my_parser = lambda {}
442
+ @klass.parser my_parser
443
+ @klass.format :json
444
+ @klass.parser.should == my_parser
445
+ end
446
+ end
447
+
448
+ describe "#no_follow" do
449
+ it "sets no_follow to false by default" do
450
+ @klass.no_follow
451
+ @klass.default_options[:no_follow].should be_false
452
+ end
453
+
454
+ it "sets the no_follow option to true" do
455
+ @klass.no_follow true
456
+ @klass.default_options[:no_follow].should be_true
457
+ end
458
+ end
459
+
460
+ describe "#maintain_method_across_redirects" do
461
+ it "sets maintain_method_across_redirects to true by default" do
462
+ @klass.maintain_method_across_redirects
463
+ @klass.default_options[:maintain_method_across_redirects].should be_true
464
+ end
465
+
466
+ it "sets the maintain_method_across_redirects option to false" do
467
+ @klass.maintain_method_across_redirects false
468
+ @klass.default_options[:maintain_method_across_redirects].should be_false
469
+ end
470
+ end
471
+
472
+ describe "#resend_on_redirect" do
473
+ it "sets resend_on_redirect to true by default" do
474
+ @klass.resend_on_redirect
475
+ @klass.default_options[:resend_on_redirect].should be_true
476
+ end
477
+
478
+ it "sets resend_on_redirect option to false" do
479
+ @klass.resend_on_redirect false
480
+ @klass.default_options[:resend_on_redirect].should be_false
481
+ end
482
+ end
483
+
484
+ describe ".follow_redirects" do
485
+ it "sets follow redirects to true by default" do
486
+ @klass.follow_redirects
487
+ @klass.default_options[:follow_redirects].should be_true
488
+ end
489
+
490
+ it "sets the follow_redirects option to false" do
491
+ @klass.follow_redirects false
492
+ @klass.default_options[:follow_redirects].should be_false
493
+ end
494
+ end
495
+
496
+ describe ".query_string_normalizer" do
497
+ it "sets the query_string_normalizer option" do
498
+ normalizer = proc {}
499
+ @klass.query_string_normalizer normalizer
500
+ @klass.default_options[:query_string_normalizer].should == normalizer
501
+ end
502
+ end
503
+
504
+ describe "with explicit override of automatic redirect handling" do
505
+ before do
506
+ @request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', format: :xml, no_follow: true)
507
+ @redirect = stub_response 'first redirect', 302
508
+ @redirect['location'] = 'http://foo.com/bar'
509
+ HTTParty::Request.stub(new: @request)
510
+ end
511
+
512
+ it "should fail with redirected GET" do
513
+ lambda do
514
+ @error = @klass.get('/foo', no_follow: true)
515
+ end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
516
+ end
517
+
518
+ it "should fail with redirected POST" do
519
+ lambda do
520
+ @klass.post('/foo', no_follow: true)
521
+ end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
522
+ end
523
+
524
+ it "should fail with redirected PATCH" do
525
+ lambda do
526
+ @klass.patch('/foo', no_follow: true)
527
+ end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
528
+ end
529
+
530
+ it "should fail with redirected DELETE" do
531
+ lambda do
532
+ @klass.delete('/foo', no_follow: true)
533
+ end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
534
+ end
535
+
536
+ it "should fail with redirected MOVE" do
537
+ lambda do
538
+ @klass.move('/foo', no_follow: true)
539
+ end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
540
+ end
541
+
542
+ it "should fail with redirected COPY" do
543
+ lambda do
544
+ @klass.copy('/foo', no_follow: true)
545
+ end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
546
+ end
547
+
548
+ it "should fail with redirected PUT" do
549
+ lambda do
550
+ @klass.put('/foo', no_follow: true)
551
+ end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
552
+ end
553
+
554
+ it "should fail with redirected HEAD" do
555
+ lambda do
556
+ @klass.head('/foo', no_follow: true)
557
+ end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
558
+ end
559
+
560
+ it "should fail with redirected OPTIONS" do
561
+ lambda do
562
+ @klass.options('/foo', no_follow: true)
563
+ end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
564
+ end
565
+ end
566
+
567
+ describe "with multiple class definitions" do
568
+ before(:each) do
569
+ @klass.instance_eval do
570
+ base_uri "http://first.com"
571
+ default_params one: 1
572
+ end
573
+
574
+ @additional_klass = Class.new
575
+ @additional_klass.instance_eval do
576
+ include HTTParty
577
+ base_uri "http://second.com"
578
+ default_params two: 2
579
+ end
580
+ end
581
+
582
+ it "should not run over each others options" do
583
+ @klass.default_options.should == { base_uri: 'http://first.com', default_params: { one: 1 } }
584
+ @additional_klass.default_options.should == { base_uri: 'http://second.com', default_params: { two: 2 } }
585
+ end
586
+ end
587
+
588
+ describe "two child classes inheriting from one parent" do
589
+ before(:each) do
590
+ @parent = Class.new do
591
+ include HTTParty
592
+ def self.name
593
+ "Parent"
594
+ end
595
+ end
596
+
597
+ @child1 = Class.new(@parent)
598
+ @child2 = Class.new(@parent)
599
+ end
600
+
601
+ it "does not modify each others inherited attributes" do
602
+ @child1.default_params joe: "alive"
603
+ @child2.default_params joe: "dead"
604
+
605
+ @child1.default_options.should == { default_params: {joe: "alive"} }
606
+ @child2.default_options.should == { default_params: {joe: "dead"} }
607
+
608
+ @parent.default_options.should == { }
609
+ end
610
+
611
+ it "inherits default_options from the superclass" do
612
+ @parent.basic_auth 'user', 'password'
613
+ @child1.default_options.should == {basic_auth: {username: 'user', password: 'password'}}
614
+ @child1.basic_auth 'u', 'p' # modifying child1 has no effect on child2
615
+ @child2.default_options.should == {basic_auth: {username: 'user', password: 'password'}}
616
+ end
617
+
618
+ it "doesn't modify the parent's default options" do
619
+ @parent.basic_auth 'user', 'password'
620
+
621
+ @child1.basic_auth 'u', 'p'
622
+ @child1.default_options.should == {basic_auth: {username: 'u', password: 'p'}}
623
+
624
+ @child1.basic_auth 'email', 'token'
625
+ @child1.default_options.should == {basic_auth: {username: 'email', password: 'token'}}
626
+
627
+ @parent.default_options.should == {basic_auth: {username: 'user', password: 'password'}}
628
+ end
629
+
630
+ it "doesn't modify hashes in the parent's default options" do
631
+ @parent.headers 'Accept' => 'application/json'
632
+ @child1.headers 'Accept' => 'application/xml'
633
+
634
+ @parent.default_options[:headers].should == {'Accept' => 'application/json'}
635
+ @child1.default_options[:headers].should == {'Accept' => 'application/xml'}
636
+ end
637
+
638
+ it "works with lambda values" do
639
+ @child1.default_options[:imaginary_option] = lambda { "This is a new lambda "}
640
+ @child1.default_options[:imaginary_option].should be_a Proc
641
+ end
642
+
643
+ it 'should dup the proc on the child class' do
644
+ imaginary_option = lambda { 2 * 3.14 }
645
+ @parent.default_options[:imaginary_option] = imaginary_option
646
+ @parent.default_options[:imaginary_option].call.should == imaginary_option.call
647
+ @child1.default_options[:imaginary_option]
648
+ @child1.default_options[:imaginary_option].call.should == imaginary_option.call
649
+ @child1.default_options[:imaginary_option].should_not be_equal imaginary_option
650
+ end
651
+
652
+ it "inherits default_cookies from the parent class" do
653
+ @parent.cookies 'type' => 'chocolate_chip'
654
+ @child1.default_cookies.should == {"type" => "chocolate_chip"}
655
+ @child1.cookies 'type' => 'snickerdoodle'
656
+ @child1.default_cookies.should == {"type" => "snickerdoodle"}
657
+ @child2.default_cookies.should == {"type" => "chocolate_chip"}
658
+ end
659
+
660
+ it "doesn't modify the parent's default cookies" do
661
+ @parent.cookies 'type' => 'chocolate_chip'
662
+
663
+ @child1.cookies 'type' => 'snickerdoodle'
664
+ @child1.default_cookies.should == {"type" => "snickerdoodle"}
665
+
666
+ @parent.default_cookies.should == {"type" => "chocolate_chip"}
667
+ end
668
+ end
669
+
670
+ describe "grand parent with inherited callback" do
671
+ before do
672
+ @grand_parent = Class.new do
673
+ def self.inherited(subclass)
674
+ subclass.instance_variable_set(:@grand_parent, true)
675
+ end
676
+ end
677
+ @parent = Class.new(@grand_parent) do
678
+ include HTTParty
679
+ end
680
+ end
681
+ it "continues running the #inherited on the parent" do
682
+ child = Class.new(@parent)
683
+ child.instance_variable_get(:@grand_parent).should be_true
684
+ end
685
+ end
686
+
687
+ describe "#get" do
688
+ it "should be able to get html" do
689
+ stub_http_response_with('google.html')
690
+ HTTParty.get('http://www.google.com').should == file_fixture('google.html')
691
+ end
692
+
693
+ it "should be able to get chunked html" do
694
+ chunks = ["Chunk1", "Chunk2", "Chunk3", "Chunk4"]
695
+ stub_chunked_http_response_with(chunks)
696
+
697
+ HTTParty.get('http://www.google.com') do |fragment|
698
+ chunks.should include(fragment)
699
+ end.should == chunks.join
700
+ end
701
+
702
+ it "should be able parse response type json automatically" do
703
+ stub_http_response_with('twitter.json')
704
+ tweets = HTTParty.get('http://twitter.com/statuses/public_timeline.json')
705
+ tweets.size.should == 20
706
+ tweets.first['user'].should == {
707
+ "name" => "Pyk",
708
+ "url" => nil,
709
+ "id" => "7694602",
710
+ "description" => nil,
711
+ "protected" => false,
712
+ "screen_name" => "Pyk",
713
+ "followers_count" => 1,
714
+ "location" => "Opera Plaza, California",
715
+ "profile_image_url" => "http://static.twitter.com/images/default_profile_normal.png"
716
+ }
717
+ end
718
+
719
+ it "should be able parse response type xml automatically" do
720
+ stub_http_response_with('twitter.xml')
721
+ tweets = HTTParty.get('http://twitter.com/statuses/public_timeline.xml')
722
+ tweets['statuses'].size.should == 20
723
+ tweets['statuses'].first['user'].should == {
724
+ "name" => "Magic 8 Bot",
725
+ "url" => nil,
726
+ "id" => "17656026",
727
+ "description" => "ask me a question",
728
+ "protected" => "false",
729
+ "screen_name" => "magic8bot",
730
+ "followers_count" => "90",
731
+ "profile_image_url" => "http://s3.amazonaws.com/twitter_production/profile_images/65565851/8ball_large_normal.jpg",
732
+ "location" => nil
733
+ }
734
+ end
735
+
736
+ it "should be able parse response type csv automatically" do
737
+ stub_http_response_with('twitter.csv')
738
+ profile = HTTParty.get('http://twitter.com/statuses/profile.csv')
739
+ profile.size.should == 2
740
+ profile[0].should == ["name","url","id","description","protected","screen_name","followers_count","profile_image_url","location"]
741
+ profile[1].should == ["Magic 8 Bot",nil,"17656026","ask me a question","false","magic8bot","90","http://s3.amazonaws.com/twitter_production/profile_images/65565851/8ball_large_normal.jpg",nil]
742
+ end
743
+
744
+ it "should not get undefined method add_node for nil class for the following xml" do
745
+ stub_http_response_with('undefined_method_add_node_for_nil.xml')
746
+ result = HTTParty.get('http://foobar.com')
747
+ result.should == {"Entities"=>{"href"=>"https://s3-sandbox.parature.com/api/v1/5578/5633/Account", "results"=>"0", "total"=>"0", "page_size"=>"25", "page"=>"1"}}
748
+ end
749
+
750
+ it "should parse empty response fine" do
751
+ stub_http_response_with('empty.xml')
752
+ result = HTTParty.get('http://foobar.com')
753
+ result.should be_nil
754
+ end
755
+
756
+ it "should accept http URIs" do
757
+ stub_http_response_with('google.html')
758
+ lambda do
759
+ HTTParty.get('http://google.com')
760
+ end.should_not raise_error(HTTParty::UnsupportedURIScheme)
761
+ end
762
+
763
+ it "should accept https URIs" do
764
+ stub_http_response_with('google.html')
765
+ lambda do
766
+ HTTParty.get('https://google.com')
767
+ end.should_not raise_error(HTTParty::UnsupportedURIScheme)
768
+ end
769
+
770
+ it "should accept webcal URIs" do
771
+ stub_http_response_with('google.html')
772
+ lambda do
773
+ HTTParty.get('webcal://google.com')
774
+ end.should_not raise_error(HTTParty::UnsupportedURIScheme)
775
+ end
776
+
777
+ it "should raise an InvalidURIError on URIs that can't be parsed at all" do
778
+ lambda do
779
+ HTTParty.get("It's the one that says 'Bad URI'")
780
+ end.should raise_error(URI::InvalidURIError)
781
+ end
782
+ end
783
+ end