httpi 2.4.3 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/development.yml +48 -0
  3. data/CHANGELOG.md +23 -0
  4. data/Gemfile +8 -6
  5. data/README.md +20 -16
  6. data/UPDATING.md +7 -0
  7. data/httpi.gemspec +5 -8
  8. data/lib/httpi/adapter/curb.rb +7 -2
  9. data/lib/httpi/adapter/em_http.rb +5 -4
  10. data/lib/httpi/adapter/excon.rb +6 -1
  11. data/lib/httpi/adapter/http.rb +9 -0
  12. data/lib/httpi/adapter/httpclient.rb +5 -0
  13. data/lib/httpi/adapter/net_http.rb +10 -2
  14. data/lib/httpi/adapter/net_http_persistent.rb +6 -2
  15. data/lib/httpi/auth/ssl.rb +53 -2
  16. data/lib/httpi/request.rb +2 -2
  17. data/lib/httpi/version.rb +1 -1
  18. data/spec/fixtures/client_cert.pem +18 -14
  19. data/spec/fixtures/client_key.pem +25 -13
  20. data/spec/httpi/adapter/curb_spec.rb +31 -7
  21. data/spec/httpi/adapter/em_http_spec.rb +23 -21
  22. data/spec/httpi/adapter/excon_spec.rb +28 -118
  23. data/spec/httpi/adapter/http_spec.rb +23 -96
  24. data/spec/httpi/adapter/httpclient_spec.rb +32 -0
  25. data/spec/httpi/adapter/net_http_persistent_spec.rb +31 -81
  26. data/spec/httpi/adapter/net_http_spec.rb +37 -181
  27. data/spec/httpi/auth/ssl_spec.rb +48 -0
  28. data/spec/httpi/httpi_spec.rb +2 -4
  29. data/spec/integration/curb_spec.rb +20 -0
  30. data/spec/integration/em_http_spec.rb +19 -2
  31. data/spec/integration/excon_spec.rb +67 -1
  32. data/spec/integration/fixtures/ca_all.pem +17 -42
  33. data/spec/integration/fixtures/server.cert +17 -17
  34. data/spec/integration/fixtures/server.key +25 -13
  35. data/spec/integration/http_spec.rb +47 -0
  36. data/spec/integration/httpclient_spec.rb +20 -0
  37. data/spec/integration/net_http_persistent_spec.rb +33 -3
  38. data/spec/integration/net_http_spec.rb +144 -1
  39. data/spec/integration/support/application.rb +3 -2
  40. data/spec/integration/support/server.rb +1 -2
  41. data/spec/spec_helper.rb +0 -2
  42. metadata +15 -43
  43. data/.travis.yml +0 -15
@@ -30,6 +30,18 @@ describe HTTPI::Adapter::HTTP do
30
30
  expect(response.headers["Set-Cookie"]).to eq(cookies)
31
31
  end
32
32
 
33
+ it "it supports read timeout" do
34
+ require "http"
35
+
36
+ request = HTTPI::Request.new(@server.url + "timeout")
37
+ request.read_timeout = 0.5 # seconds
38
+
39
+ expect do
40
+ HTTPI.get(request, adapter)
41
+ end.to raise_exception(HTTP::TimeoutError)
42
+ end
43
+
44
+
33
45
  it "executes GET requests" do
34
46
  response = HTTPI.get(@server.url, adapter)
35
47
  expect(response.body).to eq("get")
@@ -68,6 +80,22 @@ describe HTTPI::Adapter::HTTP do
68
80
  expect(response.body).to eq("basic-auth")
69
81
  end
70
82
 
83
+ it "does not support digest authentication" do
84
+ request = HTTPI::Request.new(@server.url + "digest-auth")
85
+ request.auth.digest("admin", "secret")
86
+
87
+ expect { HTTPI.get(request, adapter) }.
88
+ to raise_error(HTTPI::NotSupportedError, /does not support HTTP digest authentication/)
89
+ end
90
+
91
+ it "does not support ntlm authentication" do
92
+ request = HTTPI::Request.new(@server.url + "ntlm-auth")
93
+ request.auth.ntlm("tester", "vReqSoafRe5O")
94
+
95
+ expect { HTTPI.get(request, adapter) }.
96
+ to raise_error(HTTPI::NotSupportedError, /does not support NTLM digest authentication/)
97
+ end
98
+
71
99
  it "supports chunked response" do
72
100
  skip("Needs investigation")
73
101
  request = HTTPI::Request.new(@server.url)
@@ -103,6 +131,25 @@ describe HTTPI::Adapter::HTTP do
103
131
  response = HTTPI.get(request, adapter)
104
132
  expect(response.body).to eq("get")
105
133
  end
134
+
135
+ it "works with min_version/max_version" do
136
+ request = HTTPI::Request.new(@server.url)
137
+ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
138
+ request.auth.ssl.min_version = :TLS1_2
139
+ request.auth.ssl.max_version = :TLS1_2
140
+
141
+ response = HTTPI.get(request, adapter)
142
+ expect(response.body).to eq("get")
143
+ end
144
+
145
+ it "works with ciphers" do
146
+ request = HTTPI::Request.new(@server.url)
147
+ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
148
+ request.auth.ssl.ciphers = OpenSSL::SSL::SSLContext.new.ciphers
149
+
150
+ response = HTTPI.get(request, adapter)
151
+ expect(response.body).to eq("get")
152
+ end
106
153
  end
107
154
  end
108
155
 
@@ -30,6 +30,17 @@ describe HTTPI::Adapter::HTTPClient do
30
30
  expect(response.headers["Set-Cookie"]).to eq(cookies)
31
31
  end
32
32
 
33
+ it "it supports read timeout" do
34
+ require "httpclient"
35
+
36
+ request = HTTPI::Request.new(@server.url + "timeout")
37
+ request.read_timeout = 0.5 # seconds
38
+
39
+ expect do
40
+ HTTPI.get(request, adapter)
41
+ end.to raise_exception(HTTPClient::ReceiveTimeoutError)
42
+ end
43
+
33
44
  it "executes GET requests" do
34
45
  response = HTTPI.get(@server.url, adapter)
35
46
  expect(response.body).to eq("get")
@@ -110,6 +121,15 @@ describe HTTPI::Adapter::HTTPClient do
110
121
  response = HTTPI.get(request, adapter)
111
122
  expect(response.body).to eq("get")
112
123
  end
124
+
125
+ it "works with ciphers" do
126
+ request = HTTPI::Request.new(@server.url)
127
+ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
128
+ request.auth.ssl.ciphers = OpenSSL::SSL::SSLContext.new.ciphers
129
+
130
+ response = HTTPI.get(request, adapter)
131
+ expect(response.body).to eq("get")
132
+ end
113
133
  end
114
134
  end
115
135
 
@@ -1,7 +1,8 @@
1
1
  require "spec_helper"
2
2
  require "integration/support/server"
3
+ require "net/http/persistent"
3
4
 
4
- describe HTTPI::Adapter::NetHTTP do
5
+ describe HTTPI::Adapter::NetHTTPPersistent do
5
6
 
6
7
  subject(:adapter) { :net_http_persistent }
7
8
 
@@ -30,6 +31,17 @@ describe HTTPI::Adapter::NetHTTP do
30
31
  expect(response.headers["Set-Cookie"]).to eq(cookies)
31
32
  end
32
33
 
34
+ it "it supports read timeout" do
35
+ require "net/http/persistent"
36
+
37
+ request = HTTPI::Request.new(@server.url + "timeout")
38
+ request.read_timeout = 0.5 # seconds
39
+
40
+ expect do
41
+ HTTPI.get(request, adapter)
42
+ end.to raise_exception(Net::ReadTimeout)
43
+ end
44
+
33
45
  it "executes GET requests" do
34
46
  response = HTTPI.get(@server.url, adapter)
35
47
  expect(response.body).to eq("get")
@@ -37,8 +49,8 @@ describe HTTPI::Adapter::NetHTTP do
37
49
  end
38
50
 
39
51
  it "executes POST requests" do
40
- request = HTTPI::Request.new(url: @server.url, open_timeout: 1, read_timeout: 1, body: "<some>xml</some>")
41
-
52
+ request = HTTPI::Request.new(url: @server.url, body: "<some>xml</some>")
53
+
42
54
  response = HTTPI.post(request, adapter)
43
55
  expect(response.body).to eq("post")
44
56
  expect(response.headers["Content-Type"]).to eq("text/plain")
@@ -70,6 +82,14 @@ describe HTTPI::Adapter::NetHTTP do
70
82
  expect(response.body).to eq("basic-auth")
71
83
  end
72
84
 
85
+ it "does not support ntlm authentication" do
86
+ request = HTTPI::Request.new(@server.url + "ntlm-auth")
87
+ request.auth.ntlm("tester", "vReqSoafRe5O")
88
+
89
+ expect { HTTPI.get(request, adapter) }.
90
+ to raise_error(HTTPI::NotSupportedError, /does not support NTLM authentication/)
91
+ end
92
+
73
93
  # it does not support digest authentication
74
94
 
75
95
  it "supports chunked response" do
@@ -91,6 +111,7 @@ describe HTTPI::Adapter::NetHTTP do
91
111
  before :all do
92
112
  @server = IntegrationServer.run(:ssl => true)
93
113
  end
114
+
94
115
  after :all do
95
116
  @server.stop
96
117
  end
@@ -103,6 +124,15 @@ describe HTTPI::Adapter::NetHTTP do
103
124
  response = HTTPI.get(request, adapter)
104
125
  expect(response.body).to eq("get")
105
126
  end
127
+
128
+ it "works with ciphers" do
129
+ request = HTTPI::Request.new(@server.url)
130
+ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
131
+ request.auth.ssl.ciphers = OpenSSL::SSL::SSLContext.new.ciphers
132
+
133
+ response = HTTPI.get(request, adapter)
134
+ expect(response.body).to eq("get")
135
+ end
106
136
  end
107
137
  end
108
138
 
@@ -14,6 +14,21 @@ describe HTTPI::Adapter::NetHTTP do
14
14
  @server.stop
15
15
  end
16
16
 
17
+ context "when socks is specified" do
18
+ let(:socks_client) { mock("socks_client") }
19
+ let(:request) { HTTPI::Request.new(@server.url) }
20
+
21
+ it "uses Net::HTTP.SOCKSProxy as client" do
22
+ socks_client.expects(:new).with(URI(@server.url).host, URI(@server.url).port).returns(:socks_client_instance)
23
+ Net::HTTP.expects(:SOCKSProxy).with("localhost", 8080).returns socks_client
24
+
25
+ request.proxy = "socks://localhost:8080"
26
+ adapter = HTTPI::Adapter::NetHTTP.new(request)
27
+
28
+ expect(adapter.client).to eq(:socks_client_instance)
29
+ end
30
+ end
31
+
17
32
  it "sends and receives HTTP headers" do
18
33
  request = HTTPI::Request.new(@server.url + "x-header")
19
34
  request.headers["X-Header"] = "HTTPI"
@@ -30,6 +45,17 @@ describe HTTPI::Adapter::NetHTTP do
30
45
  expect(response.headers["Set-Cookie"]).to eq(cookies)
31
46
  end
32
47
 
48
+ it "it supports read timeout" do
49
+ require "net/http"
50
+
51
+ request = HTTPI::Request.new(@server.url + "timeout")
52
+ request.read_timeout = 0.5 # seconds
53
+
54
+ expect do
55
+ HTTPI.get(request, adapter)
56
+ end.to raise_exception(Net::ReadTimeout)
57
+ end
58
+
33
59
  it "executes GET requests" do
34
60
  response = HTTPI.get(@server.url, adapter)
35
61
  expect(response.body).to eq("get")
@@ -60,6 +86,42 @@ describe HTTPI::Adapter::NetHTTP do
60
86
  expect(response.headers["Content-Type"]).to eq("text/plain")
61
87
  end
62
88
 
89
+ context "custom methods" do
90
+ let(:request) {
91
+ HTTPI::Request.new(@server.url).tap do |r|
92
+ r.body = request_body if request_body
93
+ end
94
+ }
95
+ let(:request_body) { nil }
96
+
97
+ subject(:response) { HTTPI.request(http_method, request, adapter) }
98
+
99
+ shared_examples_for "any supported custom method" do
100
+ describe '#body' do
101
+ subject(:body) {response.body}
102
+ it { is_expected.to be == http_method.to_s }
103
+ end
104
+
105
+ describe '#headers' do
106
+ subject(:headers) {response.headers}
107
+ it { is_expected.to include('content-type' => "text/plain")}
108
+ end
109
+ end
110
+
111
+ context "PATCH method" do
112
+ let(:http_method) { :patch }
113
+ let(:request_body) { "<some>xml</some>" }
114
+
115
+ it_behaves_like "any supported custom method"
116
+ end
117
+
118
+ context "UNSUPPORTED method" do
119
+ let(:http_method) { :unsupported }
120
+
121
+ specify { expect { response }.to raise_error HTTPI::NotSupportedError }
122
+ end
123
+ end
124
+
63
125
  it "supports basic authentication" do
64
126
  request = HTTPI::Request.new(@server.url + "basic-auth")
65
127
  request.auth.basic("admin", "secret")
@@ -68,7 +130,69 @@ describe HTTPI::Adapter::NetHTTP do
68
130
  expect(response.body).to eq("basic-auth")
69
131
  end
70
132
 
71
- # it does not support digest authentication
133
+ it "does not support digest authentication" do
134
+ request = HTTPI::Request.new(@server.url + "digest-auth")
135
+ request.auth.digest("admin", "secret")
136
+
137
+ expect { HTTPI.get(request, adapter) }.
138
+ to raise_error(HTTPI::NotSupportedError, /does not support HTTP digest authentication/)
139
+ end
140
+
141
+ it "supports ntlm authentication" do
142
+ request = HTTPI::Request.new(@server.url + "ntlm-auth")
143
+ request.auth.ntlm("tester", "vReqSoafRe5O")
144
+
145
+ response = HTTPI.get(request, adapter)
146
+ expect(response.body).to eq("ntlm-auth")
147
+ end
148
+
149
+ it "does not support ntlm authentication when Net::NTLM is not available" do
150
+ Net.expects(:const_defined?).with(:NTLM).returns false
151
+
152
+ request = HTTPI::Request.new(@server.url + "ntlm-auth")
153
+ request.auth.ntlm("testing", "failures")
154
+
155
+ expect { HTTPI.get(request, adapter) }.
156
+ to raise_error(HTTPI::NotSupportedError, /Net::NTLM is not available/)
157
+ end
158
+
159
+ it "does not require ntlm when ntlm authenication is not requested" do
160
+ HTTPI::Adapter::NetHTTP.any_instance.stubs(:check_net_ntlm_version!).raises(RuntimeError)
161
+ request = HTTPI::Request.new(@server.url)
162
+ expect(request.auth.ntlm?).to be false
163
+
164
+ # make sure a request doesn't call ntlm check if we don't ask for it.
165
+ expect { HTTPI.get(request, adapter) }.not_to raise_error
166
+ HTTPI::Adapter::NetHTTP.any_instance.unstub(:check_net_ntlm_version!)
167
+ end
168
+
169
+ it "does check ntlm when ntlm authentication is requested" do
170
+ request = HTTPI::Request.new(@server.url + "ntlm-auth")
171
+ request.auth.ntlm("tester", "vReqSoafRe5O")
172
+
173
+ expect { HTTPI.get(request, adapter) }.not_to raise_error
174
+
175
+ # the check should also verify that the version of ntlm is supported and still fail if it isn't
176
+ HTTPI::Adapter::NetHTTP.any_instance.stubs(:ntlm_version).returns("0.1.1")
177
+
178
+ request = HTTPI::Request.new(@server.url + "ntlm-auth")
179
+ request.auth.ntlm("tester", "vReqSoafRe5O")
180
+
181
+ expect { HTTPI.get(request, adapter) }.to raise_error(ArgumentError, /Invalid version/)
182
+
183
+ HTTPI::Adapter::NetHTTP.any_instance.unstub(:ntlm_version)
184
+ end
185
+
186
+ it "does not crash when authenticate header is missing (on second request)" do
187
+ request = HTTPI::Request.new(@server.url + "ntlm-auth")
188
+ request.auth.ntlm("tester", "vReqSoafRe5O")
189
+
190
+ expect { HTTPI.get(request, adapter) }.
191
+ to_not raise_error
192
+
193
+ expect { HTTPI.get(request, adapter) }.
194
+ to_not raise_error
195
+ end
72
196
 
73
197
  it "supports chunked response" do
74
198
  request = HTTPI::Request.new(@server.url)
@@ -101,6 +225,25 @@ describe HTTPI::Adapter::NetHTTP do
101
225
  response = HTTPI.get(request, adapter)
102
226
  expect(response.body).to eq("get")
103
227
  end
228
+
229
+ it "works with min_version/max_version" do
230
+ request = HTTPI::Request.new(@server.url)
231
+ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
232
+ request.auth.ssl.min_version = :TLS1_2
233
+ request.auth.ssl.max_version = :TLS1_2
234
+
235
+ response = HTTPI.get(request, adapter)
236
+ expect(response.body).to eq("get")
237
+ end
238
+
239
+ it "works with ciphers" do
240
+ request = HTTPI::Request.new(@server.url)
241
+ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
242
+ request.auth.ssl.ciphers = OpenSSL::SSL::SSLContext.new.ciphers
243
+
244
+ response = HTTPI.get(request, adapter)
245
+ expect(response.body).to eq("get")
246
+ end
104
247
  end
105
248
  end
106
249
 
@@ -15,9 +15,10 @@ class IntegrationServer
15
15
  }
16
16
  end
17
17
 
18
- map "/repeat" do
18
+ map "/timeout" do
19
19
  run lambda { |env|
20
- IntegrationServer.respond_with :body => env["rack.input"].read
20
+ sleep 2
21
+ IntegrationServer.respond_with "done"
21
22
  }
22
23
  end
23
24
 
@@ -4,7 +4,6 @@ require "puma/minissl"
4
4
  require "integration/support/application"
5
5
 
6
6
  class IntegrationServer
7
-
8
7
  def self.run(options = {})
9
8
  server = new(options)
10
9
  server.run
@@ -76,7 +75,7 @@ class IntegrationServer
76
75
 
77
76
  context.key = IntegrationServer.ssl_key_file
78
77
  context.cert = IntegrationServer.ssl_cert_file
79
- context.verify_mode = Puma::MiniSSL::VERIFY_PEER
78
+ context.verify_mode = Puma::MiniSSL::VERIFY_NONE
80
79
 
81
80
  context
82
81
  end
data/spec/spec_helper.rb CHANGED
@@ -3,9 +3,7 @@ Bundler.setup(:default, :development)
3
3
 
4
4
  unless RUBY_PLATFORM =~ /java/
5
5
  require 'simplecov'
6
- require 'coveralls'
7
6
 
8
- SimpleCov.formatter = Coveralls::SimpleCov::Formatter
9
7
  SimpleCov.start do
10
8
  add_filter 'spec'
11
9
  end
metadata CHANGED
@@ -1,44 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: httpi
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.3
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Harrington
8
8
  - Martin Tepper
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-02-18 00:00:00.000000000 Z
12
+ date: 2021-10-18 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: rack
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - ">="
19
- - !ruby/object:Gem::Version
20
- version: '0'
21
- type: :runtime
22
- prerelease: false
23
- version_requirements: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- version: '0'
28
- - !ruby/object:Gem::Dependency
29
- name: socksify
30
- requirement: !ruby/object:Gem::Requirement
31
- requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
34
- version: '0'
35
- type: :runtime
36
- prerelease: false
37
- version_requirements: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - ">="
40
- - !ruby/object:Gem::Version
41
- version: '0'
42
14
  - !ruby/object:Gem::Dependency
43
15
  name: rubyntlm
44
16
  requirement: !ruby/object:Gem::Requirement
@@ -59,28 +31,28 @@ dependencies:
59
31
  requirements:
60
32
  - - "~>"
61
33
  - !ruby/object:Gem::Version
62
- version: '10.0'
34
+ version: '13.0'
63
35
  type: :development
64
36
  prerelease: false
65
37
  version_requirements: !ruby/object:Gem::Requirement
66
38
  requirements:
67
39
  - - "~>"
68
40
  - !ruby/object:Gem::Version
69
- version: '10.0'
41
+ version: '13.0'
70
42
  - !ruby/object:Gem::Dependency
71
43
  name: rspec
72
44
  requirement: !ruby/object:Gem::Requirement
73
45
  requirements:
74
46
  - - "~>"
75
47
  - !ruby/object:Gem::Version
76
- version: '2.14'
48
+ version: '3.5'
77
49
  type: :development
78
50
  prerelease: false
79
51
  version_requirements: !ruby/object:Gem::Requirement
80
52
  requirements:
81
53
  - - "~>"
82
54
  - !ruby/object:Gem::Version
83
- version: '2.14'
55
+ version: '3.5'
84
56
  - !ruby/object:Gem::Dependency
85
57
  name: mocha
86
58
  requirement: !ruby/object:Gem::Requirement
@@ -101,14 +73,14 @@ dependencies:
101
73
  requirements:
102
74
  - - "~>"
103
75
  - !ruby/object:Gem::Version
104
- version: 2.3.2
76
+ version: '5.0'
105
77
  type: :development
106
78
  prerelease: false
107
79
  version_requirements: !ruby/object:Gem::Requirement
108
80
  requirements:
109
81
  - - "~>"
110
82
  - !ruby/object:Gem::Version
111
- version: 2.3.2
83
+ version: '5.0'
112
84
  - !ruby/object:Gem::Dependency
113
85
  name: webmock
114
86
  requirement: !ruby/object:Gem::Requirement
@@ -129,14 +101,15 @@ executables: []
129
101
  extensions: []
130
102
  extra_rdoc_files: []
131
103
  files:
104
+ - ".github/workflows/development.yml"
132
105
  - ".gitignore"
133
106
  - ".rspec"
134
- - ".travis.yml"
135
107
  - CHANGELOG.md
136
108
  - Gemfile
137
109
  - LICENSE
138
110
  - README.md
139
111
  - Rakefile
112
+ - UPDATING.md
140
113
  - httpi.gemspec
141
114
  - lib/httpi.rb
142
115
  - lib/httpi/adapter.rb
@@ -204,7 +177,7 @@ homepage: http://github.com/savonrb/httpi
204
177
  licenses:
205
178
  - MIT
206
179
  metadata: {}
207
- post_install_message:
180
+ post_install_message:
208
181
  rdoc_options: []
209
182
  require_paths:
210
183
  - lib
@@ -212,16 +185,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
212
185
  requirements:
213
186
  - - ">="
214
187
  - !ruby/object:Gem::Version
215
- version: 1.9.2
188
+ version: '2.3'
216
189
  required_rubygems_version: !ruby/object:Gem::Requirement
217
190
  requirements:
218
191
  - - ">="
219
192
  - !ruby/object:Gem::Version
220
193
  version: '0'
221
194
  requirements: []
222
- rubyforge_project: httpi
223
- rubygems_version: 2.7.3
224
- signing_key:
195
+ rubygems_version: 3.2.7
196
+ signing_key:
225
197
  specification_version: 4
226
198
  summary: Common interface for Ruby's HTTP libraries
227
199
  test_files: []
data/.travis.yml DELETED
@@ -1,15 +0,0 @@
1
- language: "ruby"
2
- script: "bundle exec rake ci"
3
- sudo: false
4
- rvm:
5
- - 2.0.0
6
- - 2.1.8
7
- - 2.2.4
8
- - 2.3.0
9
- - 2.5.0
10
- - jruby-9.1.9.0
11
- env:
12
- global:
13
- - JRUBY_OPTS="--2.0"
14
- before_install:
15
- - "travis_retry gem install bundler"