savon 2.11.2 → 2.12.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ee16983ffafe63866c9a55e2f87082982882442b
4
- data.tar.gz: 031a0b620818fa9242fa6df79d716dc2d5fb0795
3
+ metadata.gz: 5f5d529eeeacffdb613697c1e822ae8f66f48405
4
+ data.tar.gz: 107dbcdf6bf87cffefafeb4216eee8e3b1a8c06f
5
5
  SHA512:
6
- metadata.gz: 32d112e59f45ec6b6f1c65f54c33030c99ae2e2c931ce476d0c62cceb7715d62772164b0f7e182ab691a8b47d8cfeb7c83140986743f3e134e91040d099905a6
7
- data.tar.gz: 0d73626cde6c3ccec91b090f0d30cb1f6031b5fdc88834e1874719f0e269ae62c5a4e9402e863a45e7fb64c176b58f6ff8bb5296a2e13d1ddd28bb1133c9af0d
6
+ metadata.gz: 49c798231744f066396510436f9eed0a272bb7f189c43395ae54a3bf3a09d012acdd3231fb03cb6d94703d83134fe50f53cfa731f62ec156f05edb2580c45988
7
+ data.tar.gz: 65dde0f62c985730aaf4d77c2414dccd04187979d3173c4419cbb6fd1c2feaa0cc79fac7a67b54b45ef66fa03e862f91b1204c76b3429d806c8ef1f06cd5ff10
@@ -5,11 +5,10 @@ before_install:
5
5
  - gem install bundler
6
6
  script: "bundle exec rake --trace"
7
7
  rvm:
8
- - 2.0.0
9
- - 2.1.8
10
8
  - 2.2.4
11
9
  - 2.3.0
12
- - jruby
10
+ - 2.4.1
11
+ - jruby-9.1.15.0
13
12
  - rbx-2
14
13
  matrix:
15
14
  allow_failures:
@@ -1,3 +1,15 @@
1
+ # 2.12.0 (2018-01-16)
2
+
3
+ * Drop support for ruby 2.1 and below.
4
+ * Fix: [#822](https://github.com/savonrb/savon/pull/822) Raise correct error when SOAP envelope only contains a string
5
+ * Fix: [#833](https://github.com/savonrb/savon/pull/833) Fixes boolean handling regression introduced in 2.11.2
6
+ * Feature: [#794](https://github.com/savonrb/savon/pull/794), add global option ssl_ciphers.
7
+ * Feature: [#753](https://github.com/savonrb/savon/pull/753) Add headers configuration to WSDLRequest#build
8
+ * Feature: [#812](https://github.com/savonrb/savon/pull/812) Allow `proxy` option to be `nil`.
9
+ * Feature: [#838](https://github.com/savonrb/savon/pull/838) Added ssl_ca_path and ssl_cert_store to globals
10
+ * Feature: [#794](https://github.com/savonrb/savon/pull/794) Add global option ssl_ciphers
11
+
12
+
1
13
  # 2.11.2 (2017-08-03)
2
14
  * Fix: [#676](https://github.com/savonrb/savon/pull/676) Fixes handling of `content!` and `attributes!`
3
15
  * Fix: [#800](https://github.com/savonrb/savon/pull/800) Fix exception calling `SOAPFault#to_s` when http.body is empty
data/README.md CHANGED
@@ -22,7 +22,7 @@ $ gem install savon
22
22
  or add it to your Gemfile like this:
23
23
 
24
24
  ```
25
- gem 'savon', '~> 2.11.1'
25
+ gem 'savon', '~> 2.12.0'
26
26
  ```
27
27
 
28
28
  ## Usage example
@@ -46,6 +46,19 @@ response.body
46
46
  For more examples, you should check out the
47
47
  [integration tests](https://github.com/savonrb/savon/tree/version2/spec/integration).
48
48
 
49
+ ## Ruby version support
50
+ * 2.12.x - MRI 2.2, 2.3, 2.4
51
+ * 2.11.x - MRI 2.0, 2.1, 2.2, and 2.3
52
+
53
+ If you are running MRI 1.8.7, try the 2.6.x branch.
54
+
55
+ ## Running tests
56
+
57
+ ```bash
58
+ $ bundle install
59
+ $ bundle exec rspec
60
+ ```
61
+
49
62
  ## FAQ
50
63
 
51
64
  * URI::InvalidURIError -- if you see this error, then it is likely that the http client you are using cannot parse the URI for your WSDL. Try `gem install httpclient` or add it to your `Gemfile`.
@@ -137,7 +137,7 @@ module Savon
137
137
 
138
138
  # Proxy server to use for all requests.
139
139
  def proxy(proxy)
140
- @options[:proxy] = proxy
140
+ @options[:proxy] = proxy unless proxy.nil?
141
141
  end
142
142
 
143
143
  # A Hash of HTTP headers.
@@ -268,6 +268,19 @@ module Savon
268
268
  @options[:ssl_ca_cert] = cert
269
269
  end
270
270
 
271
+ def ssl_ciphers(ciphers)
272
+ @options[:ssl_ciphers] = ciphers
273
+ end
274
+
275
+ # Sets the ca cert path.
276
+ def ssl_ca_cert_path(path)
277
+ @options[:ssl_ca_cert_path] = path
278
+ end
279
+
280
+ # Sets the ssl cert store.
281
+ def ssl_cert_store(store)
282
+ @options[:ssl_cert_store] = store
283
+ end
271
284
 
272
285
  # HTTP basic auth credentials.
273
286
  def basic_auth(*credentials)
@@ -9,7 +9,7 @@ module Savon
9
9
  end
10
10
 
11
11
  def to_hash(hash, path)
12
- return unless hash
12
+ return hash unless hash
13
13
  return hash.map { |value| to_hash(value, path) } if hash.is_a?(Array)
14
14
  return hash.to_s unless hash.is_a?(Hash)
15
15
 
@@ -26,13 +26,16 @@ module Savon
26
26
  def configure_ssl
27
27
  @http_request.auth.ssl.ssl_version = @globals[:ssl_version] if @globals.include? :ssl_version
28
28
  @http_request.auth.ssl.verify_mode = @globals[:ssl_verify_mode] if @globals.include? :ssl_verify_mode
29
+ @http_request.auth.ssl.ciphers = @globals[:ssl_ciphers] if @globals.include? :ssl_ciphers
29
30
 
30
31
  @http_request.auth.ssl.cert_key_file = @globals[:ssl_cert_key_file] if @globals.include? :ssl_cert_key_file
31
- @http_request.auth.ssl.cert_key = @globals[:ssl_cert_key] if @globals.include? :ssl_cert_key
32
+ @http_request.auth.ssl.cert_key = @globals[:ssl_cert_key] if @globals.include? :ssl_cert_key
32
33
  @http_request.auth.ssl.cert_file = @globals[:ssl_cert_file] if @globals.include? :ssl_cert_file
33
- @http_request.auth.ssl.cert = @globals[:ssl_cert] if @globals.include? :ssl_cert
34
+ @http_request.auth.ssl.cert = @globals[:ssl_cert] if @globals.include? :ssl_cert
34
35
  @http_request.auth.ssl.ca_cert_file = @globals[:ssl_ca_cert_file] if @globals.include? :ssl_ca_cert_file
35
- @http_request.auth.ssl.ca_cert = @globals[:ssl_ca_cert] if @globals.include? :ssl_ca_cert
36
+ @http_request.auth.ssl.ca_cert_path = @globals[:ssl_ca_cert_path] if @globals.include? :ssl_ca_cert_path
37
+ @http_request.auth.ssl.ca_cert = @globals[:ssl_ca_cert] if @globals.include? :ssl_ca_cert
38
+ @http_request.auth.ssl.cert_store = @globals[:ssl_cert_store] if @globals.include? :ssl_cert_store
36
39
 
37
40
  @http_request.auth.ssl.cert_key_password = @globals[:ssl_cert_key_password] if @globals.include? :ssl_cert_key_password
38
41
  end
@@ -55,12 +58,19 @@ module Savon
55
58
  def build
56
59
  configure_proxy
57
60
  configure_timeouts
61
+ configure_headers
58
62
  configure_ssl
59
63
  configure_auth
60
64
  configure_redirect_handling
61
65
 
62
66
  @http_request
63
67
  end
68
+
69
+ private
70
+
71
+ def configure_headers
72
+ @http_request.headers = @globals[:headers] if @globals.include? :headers
73
+ end
64
74
  end
65
75
 
66
76
  class SOAPRequest < HTTPRequest
@@ -69,7 +69,7 @@ module Savon
69
69
 
70
70
  def find(*path)
71
71
  envelope = nori.find(hash, 'Envelope')
72
- raise_invalid_response_error! unless envelope
72
+ raise_invalid_response_error! unless envelope.is_a?(Hash)
73
73
 
74
74
  nori.find(envelope, *path)
75
75
  end
@@ -1,5 +1,3 @@
1
- require "savon"
2
-
3
1
  module Savon
4
2
  class SOAPFault < Error
5
3
 
@@ -1,3 +1,3 @@
1
1
  module Savon
2
- VERSION = '2.11.2'
2
+ VERSION = '2.12.0'
3
3
  end
@@ -23,10 +23,10 @@ Gem::Specification.new do |s|
23
23
  s.add_dependency "akami", "~> 1.2"
24
24
  s.add_dependency "gyoku", "~> 1.2"
25
25
  s.add_dependency "builder", ">= 2.1.2"
26
- s.add_dependency "nokogiri", ">= 1.4.0"
26
+ s.add_dependency "nokogiri", ">= 1.8.1"
27
27
 
28
28
  s.add_development_dependency "rack"
29
- s.add_development_dependency "puma", "2.0.0.b4"
29
+ s.add_development_dependency "puma", "~> 3.0"
30
30
 
31
31
  s.add_development_dependency "rake", "~> 10.1"
32
32
  s.add_development_dependency "rspec", "~> 2.14"
@@ -0,0 +1 @@
1
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">Text</soap:Envelope>
@@ -2,14 +2,16 @@ require 'spec_helper'
2
2
 
3
3
  module LogInterceptor
4
4
  @@intercepted_request = ""
5
- def self.debug(message)
5
+ def self.debug(message = nil)
6
+ message ||= yield if block_given?
7
+
6
8
  # save only the first XMLly message
7
9
  if message.include? "xml version"
8
10
  @@intercepted_request = message if @@intercepted_request == ""
9
11
  end
10
12
  end
11
13
 
12
- def self.info(message)
14
+ def self.info(message = nil)
13
15
  end
14
16
 
15
17
  def self.get_intercepted_request
@@ -27,6 +29,7 @@ describe 'Correct translation of attributes to XML' do
27
29
 
28
30
  client = Savon.client(
29
31
  :wsdl => "http://mt205.sabameeting.com/CWS/CWS.asmx?WSDL",
32
+ :log => true,
30
33
  :logger => LogInterceptor
31
34
  )
32
35
 
@@ -48,14 +51,12 @@ describe 'Correct translation of attributes to XML' do
48
51
 
49
52
  client = Savon.client(
50
53
  :wsdl => "http://mt205.sabameeting.com/CWS/CWS.asmx?WSDL",
54
+ :log => true,
51
55
  :logger => LogInterceptor
52
56
  )
53
57
 
54
58
  response = nil
55
- begin
56
- response = call_and_fail_gracefully(client, :add_new_user, :message => { :user => {}, :attributes! => { :user => { :userID => "test" } } })
57
- rescue
58
- end
59
+ response = call_and_fail_gracefully(client, :add_new_user, :message => { :user => {}, :attributes! => { :user => { :userID => "test" } } })
59
60
 
60
61
  xml_doc = Nokogiri::XML(LogInterceptor.get_intercepted_request)
61
62
  xml_doc.remove_namespaces!
@@ -1,4 +1,4 @@
1
- require "rack/builder"
1
+ require "rack"
2
2
  require "json"
3
3
 
4
4
  class IntegrationServer
@@ -389,18 +389,27 @@ describe "Options" do
389
389
 
390
390
  context "global :ssl_version" do
391
391
  it "sets the SSL version to use" do
392
- HTTPI::Auth::SSL.any_instance.expects(:ssl_version=).with(:SSLv3).twice
392
+ HTTPI::Auth::SSL.any_instance.expects(:ssl_version=).with(:TLSv1).twice
393
393
 
394
- client = new_client(:endpoint => @server.url, :ssl_version => :SSLv3)
394
+ client = new_client(:endpoint => @server.url, :ssl_version => :TLSv1)
395
395
  client.call(:authenticate)
396
396
  end
397
397
  end
398
398
 
399
399
  context "global :ssl_verify_mode" do
400
400
  it "sets the verify mode to use" do
401
- HTTPI::Auth::SSL.any_instance.expects(:verify_mode=).with(:none).twice
401
+ HTTPI::Auth::SSL.any_instance.expects(:verify_mode=).with(:peer).twice
402
402
 
403
- client = new_client(:endpoint => @server.url, :ssl_verify_mode => :none)
403
+ client = new_client(:endpoint => @server.url, :ssl_verify_mode => :peer)
404
+ client.call(:authenticate)
405
+ end
406
+ end
407
+
408
+ context "global :ssl_ciphers" do
409
+ it "sets the ciphers to use" do
410
+ HTTPI::Auth::SSL.any_instance.expects(:ciphers=).with(:none).twice
411
+
412
+ client = new_client(:endpoint => @server.url, :ssl_ciphers => :none)
404
413
  client.call(:authenticate)
405
414
  end
406
415
  end
@@ -469,6 +478,26 @@ describe "Options" do
469
478
  end
470
479
  end
471
480
 
481
+ context "global :ssl_ca_cert_path" do
482
+ it "sets the ca cert path to use" do
483
+ ca_cert_path = "../../fixtures/ssl"
484
+ HTTPI::Auth::SSL.any_instance.expects(:ca_cert_path=).with(ca_cert_path).twice
485
+
486
+ client = new_client(:endpoint => @server.url, :ssl_ca_cert_path => ca_cert_path)
487
+ client.call(:authenticate)
488
+ end
489
+ end
490
+
491
+ context "global :ssl_ca_cert_store" do
492
+ it "sets the cert store to use" do
493
+ cert_store = OpenSSL::X509::Store.new
494
+ HTTPI::Auth::SSL.any_instance.expects(:cert_store=).with(cert_store).twice
495
+
496
+ client = new_client(:endpoint => @server.url, :ssl_cert_store => cert_store)
497
+ client.call(:authenticate)
498
+ end
499
+ end
500
+
472
501
  context "global :ssl_ca_cert" do
473
502
  it "sets the ca cert file to use" do
474
503
  ca_cert = File.open(File.expand_path("../../fixtures/ssl/client_cert.pem", __FILE__)).read
@@ -62,6 +62,39 @@ module Savon
62
62
  expect(resulting_hash).to eq good_result
63
63
  expect(xml).to eq good_xml
64
64
  end
65
+
66
+ it "properly handles boolean false" do
67
+ used_namespaces = {
68
+ %w(tns Foo) => 'ns'
69
+ }
70
+
71
+ hash = {
72
+ :foo => {
73
+ :falsey => {
74
+ :@attr1 => false,
75
+ :content! => false
76
+ }
77
+ }
78
+ }
79
+
80
+ good_result = {
81
+ "ns:Foo" => {
82
+ :falsey => {
83
+ :@attr1 => false,
84
+ :content! => false
85
+ }
86
+ }
87
+ }
88
+
89
+ good_xml = %(<ns:Foo><Falsey attr1="false">false</Falsey></ns:Foo>)
90
+
91
+ message = described_class.new(types, used_namespaces, key_converter)
92
+ resulting_hash = message.to_hash(hash, ['tns'])
93
+ xml = Gyoku.xml(resulting_hash, key_converter: key_converter)
94
+
95
+ expect(resulting_hash).to eq good_result
96
+ expect(xml).to eq good_xml
97
+ end
65
98
  end
66
99
 
67
100
  end
@@ -5,6 +5,7 @@ describe Savon::WSDLRequest do
5
5
 
6
6
  let(:globals) { Savon::GlobalOptions.new }
7
7
  let(:http_request) { HTTPI::Request.new }
8
+ let(:ciphers) { OpenSSL::Cipher.ciphers }
8
9
 
9
10
  def new_wsdl_request
10
11
  Savon::WSDLRequest.new(globals, http_request)
@@ -16,6 +17,20 @@ describe Savon::WSDLRequest do
16
17
  expect(wsdl_request.build).to be_an(HTTPI::Request)
17
18
  end
18
19
 
20
+ describe "headers" do
21
+ it "are set when specified" do
22
+ globals.headers("Proxy-Authorization" => "Basic auth")
23
+ configured_http_request = new_wsdl_request.build
24
+
25
+ expect(configured_http_request.headers["Proxy-Authorization"]).to eq("Basic auth")
26
+ end
27
+
28
+ it "are not set otherwise" do
29
+ configured_http_request = new_wsdl_request.build
30
+ expect(configured_http_request.headers).to_not include("Proxy-Authorization")
31
+ end
32
+ end
33
+
19
34
  describe "proxy" do
20
35
  it "is set when specified" do
21
36
  globals.proxy("http://proxy.example.com")
@@ -60,8 +75,8 @@ describe Savon::WSDLRequest do
60
75
 
61
76
  describe "ssl version" do
62
77
  it "is set when specified" do
63
- globals.ssl_version(:SSLv3)
64
- http_request.auth.ssl.expects(:ssl_version=).with(:SSLv3)
78
+ globals.ssl_version(:TLSv1)
79
+ http_request.auth.ssl.expects(:ssl_version=).with(:TLSv1)
65
80
 
66
81
  new_wsdl_request.build
67
82
  end
@@ -74,8 +89,8 @@ describe Savon::WSDLRequest do
74
89
 
75
90
  describe "ssl verify mode" do
76
91
  it "is set when specified" do
77
- globals.ssl_verify_mode(:none)
78
- http_request.auth.ssl.expects(:verify_mode=).with(:none)
92
+ globals.ssl_verify_mode(:peer)
93
+ http_request.auth.ssl.expects(:verify_mode=).with(:peer)
79
94
 
80
95
  new_wsdl_request.build
81
96
  end
@@ -86,6 +101,20 @@ describe Savon::WSDLRequest do
86
101
  end
87
102
  end
88
103
 
104
+ describe "ssl ciphers" do
105
+ it "is set when specified" do
106
+ globals.ssl_ciphers(ciphers)
107
+ http_request.auth.ssl.expects(:ciphers=).with(ciphers)
108
+
109
+ new_wsdl_request.build
110
+ end
111
+
112
+ it "is not set otherwise" do
113
+ http_request.auth.ssl.expects(:ciphers=).never
114
+ new_wsdl_request.build
115
+ end
116
+ end
117
+
89
118
  describe "ssl cert key file" do
90
119
  it "is set when specified" do
91
120
  cert_key = File.expand_path("../../fixtures/ssl/client_key.pem", __FILE__)
@@ -232,6 +261,7 @@ describe Savon::SOAPRequest do
232
261
 
233
262
  let(:globals) { Savon::GlobalOptions.new }
234
263
  let(:http_request) { HTTPI::Request.new }
264
+ let(:ciphers) { OpenSSL::Cipher.ciphers }
235
265
 
236
266
  def new_soap_request
237
267
  Savon::SOAPRequest.new(globals, http_request)
@@ -364,8 +394,8 @@ describe Savon::SOAPRequest do
364
394
 
365
395
  describe "ssl version" do
366
396
  it "is set when specified" do
367
- globals.ssl_version(:SSLv3)
368
- http_request.auth.ssl.expects(:ssl_version=).with(:SSLv3)
397
+ globals.ssl_version(:TLSv1)
398
+ http_request.auth.ssl.expects(:ssl_version=).with(:TLSv1)
369
399
 
370
400
  new_soap_request.build
371
401
  end
@@ -378,8 +408,8 @@ describe Savon::SOAPRequest do
378
408
 
379
409
  describe "ssl verify mode" do
380
410
  it "is set when specified" do
381
- globals.ssl_verify_mode(:none)
382
- http_request.auth.ssl.expects(:verify_mode=).with(:none)
411
+ globals.ssl_verify_mode(:peer)
412
+ http_request.auth.ssl.expects(:verify_mode=).with(:peer)
383
413
 
384
414
  new_soap_request.build
385
415
  end
@@ -390,6 +420,20 @@ describe Savon::SOAPRequest do
390
420
  end
391
421
  end
392
422
 
423
+ describe "ssl ciphers" do
424
+ it "is set when specified" do
425
+ globals.ssl_ciphers(ciphers)
426
+ http_request.auth.ssl.expects(:ciphers=).with(ciphers)
427
+
428
+ new_soap_request.build
429
+ end
430
+
431
+ it "is not set otherwise" do
432
+ http_request.auth.ssl.expects(:ciphers=).never
433
+ new_soap_request.build
434
+ end
435
+ end
436
+
393
437
  describe "ssl cert key file" do
394
438
  it "is set when specified" do
395
439
  cert_key = File.expand_path("../../fixtures/ssl/client_key.pem", __FILE__)
@@ -235,6 +235,11 @@ describe Savon::Response do
235
235
  expect(result).to be_a(Hash)
236
236
  expect(result.keys).to include(:authentication_value)
237
237
  end
238
+
239
+ it 'fails correctly when envelope contains only string' do
240
+ response = soap_response({ :body => Fixture.response(:no_body) })
241
+ expect { response.find('Body') }.to raise_error Savon::InvalidResponseError
242
+ end
238
243
  end
239
244
 
240
245
  describe "#http" do
@@ -3,7 +3,7 @@ module SpecSupport
3
3
  def call_and_fail_gracefully(client, *args, &block)
4
4
  client.call(*args, &block)
5
5
  rescue Savon::SOAPFault => e
6
- pending e.message
6
+ puts e.message
7
7
  end
8
8
 
9
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: savon
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.11.2
4
+ version: 2.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Harrington
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-03 00:00:00.000000000 Z
11
+ date: 2018-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nori
@@ -100,14 +100,14 @@ dependencies:
100
100
  requirements:
101
101
  - - ">="
102
102
  - !ruby/object:Gem::Version
103
- version: 1.4.0
103
+ version: 1.8.1
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
- version: 1.4.0
110
+ version: 1.8.1
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: rack
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -126,16 +126,16 @@ dependencies:
126
126
  name: puma
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - '='
129
+ - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: 2.0.0.b4
131
+ version: '3.0'
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
- - - '='
136
+ - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: 2.0.0.b4
138
+ version: '3.0'
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: rake
141
141
  requirement: !ruby/object:Gem::Requirement
@@ -237,6 +237,7 @@ files:
237
237
  - spec/fixtures/response/header.xml
238
238
  - spec/fixtures/response/list.xml
239
239
  - spec/fixtures/response/multi_ref.xml
240
+ - spec/fixtures/response/no_body.xml
240
241
  - spec/fixtures/response/soap_fault.xml
241
242
  - spec/fixtures/response/soap_fault12.xml
242
243
  - spec/fixtures/response/soap_fault_funky.xml
@@ -311,7 +312,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
311
312
  version: '0'
312
313
  requirements: []
313
314
  rubyforge_project: savon
314
- rubygems_version: 2.6.12
315
+ rubygems_version: 2.6.13
315
316
  signing_key:
316
317
  specification_version: 4
317
318
  summary: Heavy metal SOAP client