httparty 0.15.4 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of httparty might be problematic. Click here for more details.

Files changed (93) hide show
  1. checksums.yaml +5 -5
  2. data/.editorconfig +18 -0
  3. data/.github/workflows/ci.yml +23 -0
  4. data/.gitignore +2 -0
  5. data/.rubocop_todo.yml +1 -1
  6. data/Changelog.md +105 -0
  7. data/Gemfile +6 -1
  8. data/README.md +6 -6
  9. data/docs/README.md +100 -38
  10. data/examples/README.md +28 -11
  11. data/examples/aaws.rb +6 -2
  12. data/examples/body_stream.rb +14 -0
  13. data/examples/custom_parsers.rb +4 -0
  14. data/examples/headers_and_user_agents.rb +7 -3
  15. data/examples/idn.rb +10 -0
  16. data/examples/logging.rb +3 -3
  17. data/examples/microsoft_graph.rb +52 -0
  18. data/examples/multipart.rb +22 -0
  19. data/examples/peer_cert.rb +9 -0
  20. data/examples/stream_download.rb +8 -2
  21. data/httparty.gemspec +3 -3
  22. data/lib/httparty/connection_adapter.rb +59 -16
  23. data/lib/httparty/cookie_hash.rb +10 -8
  24. data/lib/httparty/decompressor.rb +92 -0
  25. data/lib/httparty/exceptions.rb +4 -1
  26. data/lib/httparty/hash_conversions.rb +28 -12
  27. data/lib/httparty/headers_processor.rb +32 -0
  28. data/lib/httparty/logger/apache_formatter.rb +31 -6
  29. data/lib/httparty/logger/curl_formatter.rb +9 -7
  30. data/lib/httparty/logger/logger.rb +5 -1
  31. data/lib/httparty/logger/logstash_formatter.rb +61 -0
  32. data/lib/httparty/module_inheritable_attributes.rb +6 -4
  33. data/lib/httparty/net_digest_auth.rb +15 -15
  34. data/lib/httparty/parser.rb +22 -16
  35. data/lib/httparty/request/body.rb +98 -0
  36. data/lib/httparty/request/multipart_boundary.rb +13 -0
  37. data/lib/httparty/request.rb +82 -95
  38. data/lib/httparty/response/headers.rb +4 -2
  39. data/lib/httparty/response.rb +59 -8
  40. data/lib/httparty/response_fragment.rb +21 -0
  41. data/lib/httparty/text_encoder.rb +72 -0
  42. data/lib/httparty/utils.rb +13 -0
  43. data/lib/httparty/version.rb +3 -1
  44. data/lib/httparty.rb +70 -24
  45. data/website/css/common.css +1 -1
  46. metadata +35 -100
  47. data/.travis.yml +0 -8
  48. data/features/basic_authentication.feature +0 -20
  49. data/features/command_line.feature +0 -95
  50. data/features/deals_with_http_error_codes.feature +0 -26
  51. data/features/digest_authentication.feature +0 -30
  52. data/features/handles_compressed_responses.feature +0 -27
  53. data/features/handles_multiple_formats.feature +0 -57
  54. data/features/steps/env.rb +0 -27
  55. data/features/steps/httparty_response_steps.rb +0 -56
  56. data/features/steps/httparty_steps.rb +0 -43
  57. data/features/steps/mongrel_helper.rb +0 -127
  58. data/features/steps/remote_service_steps.rb +0 -92
  59. data/features/supports_read_timeout_option.feature +0 -13
  60. data/features/supports_redirection.feature +0 -22
  61. data/features/supports_timeout_option.feature +0 -13
  62. data/spec/fixtures/delicious.xml +0 -23
  63. data/spec/fixtures/empty.xml +0 -0
  64. data/spec/fixtures/google.html +0 -3
  65. data/spec/fixtures/ssl/generate.sh +0 -29
  66. data/spec/fixtures/ssl/generated/bogushost.crt +0 -13
  67. data/spec/fixtures/ssl/generated/ca.crt +0 -16
  68. data/spec/fixtures/ssl/generated/ca.key +0 -15
  69. data/spec/fixtures/ssl/generated/selfsigned.crt +0 -14
  70. data/spec/fixtures/ssl/generated/server.crt +0 -13
  71. data/spec/fixtures/ssl/generated/server.key +0 -15
  72. data/spec/fixtures/ssl/openssl-exts.cnf +0 -9
  73. data/spec/fixtures/twitter.csv +0 -2
  74. data/spec/fixtures/twitter.json +0 -1
  75. data/spec/fixtures/twitter.xml +0 -403
  76. data/spec/fixtures/undefined_method_add_node_for_nil.xml +0 -2
  77. data/spec/httparty/connection_adapter_spec.rb +0 -495
  78. data/spec/httparty/cookie_hash_spec.rb +0 -100
  79. data/spec/httparty/exception_spec.rb +0 -45
  80. data/spec/httparty/hash_conversions_spec.rb +0 -49
  81. data/spec/httparty/logger/apache_formatter_spec.rb +0 -41
  82. data/spec/httparty/logger/curl_formatter_spec.rb +0 -119
  83. data/spec/httparty/logger/logger_spec.rb +0 -38
  84. data/spec/httparty/net_digest_auth_spec.rb +0 -268
  85. data/spec/httparty/parser_spec.rb +0 -185
  86. data/spec/httparty/request_spec.rb +0 -1251
  87. data/spec/httparty/response_spec.rb +0 -347
  88. data/spec/httparty/ssl_spec.rb +0 -74
  89. data/spec/httparty_spec.rb +0 -877
  90. data/spec/spec_helper.rb +0 -59
  91. data/spec/support/ssl_test_helper.rb +0 -47
  92. data/spec/support/ssl_test_server.rb +0 -80
  93. data/spec/support/stub_response.rb +0 -49
@@ -1,41 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
-
3
- RSpec.describe HTTParty::Logger::ApacheFormatter do
4
- let(:subject) { described_class.new(logger_double, :info) }
5
- let(:logger_double) { double('Logger') }
6
- let(:request_double) { double('Request', http_method: Net::HTTP::Get, path: "http://my.domain.com/my_path") }
7
- let(:request_time) { Time.new.strftime("%Y-%m-%d %H:%M:%S %z") }
8
-
9
- before do
10
- subject.current_time = request_time
11
- expect(logger_double).to receive(:info).with(log_message)
12
- end
13
-
14
- describe "#format" do
15
- let(:log_message) { "[HTTParty] [#{request_time}] 302 \"GET http://my.domain.com/my_path\" - " }
16
-
17
- it "formats a response in a style that resembles apache's access log" do
18
- response_double = double(
19
- code: 302,
20
- :[] => nil
21
- )
22
-
23
- subject.format(request_double, response_double)
24
- end
25
-
26
- context 'when there is a parsed response' do
27
- let(:log_message) { "[HTTParty] [#{request_time}] 200 \"GET http://my.domain.com/my_path\" 512 "}
28
-
29
- it "can handle the Content-Length header" do
30
- # Simulate a parsed response that is an array, where accessing a string key will raise an error. See Issue #299.
31
- response_double = double(
32
- code: 200,
33
- headers: { 'Content-Length' => 512 }
34
- )
35
- allow(response_double).to receive(:[]).with('Content-Length').and_raise(TypeError.new('no implicit conversion of String into Integer'))
36
-
37
- subject.format(request_double, response_double)
38
- end
39
- end
40
- end
41
- end
@@ -1,119 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
-
3
- RSpec.describe HTTParty::Logger::CurlFormatter do
4
- describe "#format" do
5
- let(:logger) { double('Logger') }
6
- let(:response_object) { Net::HTTPOK.new('1.1', 200, 'OK') }
7
- let(:parsed_response) { lambda { {"foo" => "bar"} } }
8
-
9
- let(:response) do
10
- HTTParty::Response.new(request, response_object, parsed_response)
11
- end
12
-
13
- let(:request) do
14
- HTTParty::Request.new(Net::HTTP::Get, 'http://foo.bar.com/')
15
- end
16
-
17
- subject { described_class.new(logger, :info) }
18
-
19
- before do
20
- allow(logger).to receive(:info)
21
- allow(request).to receive(:raw_body).and_return('content')
22
- allow(response_object).to receive_messages(body: "{foo:'bar'}")
23
- response_object['header-key'] = 'header-value'
24
-
25
- subject.format request, response
26
- end
27
-
28
- context 'when request is logged' do
29
- context "and request's option 'base_uri' is not present" do
30
- it 'logs url' do
31
- expect(logger).to have_received(:info).with(/\[HTTParty\] \[\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\ [+-]\d{4}\] > GET http:\/\/foo.bar.com/)
32
- end
33
- end
34
-
35
- context "and request's option 'base_uri' is present" do
36
- let(:request) do
37
- HTTParty::Request.new(Net::HTTP::Get, '/path', base_uri: 'http://foo.bar.com')
38
- end
39
-
40
- it 'logs url' do
41
- expect(logger).to have_received(:info).with(/\[HTTParty\] \[\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\ [+-]\d{4}\] > GET http:\/\/foo.bar.com\/path/)
42
- end
43
- end
44
-
45
- context 'and headers are not present' do
46
- it 'not log Headers' do
47
- expect(logger).not_to have_received(:info).with(/Headers/)
48
- end
49
- end
50
-
51
- context 'and headers are present' do
52
- let(:request) do
53
- HTTParty::Request.new(Net::HTTP::Get, '/path', base_uri: 'http://foo.bar.com', headers: { key: 'value' })
54
- end
55
-
56
- it 'logs Headers' do
57
- expect(logger).to have_received(:info).with(/Headers/)
58
- end
59
-
60
- it 'logs headers keys' do
61
- expect(logger).to have_received(:info).with(/key: value/)
62
- end
63
- end
64
-
65
- context 'and query is not present' do
66
- it 'not logs Query' do
67
- expect(logger).not_to have_received(:info).with(/Query/)
68
- end
69
- end
70
-
71
- context 'and query is present' do
72
- let(:request) do
73
- HTTParty::Request.new(Net::HTTP::Get, '/path', query: { key: 'value' })
74
- end
75
-
76
- it 'logs Query' do
77
- expect(logger).to have_received(:info).with(/Query/)
78
- end
79
-
80
- it 'logs query params' do
81
- expect(logger).to have_received(:info).with(/key: value/)
82
- end
83
- end
84
-
85
- context 'when request raw_body is present' do
86
- it 'not logs request body' do
87
- expect(logger).to have_received(:info).with(/content/)
88
- end
89
- end
90
- end
91
-
92
- context 'when response is logged' do
93
- it 'logs http version and response code' do
94
- expect(logger).to have_received(:info).with(/HTTP\/1.1 200/)
95
- end
96
-
97
- it 'logs headers' do
98
- expect(logger).to have_received(:info).with(/Header-key: header-value/)
99
- end
100
-
101
- it 'logs body' do
102
- expect(logger).to have_received(:info).with(/{foo:'bar'}/)
103
- end
104
- end
105
-
106
- it "formats a response in a style that resembles a -v curl" do
107
- logger_double = double
108
- expect(logger_double).to receive(:info).with(
109
- /\[HTTParty\] \[\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\ [+-]\d{4}\] > GET http:\/\/localhost/)
110
-
111
- subject = described_class.new(logger_double, :info)
112
-
113
- stub_http_response_with("google.html")
114
-
115
- response = HTTParty::Request.new.perform
116
- subject.format(response.request, response)
117
- end
118
- end
119
- end
@@ -1,38 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
-
3
- RSpec.describe HTTParty::Logger do
4
- describe ".build" do
5
- subject { HTTParty::Logger }
6
-
7
- it "defaults level to :info" do
8
- logger_double = double
9
- expect(subject.build(logger_double, nil, nil).level).to eq(:info)
10
- end
11
-
12
- it "defaults format to :apache" do
13
- logger_double = double
14
- expect(subject.build(logger_double, nil, nil)).to be_an_instance_of(HTTParty::Logger::ApacheFormatter)
15
- end
16
-
17
- it "builds :curl style logger" do
18
- logger_double = double
19
- expect(subject.build(logger_double, nil, :curl)).to be_an_instance_of(HTTParty::Logger::CurlFormatter)
20
- end
21
-
22
- it "builds :custom style logger" do
23
- CustomFormatter = Class.new(HTTParty::Logger::CurlFormatter)
24
- HTTParty::Logger.add_formatter(:custom, CustomFormatter)
25
-
26
- logger_double = double
27
- expect(subject.build(logger_double, nil, :custom)).
28
- to be_an_instance_of(CustomFormatter)
29
- end
30
- it "raises error when formatter exists" do
31
- CustomFormatter2= Class.new(HTTParty::Logger::CurlFormatter)
32
- HTTParty::Logger.add_formatter(:custom2, CustomFormatter2)
33
-
34
- expect{ HTTParty::Logger.add_formatter(:custom2, CustomFormatter2) }.
35
- to raise_error HTTParty::Error
36
- end
37
- end
38
- end
@@ -1,268 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
-
3
- RSpec.describe Net::HTTPHeader::DigestAuthenticator do
4
- def setup_digest(response)
5
- digest = Net::HTTPHeader::DigestAuthenticator.new("Mufasa",
6
- "Circle Of Life", "GET", "/dir/index.html", response)
7
- allow(digest).to receive(:random).and_return("deadbeef")
8
- allow(Digest::MD5).to receive(:hexdigest) { |str| "md5(#{str})" }
9
- digest
10
- end
11
-
12
- def authorization_header
13
- @digest.authorization_header.join(", ")
14
- end
15
-
16
- def cookie_header
17
- @digest.cookie_header
18
- end
19
-
20
- context 'Net::HTTPHeader#digest_auth' do
21
- let(:headers) {
22
- (Class.new do
23
- include Net::HTTPHeader
24
- def initialize
25
- @header = {}
26
- end
27
- end).new
28
- }
29
-
30
- let(:response){
31
- (Class.new do
32
- include Net::HTTPHeader
33
- def initialize
34
- @header = {}
35
- self['WWW-Authenticate'] =
36
- 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"'
37
- end
38
- end).new
39
- }
40
-
41
- it 'should set the authorization header' do
42
- expect(headers['authorization']).to be_nil
43
- headers.digest_auth('user','pass', response)
44
- expect(headers['authorization']).to_not be_empty
45
- end
46
- end
47
-
48
- context "with a cookie value in the response header" do
49
- before do
50
- @digest = setup_digest({
51
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com"',
52
- 'Set-Cookie' => 'custom-cookie=1234567'
53
- })
54
- end
55
-
56
- it "should set cookie header" do
57
- expect(cookie_header).to include('custom-cookie=1234567')
58
- end
59
- end
60
-
61
- context "without a cookie value in the response header" do
62
- before do
63
- @digest = setup_digest({
64
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com"'
65
- })
66
- end
67
-
68
- it "should set empty cookie header array" do
69
- expect(cookie_header).to eql []
70
- end
71
- end
72
-
73
- context "with an opaque value in the response header" do
74
- before do
75
- @digest = setup_digest({
76
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com", opaque="solid"'
77
- })
78
- end
79
-
80
- it "should set opaque" do
81
- expect(authorization_header).to include('opaque="solid"')
82
- end
83
- end
84
-
85
- context "without an opaque valid in the response header" do
86
- before do
87
- @digest = setup_digest({
88
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com"'
89
- })
90
- end
91
-
92
- it "should not set opaque" do
93
- expect(authorization_header).not_to include("opaque=")
94
- end
95
- end
96
-
97
- context "with specified quality of protection (qop)" do
98
- before do
99
- @digest = setup_digest({
100
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth"'
101
- })
102
- end
103
-
104
- it "should set prefix" do
105
- expect(authorization_header).to match(/^Digest /)
106
- end
107
-
108
- it "should set username" do
109
- expect(authorization_header).to include('username="Mufasa"')
110
- end
111
-
112
- it "should set digest-uri" do
113
- expect(authorization_header).to include('uri="/dir/index.html"')
114
- end
115
-
116
- it "should set qop" do
117
- expect(authorization_header).to include('qop="auth"')
118
- end
119
-
120
- it "should set cnonce" do
121
- expect(authorization_header).to include('cnonce="md5(deadbeef)"')
122
- end
123
-
124
- it "should set nonce-count" do
125
- expect(authorization_header).to include("nc=00000001")
126
- end
127
-
128
- it "should set response" do
129
- request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:00000001:md5(deadbeef):auth:md5(GET:/dir/index.html))"
130
- expect(authorization_header).to include(%(response="#{request_digest}"))
131
- end
132
- end
133
-
134
- context "when quality of protection (qop) is unquoted" do
135
- before do
136
- @digest = setup_digest({
137
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop=auth'
138
- })
139
- end
140
-
141
- it "should still set qop" do
142
- expect(authorization_header).to include('qop="auth"')
143
- end
144
- end
145
-
146
- context "with unspecified quality of protection (qop)" do
147
- before do
148
- @digest = setup_digest({
149
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE"'
150
- })
151
- end
152
-
153
- it "should set prefix" do
154
- expect(authorization_header).to match(/^Digest /)
155
- end
156
-
157
- it "should set username" do
158
- expect(authorization_header).to include('username="Mufasa"')
159
- end
160
-
161
- it "should set digest-uri" do
162
- expect(authorization_header).to include('uri="/dir/index.html"')
163
- end
164
-
165
- it "should not set qop" do
166
- expect(authorization_header).not_to include("qop=")
167
- end
168
-
169
- it "should not set cnonce" do
170
- expect(authorization_header).not_to include("cnonce=")
171
- end
172
-
173
- it "should not set nonce-count" do
174
- expect(authorization_header).not_to include("nc=")
175
- end
176
-
177
- it "should set response" do
178
- request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:md5(GET:/dir/index.html))"
179
- expect(authorization_header).to include(%(response="#{request_digest}"))
180
- end
181
- end
182
-
183
- context "with http basic auth response when net digest auth expected" do
184
- it "should not fail" do
185
- @digest = setup_digest({
186
- 'www-authenticate' => 'WWW-Authenticate: Basic realm="testrealm.com""'
187
- })
188
-
189
- expect(authorization_header).to include("Digest")
190
- end
191
- end
192
-
193
- context "with multiple authenticate headers" do
194
- before do
195
- @digest = setup_digest({
196
- 'www-authenticate' => 'NTLM, Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth"'
197
- })
198
- end
199
-
200
- it "should set prefix" do
201
- expect(authorization_header).to match(/^Digest /)
202
- end
203
-
204
- it "should set username" do
205
- expect(authorization_header).to include('username="Mufasa"')
206
- end
207
-
208
- it "should set digest-uri" do
209
- expect(authorization_header).to include('uri="/dir/index.html"')
210
- end
211
-
212
- it "should set qop" do
213
- expect(authorization_header).to include('qop="auth"')
214
- end
215
-
216
- it "should set cnonce" do
217
- expect(authorization_header).to include('cnonce="md5(deadbeef)"')
218
- end
219
-
220
- it "should set nonce-count" do
221
- expect(authorization_header).to include("nc=00000001")
222
- end
223
-
224
- it "should set response" do
225
- request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:00000001:md5(deadbeef):auth:md5(GET:/dir/index.html))"
226
- expect(authorization_header).to include(%(response="#{request_digest}"))
227
- end
228
- end
229
-
230
- context "with algorithm specified" do
231
- before do
232
- @digest = setup_digest({
233
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth", algorithm=MD5'
234
- })
235
- end
236
-
237
- it "should recognise algorithm was specified" do
238
- expect( @digest.send :algorithm_present? ).to be(true)
239
- end
240
-
241
- it "should set the algorithm header" do
242
- expect(authorization_header).to include('algorithm="MD5"')
243
- end
244
- end
245
-
246
- context "with md5-sess algorithm specified" do
247
- before do
248
- @digest = setup_digest({
249
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth", algorithm=MD5-sess'
250
- })
251
- end
252
-
253
- it "should recognise algorithm was specified" do
254
- expect( @digest.send :algorithm_present? ).to be(true)
255
- end
256
-
257
- it "should set the algorithm header" do
258
- expect(authorization_header).to include('algorithm="MD5-sess"')
259
- end
260
-
261
- it "should set response using md5-sess algorithm" do
262
- request_digest = "md5(md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:md5(deadbeef)):NONCE:00000001:md5(deadbeef):auth:md5(GET:/dir/index.html))"
263
- expect(authorization_header).to include(%(response="#{request_digest}"))
264
- end
265
-
266
- end
267
-
268
- end
@@ -1,185 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
-
3
- RSpec.describe HTTParty::Parser do
4
- describe ".SupportedFormats" do
5
- it "returns a hash" do
6
- expect(HTTParty::Parser::SupportedFormats).to be_instance_of(Hash)
7
- end
8
- end
9
-
10
- describe ".call" do
11
- it "generates an HTTParty::Parser instance with the given body and format" do
12
- expect(HTTParty::Parser).to receive(:new).with('body', :plain).and_return(double(parse: nil))
13
- HTTParty::Parser.call('body', :plain)
14
- end
15
-
16
- it "calls #parse on the parser" do
17
- parser = double('Parser')
18
- expect(parser).to receive(:parse)
19
- allow(HTTParty::Parser).to receive_messages(new: parser)
20
- parser = HTTParty::Parser.call('body', :plain)
21
- end
22
- end
23
-
24
- describe ".formats" do
25
- it "returns the SupportedFormats constant" do
26
- expect(HTTParty::Parser.formats).to eq(HTTParty::Parser::SupportedFormats)
27
- end
28
-
29
- it "returns the SupportedFormats constant for subclasses" do
30
- class MyParser < HTTParty::Parser
31
- SupportedFormats = {"application/atom+xml" => :atom}
32
- end
33
- expect(MyParser.formats).to eq({"application/atom+xml" => :atom})
34
- end
35
- end
36
-
37
- describe ".format_from_mimetype" do
38
- it "returns a symbol representing the format mimetype" do
39
- expect(HTTParty::Parser.format_from_mimetype("text/plain")).to eq(:plain)
40
- end
41
-
42
- it "returns nil when the mimetype is not supported" do
43
- expect(HTTParty::Parser.format_from_mimetype("application/atom+xml")).to be_nil
44
- end
45
- end
46
-
47
- describe ".supported_formats" do
48
- it "returns a unique set of supported formats represented by symbols" do
49
- expect(HTTParty::Parser.supported_formats).to eq(HTTParty::Parser::SupportedFormats.values.uniq)
50
- end
51
- end
52
-
53
- describe ".supports_format?" do
54
- it "returns true for a supported format" do
55
- allow(HTTParty::Parser).to receive_messages(supported_formats: [:json])
56
- expect(HTTParty::Parser.supports_format?(:json)).to be_truthy
57
- end
58
-
59
- it "returns false for an unsupported format" do
60
- allow(HTTParty::Parser).to receive_messages(supported_formats: [])
61
- expect(HTTParty::Parser.supports_format?(:json)).to be_falsey
62
- end
63
- end
64
-
65
- describe "#parse" do
66
- before do
67
- @parser = HTTParty::Parser.new('body', :json)
68
- end
69
-
70
- it "attempts to parse supported formats" do
71
- allow(@parser).to receive_messages(supports_format?: true)
72
- expect(@parser).to receive(:parse_supported_format)
73
- @parser.parse
74
- end
75
-
76
- it "returns the unparsed body when the format is unsupported" do
77
- allow(@parser).to receive_messages(supports_format?: false)
78
- expect(@parser.parse).to eq(@parser.body)
79
- end
80
-
81
- it "returns nil for an empty body" do
82
- allow(@parser).to receive_messages(body: '')
83
- expect(@parser.parse).to be_nil
84
- end
85
-
86
- it "returns nil for a nil body" do
87
- allow(@parser).to receive_messages(body: nil)
88
- expect(@parser.parse).to be_nil
89
- end
90
-
91
- it "returns nil for a 'null' body" do
92
- allow(@parser).to receive_messages(body: "null")
93
- expect(@parser.parse).to be_nil
94
- end
95
-
96
- it "returns nil for a body with spaces only" do
97
- allow(@parser).to receive_messages(body: " ")
98
- expect(@parser.parse).to be_nil
99
- end
100
-
101
- it "does not raise exceptions for bodies with invalid encodings" do
102
- allow(@parser).to receive_messages(body: "\x80")
103
- allow(@parser).to receive_messages(supports_format?: false)
104
- expect(@parser.parse).to_not be_nil
105
- end
106
-
107
- it "ignores utf-8 bom" do
108
- allow(@parser).to receive_messages(body: "\xEF\xBB\xBF\{\"hi\":\"yo\"\}")
109
- expect(@parser.parse).to eq({"hi"=>"yo"})
110
- end
111
-
112
- it "parses ascii 8bit encoding" do
113
- allow(@parser).to receive_messages(
114
- body: "{\"currency\":\"\xE2\x82\xAC\"}".force_encoding('ASCII-8BIT')
115
- )
116
- expect(@parser.parse).to eq({"currency" => "€"})
117
- end
118
- end
119
-
120
- describe "#supports_format?" do
121
- it "utilizes the class method to determine if the format is supported" do
122
- expect(HTTParty::Parser).to receive(:supports_format?).with(:json)
123
- parser = HTTParty::Parser.new('body', :json)
124
- parser.send(:supports_format?)
125
- end
126
- end
127
-
128
- describe "#parse_supported_format" do
129
- it "calls the parser for the given format" do
130
- parser = HTTParty::Parser.new('body', :json)
131
- expect(parser).to receive(:json)
132
- parser.send(:parse_supported_format)
133
- end
134
-
135
- context "when a parsing method does not exist for the given format" do
136
- it "raises an exception" do
137
- parser = HTTParty::Parser.new('body', :atom)
138
- expect do
139
- parser.send(:parse_supported_format)
140
- end.to raise_error(NotImplementedError, "HTTParty::Parser has not implemented a parsing method for the :atom format.")
141
- end
142
-
143
- it "raises a useful exception message for subclasses" do
144
- atom_parser = Class.new(HTTParty::Parser) do
145
- def self.name
146
- 'AtomParser'
147
- end
148
- end
149
- parser = atom_parser.new 'body', :atom
150
- expect do
151
- parser.send(:parse_supported_format)
152
- end.to raise_error(NotImplementedError, "AtomParser has not implemented a parsing method for the :atom format.")
153
- end
154
- end
155
- end
156
-
157
- context "parsers" do
158
- subject do
159
- HTTParty::Parser.new('body', nil)
160
- end
161
-
162
- it "parses xml with MultiXml" do
163
- expect(MultiXml).to receive(:parse).with('body')
164
- subject.send(:xml)
165
- end
166
-
167
- it "parses json with JSON" do
168
- expect(JSON).to receive(:parse).with('body', :quirks_mode => true, :allow_nan => true)
169
- subject.send(:json)
170
- end
171
-
172
- it "parses html by simply returning the body" do
173
- expect(subject.send(:html)).to eq('body')
174
- end
175
-
176
- it "parses plain text by simply returning the body" do
177
- expect(subject.send(:plain)).to eq('body')
178
- end
179
-
180
- it "parses csv with CSV" do
181
- expect(CSV).to receive(:parse).with('body')
182
- subject.send(:csv)
183
- end
184
- end
185
- end