savon 2.11.1 → 2.15.1

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 (92) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +132 -73
  3. data/README.md +35 -20
  4. data/lib/savon/block_interface.rb +1 -0
  5. data/lib/savon/builder.rb +126 -30
  6. data/lib/savon/client.rb +2 -2
  7. data/lib/savon/header.rb +2 -6
  8. data/lib/savon/http_error.rb +4 -4
  9. data/lib/savon/log_message.rb +2 -1
  10. data/lib/savon/message.rb +1 -0
  11. data/lib/savon/mock/expectation.rb +1 -0
  12. data/lib/savon/mock/spec_helper.rb +1 -0
  13. data/lib/savon/mock.rb +1 -0
  14. data/lib/savon/model.rb +4 -3
  15. data/lib/savon/operation.rb +22 -19
  16. data/lib/savon/options.rb +98 -19
  17. data/lib/savon/qualified_message.rb +29 -27
  18. data/lib/savon/request.rb +22 -6
  19. data/lib/savon/request_logger.rb +8 -2
  20. data/lib/savon/response.rb +58 -10
  21. data/lib/savon/soap_fault.rb +3 -4
  22. data/lib/savon/string_utils.rb +17 -0
  23. data/lib/savon/version.rb +2 -1
  24. data/lib/savon.rb +2 -0
  25. metadata +80 -100
  26. data/.gitignore +0 -14
  27. data/.travis.yml +0 -15
  28. data/.yardopts +0 -6
  29. data/CONTRIBUTING.md +0 -46
  30. data/Gemfile +0 -18
  31. data/donate.png +0 -0
  32. data/lib/savon/core_ext/string.rb +0 -29
  33. data/savon.gemspec +0 -46
  34. data/spec/fixtures/gzip/message.gz +0 -0
  35. data/spec/fixtures/response/another_soap_fault.xml +0 -14
  36. data/spec/fixtures/response/authentication.xml +0 -14
  37. data/spec/fixtures/response/f5.xml +0 -39
  38. data/spec/fixtures/response/header.xml +0 -13
  39. data/spec/fixtures/response/list.xml +0 -18
  40. data/spec/fixtures/response/multi_ref.xml +0 -39
  41. data/spec/fixtures/response/soap_fault.xml +0 -8
  42. data/spec/fixtures/response/soap_fault12.xml +0 -18
  43. data/spec/fixtures/response/soap_fault_funky.xml +0 -8
  44. data/spec/fixtures/response/taxcloud.xml +0 -1
  45. data/spec/fixtures/ssl/client_cert.pem +0 -16
  46. data/spec/fixtures/ssl/client_encrypted_key.pem +0 -30
  47. data/spec/fixtures/ssl/client_encrypted_key_cert.pem +0 -24
  48. data/spec/fixtures/ssl/client_key.pem +0 -15
  49. data/spec/fixtures/wsdl/authentication.xml +0 -63
  50. data/spec/fixtures/wsdl/betfair.xml +0 -2981
  51. data/spec/fixtures/wsdl/edialog.xml +0 -15416
  52. data/spec/fixtures/wsdl/interhome.xml +0 -2137
  53. data/spec/fixtures/wsdl/lower_camel.xml +0 -52
  54. data/spec/fixtures/wsdl/multiple_namespaces.xml +0 -92
  55. data/spec/fixtures/wsdl/multiple_types.xml +0 -60
  56. data/spec/fixtures/wsdl/no_message_tag.xml +0 -1267
  57. data/spec/fixtures/wsdl/taxcloud.xml +0 -934
  58. data/spec/fixtures/wsdl/team_software.xml +0 -1
  59. data/spec/fixtures/wsdl/vies.xml +0 -176
  60. data/spec/fixtures/wsdl/wasmuth.xml +0 -153
  61. data/spec/integration/centra_spec.rb +0 -66
  62. data/spec/integration/email_example_spec.rb +0 -32
  63. data/spec/integration/random_quote_spec.rb +0 -23
  64. data/spec/integration/ratp_example_spec.rb +0 -28
  65. data/spec/integration/stockquote_example_spec.rb +0 -28
  66. data/spec/integration/support/application.rb +0 -82
  67. data/spec/integration/support/server.rb +0 -84
  68. data/spec/integration/temperature_example_spec.rb +0 -46
  69. data/spec/integration/zipcode_example_spec.rb +0 -42
  70. data/spec/savon/builder_spec.rb +0 -137
  71. data/spec/savon/client_spec.rb +0 -271
  72. data/spec/savon/core_ext/string_spec.rb +0 -37
  73. data/spec/savon/features/message_tag_spec.rb +0 -61
  74. data/spec/savon/http_error_spec.rb +0 -49
  75. data/spec/savon/log_message_spec.rb +0 -44
  76. data/spec/savon/message_spec.rb +0 -70
  77. data/spec/savon/mock_spec.rb +0 -174
  78. data/spec/savon/model_spec.rb +0 -182
  79. data/spec/savon/observers_spec.rb +0 -92
  80. data/spec/savon/operation_spec.rb +0 -230
  81. data/spec/savon/options_spec.rb +0 -1064
  82. data/spec/savon/qualified_message_spec.rb +0 -20
  83. data/spec/savon/request_logger_spec.rb +0 -37
  84. data/spec/savon/request_spec.rb +0 -496
  85. data/spec/savon/response_spec.rb +0 -270
  86. data/spec/savon/soap_fault_spec.rb +0 -131
  87. data/spec/spec_helper.rb +0 -30
  88. data/spec/support/adapters.rb +0 -48
  89. data/spec/support/endpoint.rb +0 -25
  90. data/spec/support/fixture.rb +0 -39
  91. data/spec/support/integration.rb +0 -9
  92. data/spec/support/stdout.rb +0 -25
data/lib/savon/options.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "logger"
2
3
  require "httpi"
3
4
 
@@ -73,24 +74,26 @@ module Savon
73
74
  @option_type = :global
74
75
 
75
76
  defaults = {
76
- :encoding => "UTF-8",
77
- :soap_version => 1,
78
- :namespaces => {},
79
- :logger => Logger.new($stdout),
80
- :log => false,
81
- :filters => [],
82
- :pretty_print_xml => false,
83
- :raise_errors => true,
84
- :strip_namespaces => true,
85
- :convert_response_tags_to => lambda { |tag| tag.snakecase.to_sym},
86
- :convert_attributes_to => lambda { |k,v| [k,v] },
87
- :multipart => false,
88
- :adapter => nil,
89
- :use_wsa_headers => false,
90
- :no_message_tag => false,
91
- :follow_redirects => false,
92
- :unwrap => false,
93
- :host => nil
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
94
97
  }
95
98
 
96
99
  options = defaults.merge(options)
@@ -136,7 +139,7 @@ module Savon
136
139
 
137
140
  # Proxy server to use for all requests.
138
141
  def proxy(proxy)
139
- @options[:proxy] = proxy
142
+ @options[:proxy] = proxy unless proxy.nil?
140
143
  end
141
144
 
142
145
  # A Hash of HTTP headers.
@@ -154,6 +157,11 @@ module Savon
154
157
  @options[:read_timeout] = read_timeout
155
158
  end
156
159
 
160
+ # Write timeout in seconds.
161
+ def write_timeout(write_timeout)
162
+ @options[:write_timeout] = write_timeout
163
+ end
164
+
157
165
  # The encoding to use. Defaults to "UTF-8".
158
166
  def encoding(encoding)
159
167
  @options[:encoding] = encoding
@@ -212,6 +220,11 @@ module Savon
212
220
  @options[:logger].level = levels[level]
213
221
  end
214
222
 
223
+ # To log headers or not.
224
+ def log_headers(log_headers)
225
+ @options[:log_headers] = log_headers
226
+ end
227
+
215
228
  # A list of XML tags to filter from logged SOAP messages.
216
229
  def filters(*filters)
217
230
  @options[:filters] = filters.flatten
@@ -227,6 +240,16 @@ module Savon
227
240
  @options[:ssl_version] = version
228
241
  end
229
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
+
230
253
  # Whether and how to to verify the connection.
231
254
  def ssl_verify_mode(verify_mode)
232
255
  @options[:ssl_verify_mode] = verify_mode
@@ -267,6 +290,19 @@ module Savon
267
290
  @options[:ssl_ca_cert] = cert
268
291
  end
269
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
270
306
 
271
307
  # HTTP basic auth credentials.
272
308
  def basic_auth(*credentials)
@@ -288,6 +324,11 @@ module Savon
288
324
  @options[:strip_namespaces] = strip_namespaces
289
325
  end
290
326
 
327
+ # Instruct Nori whether to delete namespace attributes from XML nodes.
328
+ def delete_namespace_attributes(delete_namespace_attributes)
329
+ @options[:delete_namespace_attributes] = delete_namespace_attributes
330
+ end
331
+
291
332
  # Tell Gyoku how to convert Hash key Symbols to XML tags.
292
333
  # Accepts one of :lower_camelcase, :camelcase, :upcase, or :none.
293
334
  def convert_request_keys_to(converter)
@@ -377,6 +418,40 @@ module Savon
377
418
  @options[:attributes] = attributes
378
419
  end
379
420
 
421
+ # Attachments for the SOAP message (https://www.w3.org/TR/SOAP-attachments)
422
+ #
423
+ # should pass an Array or a Hash; items should be path strings or
424
+ # { filename: 'file.name', content: 'content' } objects
425
+ # The Content-ID in multipart message sections will be the filename or the key if Hash is given
426
+ #
427
+ # usage examples:
428
+ #
429
+ # response = client.call :operation1 do
430
+ # message param1: 'value'
431
+ # attachments [
432
+ # { filename: 'x1.xml', content: '<xml>abc</xml>'},
433
+ # { filename: 'x2.xml', content: '<xml>abc</xml>'}
434
+ # ]
435
+ # end
436
+ # # Content-ID will be x1.xml and x2.xml
437
+ #
438
+ # response = client.call :operation1 do
439
+ # message param1: 'value'
440
+ # attachments 'x1.xml' => '/tmp/1281ab7d7d.xml', 'x2.xml' => '/tmp/4c5v8e833a.xml'
441
+ # end
442
+ # # Content-ID will be x1.xml and x2.xml
443
+ #
444
+ # response = client.call :operation1 do
445
+ # message param1: 'value'
446
+ # attachments [ '/tmp/1281ab7d7d.xml', '/tmp/4c5v8e833a.xml']
447
+ # end
448
+ # # Content-ID will be 1281ab7d7d.xml and 4c5v8e833a.xml
449
+ #
450
+ # The Content-ID is important if you want to refer to the attachments from the SOAP request
451
+ def attachments(attachments)
452
+ @options[:attachments] = attachments
453
+ end
454
+
380
455
  # Value of the SOAPAction HTTP header.
381
456
  def soap_action(soap_action)
382
457
  @options[:soap_action] = soap_action
@@ -406,5 +481,9 @@ module Savon
406
481
  def multipart(multipart)
407
482
  @options[:multipart] = multipart
408
483
  end
484
+
485
+ def headers(headers)
486
+ @options[:headers] = headers
487
+ end
409
488
  end
410
489
  end
@@ -1,50 +1,52 @@
1
+ # frozen_string_literal: true
1
2
  require "gyoku"
2
3
 
3
4
  module Savon
4
5
  class QualifiedMessage
5
-
6
6
  def initialize(types, used_namespaces, key_converter)
7
- @types = types
7
+ @types = types
8
8
  @used_namespaces = used_namespaces
9
- @key_converter = key_converter
9
+ @key_converter = key_converter
10
10
  end
11
11
 
12
12
  def to_hash(hash, path)
13
13
  return hash unless hash
14
- return hash.map { |value| to_hash(value, path) } if hash.kind_of?(Array)
15
- return hash.to_s unless hash.kind_of? Hash
16
-
17
- hash.inject({}) do |newhash, (key, value)|
18
- if key == :order!
19
- add_namespaces_to_values(value, path)
20
- newhash.merge(key => value)
14
+ return hash.map { |value| to_hash(value, path) } if hash.is_a?(Array)
15
+ return hash.to_s unless hash.is_a?(Hash)
16
+
17
+ hash.each_with_object({}) do |(key, value), newhash|
18
+ case key
19
+ when :order!
20
+ newhash[key] = add_namespaces_to_values(value, path)
21
+ when :attributes!, :content!
22
+ newhash[key] = to_hash(value, path)
21
23
  else
22
- translated_key = Gyoku.xml_tag(key, :key_converter => @key_converter).to_s
23
- translated_key << "!" if key[-1] == "!"
24
- newpath = path + [translated_key]
25
-
26
- if @used_namespaces[newpath]
27
- newhash.merge(
28
- "#{@used_namespaces[newpath]}:#{translated_key}" =>
29
- to_hash(value, @types[newpath] ? [@types[newpath]] : newpath)
30
- )
24
+ if key.to_s =~ /!$/
25
+ newhash[key] = value
31
26
  else
32
- newhash.merge(translated_key => value)
27
+ translated_key = translate_tag(key)
28
+ newkey = add_namespaces_to_values(key, path).first
29
+ newpath = path + [translated_key]
30
+ newhash[newkey] = to_hash(value, @types[newpath] ? [@types[newpath]] : newpath)
33
31
  end
34
32
  end
33
+ newhash
35
34
  end
36
35
  end
37
36
 
38
37
  private
39
38
 
40
- def add_namespaces_to_values(values, path)
41
- values.collect! { |value|
42
- camelcased_value = Gyoku.xml_tag(value, :key_converter => @key_converter)
43
- namespace_path = path + [camelcased_value.to_s]
44
- namespace = @used_namespaces[namespace_path]
45
- "#{namespace.blank? ? '' : namespace + ":"}#{camelcased_value}"
46
- }
39
+ def translate_tag(key)
40
+ Gyoku.xml_tag(key, :key_converter => @key_converter).to_s
47
41
  end
48
42
 
43
+ def add_namespaces_to_values(values, path)
44
+ Array(values).collect do |value|
45
+ translated_value = translate_tag(value)
46
+ namespace_path = path + [translated_value]
47
+ namespace = @used_namespaces[namespace_path] || ''
48
+ namespace.empty? ? value : "#{namespace}:#{translated_value}"
49
+ end
50
+ end
49
51
  end
50
52
  end
data/lib/savon/request.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "httpi"
2
3
 
3
4
  module Savon
@@ -21,18 +22,25 @@ module Savon
21
22
  def configure_timeouts
22
23
  @http_request.open_timeout = @globals[:open_timeout] if @globals.include? :open_timeout
23
24
  @http_request.read_timeout = @globals[:read_timeout] if @globals.include? :read_timeout
25
+ @http_request.write_timeout = @globals[:write_timeout] if @globals.include? :write_timeout
24
26
  end
25
27
 
26
28
  def configure_ssl
27
29
  @http_request.auth.ssl.ssl_version = @globals[:ssl_version] if @globals.include? :ssl_version
30
+ @http_request.auth.ssl.min_version = @globals[:ssl_min_version] if @globals.include? :ssl_min_version
31
+ @http_request.auth.ssl.max_version = @globals[:ssl_max_version] if @globals.include? :ssl_max_version
32
+
28
33
  @http_request.auth.ssl.verify_mode = @globals[:ssl_verify_mode] if @globals.include? :ssl_verify_mode
34
+ @http_request.auth.ssl.ciphers = @globals[:ssl_ciphers] if @globals.include? :ssl_ciphers
29
35
 
30
36
  @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
37
+ @http_request.auth.ssl.cert_key = @globals[:ssl_cert_key] if @globals.include? :ssl_cert_key
32
38
  @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
39
+ @http_request.auth.ssl.cert = @globals[:ssl_cert] if @globals.include? :ssl_cert
34
40
  @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
41
+ @http_request.auth.ssl.ca_cert_path = @globals[:ssl_ca_cert_path] if @globals.include? :ssl_ca_cert_path
42
+ @http_request.auth.ssl.ca_cert = @globals[:ssl_ca_cert] if @globals.include? :ssl_ca_cert
43
+ @http_request.auth.ssl.cert_store = @globals[:ssl_cert_store] if @globals.include? :ssl_cert_store
36
44
 
37
45
  @http_request.auth.ssl.cert_key_password = @globals[:ssl_cert_key_password] if @globals.include? :ssl_cert_key_password
38
46
  end
@@ -55,12 +63,19 @@ module Savon
55
63
  def build
56
64
  configure_proxy
57
65
  configure_timeouts
66
+ configure_headers
58
67
  configure_ssl
59
68
  configure_auth
60
69
  configure_redirect_handling
61
70
 
62
71
  @http_request
63
72
  end
73
+
74
+ private
75
+
76
+ def configure_headers
77
+ @http_request.headers = @globals[:headers] if @globals.include? :headers
78
+ end
64
79
  end
65
80
 
66
81
  class SOAPRequest < HTTPRequest
@@ -72,9 +87,9 @@ module Savon
72
87
 
73
88
  def build(options = {})
74
89
  configure_proxy
75
- configure_cookies options[:cookies]
76
90
  configure_timeouts
77
- configure_headers options[:soap_action]
91
+ configure_headers options[:soap_action], options[:headers]
92
+ configure_cookies options[:cookies]
78
93
  configure_ssl
79
94
  configure_auth
80
95
  configure_redirect_handling
@@ -88,8 +103,9 @@ module Savon
88
103
  @http_request.set_cookies(cookies) if cookies
89
104
  end
90
105
 
91
- def configure_headers(soap_action)
106
+ def configure_headers(soap_action, headers)
92
107
  @http_request.headers = @globals[:headers] if @globals.include? :headers
108
+ @http_request.headers.merge!(headers) if headers
93
109
  @http_request.headers["SOAPAction"] ||= %{"#{soap_action}"} if soap_action
94
110
  @http_request.headers["Content-Type"] ||= CONTENT_TYPE[@globals[:soap_version]] % @globals[:encoding]
95
111
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "savon/log_message"
2
3
 
3
4
  module Savon
@@ -23,21 +24,26 @@ module Savon
23
24
  @globals[:log]
24
25
  end
25
26
 
27
+ def log_headers?
28
+ @globals[:log_headers]
29
+ end
30
+
26
31
  private
27
32
 
28
33
  def log_request(request)
29
34
  logger.info { "SOAP request: #{request.url}" }
30
- logger.info { headers_to_log(request.headers) }
35
+ logger.info { headers_to_log(request.headers) } if log_headers?
31
36
  logger.debug { body_to_log(request.body) }
32
37
  end
33
38
 
34
39
  def log_response(response)
35
40
  logger.info { "SOAP response (status #{response.code})" }
41
+ logger.debug { headers_to_log(response.headers) } if log_headers?
36
42
  logger.debug { body_to_log(response.body) }
37
43
  end
38
44
 
39
45
  def headers_to_log(headers)
40
- headers.map { |key, value| "#{key}: #{value}" }.join(", ")
46
+ headers.map { |key, value| "#{key}: #{value}" }.join("\n")
41
47
  end
42
48
 
43
49
  def body_to_log(body)
@@ -1,14 +1,20 @@
1
+ # frozen_string_literal: true
1
2
  require "nori"
2
3
  require "savon/soap_fault"
3
4
  require "savon/http_error"
4
5
 
5
6
  module Savon
6
7
  class Response
8
+ CRLF = /\r\n/
9
+ WSP = /[#{%Q|\x9\x20|}]/
7
10
 
8
11
  def initialize(http, globals, locals)
9
12
  @http = http
10
13
  @globals = globals
11
14
  @locals = locals
15
+ @attachments = []
16
+ @xml = ''
17
+ @has_parsed_body = false
12
18
 
13
19
  build_soap_and_http_errors!
14
20
  raise_soap_and_http_errors! if @globals[:raise_errors]
@@ -48,12 +54,17 @@ module Savon
48
54
  result.kind_of?(Array) ? result.compact : [result].compact
49
55
  end
50
56
 
51
- def hash
52
- @hash ||= nori.parse(xml)
57
+ def full_hash
58
+ @full_hash ||= nori.parse(xml)
53
59
  end
54
60
 
55
61
  def xml
56
- @http.body
62
+ if multipart?
63
+ parse_body unless @has_parsed_body
64
+ @xml
65
+ else
66
+ @http.body
67
+ end
57
68
  end
58
69
 
59
70
  alias_method :to_xml, :xml
@@ -68,14 +79,50 @@ module Savon
68
79
  end
69
80
 
70
81
  def find(*path)
71
- envelope = nori.find(hash, 'Envelope')
72
- raise_invalid_response_error! unless envelope
82
+ envelope = nori.find(full_hash, 'Envelope')
83
+ raise_invalid_response_error! unless envelope.is_a?(Hash)
73
84
 
74
85
  nori.find(envelope, *path)
75
86
  end
76
87
 
88
+ def attachments
89
+ if multipart?
90
+ parse_body unless @has_parsed_body
91
+ @attachments
92
+ else
93
+ []
94
+ end
95
+ end
96
+
97
+ def multipart?
98
+ !(http.headers['content-type'] =~ /^multipart/im).nil?
99
+ end
100
+
77
101
  private
78
102
 
103
+ def boundary
104
+ return unless multipart?
105
+ Mail::Field.new('content-type', http.headers['content-type']).parameters['boundary']
106
+ end
107
+
108
+ def parse_body
109
+ http.body.force_encoding Encoding::ASCII_8BIT
110
+ parts = http.body.split(/(?:\A|\r\n)--#{Regexp.escape(boundary)}(?=(?:--)?\s*$)/)
111
+ parts[1..-1].to_a.each_with_index do |part, index|
112
+ header_part, body_part = part.lstrip.split(/#{CRLF}#{CRLF}|#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m, 2)
113
+ section = Mail::Part.new(
114
+ body: body_part
115
+ )
116
+ section.header = header_part
117
+ if index == 0
118
+ @xml = section.body.to_s
119
+ else
120
+ @attachments << section
121
+ end
122
+ end
123
+ @has_parsed_body = true
124
+ end
125
+
79
126
  def build_soap_and_http_errors!
80
127
  @soap_fault = SOAPFault.new(@http, nori, xml) if soap_fault?
81
128
  @http_error = HTTPError.new(@http) if http_error?
@@ -98,11 +145,12 @@ module Savon
98
145
  return @nori if @nori
99
146
 
100
147
  nori_options = {
101
- :strip_namespaces => @globals[:strip_namespaces],
102
- :convert_tags_to => @globals[:convert_response_tags_to],
103
- :convert_attributes_to => @globals[:convert_attributes_to],
104
- :advanced_typecasting => @locals[:advanced_typecasting],
105
- :parser => @locals[:response_parser]
148
+ :delete_namespace_attributes => @globals[:delete_namespace_attributes],
149
+ :strip_namespaces => @globals[:strip_namespaces],
150
+ :convert_tags_to => @globals[:convert_response_tags_to],
151
+ :convert_attributes_to => @globals[:convert_attributes_to],
152
+ :advanced_typecasting => @locals[:advanced_typecasting],
153
+ :parser => @locals[:response_parser]
106
154
  }
107
155
 
108
156
  non_nil_nori_options = nori_options.reject { |_, value| value.nil? }
@@ -1,12 +1,11 @@
1
- require "savon"
2
-
1
+ # frozen_string_literal: true
3
2
  module Savon
4
3
  class SOAPFault < Error
5
4
 
6
5
  def self.present?(http, xml = nil)
7
6
  xml ||= http.body
8
7
  fault_node = xml.include?("Fault>")
9
- soap1_fault = xml.include?("faultcode>") && xml.include?("faultstring>")
8
+ soap1_fault = xml.match(/faultcode\/?\>/) && xml.match(/faultstring\/?\>/)
10
9
  soap2_fault = xml.include?("Code>") && xml.include?("Reason>")
11
10
 
12
11
  fault_node && (soap1_fault || soap2_fault)
@@ -27,7 +26,7 @@ module Savon
27
26
 
28
27
  def to_hash
29
28
  parsed = nori.parse(xml || http.body)
30
- nori.find(parsed, 'Envelope', 'Body')
29
+ nori.find(parsed, 'Envelope', 'Body') || {}
31
30
  end
32
31
 
33
32
  private
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Savon
4
+ module StringUtils
5
+ def self.snakecase(inputstring)
6
+ str = inputstring.dup
7
+ str.gsub! /::/, '/'
8
+ str.gsub! /([A-Z]+)([A-Z][a-z])/, '\1_\2'
9
+ str.gsub! /([a-z\d])([A-Z])/, '\1_\2'
10
+ str.tr! ".", "_"
11
+ str.tr! "-", "_"
12
+ str.downcase!
13
+ str
14
+ end
15
+ end
16
+ end
17
+
data/lib/savon/version.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Savon
2
- VERSION = '2.11.1'
3
+ VERSION = '2.15.1'
3
4
  end
data/lib/savon.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Savon
2
3
 
3
4
  Error = Class.new(RuntimeError)
@@ -25,3 +26,4 @@ end
25
26
  require "savon/version"
26
27
  require "savon/client"
27
28
  require "savon/model"
29
+ require "savon/string_utils"