httparty 0.16.2 → 0.22.0

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 (96) hide show
  1. checksums.yaml +5 -5
  2. data/.editorconfig +18 -0
  3. data/.github/dependabot.yml +6 -0
  4. data/.github/workflows/ci.yml +23 -0
  5. data/.gitignore +2 -0
  6. data/.rubocop_todo.yml +1 -1
  7. data/Changelog.md +425 -280
  8. data/Gemfile +7 -0
  9. data/Guardfile +3 -2
  10. data/README.md +5 -5
  11. data/docs/README.md +90 -5
  12. data/examples/README.md +28 -11
  13. data/examples/aaws.rb +6 -2
  14. data/examples/body_stream.rb +14 -0
  15. data/examples/idn.rb +10 -0
  16. data/examples/microsoft_graph.rb +52 -0
  17. data/examples/multipart.rb +22 -0
  18. data/examples/peer_cert.rb +9 -0
  19. data/examples/stream_download.rb +8 -2
  20. data/httparty.gemspec +4 -3
  21. data/lib/httparty/connection_adapter.rb +44 -20
  22. data/lib/httparty/cookie_hash.rb +10 -8
  23. data/lib/httparty/decompressor.rb +102 -0
  24. data/lib/httparty/exceptions.rb +3 -1
  25. data/lib/httparty/hash_conversions.rb +10 -4
  26. data/lib/httparty/headers_processor.rb +32 -0
  27. data/lib/httparty/logger/apache_formatter.rb +31 -6
  28. data/lib/httparty/logger/curl_formatter.rb +9 -7
  29. data/lib/httparty/logger/logger.rb +5 -1
  30. data/lib/httparty/logger/logstash_formatter.rb +62 -0
  31. data/lib/httparty/module_inheritable_attributes.rb +9 -9
  32. data/lib/httparty/net_digest_auth.rb +15 -15
  33. data/lib/httparty/parser.rb +12 -5
  34. data/lib/httparty/request/body.rb +54 -27
  35. data/lib/httparty/request/multipart_boundary.rb +2 -0
  36. data/lib/httparty/request.rb +105 -107
  37. data/lib/httparty/response/headers.rb +4 -2
  38. data/lib/httparty/response.rb +52 -9
  39. data/lib/httparty/response_fragment.rb +21 -0
  40. data/lib/httparty/text_encoder.rb +72 -0
  41. data/lib/httparty/utils.rb +13 -0
  42. data/lib/httparty/version.rb +3 -1
  43. data/lib/httparty.rb +81 -33
  44. data/script/release +4 -4
  45. data/website/css/common.css +1 -1
  46. metadata +50 -107
  47. data/.simplecov +0 -1
  48. data/.travis.yml +0 -10
  49. data/features/basic_authentication.feature +0 -20
  50. data/features/command_line.feature +0 -95
  51. data/features/deals_with_http_error_codes.feature +0 -26
  52. data/features/digest_authentication.feature +0 -30
  53. data/features/handles_compressed_responses.feature +0 -27
  54. data/features/handles_multiple_formats.feature +0 -57
  55. data/features/steps/env.rb +0 -27
  56. data/features/steps/httparty_response_steps.rb +0 -56
  57. data/features/steps/httparty_steps.rb +0 -43
  58. data/features/steps/mongrel_helper.rb +0 -127
  59. data/features/steps/remote_service_steps.rb +0 -92
  60. data/features/supports_read_timeout_option.feature +0 -13
  61. data/features/supports_redirection.feature +0 -22
  62. data/features/supports_timeout_option.feature +0 -13
  63. data/spec/fixtures/delicious.xml +0 -23
  64. data/spec/fixtures/empty.xml +0 -0
  65. data/spec/fixtures/google.html +0 -3
  66. data/spec/fixtures/ssl/generate.sh +0 -29
  67. data/spec/fixtures/ssl/generated/bogushost.crt +0 -13
  68. data/spec/fixtures/ssl/generated/ca.crt +0 -16
  69. data/spec/fixtures/ssl/generated/ca.key +0 -15
  70. data/spec/fixtures/ssl/generated/selfsigned.crt +0 -14
  71. data/spec/fixtures/ssl/generated/server.crt +0 -13
  72. data/spec/fixtures/ssl/generated/server.key +0 -15
  73. data/spec/fixtures/ssl/openssl-exts.cnf +0 -9
  74. data/spec/fixtures/tiny.gif +0 -0
  75. data/spec/fixtures/twitter.csv +0 -2
  76. data/spec/fixtures/twitter.json +0 -1
  77. data/spec/fixtures/twitter.xml +0 -403
  78. data/spec/fixtures/undefined_method_add_node_for_nil.xml +0 -2
  79. data/spec/httparty/connection_adapter_spec.rb +0 -498
  80. data/spec/httparty/cookie_hash_spec.rb +0 -100
  81. data/spec/httparty/exception_spec.rb +0 -45
  82. data/spec/httparty/hash_conversions_spec.rb +0 -56
  83. data/spec/httparty/logger/apache_formatter_spec.rb +0 -41
  84. data/spec/httparty/logger/curl_formatter_spec.rb +0 -119
  85. data/spec/httparty/logger/logger_spec.rb +0 -38
  86. data/spec/httparty/net_digest_auth_spec.rb +0 -270
  87. data/spec/httparty/parser_spec.rb +0 -190
  88. data/spec/httparty/request/body_spec.rb +0 -60
  89. data/spec/httparty/request_spec.rb +0 -1312
  90. data/spec/httparty/response_spec.rb +0 -347
  91. data/spec/httparty/ssl_spec.rb +0 -74
  92. data/spec/httparty_spec.rb +0 -896
  93. data/spec/spec_helper.rb +0 -51
  94. data/spec/support/ssl_test_helper.rb +0 -47
  95. data/spec/support/ssl_test_server.rb +0 -80
  96. 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,270 +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
- @path = '/'
27
- @method = 'GET'
28
- end
29
- end).new
30
- }
31
-
32
- let(:response){
33
- (Class.new do
34
- include Net::HTTPHeader
35
- def initialize
36
- @header = {}
37
- self['WWW-Authenticate'] =
38
- 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"'
39
- end
40
- end).new
41
- }
42
-
43
- it 'should set the authorization header' do
44
- expect(headers['authorization']).to be_nil
45
- headers.digest_auth('user','pass', response)
46
- expect(headers['authorization']).to_not be_empty
47
- end
48
- end
49
-
50
- context "with a cookie value in the response header" do
51
- before do
52
- @digest = setup_digest({
53
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com"',
54
- 'Set-Cookie' => 'custom-cookie=1234567'
55
- })
56
- end
57
-
58
- it "should set cookie header" do
59
- expect(cookie_header).to include('custom-cookie=1234567')
60
- end
61
- end
62
-
63
- context "without a cookie value in the response header" do
64
- before do
65
- @digest = setup_digest({
66
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com"'
67
- })
68
- end
69
-
70
- it "should set empty cookie header array" do
71
- expect(cookie_header).to eql []
72
- end
73
- end
74
-
75
- context "with an opaque value in the response header" do
76
- before do
77
- @digest = setup_digest({
78
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com", opaque="solid"'
79
- })
80
- end
81
-
82
- it "should set opaque" do
83
- expect(authorization_header).to include('opaque="solid"')
84
- end
85
- end
86
-
87
- context "without an opaque valid in the response header" do
88
- before do
89
- @digest = setup_digest({
90
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com"'
91
- })
92
- end
93
-
94
- it "should not set opaque" do
95
- expect(authorization_header).not_to include("opaque=")
96
- end
97
- end
98
-
99
- context "with specified quality of protection (qop)" do
100
- before do
101
- @digest = setup_digest({
102
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth"'
103
- })
104
- end
105
-
106
- it "should set prefix" do
107
- expect(authorization_header).to match(/^Digest /)
108
- end
109
-
110
- it "should set username" do
111
- expect(authorization_header).to include('username="Mufasa"')
112
- end
113
-
114
- it "should set digest-uri" do
115
- expect(authorization_header).to include('uri="/dir/index.html"')
116
- end
117
-
118
- it "should set qop" do
119
- expect(authorization_header).to include('qop="auth"')
120
- end
121
-
122
- it "should set cnonce" do
123
- expect(authorization_header).to include('cnonce="md5(deadbeef)"')
124
- end
125
-
126
- it "should set nonce-count" do
127
- expect(authorization_header).to include("nc=00000001")
128
- end
129
-
130
- it "should set response" do
131
- request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:00000001:md5(deadbeef):auth:md5(GET:/dir/index.html))"
132
- expect(authorization_header).to include(%(response="#{request_digest}"))
133
- end
134
- end
135
-
136
- context "when quality of protection (qop) is unquoted" do
137
- before do
138
- @digest = setup_digest({
139
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop=auth'
140
- })
141
- end
142
-
143
- it "should still set qop" do
144
- expect(authorization_header).to include('qop="auth"')
145
- end
146
- end
147
-
148
- context "with unspecified quality of protection (qop)" do
149
- before do
150
- @digest = setup_digest({
151
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE"'
152
- })
153
- end
154
-
155
- it "should set prefix" do
156
- expect(authorization_header).to match(/^Digest /)
157
- end
158
-
159
- it "should set username" do
160
- expect(authorization_header).to include('username="Mufasa"')
161
- end
162
-
163
- it "should set digest-uri" do
164
- expect(authorization_header).to include('uri="/dir/index.html"')
165
- end
166
-
167
- it "should not set qop" do
168
- expect(authorization_header).not_to include("qop=")
169
- end
170
-
171
- it "should not set cnonce" do
172
- expect(authorization_header).not_to include("cnonce=")
173
- end
174
-
175
- it "should not set nonce-count" do
176
- expect(authorization_header).not_to include("nc=")
177
- end
178
-
179
- it "should set response" do
180
- request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:md5(GET:/dir/index.html))"
181
- expect(authorization_header).to include(%(response="#{request_digest}"))
182
- end
183
- end
184
-
185
- context "with http basic auth response when net digest auth expected" do
186
- it "should not fail" do
187
- @digest = setup_digest({
188
- 'www-authenticate' => 'WWW-Authenticate: Basic realm="testrealm.com""'
189
- })
190
-
191
- expect(authorization_header).to include("Digest")
192
- end
193
- end
194
-
195
- context "with multiple authenticate headers" do
196
- before do
197
- @digest = setup_digest({
198
- 'www-authenticate' => 'NTLM, Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth"'
199
- })
200
- end
201
-
202
- it "should set prefix" do
203
- expect(authorization_header).to match(/^Digest /)
204
- end
205
-
206
- it "should set username" do
207
- expect(authorization_header).to include('username="Mufasa"')
208
- end
209
-
210
- it "should set digest-uri" do
211
- expect(authorization_header).to include('uri="/dir/index.html"')
212
- end
213
-
214
- it "should set qop" do
215
- expect(authorization_header).to include('qop="auth"')
216
- end
217
-
218
- it "should set cnonce" do
219
- expect(authorization_header).to include('cnonce="md5(deadbeef)"')
220
- end
221
-
222
- it "should set nonce-count" do
223
- expect(authorization_header).to include("nc=00000001")
224
- end
225
-
226
- it "should set response" do
227
- request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:00000001:md5(deadbeef):auth:md5(GET:/dir/index.html))"
228
- expect(authorization_header).to include(%(response="#{request_digest}"))
229
- end
230
- end
231
-
232
- context "with algorithm specified" do
233
- before do
234
- @digest = setup_digest({
235
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth", algorithm=MD5'
236
- })
237
- end
238
-
239
- it "should recognise algorithm was specified" do
240
- expect( @digest.send :algorithm_present? ).to be(true)
241
- end
242
-
243
- it "should set the algorithm header" do
244
- expect(authorization_header).to include('algorithm="MD5"')
245
- end
246
- end
247
-
248
- context "with md5-sess algorithm specified" do
249
- before do
250
- @digest = setup_digest({
251
- 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth", algorithm=MD5-sess'
252
- })
253
- end
254
-
255
- it "should recognise algorithm was specified" do
256
- expect( @digest.send :algorithm_present? ).to be(true)
257
- end
258
-
259
- it "should set the algorithm header" do
260
- expect(authorization_header).to include('algorithm="MD5-sess"')
261
- end
262
-
263
- it "should set response using md5-sess algorithm" do
264
- 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))"
265
- expect(authorization_header).to include(%(response="#{request_digest}"))
266
- end
267
-
268
- end
269
-
270
- end
@@ -1,190 +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
- klass = Class.new(HTTParty::Parser)
31
- klass::SupportedFormats = { "application/atom+xml" => :atom }
32
-
33
- expect(klass.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
- it "attempts to parse supported formats" do
67
- parser = HTTParty::Parser.new('body', :json)
68
- allow(parser).to receive_messages(supports_format?: true)
69
-
70
- expect(parser).to receive(:parse_supported_format)
71
- parser.parse
72
- end
73
-
74
- it "returns the unparsed body when the format is unsupported" do
75
- parser = HTTParty::Parser.new('body', :json)
76
- allow(parser).to receive_messages(supports_format?: false)
77
-
78
- expect(parser.parse).to eq(parser.body)
79
- end
80
-
81
- it "returns nil for an empty body" do
82
- parser = HTTParty::Parser.new('', :json)
83
- expect(parser.parse).to be_nil
84
- end
85
-
86
- it "returns nil for a nil body" do
87
- parser = HTTParty::Parser.new(nil, :json)
88
- expect(parser.parse).to be_nil
89
- end
90
-
91
- it "returns nil for a 'null' body" do
92
- parser = HTTParty::Parser.new("null", :json)
93
- expect(parser.parse).to be_nil
94
- end
95
-
96
- it "returns nil for a body with spaces only" do
97
- parser = HTTParty::Parser.new(" ", :json)
98
- expect(parser.parse).to be_nil
99
- end
100
-
101
- it "does not raise exceptions for bodies with invalid encodings" do
102
- parser = HTTParty::Parser.new("\x80", :invalid_format)
103
- expect(parser.parse).to_not be_nil
104
- end
105
-
106
- it "ignores utf-8 bom" do
107
- parser = HTTParty::Parser.new("\xEF\xBB\xBF\{\"hi\":\"yo\"\}", :json)
108
- expect(parser.parse).to eq({"hi"=>"yo"})
109
- end
110
-
111
- it "parses ascii 8bit encoding" do
112
- parser = HTTParty::Parser.new(
113
- "{\"currency\":\"\xE2\x82\xAC\"}".force_encoding('ASCII-8BIT'),
114
- :json
115
- )
116
- expect(parser.parse).to eq({"currency" => "€"})
117
- end
118
-
119
- it "parses frozen strings" do
120
- parser = HTTParty::Parser.new('{"a":1}'.freeze, :json)
121
- expect(parser.parse).to eq("a" => 1)
122
- end
123
- end
124
-
125
- describe "#supports_format?" do
126
- it "utilizes the class method to determine if the format is supported" do
127
- expect(HTTParty::Parser).to receive(:supports_format?).with(:json)
128
- parser = HTTParty::Parser.new('body', :json)
129
- parser.send(:supports_format?)
130
- end
131
- end
132
-
133
- describe "#parse_supported_format" do
134
- it "calls the parser for the given format" do
135
- parser = HTTParty::Parser.new('body', :json)
136
- expect(parser).to receive(:json)
137
- parser.send(:parse_supported_format)
138
- end
139
-
140
- context "when a parsing method does not exist for the given format" do
141
- it "raises an exception" do
142
- parser = HTTParty::Parser.new('body', :atom)
143
- expect do
144
- parser.send(:parse_supported_format)
145
- end.to raise_error(NotImplementedError, "HTTParty::Parser has not implemented a parsing method for the :atom format.")
146
- end
147
-
148
- it "raises a useful exception message for subclasses" do
149
- atom_parser = Class.new(HTTParty::Parser) do
150
- def self.name
151
- 'AtomParser'
152
- end
153
- end
154
- parser = atom_parser.new 'body', :atom
155
- expect do
156
- parser.send(:parse_supported_format)
157
- end.to raise_error(NotImplementedError, "AtomParser has not implemented a parsing method for the :atom format.")
158
- end
159
- end
160
- end
161
-
162
- context "parsers" do
163
- subject do
164
- HTTParty::Parser.new('body', nil)
165
- end
166
-
167
- it "parses xml with MultiXml" do
168
- expect(MultiXml).to receive(:parse).with('body')
169
- subject.send(:xml)
170
- end
171
-
172
- it "parses json with JSON" do
173
- expect(JSON).to receive(:parse).with('body', :quirks_mode => true, :allow_nan => true)
174
- subject.send(:json)
175
- end
176
-
177
- it "parses html by simply returning the body" do
178
- expect(subject.send(:html)).to eq('body')
179
- end
180
-
181
- it "parses plain text by simply returning the body" do
182
- expect(subject.send(:plain)).to eq('body')
183
- end
184
-
185
- it "parses csv with CSV" do
186
- expect(CSV).to receive(:parse).with('body')
187
- subject.send(:csv)
188
- end
189
- end
190
- end