savon 2.16.0 → 2.17.4

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.
data/lib/savon/client.rb CHANGED
@@ -1,19 +1,26 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "savon/operation"
3
- require "savon/request"
4
+ require "savon/transport/httpi"
5
+ require "savon/transport/faraday"
4
6
  require "savon/options"
5
7
  require "savon/block_interface"
6
8
  require "wasabi"
7
9
 
8
10
  module Savon
11
+ # The main entry point for Savon.
12
+ #
13
+ # Holds global configuration, owns the WSDL document, and dispatches
14
+ # named operations. A single Client instance is typically shared across
15
+ # multiple calls to the same service.
9
16
  class Client
10
-
11
17
  def initialize(globals = {}, &block)
12
- unless globals.kind_of? Hash
18
+ unless globals.is_a? Hash
13
19
  raise_version1_initialize_error! globals
14
20
  end
15
21
 
16
22
  set_globals(globals, block)
23
+ @globals.validate_transport!
17
24
 
18
25
  unless wsdl_or_endpoint_and_namespace_specified?
19
26
  raise_initialization_error!
@@ -24,13 +31,25 @@ module Savon
24
31
 
25
32
  attr_reader :globals, :wsdl
26
33
 
34
+ # Returns the memoized Faraday::Connection for this client.
35
+ # Callers use this to configure middleware, SSL, auth, timeouts, and any
36
+ # other transport-level concern before making calls.
37
+ # Raises ArgumentError if transport is not :faraday.
38
+ def faraday
39
+ unless @globals[:transport] == :faraday
40
+ raise ArgumentError, "client.faraday is only available when transport: :faraday is set"
41
+ end
42
+
43
+ @faraday ||= ::Faraday.new
44
+ end
45
+
27
46
  def operations
28
47
  raise_missing_wsdl_error! unless @wsdl.document?
29
48
  @wsdl.soap_actions
30
49
  end
31
50
 
32
51
  def operation(operation_name)
33
- Operation.create(operation_name, @wsdl, @globals)
52
+ Operation.create(operation_name, @wsdl, @globals, build_transport)
34
53
  end
35
54
 
36
55
  def call(operation_name, locals = {}, &block)
@@ -48,6 +67,15 @@ module Savon
48
67
 
49
68
  private
50
69
 
70
+ # Builds the transport for a single operation.
71
+ def build_transport
72
+ if @globals[:transport] == :faraday
73
+ Transport::Faraday.new(faraday, @globals)
74
+ else
75
+ Transport::HTTPI.new(@globals)
76
+ end
77
+ end
78
+
51
79
  def set_globals(globals, block)
52
80
  globals = GlobalOptions.new(globals)
53
81
  BlockInterface.new(globals).evaluate(block) if block
@@ -58,12 +86,16 @@ module Savon
58
86
  def build_wsdl_document
59
87
  @wsdl = Wasabi::Document.new
60
88
 
61
- @wsdl.document = @globals[:wsdl] if @globals.include? :wsdl
62
- @wsdl.endpoint = @globals[:endpoint] if @globals.include? :endpoint
63
- @wsdl.namespace = @globals[:namespace] if @globals.include? :namespace
64
- @wsdl.adapter = @globals[:adapter] if @globals.include? :adapter
89
+ @wsdl.document = @globals[:wsdl] if @globals.include? :wsdl
90
+ @wsdl.endpoint = @globals[:endpoint] if @globals.include? :endpoint
91
+ @wsdl.namespace = @globals[:namespace] if @globals.include? :namespace
65
92
 
66
- @wsdl.request = WSDLRequest.new(@globals).build
93
+ if @globals[:transport] == :faraday
94
+ @wsdl.request = faraday
95
+ else
96
+ @wsdl.adapter = @globals[:adapter] if @globals.include? :adapter
97
+ @wsdl.request = Transport::HTTPI.new(@globals).wsdl_request
98
+ end
67
99
  end
68
100
 
69
101
  def wsdl_or_endpoint_and_namespace_specified?
@@ -72,9 +104,9 @@ module Savon
72
104
 
73
105
  def raise_version1_initialize_error!(object)
74
106
  raise InitializationError,
75
- "Some code tries to initialize Savon with the #{object.inspect} (#{object.class}) \n" \
76
- "Savon 2 expects a Hash of options for creating a new client and executing requests.\n" \
77
- "Please read the updated documentation for version 2: http://savonrb.com/version2.html"
107
+ "Some code tries to initialize Savon with the #{object.inspect} (#{object.class}) \n" \
108
+ "Savon 2 expects a Hash of options for creating a new client and executing requests.\n" \
109
+ "Please read the updated documentation for version 2: http://savonrb.com/version2.html"
78
110
  end
79
111
 
80
112
  def raise_initialization_error!
@@ -88,6 +120,5 @@ module Savon
88
120
  def raise_missing_wsdl_error!
89
121
  raise "Unable to inspect the service without a WSDL document."
90
122
  end
91
-
92
123
  end
93
124
  end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "gyoku"
4
+ require "uri"
5
+
6
+ module Savon
7
+ # Resolves the effective value of options whose answer combines more than
8
+ # one source: the per-request options, the client options, the WSDL
9
+ # document, and built-in defaults.
10
+ #
11
+ # {Savon::GlobalOptions} and {Savon::LocalOptions} store what the caller
12
+ # said. EffectiveOptions answers what the request uses. It owns the
13
+ # precedence rules and exposes one reader per resolvable option, so every
14
+ # consumer of an option resolves it identically. Resolution is a pure read.
15
+ # It never mutates the options or the WSDL document.
16
+ class EffectiveOptions
17
+ # @param operation_name [Symbol] the SOAP operation being called
18
+ # @param wsdl [Wasabi::Document] the parsed WSDL, or an empty document
19
+ # when the client was configured without one
20
+ # @param globals [Savon::GlobalOptions] the client-level options
21
+ # @param locals [Savon::LocalOptions] the per-request options
22
+ def initialize(operation_name, wsdl, globals, locals)
23
+ @operation_name = operation_name
24
+ @wsdl = wsdl
25
+ @globals = globals
26
+ @locals = locals
27
+ end
28
+
29
+ # Resolves the SOAPAction of the request (SOAP 1.1 §6.1.1).
30
+ #
31
+ # An explicit local +:soap_action+ wins. A local +false+ or +nil+ disables
32
+ # the action, so no SOAPAction HTTP header is sent and an enabled
33
+ # +wsa:Action+ header stays empty. Without a local value the WSDL provides
34
+ # the soapAction of the operation. Without a WSDL document the operation
35
+ # name is converted to an XML tag as a best-effort default.
36
+ #
37
+ # @return [String, nil] the action, or +nil+ when explicitly disabled
38
+ def soap_action
39
+ return if @locals.include?(:soap_action) && !@locals[:soap_action]
40
+
41
+ @locals[:soap_action] ||
42
+ (@wsdl.document? && @wsdl.soap_action(@operation_name.to_sym)) ||
43
+ Gyoku.xml_tag(@operation_name, key_converter: @globals[:convert_request_keys_to])
44
+ end
45
+
46
+ # Resolves the endpoint URL the request is sent to.
47
+ #
48
+ # A global +:endpoint+ wins over the service address of the WSDL. The
49
+ # global +:host+ option replaces host and port of the WSDL address and
50
+ # keeps scheme, path and query. The override is applied to a copy. The
51
+ # WSDL document keeps its parsed address.
52
+ #
53
+ # @return [URI, String, nil] the endpoint as provided by the winning source
54
+ def endpoint
55
+ return @globals[:endpoint] if @globals[:endpoint]
56
+ return @wsdl.endpoint unless @globals[:host]
57
+
58
+ host_url = URI.parse(@globals[:host])
59
+ url = @wsdl.endpoint.dup
60
+ url.host = host_url.host
61
+ url.port = host_url.port
62
+ url
63
+ end
64
+
65
+ # Resolves the WSSE auth credentials passed to Akami. A local value takes
66
+ # precedence over the global one. A local +false+ disables auth even when a
67
+ # global value is set. A local +nil+ leaves the option unset and falls
68
+ # through to the global value.
69
+ #
70
+ # @return [Array<String>, false, nil] the credentials, +false+ to disable,
71
+ # or +nil+ when unset in both scopes
72
+ def wsse_auth
73
+ prefer_local(:wsse_auth)
74
+ end
75
+
76
+ # Resolves whether Akami emits a +wsu:Timestamp+ header, using the same
77
+ # local-over-global rule as {#wsse_auth}. A local +false+ disables it and a
78
+ # local +nil+ keeps the global value.
79
+ #
80
+ # @return [Boolean, nil]
81
+ def wsse_timestamp
82
+ prefer_local(:wsse_timestamp)
83
+ end
84
+
85
+ # Resolves the WSSE signature used to sign the request. This option is a
86
+ # signature object or +nil+ and has no "disable" value, so any falsy local
87
+ # value falls back to the global one. {Savon::Builder} and {Savon::Header}
88
+ # both read it and must resolve it identically.
89
+ #
90
+ # @return [Akami::WSSE::Signature, nil]
91
+ def wsse_signature
92
+ @locals[:wsse_signature] || @globals[:wsse_signature]
93
+ end
94
+
95
+ # Resolves the SOAP header content. When both scopes provide a Hash the two
96
+ # are merged and local keys win. Otherwise the local value is preferred and
97
+ # falls back to the global one. A Hash is rendered to XML by Gyoku. A String,
98
+ # or any object responding to +#to_s+, is used verbatim.
99
+ #
100
+ # @return [Hash, String, nil]
101
+ def soap_header
102
+ global = @globals[:soap_header]
103
+ local = @locals[:soap_header]
104
+
105
+ if global.is_a?(Hash) && local.is_a?(Hash)
106
+ global.merge(local)
107
+ else
108
+ local || global
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ # Resolves an option where a local +false+ is meaningful and disables it.
115
+ # Only a local +nil+ falls through to the global value. Contrast with
116
+ # {#wsse_signature}, which treats any falsy local as unset.
117
+ #
118
+ # @param key [Symbol] the option name
119
+ # @return [Object, nil] the local value unless it is +nil+, else the global
120
+ def prefer_local(key)
121
+ @locals[key].nil? ? @globals[key] : @locals[key]
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
5
+ module Savon
6
+ # Formats the replacement Faraday setup for one HTTPI-only option.
7
+ class FaradayMigrationHint
8
+ VALUE_AWARE_OPTIONS = %i[
9
+ proxy
10
+ open_timeout
11
+ read_timeout
12
+ write_timeout
13
+ ssl_version
14
+ ssl_min_version
15
+ ssl_max_version
16
+ ssl_verify_mode
17
+ ssl_cert_key_file
18
+ ssl_cert_file
19
+ ssl_ca_cert_file
20
+ ssl_ciphers
21
+ ssl_ca_cert_path
22
+ adapter
23
+ ].freeze
24
+
25
+ STATIC_HINTS = {
26
+ ssl_cert_key: "client.faraday.ssl.client_key = key",
27
+ ssl_cert_key_password: [
28
+ "key = OpenSSL::PKey.read(File.read(key_path), password)",
29
+ "client.faraday.ssl.client_key = key"
30
+ ],
31
+ ssl_cert: "client.faraday.ssl.client_cert = cert",
32
+ ssl_ca_cert: [
33
+ "store = OpenSSL::X509::Store.new",
34
+ "store.set_default_paths",
35
+ "store.add_cert(cert)",
36
+ "client.faraday.ssl.cert_store = store"
37
+ ],
38
+ ssl_cert_store: "client.faraday.ssl.cert_store = store",
39
+ basic_auth: "client.faraday.request :authorization, :basic, user, pass",
40
+ digest_auth: [
41
+ "gem 'faraday-digestauth'",
42
+ "require 'faraday/digestauth'",
43
+ "client.faraday.request :digest, user, pass"
44
+ ],
45
+ ntlm: [
46
+ "gem 'faraday-ntlm_auth'",
47
+ "require 'faraday/ntlm_auth'",
48
+ "client.faraday.adapter :net_http_persistent",
49
+ "client.faraday.request :ntlm_auth, auth: [user, pass, domain]"
50
+ ],
51
+ follow_redirects: [
52
+ "gem 'faraday-follow_redirects'",
53
+ "require 'faraday/follow_redirects'",
54
+ "client.faraday.response :follow_redirects"
55
+ ]
56
+ }.freeze
57
+
58
+ OPTIONS = (VALUE_AWARE_OPTIONS + STATIC_HINTS.keys).freeze
59
+
60
+ def initialize(option, value)
61
+ @option = option
62
+ @value = value
63
+ end
64
+
65
+ def message
66
+ hint = hint_lines
67
+ return " #{option} - Use: #{hint}" unless hint.is_a?(Array)
68
+
69
+ " #{option} - Use:\n#{hint.map { |line| " #{line}" }.join("\n")}"
70
+ end
71
+
72
+ private
73
+
74
+ attr_reader :option, :value
75
+
76
+ def hint_lines
77
+ case option
78
+ when :proxy
79
+ "client.faraday.proxy = #{redacted_proxy_value}"
80
+ when :open_timeout, :read_timeout, :write_timeout
81
+ "client.faraday.options.#{option} = #{value.inspect}"
82
+ when :ssl_version, :ssl_min_version, :ssl_max_version
83
+ ssl_version_hint
84
+ when :ssl_verify_mode
85
+ ssl_verify_mode_hint
86
+ when :ssl_cert_key_file
87
+ ssl_cert_key_file_hint
88
+ when :ssl_cert_file
89
+ ssl_cert_file_hint
90
+ when :ssl_ca_cert_file
91
+ "client.faraday.ssl.ca_file = #{value.inspect}"
92
+ when :ssl_ciphers
93
+ "client.faraday.ssl.ciphers = #{value.inspect}"
94
+ when :ssl_ca_cert_path
95
+ "client.faraday.ssl.ca_path = #{value.inspect}"
96
+ when :adapter
97
+ adapter_hint
98
+ else
99
+ STATIC_HINTS.fetch(option)
100
+ end
101
+ end
102
+
103
+ def redacted_proxy_value
104
+ uri = URI.parse(value.to_s)
105
+ return value.inspect unless uri.absolute? && uri.host
106
+
107
+ redacted = uri.dup
108
+ redacted.userinfo = "..." if redacted.userinfo
109
+ redacted.path = "" if redacted.respond_to?(:path=)
110
+ redacted.query = nil if redacted.respond_to?(:query=)
111
+ redacted.fragment = nil if redacted.respond_to?(:fragment=)
112
+ redacted.to_s.inspect
113
+ rescue URI::InvalidURIError
114
+ '"[redacted proxy URL]"'
115
+ end
116
+
117
+ def ssl_version_hint
118
+ faraday_option = {
119
+ ssl_version: "version",
120
+ ssl_min_version: "min_version",
121
+ ssl_max_version: "max_version"
122
+ }.fetch(option)
123
+
124
+ "client.faraday.ssl.#{faraday_option} = #{value.inspect}"
125
+ end
126
+
127
+ def ssl_cert_key_file_hint
128
+ [
129
+ "key = OpenSSL::PKey.read(File.read(#{value.inspect}))",
130
+ "client.faraday.ssl.client_key = key"
131
+ ]
132
+ end
133
+
134
+ def ssl_cert_file_hint
135
+ [
136
+ "cert = OpenSSL::X509::Certificate.new(File.read(#{value.inspect}))",
137
+ "client.faraday.ssl.client_cert = cert"
138
+ ]
139
+ end
140
+
141
+ def adapter_hint
142
+ case value
143
+ when :net_http
144
+ "client.faraday.adapter :net_http"
145
+ when :httpclient
146
+ [
147
+ "gem 'faraday-httpclient'",
148
+ "require 'faraday/httpclient'",
149
+ "client.faraday.adapter :httpclient"
150
+ ]
151
+ when :excon
152
+ [
153
+ "gem 'faraday-excon'",
154
+ "require 'faraday/excon'",
155
+ "client.faraday.adapter :excon"
156
+ ]
157
+ when :net_http_persistent
158
+ [
159
+ "gem 'faraday-net_http_persistent'",
160
+ "require 'faraday/net_http_persistent'",
161
+ "client.faraday.adapter :net_http_persistent"
162
+ ]
163
+ else
164
+ [
165
+ "choose and install a Faraday adapter matching #{value.inspect}",
166
+ "client.faraday.adapter :net_http"
167
+ ]
168
+ end
169
+ end
170
+
171
+ def ssl_verify_mode_hint
172
+ case value
173
+ when :none
174
+ "client.faraday.ssl.verify_mode = OpenSSL::SSL::VERIFY_NONE"
175
+ when :peer
176
+ "client.faraday.ssl.verify_mode = OpenSSL::SSL::VERIFY_PEER"
177
+ when :fail_if_no_peer_cert
178
+ "client.faraday.ssl.verify_mode = OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT"
179
+ when :client_once
180
+ "client.faraday.ssl.verify_mode = OpenSSL::SSL::VERIFY_CLIENT_ONCE"
181
+ else
182
+ "client.faraday.ssl.verify_mode = OpenSSL::SSL::VERIFY_PEER or OpenSSL::SSL::VERIFY_NONE"
183
+ end
184
+ end
185
+ end
186
+ end
data/lib/savon/header.rb CHANGED
@@ -1,89 +1,136 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "akami"
3
4
  require "gyoku"
4
- require "securerandom"
5
+ require "savon/addressing"
5
6
 
6
7
  module Savon
8
+ # Assembles the SOAP +Header+ element of a request envelope and renders it to
9
+ # XML.
10
+ #
11
+ # A SOAP Header carries a request's out-of-band metadata. This object is
12
+ # responsible for every header block Savon can emit, concatenated in this
13
+ # order:
14
+ #
15
+ # 1. application headers supplied by the caller (the +soap_header+ option),
16
+ # 2. the WS-Addressing headers, rendered by {Savon::Addressing},
17
+ # 3. the WS-Security header with UsernameToken credentials, a +wsu:Timestamp+
18
+ # and an XML Signature (OASIS WSS SOAP Message Security 1.1).
19
+ #
20
+ # WS-Security markup is delegated to Akami and Hash-to-XML conversion to Gyoku.
7
21
  class Header
22
+ # @param globals [Savon::GlobalOptions] client-level options, read for the
23
+ # Gyoku key converter (+:convert_request_keys_to+) and the WS-Addressing
24
+ # toggle (+:use_wsa_headers+)
25
+ # @param effective [Savon::EffectiveOptions] resolves the WSSE, soap_header,
26
+ # soap_action and endpoint values for the request
27
+ def initialize(globals, effective)
28
+ @gyoku_options = { key_converter: globals[:convert_request_keys_to] }
29
+
30
+ @wsse_auth = effective.wsse_auth
31
+ @wsse_timestamp = effective.wsse_timestamp
32
+ @wsse_signature = effective.wsse_signature
33
+ @soap_header = effective.soap_header
34
+
35
+ @addressing = build_addressing(globals, effective)
36
+ @header = build
37
+ end
8
38
 
9
- def initialize(globals, locals)
10
- @gyoku_options = { :key_converter => globals[:convert_request_keys_to] }
11
-
12
- @wsse_auth = locals[:wsse_auth].nil? ? globals[:wsse_auth] : locals[:wsse_auth]
13
- @wsse_timestamp = locals[:wsse_timestamp].nil? ? globals[:wsse_timestamp] : locals[:wsse_timestamp]
14
- @wsse_signature = locals[:wsse_signature].nil? ? globals[:wsse_signature] : locals[:wsse_signature]
15
-
16
- @global_header = globals[:soap_header]
17
- @local_header = locals[:soap_header]
18
-
19
- @globals = globals
20
- @locals = locals
39
+ attr_reader :gyoku_options, :wsse_auth, :wsse_timestamp, :wsse_signature
21
40
 
22
- @header = build
41
+ # Returns the XML attributes of the enclosing Header element. Only the
42
+ # WS-Addressing section declares one, the +xmlns:wsa+ prefix.
43
+ #
44
+ # @return [Hash{String => String}]
45
+ def attributes
46
+ @addressing.namespace_attributes
23
47
  end
24
48
 
25
- attr_reader :local_header, :global_header, :gyoku_options,
26
- :wsse_auth, :wsse_timestamp, :wsse_signature
27
-
49
+ # @return [Boolean] whether the rendered header is empty, i.e. there is no
50
+ # header content to include in the envelope
28
51
  def empty?
29
52
  @header.empty?
30
53
  end
31
54
 
55
+ # Returns the rendered SOAP header XML. The header is built once during
56
+ # construction and cached, so this is a cheap, repeatable read.
57
+ #
58
+ # @return [String] the header XML (may be empty)
32
59
  def to_s
33
60
  @header
34
61
  end
35
62
 
36
63
  private
37
64
 
65
+ # Constructs the WS-Addressing section from the resolved request values.
66
+ # Resolving the endpoint raises without a WSDL document and without a
67
+ # global +:endpoint+, so action and endpoint are only resolved when the
68
+ # +:use_wsa_headers+ global asks for the headers.
69
+ #
70
+ # @return [Savon::Addressing]
71
+ def build_addressing(globals, effective)
72
+ return Addressing.new(enabled: false) unless globals[:use_wsa_headers]
73
+
74
+ Addressing.new(
75
+ enabled: true,
76
+ action: effective.soap_action,
77
+ to: effective.endpoint
78
+ )
79
+ end
80
+
81
+ # Concatenates the three header sections in document order: caller content,
82
+ # WS-Addressing, then WSSE security.
83
+ #
84
+ # @return [String]
38
85
  def build
39
- build_header + build_wsa_header + build_wsse_header
86
+ build_header + @addressing.to_xml + build_wsse_header
40
87
  end
41
88
 
89
+ # Renders the resolved soap_header. {Savon::EffectiveOptions#soap_header}
90
+ # merges the global and local values, so there is nothing to combine here.
91
+ # Hash-to-XML conversion only. Strings pass through.
92
+ #
93
+ # @return [String]
42
94
  def build_header
43
- header =
44
- if global_header.kind_of?(Hash) && local_header.kind_of?(Hash)
45
- global_header.merge(local_header)
46
- elsif local_header
47
- local_header
48
- else
49
- global_header
50
- end
51
-
52
- convert_to_xml(header)
95
+ convert_to_xml(@soap_header)
53
96
  end
54
97
 
98
+ # Builds the WS-Security header via Akami, or an empty string when no WSSE
99
+ # content (credentials, timestamp or signature) applies.
100
+ #
101
+ # @return [String]
55
102
  def build_wsse_header
56
103
  wsse_header = akami
57
104
  wsse_header.respond_to?(:to_xml) ? wsse_header.to_xml : ""
58
105
  end
59
106
 
60
- def build_wsa_header
61
- return '' unless @globals[:use_wsa_headers]
62
- convert_to_xml({
63
- 'wsa:Action' => @locals[:soap_action],
64
- 'wsa:To' => @globals[:endpoint],
65
- 'wsa:MessageID' => "urn:uuid:#{SecureRandom.uuid}"
66
- })
67
- end
68
-
107
+ # Renders a header value to XML. A Hash goes through Gyoku (honouring the
108
+ # configured key converter), anything else is coerced with +#to_s+.
109
+ #
110
+ # @param hash_or_string [Hash, #to_s] the header content
111
+ # @return [String]
69
112
  def convert_to_xml(hash_or_string)
70
- if hash_or_string.kind_of? Hash
113
+ if hash_or_string.is_a? Hash
71
114
  Gyoku.xml(hash_or_string, gyoku_options)
72
115
  else
73
116
  hash_or_string.to_s
74
117
  end
75
118
  end
76
119
 
120
+ # Configures an Akami WSSE object from the resolved WSSE options. Sets the
121
+ # UsernameToken credentials, enables the wsu:Timestamp, and attaches the XML
122
+ # Signature when a document has been assigned to it.
123
+ #
124
+ # @return [Akami::WSSE] configured, not yet serialized
77
125
  def akami
78
126
  wsse = Akami.wsse
79
127
  wsse.credentials(*wsse_auth) if wsse_auth
80
128
  wsse.timestamp = wsse_timestamp if wsse_timestamp
81
- if wsse_signature && wsse_signature.have_document?
129
+ if wsse_signature&.have_document?
82
130
  wsse.signature = wsse_signature
83
131
  end
84
132
 
85
133
  wsse
86
134
  end
87
-
88
135
  end
89
136
  end
@@ -2,26 +2,26 @@
2
2
 
3
3
  module Savon
4
4
  class HTTPError < Error
5
-
6
5
  def self.present?(http)
7
6
  http.error?
8
7
  end
9
8
 
10
- def initialize(http)
11
- @http = http
9
+ def initialize(transport_response)
10
+ @transport_response = transport_response
12
11
  end
13
12
 
14
- attr_reader :http
13
+ def http
14
+ @transport_response
15
+ end
15
16
 
16
17
  def to_s
17
- String.new("HTTP error (#{@http.code})").tap do |str_error|
18
- str_error << ": #{@http.body}" unless @http.body.empty?
18
+ String.new("HTTP error (#{http.code})").tap do |str_error|
19
+ str_error << ": #{http.body}" unless http.body.empty?
19
20
  end
20
21
  end
21
22
 
22
23
  def to_hash
23
- { :code => @http.code, :headers => @http.headers, :body => @http.body }
24
+ { code: http.code, headers: http.headers, body: http.body }
24
25
  end
25
-
26
26
  end
27
27
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "nokogiri"
3
4
 
4
5
  module Savon
5
6
  class LogMessage
6
-
7
7
  def initialize(message, filters = [], pretty_print = false)
8
8
  @message = message
9
9
  @filters = filters
@@ -46,8 +46,7 @@ module Savon
46
46
  end
47
47
 
48
48
  def nokogiri_options
49
- @pretty_print ? { :indent => 2 } : { :save_with => Nokogiri::XML::Node::SaveOptions::AS_XML }
49
+ @pretty_print ? { indent: 2 } : { save_with: Nokogiri::XML::Node::SaveOptions::AS_XML }
50
50
  end
51
-
52
51
  end
53
52
  end
data/lib/savon/message.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "savon/qualified_message"
3
4
  require "gyoku"
4
5
 
5
6
  module Savon
6
7
  class Message
7
-
8
8
  def initialize(message_tag, namespace_identifier, types, used_namespaces, message, element_form_default, key_converter, unwrap)
9
9
  @message_tag = message_tag
10
10
  @namespace_identifier = namespace_identifier
@@ -18,21 +18,20 @@ module Savon
18
18
  end
19
19
 
20
20
  def to_s
21
- return @message.to_s unless @message.kind_of? Hash
21
+ return @message.to_s unless @message.is_a? Hash
22
22
 
23
23
  if @element_form_default == :qualified
24
24
  @message = QualifiedMessage.new(@types, @used_namespaces, @key_converter).to_hash(@message, [@message_tag.to_s])
25
25
  end
26
26
 
27
27
  gyoku_options = {
28
- :element_form_default => @element_form_default,
29
- :namespace => @namespace_identifier,
30
- :key_converter => @key_converter,
31
- :unwrap => @unwrap
28
+ element_form_default: @element_form_default,
29
+ namespace: @namespace_identifier,
30
+ key_converter: @key_converter,
31
+ unwrap: @unwrap
32
32
  }
33
33
 
34
34
  Gyoku.xml(@message, gyoku_options)
35
35
  end
36
-
37
36
  end
38
37
  end