dnclabs-httparty 0.6.1.2010090201

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 (69) hide show
  1. data/.gitignore +7 -0
  2. data/History +216 -0
  3. data/MIT-LICENSE +20 -0
  4. data/Manifest +47 -0
  5. data/README.rdoc +54 -0
  6. data/Rakefile +89 -0
  7. data/VERSION +1 -0
  8. data/bin/httparty +108 -0
  9. data/cucumber.yml +1 -0
  10. data/examples/aaws.rb +32 -0
  11. data/examples/basic.rb +11 -0
  12. data/examples/custom_parsers.rb +67 -0
  13. data/examples/delicious.rb +37 -0
  14. data/examples/google.rb +16 -0
  15. data/examples/rubyurl.rb +14 -0
  16. data/examples/twitter.rb +31 -0
  17. data/examples/whoismyrep.rb +10 -0
  18. data/features/basic_authentication.feature +20 -0
  19. data/features/command_line.feature +7 -0
  20. data/features/deals_with_http_error_codes.feature +26 -0
  21. data/features/digest_authentication.feature +20 -0
  22. data/features/handles_compressed_responses.feature +19 -0
  23. data/features/handles_multiple_formats.feature +34 -0
  24. data/features/steps/env.rb +23 -0
  25. data/features/steps/httparty_response_steps.rb +26 -0
  26. data/features/steps/httparty_steps.rb +27 -0
  27. data/features/steps/mongrel_helper.rb +94 -0
  28. data/features/steps/remote_service_steps.rb +69 -0
  29. data/features/supports_redirection.feature +22 -0
  30. data/features/supports_timeout_option.feature +13 -0
  31. data/httparty.gemspec +146 -0
  32. data/lib/httparty.rb +365 -0
  33. data/lib/httparty/cookie_hash.rb +22 -0
  34. data/lib/httparty/core_extensions.rb +31 -0
  35. data/lib/httparty/exceptions.rb +26 -0
  36. data/lib/httparty/module_inheritable_attributes.rb +34 -0
  37. data/lib/httparty/net_digest_auth.rb +35 -0
  38. data/lib/httparty/parser.rb +141 -0
  39. data/lib/httparty/request.rb +231 -0
  40. data/lib/httparty/response.rb +79 -0
  41. data/spec/fixtures/delicious.xml +23 -0
  42. data/spec/fixtures/empty.xml +0 -0
  43. data/spec/fixtures/google.html +3 -0
  44. data/spec/fixtures/ssl/generate.sh +29 -0
  45. data/spec/fixtures/ssl/generated/1fe462c2.0 +15 -0
  46. data/spec/fixtures/ssl/generated/bogushost.crt +13 -0
  47. data/spec/fixtures/ssl/generated/ca.crt +15 -0
  48. data/spec/fixtures/ssl/generated/ca.key +15 -0
  49. data/spec/fixtures/ssl/generated/selfsigned.crt +14 -0
  50. data/spec/fixtures/ssl/generated/server.crt +13 -0
  51. data/spec/fixtures/ssl/generated/server.key +15 -0
  52. data/spec/fixtures/ssl/openssl-exts.cnf +9 -0
  53. data/spec/fixtures/twitter.json +1 -0
  54. data/spec/fixtures/twitter.xml +403 -0
  55. data/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
  56. data/spec/httparty/cookie_hash_spec.rb +71 -0
  57. data/spec/httparty/parser_spec.rb +155 -0
  58. data/spec/httparty/request_spec.rb +430 -0
  59. data/spec/httparty/response_spec.rb +188 -0
  60. data/spec/httparty/ssl_spec.rb +54 -0
  61. data/spec/httparty_spec.rb +570 -0
  62. data/spec/spec.opts +3 -0
  63. data/spec/spec_helper.rb +20 -0
  64. data/spec/support/ssl_test_helper.rb +25 -0
  65. data/spec/support/ssl_test_server.rb +69 -0
  66. data/spec/support/stub_response.rb +30 -0
  67. data/website/css/common.css +47 -0
  68. data/website/index.html +73 -0
  69. metadata +245 -0
@@ -0,0 +1,188 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe HTTParty::Response do
4
+ before do
5
+ @last_modified = Date.new(2010, 1, 15).to_s
6
+ @content_length = '1024'
7
+ @response_object = {'foo' => 'bar'}
8
+ @response_object = Net::HTTPOK.new('1.1', 200, 'OK')
9
+ @response_object.stub(:body => "{foo:'bar'}")
10
+ @response_object['last-modified'] = @last_modified
11
+ @response_object['content-length'] = @content_length
12
+ @parsed_response = {"foo" => "bar"}
13
+ @response = HTTParty::Response.new(@response_object, @parsed_response)
14
+ end
15
+
16
+ describe ".underscore" do
17
+ it "works with one capitalized word" do
18
+ HTTParty::Response.underscore("Accepted").should == "accepted"
19
+ end
20
+
21
+ it "works with titlecase" do
22
+ HTTParty::Response.underscore("BadGateway").should == "bad_gateway"
23
+ end
24
+
25
+ it "works with all caps" do
26
+ HTTParty::Response.underscore("OK").should == "ok"
27
+ end
28
+ end
29
+
30
+ describe "initialization" do
31
+ it "should set the Net::HTTP Response" do
32
+ @response.response.should == @response_object
33
+ end
34
+
35
+ it "should set body" do
36
+ @response.body.should == @response_object.body
37
+ end
38
+
39
+ it "should set code" do
40
+ @response.code.should.to_s == @response_object.code
41
+ end
42
+
43
+ it "should set code as a Fixnum" do
44
+ @response.code.should be_an_instance_of(Fixnum)
45
+ end
46
+ end
47
+
48
+ it "returns response headers" do
49
+ response = HTTParty::Response.new(@response_object, @parsed_response)
50
+ response.headers.should == {'last-modified' => [@last_modified], 'content-length' => [@content_length]}
51
+ end
52
+
53
+ it "should send missing methods to delegate" do
54
+ response = HTTParty::Response.new(@response_object, {'foo' => 'bar'})
55
+ response['foo'].should == 'bar'
56
+ end
57
+
58
+ it "should be able to iterate if it is array" do
59
+ response = HTTParty::Response.new(@response_object, [{'foo' => 'bar'}, {'foo' => 'baz'}])
60
+ response.size.should == 2
61
+ expect {
62
+ response.each { |item| }
63
+ }.to_not raise_error
64
+ end
65
+
66
+ it "allows headers to be accessed by mixed-case names in hash notation" do
67
+ response = HTTParty::Response.new(@response_object, @parsed_response)
68
+ response.headers['Content-LENGTH'].should == @content_length
69
+ end
70
+
71
+ it "returns a comma-delimited value when multiple values exist" do
72
+ @response_object.add_field 'set-cookie', 'csrf_id=12345; path=/'
73
+ @response_object.add_field 'set-cookie', '_github_ses=A123CdE; path=/'
74
+ response = HTTParty::Response.new(@response_object, @parsed_response)
75
+ response.headers['set-cookie'].should == "csrf_id=12345; path=/, _github_ses=A123CdE; path=/"
76
+ end
77
+
78
+ # Backwards-compatibility - previously, #headers returned a Hash
79
+ it "responds to hash methods" do
80
+ response = HTTParty::Response.new(@response_object, @parsed_response)
81
+ hash_methods = {}.methods - response.headers.methods
82
+ hash_methods.each do |method_name|
83
+ response.headers.respond_to?(method_name).should be_true
84
+ end
85
+ end
86
+
87
+ xit "should allow hashes to be accessed with dot notation" do
88
+ response = HTTParty::Response.new({'foo' => 'bar'}, "{foo:'bar'}", 200, 'OK')
89
+ response.foo.should == 'bar'
90
+ end
91
+
92
+ xit "should allow nested hashes to be accessed with dot notation" do
93
+ response = HTTParty::Response.new({'foo' => {'bar' => 'baz'}}, "{foo: {bar:'baz'}}", 200, 'OK')
94
+ response.foo.should == {'bar' => 'baz'}
95
+ response.foo.bar.should == 'baz'
96
+ end
97
+
98
+ describe "semantic methods for response codes" do
99
+ def response_mock(klass)
100
+ r = klass.new('', '', '')
101
+ r.stub(:body)
102
+ r
103
+ end
104
+
105
+ context "major codes" do
106
+ it "is information" do
107
+ net_response = response_mock(Net::HTTPInformation)
108
+ response = HTTParty::Response.new(net_response, '')
109
+ response.information?.should be_true
110
+ end
111
+
112
+ it "is success" do
113
+ net_response = response_mock(Net::HTTPSuccess)
114
+ response = HTTParty::Response.new(net_response, '')
115
+ response.success?.should be_true
116
+ end
117
+
118
+ it "is redirection" do
119
+ net_response = response_mock(Net::HTTPRedirection)
120
+ response = HTTParty::Response.new(net_response, '')
121
+ response.redirection?.should be_true
122
+ end
123
+
124
+ it "is client error" do
125
+ net_response = response_mock(Net::HTTPClientError)
126
+ response = HTTParty::Response.new(net_response, '')
127
+ response.client_error?.should be_true
128
+ end
129
+
130
+ it "is server error" do
131
+ net_response = response_mock(Net::HTTPServerError)
132
+ response = HTTParty::Response.new(net_response, '')
133
+ response.server_error?.should be_true
134
+ end
135
+ end
136
+
137
+ context "for specific codes" do
138
+ SPECIFIC_CODES = {
139
+ :accepted? => Net::HTTPAccepted,
140
+ :bad_gateway? => Net::HTTPBadGateway,
141
+ :bad_request? => Net::HTTPBadRequest,
142
+ :conflict? => Net::HTTPConflict,
143
+ :continue? => Net::HTTPContinue,
144
+ :created? => Net::HTTPCreated,
145
+ :expectation_failed? => Net::HTTPExpectationFailed,
146
+ :forbidden? => Net::HTTPForbidden,
147
+ :found? => Net::HTTPFound,
148
+ :gateway_time_out? => Net::HTTPGatewayTimeOut,
149
+ :gone? => Net::HTTPGone,
150
+ :internal_server_error? => Net::HTTPInternalServerError,
151
+ :length_required? => Net::HTTPLengthRequired,
152
+ :method_not_allowed? => Net::HTTPMethodNotAllowed,
153
+ :moved_permanently? => Net::HTTPMovedPermanently,
154
+ :multiple_choice? => Net::HTTPMultipleChoice,
155
+ :no_content? => Net::HTTPNoContent,
156
+ :non_authoritative_information? => Net::HTTPNonAuthoritativeInformation,
157
+ :not_acceptable? => Net::HTTPNotAcceptable,
158
+ :not_found? => Net::HTTPNotFound,
159
+ :not_implemented? => Net::HTTPNotImplemented,
160
+ :not_modified? => Net::HTTPNotModified,
161
+ :ok? => Net::HTTPOK,
162
+ :partial_content? => Net::HTTPPartialContent,
163
+ :payment_required? => Net::HTTPPaymentRequired,
164
+ :precondition_failed? => Net::HTTPPreconditionFailed,
165
+ :proxy_authentication_required? => Net::HTTPProxyAuthenticationRequired,
166
+ :request_entity_too_large? => Net::HTTPRequestEntityTooLarge,
167
+ :request_time_out? => Net::HTTPRequestTimeOut,
168
+ :request_uri_too_long? => Net::HTTPRequestURITooLong,
169
+ :requested_range_not_satisfiable? => Net::HTTPRequestedRangeNotSatisfiable,
170
+ :reset_content? => Net::HTTPResetContent,
171
+ :see_other? => Net::HTTPSeeOther,
172
+ :service_unavailable? => Net::HTTPServiceUnavailable,
173
+ :switch_protocol? => Net::HTTPSwitchProtocol,
174
+ :temporary_redirect? => Net::HTTPTemporaryRedirect,
175
+ :unauthorized? => Net::HTTPUnauthorized,
176
+ :unsupported_media_type? => Net::HTTPUnsupportedMediaType,
177
+ :use_proxy? => Net::HTTPUseProxy,
178
+ :version_not_supported? => Net::HTTPVersionNotSupported
179
+ }.each do |method, klass|
180
+ it "responds to #{method}" do
181
+ net_response = response_mock(klass)
182
+ response = HTTParty::Response.new(net_response, '')
183
+ response.__send__(method).should be_true
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,54 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe HTTParty::Request do
4
+ context "SSL certificate verification" do
5
+ before do
6
+ FakeWeb.allow_net_connect = true # enable network connections just for this test
7
+ end
8
+
9
+ after do
10
+ FakeWeb.allow_net_connect = false # Restore allow_net_connect value for testing
11
+ end
12
+
13
+ it "should work with when no trusted CA list is specified" do
14
+ ssl_verify_test(nil, nil, "selfsigned.crt").should == {'success' => true}
15
+ end
16
+
17
+ it "should work with when no trusted CA list is specified, even with a bogus hostname" do
18
+ ssl_verify_test(nil, nil, "bogushost.crt").should == {'success' => true}
19
+ end
20
+
21
+ it "should work when using ssl_ca_file with a self-signed CA" do
22
+ ssl_verify_test(:ssl_ca_file, "selfsigned.crt", "selfsigned.crt").should == {'success' => true}
23
+ end
24
+
25
+ it "should work when using ssl_ca_file with a certificate authority" do
26
+ ssl_verify_test(:ssl_ca_file, "ca.crt", "server.crt").should == {'success' => true}
27
+ end
28
+ it "should work when using ssl_ca_path with a certificate authority" do
29
+ ssl_verify_test(:ssl_ca_path, ".", "server.crt").should == {'success' => true}
30
+ end
31
+
32
+ it "should fail when using ssl_ca_file and the server uses an unrecognized certificate authority" do
33
+ lambda do
34
+ ssl_verify_test(:ssl_ca_file, "ca.crt", "selfsigned.crt")
35
+ end.should raise_error(OpenSSL::SSL::SSLError)
36
+ end
37
+ it "should fail when using ssl_ca_path and the server uses an unrecognized certificate authority" do
38
+ lambda do
39
+ ssl_verify_test(:ssl_ca_path, ".", "selfsigned.crt")
40
+ end.should raise_error(OpenSSL::SSL::SSLError)
41
+ end
42
+
43
+ it "should fail when using ssl_ca_file and the server uses a bogus hostname" do
44
+ lambda do
45
+ ssl_verify_test(:ssl_ca_file, "ca.crt", "bogushost.crt")
46
+ end.should raise_error(OpenSSL::SSL::SSLError)
47
+ end
48
+ it "should fail when using ssl_ca_path and the server uses a bogus hostname" do
49
+ lambda do
50
+ ssl_verify_test(:ssl_ca_path, ".", "bogushost.crt")
51
+ end.should raise_error(OpenSSL::SSL::SSLError)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,570 @@
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
+ it "warns with a deprecation message" do
14
+ Kernel.should_receive(:warn).with("Deprecated: Use HTTParty::Parser::SupportedFormats")
15
+ HTTParty::AllowedFormats
16
+ end
17
+
18
+ it "returns HTTPart::Parser::SupportedFormats" do
19
+ HTTParty::AllowedFormats.should == HTTParty::Parser::SupportedFormats
20
+ end
21
+ end
22
+
23
+ describe "base uri" do
24
+ before(:each) do
25
+ @klass.base_uri('api.foo.com/v1')
26
+ end
27
+
28
+ it "should have reader" do
29
+ @klass.base_uri.should == 'http://api.foo.com/v1'
30
+ end
31
+
32
+ it 'should have writer' do
33
+ @klass.base_uri('http://api.foobar.com')
34
+ @klass.base_uri.should == 'http://api.foobar.com'
35
+ end
36
+
37
+ it 'should not modify the parameter during assignment' do
38
+ uri = 'http://api.foobar.com'
39
+ @klass.base_uri(uri)
40
+ uri.should == 'http://api.foobar.com'
41
+ end
42
+ end
43
+
44
+ describe "#normalize_base_uri" do
45
+ it "should add http if not present for non ssl requests" do
46
+ uri = HTTParty.normalize_base_uri('api.foobar.com')
47
+ uri.should == 'http://api.foobar.com'
48
+ end
49
+
50
+ it "should add https if not present for ssl requests" do
51
+ uri = HTTParty.normalize_base_uri('api.foo.com/v1:443')
52
+ uri.should == 'https://api.foo.com/v1:443'
53
+ end
54
+
55
+ it "should not remove https for ssl requests" do
56
+ uri = HTTParty.normalize_base_uri('https://api.foo.com/v1:443')
57
+ uri.should == 'https://api.foo.com/v1:443'
58
+ end
59
+
60
+ it 'should not modify the parameter' do
61
+ uri = 'http://api.foobar.com'
62
+ HTTParty.normalize_base_uri(uri)
63
+ uri.should == 'http://api.foobar.com'
64
+ end
65
+ end
66
+
67
+ describe "headers" do
68
+ def expect_headers(header={})
69
+ HTTParty::Request.should_receive(:new) \
70
+ .with(anything, anything, hash_including({ :headers => header })) \
71
+ .and_return(mock("mock response", :perform => nil))
72
+ end
73
+
74
+ it "should default to empty hash" do
75
+ @klass.headers.should == {}
76
+ end
77
+
78
+ it "should be able to be updated" do
79
+ init_headers = {:foo => 'bar', :baz => 'spax'}
80
+ @klass.headers init_headers
81
+ @klass.headers.should == init_headers
82
+ end
83
+
84
+ it "uses the class headers when sending a request" do
85
+ expect_headers(:foo => 'bar')
86
+ @klass.headers(:foo => 'bar')
87
+ @klass.get('')
88
+ end
89
+
90
+ it "overwrites class headers when passing in headers" do
91
+ expect_headers(:baz => 'spax')
92
+ @klass.headers(:foo => 'bar')
93
+ @klass.get('', :headers => {:baz => 'spax'})
94
+ end
95
+
96
+ context "with cookies" do
97
+ it 'utilizes the class-level cookies' do
98
+ expect_headers(:foo => 'bar', 'cookie' => 'type=snickerdoodle')
99
+ @klass.headers(:foo => 'bar')
100
+ @klass.cookies(:type => 'snickerdoodle')
101
+ @klass.get('')
102
+ end
103
+
104
+ it 'adds cookies to the headers' do
105
+ expect_headers(:foo => 'bar', 'cookie' => 'type=snickerdoodle')
106
+ @klass.headers(:foo => 'bar')
107
+ @klass.get('', :cookies => {:type => 'snickerdoodle'})
108
+ end
109
+
110
+ it 'adds optional cookies to the optional headers' do
111
+ expect_headers(:baz => 'spax', 'cookie' => 'type=snickerdoodle')
112
+ @klass.get('', :cookies => {:type => 'snickerdoodle'}, :headers => {:baz => 'spax'})
113
+ end
114
+ end
115
+ end
116
+
117
+ describe "cookies" do
118
+ def expect_cookie_header(s)
119
+ HTTParty::Request.should_receive(:new) \
120
+ .with(anything, anything, hash_including({ :headers => { "cookie" => s } })) \
121
+ .and_return(mock("mock response", :perform => nil))
122
+ end
123
+
124
+ it "should not be in the headers by default" do
125
+ HTTParty::Request.stub!(:new).and_return(stub(nil, :perform => nil))
126
+ @klass.get("")
127
+ @klass.headers.keys.should_not include("cookie")
128
+ end
129
+
130
+ it "should raise an ArgumentError if passed a non-Hash" do
131
+ lambda do
132
+ @klass.cookies("nonsense")
133
+ end.should raise_error(ArgumentError)
134
+ end
135
+
136
+ it "should allow a cookie to be specified with a one-off request" do
137
+ expect_cookie_header "type=snickerdoodle"
138
+ @klass.get("", :cookies => { :type => "snickerdoodle" })
139
+ end
140
+
141
+ describe "when a cookie is set at the class level" do
142
+ before(:each) do
143
+ @klass.cookies({ :type => "snickerdoodle" })
144
+ end
145
+
146
+ it "should include that cookie in the request" do
147
+ expect_cookie_header "type=snickerdoodle"
148
+ @klass.get("")
149
+ end
150
+
151
+ it "should pass the proper cookies when requested multiple times" do
152
+ 2.times do
153
+ expect_cookie_header "type=snickerdoodle"
154
+ @klass.get("")
155
+ end
156
+ end
157
+
158
+ it "should allow the class defaults to be overridden" do
159
+ expect_cookie_header "type=chocolate_chip"
160
+
161
+ @klass.get("", :cookies => { :type => "chocolate_chip" })
162
+ end
163
+ end
164
+
165
+ describe "in a class with multiple methods that use different cookies" do
166
+ before(:each) do
167
+ @klass.instance_eval do
168
+ def first_method
169
+ get("first_method", :cookies => { :first_method_cookie => "foo" })
170
+ end
171
+
172
+ def second_method
173
+ get("second_method", :cookies => { :second_method_cookie => "foo" })
174
+ end
175
+ end
176
+ end
177
+
178
+ it "should not allow cookies used in one method to carry over into other methods" do
179
+ expect_cookie_header "first_method_cookie=foo"
180
+ @klass.first_method
181
+
182
+ expect_cookie_header "second_method_cookie=foo"
183
+ @klass.second_method
184
+ end
185
+ end
186
+ end
187
+
188
+ describe "default params" do
189
+ it "should default to empty hash" do
190
+ @klass.default_params.should == {}
191
+ end
192
+
193
+ it "should be able to be updated" do
194
+ new_defaults = {:foo => 'bar', :baz => 'spax'}
195
+ @klass.default_params new_defaults
196
+ @klass.default_params.should == new_defaults
197
+ end
198
+ end
199
+
200
+ describe "default timeout" do
201
+ it "should default to nil" do
202
+ @klass.default_options[:timeout].should == nil
203
+ end
204
+
205
+ it "should support updating" do
206
+ @klass.default_timeout 10
207
+ @klass.default_options[:timeout].should == 10
208
+ end
209
+ end
210
+
211
+ describe "debug_output" do
212
+ it "stores the given stream as a default_option" do
213
+ @klass.debug_output $stdout
214
+ @klass.default_options[:debug_output].should == $stdout
215
+ end
216
+
217
+ it "stores the $stderr stream by default" do
218
+ @klass.debug_output
219
+ @klass.default_options[:debug_output].should == $stderr
220
+ end
221
+ end
222
+
223
+ describe "basic http authentication" do
224
+ it "should work" do
225
+ @klass.basic_auth 'foobar', 'secret'
226
+ @klass.default_options[:basic_auth].should == {:username => 'foobar', :password => 'secret'}
227
+ end
228
+ end
229
+
230
+ describe "digest http authentication" do
231
+ it "should work" do
232
+ @klass.digest_auth 'foobar', 'secret'
233
+ @klass.default_options[:digest_auth].should == {:username => 'foobar', :password => 'secret'}
234
+ end
235
+ end
236
+
237
+ describe "parser" do
238
+ class CustomParser
239
+ def self.parse(body)
240
+ return {:sexy => true}
241
+ end
242
+ end
243
+
244
+ let(:parser) do
245
+ Proc.new{ |data, format| CustomParser.parse(data) }
246
+ end
247
+
248
+ it "should set parser options" do
249
+ @klass.parser parser
250
+ @klass.default_options[:parser].should == parser
251
+ end
252
+
253
+ it "should be able parse response with custom parser" do
254
+ @klass.parser parser
255
+ FakeWeb.register_uri(:get, 'http://twitter.com/statuses/public_timeline.xml', :body => 'tweets')
256
+ custom_parsed_response = @klass.get('http://twitter.com/statuses/public_timeline.xml')
257
+ custom_parsed_response[:sexy].should == true
258
+ end
259
+
260
+ it "raises UnsupportedFormat when the parser cannot handle the format" do
261
+ @klass.format :json
262
+ class MyParser < HTTParty::Parser
263
+ SupportedFormats = {}
264
+ end unless defined?(MyParser)
265
+ expect do
266
+ @klass.parser MyParser
267
+ end.to raise_error(HTTParty::UnsupportedFormat)
268
+ end
269
+
270
+ it 'does not validate format whe custom parser is a proc' do
271
+ expect do
272
+ @klass.format :json
273
+ @klass.parser lambda {|body, format|}
274
+ end.to_not raise_error(HTTParty::UnsupportedFormat)
275
+ end
276
+ end
277
+
278
+ describe "format" do
279
+ it "should allow xml" do
280
+ @klass.format :xml
281
+ @klass.default_options[:format].should == :xml
282
+ end
283
+
284
+ it "should allow json" do
285
+ @klass.format :json
286
+ @klass.default_options[:format].should == :json
287
+ end
288
+
289
+ it "should allow yaml" do
290
+ @klass.format :yaml
291
+ @klass.default_options[:format].should == :yaml
292
+ end
293
+
294
+ it "should allow plain" do
295
+ @klass.format :plain
296
+ @klass.default_options[:format].should == :plain
297
+ end
298
+
299
+ it 'should not allow funky format' do
300
+ lambda do
301
+ @klass.format :foobar
302
+ end.should raise_error(HTTParty::UnsupportedFormat)
303
+ end
304
+
305
+ it 'should only print each format once with an exception' do
306
+ lambda do
307
+ @klass.format :foobar
308
+ end.should raise_error(HTTParty::UnsupportedFormat, "':foobar' Must be one of: html, json, plain, xml, yaml")
309
+ end
310
+
311
+ it 'sets the default parser' do
312
+ @klass.default_options[:parser].should be_nil
313
+ @klass.format :json
314
+ @klass.default_options[:parser].should == HTTParty::Parser
315
+ end
316
+
317
+ it 'does not reset parser to the default parser' do
318
+ my_parser = lambda {}
319
+ @klass.parser my_parser
320
+ @klass.format :json
321
+ @klass.parser.should == my_parser
322
+ end
323
+ end
324
+
325
+ describe "#no_follow" do
326
+ it "sets no_follow to false by default" do
327
+ @klass.no_follow
328
+ @klass.default_options[:no_follow].should be_false
329
+ end
330
+
331
+ it "sets the no_follow option to true" do
332
+ @klass.no_follow true
333
+ @klass.default_options[:no_follow].should be_true
334
+ end
335
+ end
336
+
337
+ describe "#maintain_method_across_redirects" do
338
+ it "sets maintain_method_across_redirects to true by default" do
339
+ @klass.maintain_method_across_redirects
340
+ @klass.default_options[:maintain_method_across_redirects].should be_true
341
+ end
342
+
343
+ it "sets the maintain_method_across_redirects option to false" do
344
+ @klass.maintain_method_across_redirects false
345
+ @klass.default_options[:maintain_method_across_redirects].should be_false
346
+ end
347
+ end
348
+
349
+ describe "with explicit override of automatic redirect handling" do
350
+ before do
351
+ @request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', :format => :xml, :no_follow => true)
352
+ @redirect = stub_response 'first redirect', 302
353
+ @redirect['location'] = 'http://foo.com/bar'
354
+ HTTParty::Request.stub(:new => @request)
355
+ end
356
+
357
+ it "should fail with redirected GET" do
358
+ lambda do
359
+ @error = @klass.get('/foo', :no_follow => true)
360
+ end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
361
+ end
362
+
363
+ it "should fail with redirected POST" do
364
+ lambda do
365
+ @klass.post('/foo', :no_follow => true)
366
+ end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
367
+ end
368
+
369
+ it "should fail with redirected DELETE" do
370
+ lambda do
371
+ @klass.delete('/foo', :no_follow => true)
372
+ end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
373
+ end
374
+
375
+ it "should fail with redirected PUT" do
376
+ lambda do
377
+ @klass.put('/foo', :no_follow => true)
378
+ end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
379
+ end
380
+
381
+ it "should fail with redirected HEAD" do
382
+ lambda do
383
+ @klass.head('/foo', :no_follow => true)
384
+ end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
385
+ end
386
+
387
+ it "should fail with redirected OPTIONS" do
388
+ lambda do
389
+ @klass.options('/foo', :no_follow => true)
390
+ end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
391
+ end
392
+ end
393
+
394
+ describe "with multiple class definitions" do
395
+ before(:each) do
396
+ @klass.instance_eval do
397
+ base_uri "http://first.com"
398
+ default_params :one => 1
399
+ end
400
+
401
+ @additional_klass = Class.new
402
+ @additional_klass.instance_eval do
403
+ include HTTParty
404
+ base_uri "http://second.com"
405
+ default_params :two => 2
406
+ end
407
+ end
408
+
409
+ it "should not run over each others options" do
410
+ @klass.default_options.should == { :base_uri => 'http://first.com', :default_params => { :one => 1 } }
411
+ @additional_klass.default_options.should == { :base_uri => 'http://second.com', :default_params => { :two => 2 } }
412
+ end
413
+ end
414
+
415
+ describe "two child classes inheriting from one parent" do
416
+ before(:each) do
417
+ @parent = Class.new do
418
+ include HTTParty
419
+ def self.name
420
+ "Parent"
421
+ end
422
+ end
423
+
424
+ @child1 = Class.new(@parent)
425
+ @child2 = Class.new(@parent)
426
+ end
427
+
428
+ it "does not modify each others inherited attributes" do
429
+ @child1.default_params :joe => "alive"
430
+ @child2.default_params :joe => "dead"
431
+
432
+ @child1.default_options.should == { :default_params => {:joe => "alive"} }
433
+ @child2.default_options.should == { :default_params => {:joe => "dead"} }
434
+
435
+ @parent.default_options.should == { }
436
+ end
437
+
438
+ it "inherits default_options from the superclass" do
439
+ @parent.basic_auth 'user', 'password'
440
+ @child1.default_options.should == {:basic_auth => {:username => 'user', :password => 'password'}}
441
+ @child1.basic_auth 'u', 'p' # modifying child1 has no effect on child2
442
+ @child2.default_options.should == {:basic_auth => {:username => 'user', :password => 'password'}}
443
+ end
444
+
445
+ it "doesn't modify the parent's default options" do
446
+ @parent.basic_auth 'user', 'password'
447
+
448
+ @child1.basic_auth 'u', 'p'
449
+ @child1.default_options.should == {:basic_auth => {:username => 'u', :password => 'p'}}
450
+
451
+ @child1.basic_auth 'email', 'token'
452
+ @child1.default_options.should == {:basic_auth => {:username => 'email', :password => 'token'}}
453
+
454
+ @parent.default_options.should == {:basic_auth => {:username => 'user', :password => 'password'}}
455
+ end
456
+
457
+ it "inherits default_cookies from the parent class" do
458
+ @parent.cookies 'type' => 'chocolate_chip'
459
+ @child1.default_cookies.should == {"type" => "chocolate_chip"}
460
+ @child1.cookies 'type' => 'snickerdoodle'
461
+ @child1.default_cookies.should == {"type" => "snickerdoodle"}
462
+ @child2.default_cookies.should == {"type" => "chocolate_chip"}
463
+ end
464
+
465
+ it "doesn't modify the parent's default cookies" do
466
+ @parent.cookies 'type' => 'chocolate_chip'
467
+
468
+ @child1.cookies 'type' => 'snickerdoodle'
469
+ @child1.default_cookies.should == {"type" => "snickerdoodle"}
470
+
471
+ @parent.default_cookies.should == {"type" => "chocolate_chip"}
472
+ end
473
+ end
474
+
475
+ describe "grand parent with inherited callback" do
476
+ before do
477
+ @grand_parent = Class.new do
478
+ def self.inherited(subclass)
479
+ subclass.instance_variable_set(:@grand_parent, true)
480
+ end
481
+ end
482
+ @parent = Class.new(@grand_parent) do
483
+ include HTTParty
484
+ end
485
+ end
486
+ it "continues running the #inherited on the parent" do
487
+ child = Class.new(@parent)
488
+ child.instance_variable_get(:@grand_parent).should be_true
489
+ end
490
+ end
491
+
492
+ describe "#get" do
493
+ it "should be able to get html" do
494
+ stub_http_response_with('google.html')
495
+ HTTParty.get('http://www.google.com').should == file_fixture('google.html')
496
+ end
497
+
498
+ it "should be able parse response type json automatically" do
499
+ stub_http_response_with('twitter.json')
500
+ tweets = HTTParty.get('http://twitter.com/statuses/public_timeline.json')
501
+ tweets.size.should == 20
502
+ tweets.first['user'].should == {
503
+ "name" => "Pyk",
504
+ "url" => nil,
505
+ "id" => "7694602",
506
+ "description" => nil,
507
+ "protected" => false,
508
+ "screen_name" => "Pyk",
509
+ "followers_count" => 1,
510
+ "location" => "Opera Plaza, California",
511
+ "profile_image_url" => "http://static.twitter.com/images/default_profile_normal.png"
512
+ }
513
+ end
514
+
515
+ it "should be able parse response type xml automatically" do
516
+ stub_http_response_with('twitter.xml')
517
+ tweets = HTTParty.get('http://twitter.com/statuses/public_timeline.xml')
518
+ tweets['statuses'].size.should == 20
519
+ tweets['statuses'].first['user'].should == {
520
+ "name" => "Magic 8 Bot",
521
+ "url" => nil,
522
+ "id" => "17656026",
523
+ "description" => "ask me a question",
524
+ "protected" => "false",
525
+ "screen_name" => "magic8bot",
526
+ "followers_count" => "90",
527
+ "profile_image_url" => "http://s3.amazonaws.com/twitter_production/profile_images/65565851/8ball_large_normal.jpg",
528
+ "location" => nil
529
+ }
530
+ end
531
+
532
+ it "should not get undefined method add_node for nil class for the following xml" do
533
+ stub_http_response_with('undefined_method_add_node_for_nil.xml')
534
+ result = HTTParty.get('http://foobar.com')
535
+ result.should == {"Entities"=>{"href"=>"https://s3-sandbox.parature.com/api/v1/5578/5633/Account", "results"=>"0", "total"=>"0", "page_size"=>"25", "page"=>"1"}}
536
+ end
537
+
538
+ it "should parse empty response fine" do
539
+ stub_http_response_with('empty.xml')
540
+ result = HTTParty.get('http://foobar.com')
541
+ result.should be_nil
542
+ end
543
+
544
+ it "should accept http URIs" do
545
+ stub_http_response_with('google.html')
546
+ lambda do
547
+ HTTParty.get('http://google.com')
548
+ end.should_not raise_error(HTTParty::UnsupportedURIScheme)
549
+ end
550
+
551
+ it "should accept https URIs" do
552
+ stub_http_response_with('google.html')
553
+ lambda do
554
+ HTTParty.get('https://google.com')
555
+ end.should_not raise_error(HTTParty::UnsupportedURIScheme)
556
+ end
557
+
558
+ it "should raise an ArgumentError on URIs that are not http or https" do
559
+ lambda do
560
+ HTTParty.get("file:///there_is_no_party_on/my/filesystem")
561
+ end.should raise_error(HTTParty::UnsupportedURIScheme)
562
+ end
563
+
564
+ it "should raise an InvalidURIError on URIs that can't be parsed at all" do
565
+ lambda do
566
+ HTTParty.get("It's the one that says 'Bad URI'")
567
+ end.should raise_error(URI::InvalidURIError)
568
+ end
569
+ end
570
+ end