savon 2.16.0 → 2.17.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.
data/lib/savon/options.rb CHANGED
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "logger"
3
4
  require "httpi"
4
5
 
5
6
  module Savon
7
+ # Base class for GlobalOptions and LocalOptions.
8
+ # Stores options in a hash, dispatches setter calls by method name,
9
+ # raises UnknownOptionError for anything not defined on the subclass.
6
10
  class Options
7
-
8
11
  def initialize(options = {})
9
12
  @options = {}
10
13
  assign options
@@ -18,7 +21,7 @@ module Savon
18
21
 
19
22
  def []=(option, value)
20
23
  value = [value].flatten
21
- self.send(option, *value)
24
+ send(option, *value)
22
25
  end
23
26
 
24
27
  def include?(option)
@@ -29,7 +32,7 @@ module Savon
29
32
 
30
33
  def assign(options)
31
34
  options.each do |option, value|
32
- self.send(option, value)
35
+ send(option, value)
33
36
  end
34
37
  end
35
38
 
@@ -38,6 +41,8 @@ module Savon
38
41
  end
39
42
  end
40
43
 
44
+ # Options available in both GlobalOptions and LocalOptions.
45
+ # Currently covers WSSE authentication and timestamp headers.
41
46
  module SharedOptions
42
47
  # WSSE auth credentials for Akami.
43
48
  # Local will override the global wsse_auth value, e.g.
@@ -46,11 +51,12 @@ module Savon
46
51
  # global == [user, pass] && local == nil => [user, pass]
47
52
  def wsse_auth(*credentials)
48
53
  credentials.flatten!
49
- if credentials.size == 1
50
- @options[:wsse_auth] = credentials.first
51
- else
52
- @options[:wsse_auth] = credentials
53
- end
54
+ @options[:wsse_auth] =
55
+ if credentials.size == 1
56
+ credentials.first
57
+ else
58
+ credentials
59
+ end
54
60
  end
55
61
 
56
62
  # Instruct Akami to enable wsu:Timestamp headers.
@@ -67,33 +73,229 @@ module Savon
67
73
  end
68
74
  end
69
75
 
76
+ # HTTPI-specific transport options included in GlobalOptions.
77
+ #
78
+ # Every option in this module is handled by HTTPI and has no effect when
79
+ # transport: :faraday is set. Faraday callers configure these concerns
80
+ # directly on the Faraday::Connection returned by client.faraday.
81
+ module HTTPITransportOptions
82
+ # Maps each httpi-only option to the Faraday equivalent so the
83
+ # error raised at init tells the caller exactly what to do instead.
84
+ # Options with a :default entry are only flagged when the caller
85
+ # sets a value that differs from the GlobalOptions default.
86
+ FARADAY_INCOMPATIBLE_GLOBALS = {
87
+ proxy: { hint: "client.faraday.proxy = url" },
88
+ open_timeout: { hint: "client.faraday.options.timeout = N" },
89
+ read_timeout: { hint: "client.faraday.options.timeout = N" },
90
+ write_timeout: { hint: "client.faraday.options.write_timeout = N" },
91
+ ssl_version: { hint: "client.faraday.ssl.version = version" },
92
+ ssl_min_version: { hint: "client.faraday.ssl.min_version = version" },
93
+ ssl_max_version: { hint: "client.faraday.ssl.max_version = version" },
94
+ ssl_verify_mode: { hint: "client.faraday.ssl.verify = true/false" },
95
+ ssl_cert_key_file: { hint: "client.faraday.ssl.client_key_file = path" },
96
+ ssl_cert_key: { hint: "client.faraday.ssl.client_key = key" },
97
+ ssl_cert_key_password: { hint: "configure ssl context on client.faraday.ssl" },
98
+ ssl_cert_file: { hint: "client.faraday.ssl.client_cert_file = path" },
99
+ ssl_cert: { hint: "client.faraday.ssl.client_cert = cert" },
100
+ ssl_ca_cert_file: { hint: "client.faraday.ssl.ca_file = path" },
101
+ ssl_ca_cert: { hint: "client.faraday.ssl.ca_cert = cert" },
102
+ ssl_ciphers: { hint: "client.faraday.ssl.ciphers = ciphers" },
103
+ ssl_ca_cert_path: { hint: "client.faraday.ssl.ca_path = path" },
104
+ ssl_cert_store: { hint: "client.faraday.ssl.cert_store = store" },
105
+ basic_auth: { hint: "client.faraday.request :basic_auth, user, pass" },
106
+ digest_auth: { hint: "client.faraday.request :authorization, :Digest, credentials" },
107
+ ntlm: { hint: "client.faraday.request :ntlm, user, pass" },
108
+ follow_redirects: { hint: "client.faraday.use :follow_redirects", default: false },
109
+ adapter: { hint: "client.faraday.adapter :net_http", default: nil }
110
+ }.freeze
111
+
112
+ # Validates that the chosen transport is compatible with the options set.
113
+ # Must be called after all options (including any block-form options) are set.
114
+ # Collects every conflict and raises a single InitializationError listing all
115
+ # problems and solutions at once.
116
+ def validate_transport!
117
+ return unless self[:transport] == :faraday
118
+
119
+ unless faraday_loaded?
120
+ raise InitializationError,
121
+ "transport: :faraday requires the faraday gem.\n" \
122
+ "Add to your Gemfile: gem 'faraday'"
123
+ end
124
+
125
+ violations = FARADAY_INCOMPATIBLE_GLOBALS.filter_map { |option, config|
126
+ next unless include?(option)
127
+ next if config.key?(:default) && self[option] == config[:default]
128
+
129
+ " #{option} - Use: #{config[:hint]}"
130
+ }
131
+
132
+ return if violations.empty?
133
+
134
+ raise InitializationError,
135
+ "The following options are not supported with transport: :faraday:\n#{violations.join("\n")}"
136
+ end
137
+
138
+ # Proxy server to use for all requests.
139
+ def proxy(proxy)
140
+ @options[:proxy] = proxy unless proxy.nil?
141
+ end
142
+
143
+ # Open timeout in seconds.
144
+ def open_timeout(open_timeout)
145
+ @options[:open_timeout] = open_timeout
146
+ end
147
+
148
+ # Read timeout in seconds.
149
+ def read_timeout(read_timeout)
150
+ @options[:read_timeout] = read_timeout
151
+ end
152
+
153
+ # Write timeout in seconds.
154
+ def write_timeout(write_timeout)
155
+ @options[:write_timeout] = write_timeout
156
+ end
157
+
158
+ # Specifies the SSL version to use.
159
+ def ssl_version(version)
160
+ @options[:ssl_version] = version
161
+ end
162
+
163
+ # Specifies the minimum SSL version to use.
164
+ def ssl_min_version(version)
165
+ @options[:ssl_min_version] = version
166
+ end
167
+
168
+ # Specifies the maximum SSL version to use.
169
+ def ssl_max_version(version)
170
+ @options[:ssl_max_version] = version
171
+ end
172
+
173
+ # Whether and how to verify the SSL connection.
174
+ def ssl_verify_mode(verify_mode)
175
+ @options[:ssl_verify_mode] = verify_mode
176
+ end
177
+
178
+ # Sets the cert key file to use.
179
+ def ssl_cert_key_file(file)
180
+ @options[:ssl_cert_key_file] = file
181
+ end
182
+
183
+ # Sets the cert key to use.
184
+ def ssl_cert_key(key)
185
+ @options[:ssl_cert_key] = key
186
+ end
187
+
188
+ # Sets the cert key password to use.
189
+ def ssl_cert_key_password(password)
190
+ @options[:ssl_cert_key_password] = password
191
+ end
192
+
193
+ # Sets the cert file to use.
194
+ def ssl_cert_file(file)
195
+ @options[:ssl_cert_file] = file
196
+ end
197
+
198
+ # Sets the cert to use.
199
+ def ssl_cert(cert)
200
+ @options[:ssl_cert] = cert
201
+ end
202
+
203
+ # Sets the CA cert file to use.
204
+ def ssl_ca_cert_file(file)
205
+ @options[:ssl_ca_cert_file] = file
206
+ end
207
+
208
+ # Sets the CA cert to use.
209
+ def ssl_ca_cert(cert)
210
+ @options[:ssl_ca_cert] = cert
211
+ end
212
+
213
+ # Sets the SSL ciphers to use.
214
+ def ssl_ciphers(ciphers)
215
+ @options[:ssl_ciphers] = ciphers
216
+ end
217
+
218
+ # Sets the CA cert path.
219
+ def ssl_ca_cert_path(path)
220
+ @options[:ssl_ca_cert_path] = path
221
+ end
222
+
223
+ # Sets the SSL cert store.
224
+ def ssl_cert_store(store)
225
+ @options[:ssl_cert_store] = store
226
+ end
227
+
228
+ # HTTP basic auth credentials.
229
+ def basic_auth(*credentials)
230
+ @options[:basic_auth] = credentials.flatten
231
+ end
232
+
233
+ # HTTP digest auth credentials.
234
+ def digest_auth(*credentials)
235
+ @options[:digest_auth] = credentials.flatten
236
+ end
237
+
238
+ # NTLM auth credentials.
239
+ def ntlm(*credentials)
240
+ @options[:ntlm] = credentials.flatten
241
+ end
242
+
243
+ # Instruct requests to follow HTTP redirects.
244
+ def follow_redirects(follow_redirects)
245
+ @options[:follow_redirects] = follow_redirects
246
+ end
247
+
248
+ # Instruct Savon which HTTPI adapter to use instead of the default.
249
+ def adapter(adapter)
250
+ @options[:adapter] = adapter
251
+ end
252
+
253
+ private
254
+
255
+ # Attempts to load faraday. Returns true if available, false on LoadError.
256
+ def faraday_loaded?
257
+ require "faraday"
258
+ true
259
+ rescue LoadError
260
+ false
261
+ end
262
+ end
263
+
264
+ # Client-level options applied to every request made by a Savon::Client instance.
265
+ # Covers service location, SOAP configuration, logging, response parsing,
266
+ # and transport selection. HTTPI-specific options (proxy, timeouts, SSL, auth)
267
+ # come from HTTPITransportOptions.
70
268
  class GlobalOptions < Options
71
269
  include SharedOptions
270
+ include HTTPITransportOptions
72
271
 
73
272
  def initialize(options = {})
74
273
  @option_type = :global
75
274
 
76
275
  defaults = {
77
- :encoding => "UTF-8",
78
- :soap_version => 1,
79
- :namespaces => {},
80
- :logger => Logger.new($stdout),
81
- :log => false,
82
- :log_headers => true,
83
- :filters => [],
84
- :pretty_print_xml => false,
85
- :raise_errors => true,
86
- :strip_namespaces => true,
87
- :delete_namespace_attributes => false,
88
- :convert_response_tags_to => lambda { |tag| StringUtils.snakecase(tag).to_sym},
89
- :convert_attributes_to => lambda { |k,v| [k,v] },
90
- :multipart => false,
91
- :adapter => nil,
92
- :use_wsa_headers => false,
93
- :no_message_tag => false,
94
- :follow_redirects => false,
95
- :unwrap => false,
96
- :host => nil
276
+ encoding: "UTF-8",
277
+ soap_version: 1,
278
+ namespaces: {},
279
+ logger: Logger.new($stdout),
280
+ log: false,
281
+ log_headers: true,
282
+ filters: [],
283
+ pretty_print_xml: false,
284
+ raise_errors: true,
285
+ strip_namespaces: true,
286
+ delete_namespace_attributes: false,
287
+ convert_response_tags_to: ->(tag) { StringUtils.snakecase(tag).to_sym },
288
+ convert_attributes_to: ->(k, v) { [k, v] },
289
+ multipart: false,
290
+ use_wsa_headers: false,
291
+ no_message_tag: false,
292
+ unwrap: false,
293
+ host: nil,
294
+ transport: :httpi,
295
+
296
+ # httpi transport defaults
297
+ adapter: nil,
298
+ follow_redirects: false
97
299
  }
98
300
 
99
301
  options = defaults.merge(options)
@@ -112,7 +314,7 @@ module Savon
112
314
  @options[:wsdl] = wsdl_address
113
315
  end
114
316
 
115
- # set different host for actions in WSDL
317
+ # Set a different host for actions in the WSDL.
116
318
  def host(host)
117
319
  @options[:host] = host
118
320
  end
@@ -127,7 +329,7 @@ module Savon
127
329
  @options[:namespace] = namespace
128
330
  end
129
331
 
130
- # The namespace identifer.
332
+ # The namespace identifier.
131
333
  def namespace_identifier(identifier)
132
334
  @options[:namespace_identifier] = identifier
133
335
  end
@@ -137,31 +339,11 @@ module Savon
137
339
  @options[:namespaces] = namespaces
138
340
  end
139
341
 
140
- # Proxy server to use for all requests.
141
- def proxy(proxy)
142
- @options[:proxy] = proxy unless proxy.nil?
143
- end
144
-
145
- # A Hash of HTTP headers.
342
+ # A Hash of HTTP headers sent with every request.
146
343
  def headers(headers)
147
344
  @options[:headers] = headers
148
345
  end
149
346
 
150
- # Open timeout in seconds.
151
- def open_timeout(open_timeout)
152
- @options[:open_timeout] = open_timeout
153
- end
154
-
155
- # Read timeout in seconds.
156
- def read_timeout(read_timeout)
157
- @options[:read_timeout] = read_timeout
158
- end
159
-
160
- # Write timeout in seconds.
161
- def write_timeout(write_timeout)
162
- @options[:write_timeout] = write_timeout
163
- end
164
-
165
347
  # The encoding to use. Defaults to "UTF-8".
166
348
  def encoding(encoding)
167
349
  @options[:encoding] = encoding
@@ -210,7 +392,7 @@ module Savon
210
392
 
211
393
  # Changes the Logger's log level.
212
394
  def log_level(level)
213
- levels = { :debug => 0, :info => 1, :warn => 2, :error => 3, :fatal => 4 }
395
+ levels = { debug: 0, info: 1, warn: 2, error: 3, fatal: 4 }
214
396
 
215
397
  unless levels.include? level
216
398
  raise ArgumentError, "Invalid log level: #{level.inspect}\n" \
@@ -220,7 +402,7 @@ module Savon
220
402
  @options[:logger].level = levels[level]
221
403
  end
222
404
 
223
- # To log headers or not.
405
+ # Whether to log headers.
224
406
  def log_headers(log_headers)
225
407
  @options[:log_headers] = log_headers
226
408
  end
@@ -235,90 +417,6 @@ module Savon
235
417
  @options[:pretty_print_xml] = pretty_print_xml
236
418
  end
237
419
 
238
- # Specifies the SSL version to use.
239
- def ssl_version(version)
240
- @options[:ssl_version] = version
241
- end
242
-
243
- # Specifies the SSL version to use.
244
- def ssl_min_version(version)
245
- @options[:ssl_min_version] = version
246
- end
247
-
248
- # Specifies the SSL version to use.
249
- def ssl_max_version(version)
250
- @options[:ssl_max_version] = version
251
- end
252
-
253
- # Whether and how to to verify the connection.
254
- def ssl_verify_mode(verify_mode)
255
- @options[:ssl_verify_mode] = verify_mode
256
- end
257
-
258
- # Sets the cert key file to use.
259
- def ssl_cert_key_file(file)
260
- @options[:ssl_cert_key_file] = file
261
- end
262
-
263
- # Sets the cert key to use.
264
- def ssl_cert_key(key)
265
- @options[:ssl_cert_key] = key
266
- end
267
-
268
- # Sets the cert key password to use.
269
- def ssl_cert_key_password(password)
270
- @options[:ssl_cert_key_password] = password
271
- end
272
-
273
- # Sets the cert file to use.
274
- def ssl_cert_file(file)
275
- @options[:ssl_cert_file] = file
276
- end
277
-
278
- # Sets the cert to use.
279
- def ssl_cert(cert)
280
- @options[:ssl_cert] = cert
281
- end
282
-
283
- # Sets the ca cert file to use.
284
- def ssl_ca_cert_file(file)
285
- @options[:ssl_ca_cert_file] = file
286
- end
287
-
288
- # Sets the ca cert to use.
289
- def ssl_ca_cert(cert)
290
- @options[:ssl_ca_cert] = cert
291
- end
292
-
293
- def ssl_ciphers(ciphers)
294
- @options[:ssl_ciphers] = ciphers
295
- end
296
-
297
- # Sets the ca cert path.
298
- def ssl_ca_cert_path(path)
299
- @options[:ssl_ca_cert_path] = path
300
- end
301
-
302
- # Sets the ssl cert store.
303
- def ssl_cert_store(store)
304
- @options[:ssl_cert_store] = store
305
- end
306
-
307
- # HTTP basic auth credentials.
308
- def basic_auth(*credentials)
309
- @options[:basic_auth] = credentials.flatten
310
- end
311
-
312
- # HTTP digest auth credentials.
313
- def digest_auth(*credentials)
314
- @options[:digest_auth] = credentials.flatten
315
- end
316
-
317
- # NTLM auth credentials.
318
- def ntlm(*credentials)
319
- @options[:ntlm] = credentials.flatten
320
- end
321
-
322
420
  # Instruct Nori whether to strip namespaces from XML nodes.
323
421
  def strip_namespaces(strip_namespaces)
324
422
  @options[:strip_namespaces] = strip_namespaces
@@ -335,22 +433,22 @@ module Savon
335
433
  @options[:convert_request_keys_to] = converter
336
434
  end
337
435
 
338
- # Tell Gyoku to unwrap Array of Hashes
339
- # Accepts a boolean, default to false
436
+ # Tell Gyoku to unwrap Array of Hashes.
437
+ # Accepts a boolean, defaults to false.
340
438
  def unwrap(unwrap)
341
439
  @options[:unwrap] = unwrap
342
440
  end
343
441
 
344
442
  # Tell Nori how to convert XML tags from the SOAP response into Hash keys.
345
443
  # Accepts a lambda or a block which receives an XML tag and returns a Hash key.
346
- # Defaults to convert tags to snakecase Symbols.
444
+ # Defaults to converting tags to snakecase Symbols.
347
445
  def convert_response_tags_to(converter = nil, &block)
348
446
  @options[:convert_response_tags_to] = block || converter
349
447
  end
350
448
 
351
449
  # Tell Nori how to convert XML attributes on tags from the SOAP response into Hash keys.
352
450
  # Accepts a lambda or a block which receives an XML tag and returns a Hash key.
353
- # Defaults to doing nothing
451
+ # Defaults to doing nothing.
354
452
  def convert_attributes_to(converter = nil, &block)
355
453
  @options[:convert_attributes_to] = block || converter
356
454
  end
@@ -360,26 +458,27 @@ module Savon
360
458
  @options[:multipart] = multipart
361
459
  end
362
460
 
363
- # Instruct Savon what HTTPI adapter it should use instead of default
364
- def adapter(adapter)
365
- @options[:adapter] = adapter
366
- end
367
-
368
461
  # Enable inclusion of WS-Addressing headers.
369
462
  def use_wsa_headers(use)
370
463
  @options[:use_wsa_headers] = use
371
464
  end
372
465
 
466
+ # Suppress the message tag wrapper around the SOAP body.
373
467
  def no_message_tag(bool)
374
468
  @options[:no_message_tag] = bool
375
469
  end
376
470
 
377
- # Instruct requests to follow HTTP redirects.
378
- def follow_redirects(follow_redirects)
379
- @options[:follow_redirects] = follow_redirects
471
+ # HTTP transport to use. Accepts :httpi (default) or :faraday.
472
+ # When set to :faraday, configure transport concerns directly on the
473
+ # Faraday::Connection returned by client.faraday instead of using
474
+ # the HTTPITransportOptions.
475
+ def transport(transport)
476
+ @options[:transport] = transport
380
477
  end
381
478
  end
382
479
 
480
+ # Per-request options passed to client.call.
481
+ # Overrides or extends the matching GlobalOptions for a single SOAP operation.
383
482
  class LocalOptions < Options
384
483
  include SharedOptions
385
484
 
@@ -387,9 +486,9 @@ module Savon
387
486
  @option_type = :local
388
487
 
389
488
  defaults = {
390
- :advanced_typecasting => true,
391
- :response_parser => :nokogiri,
392
- :multipart => false
489
+ advanced_typecasting: true,
490
+ response_parser: :nokogiri,
491
+ multipart: false
393
492
  }
394
493
 
395
494
  super defaults.merge(options)
@@ -397,7 +496,7 @@ module Savon
397
496
 
398
497
  # The local SOAP header. Expected to be a Hash or respond to #to_s.
399
498
  # Will be merged with the global SOAP header if both are Hashes.
400
- # Otherwise the local option will be prefered.
499
+ # Otherwise the local option will be preferred.
401
500
  def soap_header(header)
402
501
  @options[:soap_header] = header
403
502
  end
@@ -482,6 +581,7 @@ module Savon
482
581
  @options[:multipart] = multipart
483
582
  end
484
583
 
584
+ # Per-request HTTP headers. Merged with global headers for each request.
485
585
  def headers(headers)
486
586
  @options[:headers] = headers
487
587
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "gyoku"
3
4
 
4
5
  module Savon
@@ -37,7 +38,7 @@ module Savon
37
38
  private
38
39
 
39
40
  def translate_tag(key)
40
- Gyoku.xml_tag(key, :key_converter => @key_converter).to_s
41
+ Gyoku.xml_tag(key, key_converter: @key_converter).to_s
41
42
  end
42
43
 
43
44
  def add_namespaces_to_values(values, path)
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "nori"
3
4
  require "savon/soap_fault"
4
5
  require "savon/http_error"
@@ -6,14 +7,14 @@ require "savon/http_error"
6
7
  module Savon
7
8
  class Response
8
9
  CRLF = /\r\n/
9
- WSP = /[#{%Q|\x9\x20|}]/
10
+ WSP = /[\t ]/
10
11
 
11
12
  def initialize(http, globals, locals)
12
- @http = http
13
- @globals = globals
14
- @locals = locals
15
- @attachments = []
16
- @xml = ''
13
+ @http = http
14
+ @globals = globals
15
+ @locals = locals
16
+ @attachments = []
17
+ @xml = ''
17
18
  @has_parsed_body = false
18
19
 
19
20
  build_soap_and_http_errors!
@@ -25,7 +26,7 @@ module Savon
25
26
  def success?
26
27
  !soap_fault? && !http_error?
27
28
  end
28
- alias_method :successful?, :success?
29
+ alias successful? success?
29
30
 
30
31
  def soap_fault?
31
32
  SOAPFault.present?(@http, xml)
@@ -43,15 +44,16 @@ module Savon
43
44
  find('Body')
44
45
  end
45
46
 
46
- alias_method :to_hash, :body
47
+ alias to_hash body
47
48
 
48
49
  def to_array(*path)
49
- result = path.inject body do |memo, key|
50
+ result = path.inject(body) { |memo, key|
50
51
  return [] if memo[key].nil?
52
+
51
53
  memo[key]
52
- end
54
+ }
53
55
 
54
- result.kind_of?(Array) ? result.compact : [result].compact
56
+ result.is_a?(Array) ? result.compact : [result].compact
55
57
  end
56
58
 
57
59
  def hash
@@ -72,8 +74,8 @@ module Savon
72
74
  end
73
75
  end
74
76
 
75
- alias_method :to_xml, :xml
76
- alias_method :to_s, :xml
77
+ alias to_xml xml
78
+ alias to_s xml
77
79
 
78
80
  def doc
79
81
  @doc ||= Nokogiri.XML(xml)
@@ -107,19 +109,20 @@ module Savon
107
109
 
108
110
  def boundary
109
111
  return unless multipart?
112
+
110
113
  Mail::Field.new('content-type', http.headers['content-type']).parameters['boundary']
111
114
  end
112
115
 
113
116
  def parse_body
114
117
  http.body.force_encoding Encoding::ASCII_8BIT
115
118
  parts = http.body.split(/(?:\A|\r\n)--#{Regexp.escape(boundary)}(?=(?:--)?\s*$)/)
116
- parts[1..-1].to_a.each_with_index do |part, index|
119
+ parts[1..].to_a.each_with_index do |part, index|
117
120
  header_part, body_part = part.lstrip.split(/#{CRLF}#{CRLF}|#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m, 2)
118
121
  section = Mail::Part.new(
119
122
  body: body_part
120
123
  )
121
124
  section.header = header_part
122
- if index == 0
125
+ if index.zero?
123
126
  @xml = section.body.to_s
124
127
  else
125
128
  @attachments << section
@@ -139,7 +142,7 @@ module Savon
139
142
  end
140
143
 
141
144
  def raise_invalid_response_error!
142
- raise InvalidResponseError, "Unable to parse response body:\n" + xml.inspect
145
+ raise InvalidResponseError, "Unable to parse response body:\n#{xml.inspect}"
143
146
  end
144
147
 
145
148
  def xml_namespaces
@@ -150,17 +153,16 @@ module Savon
150
153
  return @nori if @nori
151
154
 
152
155
  nori_options = {
153
- :delete_namespace_attributes => @globals[:delete_namespace_attributes],
154
- :strip_namespaces => @globals[:strip_namespaces],
155
- :convert_tags_to => @globals[:convert_response_tags_to],
156
- :convert_attributes_to => @globals[:convert_attributes_to],
157
- :advanced_typecasting => @locals[:advanced_typecasting],
158
- :parser => @locals[:response_parser]
156
+ delete_namespace_attributes: @globals[:delete_namespace_attributes],
157
+ strip_namespaces: @globals[:strip_namespaces],
158
+ convert_tags_to: @globals[:convert_response_tags_to],
159
+ convert_attributes_to: @globals[:convert_attributes_to],
160
+ advanced_typecasting: @locals[:advanced_typecasting],
161
+ parser: @locals[:response_parser]
159
162
  }
160
163
 
161
164
  non_nil_nori_options = nori_options.reject { |_, value| value.nil? }
162
165
  @nori = Nori.new(non_nil_nori_options)
163
166
  end
164
-
165
167
  end
166
168
  end