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/README.md CHANGED
@@ -2,18 +2,18 @@
2
2
 
3
3
  Heavy metal SOAP client
4
4
 
5
- [Documentation](http://savonrb.com) | [RDoc](http://rubydoc.info/gems/savon) |
5
+ [Documentation](https://www.rubydoc.info/gems/savon/) | [Support](https://stackoverflow.com/questions/tagged/savon) |
6
6
  [Mailing list](https://groups.google.com/forum/#!forum/savonrb) | [Twitter](http://twitter.com/savonrb)
7
7
 
8
- [![Build Status](https://secure.travis-ci.org/savonrb/savon.png?branch=master)](http://travis-ci.org/savonrb/savon)
9
- [![Gem Version](https://badge.fury.io/rb/savon.png)](http://badge.fury.io/rb/savon)
10
- [![Code Climate](https://codeclimate.com/github/savonrb/savon.png)](https://codeclimate.com/github/savonrb/savon)
11
- [![Coverage Status](https://coveralls.io/repos/savonrb/savon/badge.png?branch=version2)](https://coveralls.io/r/savonrb/savon)
8
+ [![Ruby](https://github.com/savonrb/savon/actions/workflows/ci.yml/badge.svg)](https://github.com/savonrb/savon/actions/workflows/ci.yml)
9
+ [![Gem Version](https://badge.fury.io/rb/savon.svg)](http://badge.fury.io/rb/savon)
10
+ [![Code Climate](https://codeclimate.com/github/savonrb/savon.svg)](https://codeclimate.com/github/savonrb/savon)
11
+ [![Coverage Status](https://coveralls.io/repos/savonrb/savon/badge.svg)](https://coveralls.io/r/savonrb/savon)
12
12
 
13
13
 
14
- ## Version 2
14
+ ## Installation
15
15
 
16
- Savon version 2 is available through [Rubygems](http://rubygems.org/gems/savon) and can be installed via:
16
+ Savon is available through [Rubygems](http://rubygems.org/gems/savon) and can be installed via:
17
17
 
18
18
  ```
19
19
  $ gem install savon
@@ -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.10.0'
25
+ gem 'savon', '~> 2.15.0'
26
26
  ```
27
27
 
28
28
  ## Usage example
@@ -33,6 +33,12 @@ require 'savon'
33
33
  # create a client for the service
34
34
  client = Savon.client(wsdl: 'http://service.example.com?wsdl')
35
35
 
36
+ # or: create a client with a wsdl provided as a string
37
+ client = Savon.client do |config|
38
+ wsdl_content = File.read("/path/to/wsdl")
39
+ config.wsdl wsdl_content
40
+ end
41
+
36
42
  client.operations
37
43
  # => [:find_user, :list_users]
38
44
 
@@ -46,24 +52,33 @@ response.body
46
52
  For more examples, you should check out the
47
53
  [integration tests](https://github.com/savonrb/savon/tree/version2/spec/integration).
48
54
 
49
- ## FAQ
55
+ ## Ruby version support
50
56
 
51
- * 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`.
52
- - See https://github.com/savonrb/savon/issues/488 for more info
57
+ Every savon release is tested with contemporary supported versions of ruby. Historical compatibility information:
53
58
 
54
- ## Give back
59
+ * `main` - same support as Ruby
60
+ * 2.15.x - MRI 3.0, 3.1, 3.2, 3.3
61
+ * 2.13.x, 2.14.x - MRI 2.7, 3.0, 3.1
62
+ * 2.12.x - MRI 2.2, 2.3, 2.4, 2.5
63
+ * 2.11.x - MRI 2.0, 2.1, 2.2, and 2.3
55
64
 
56
- If you're using Savon and you or your company is making money from it, then please consider
57
- donating via [Gittip](https://www.gittip.com/tjarratt/) so that I can continue to improve it.
65
+ If you are running MRI 1.8.7, try a 2.6.x release.
58
66
 
59
- [![donate](donate.png)](https://www.gittip.com/tjarratt/)
67
+ Most changes are not backported to older versions of savon, or unsupported ruby versions.
60
68
 
69
+ ## Running tests
61
70
 
62
- ## Documentation
71
+ ```bash
72
+ $ bundle install
73
+ $ bundle exec rspec
74
+ ```
75
+
76
+ ## FAQ
77
+
78
+ * 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`.
79
+ - See https://github.com/savonrb/savon/issues/488 for more info
63
80
 
64
- Please make sure to [read the documentation](http://savonrb.com/version2/).
65
81
 
66
- And if you find any problems with it or if you think something's missing,
67
- feel free to [help out and improve the documentation](https://github.com/savonrb/savonrb.com).
82
+ ## Documentation
68
83
 
69
- Donate icon from the [Noun Project](http://thenounproject.com/noun/donate/#icon-No285).
84
+ Please be sure to [read the documentation](https://www.rubydoc.info/github/savonrb/savon/).
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Savon
2
3
  class BlockInterface
3
4
 
data/lib/savon/builder.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "savon/header"
2
3
  require "savon/message"
3
4
  require "nokogiri"
@@ -6,6 +7,7 @@ require "gyoku"
6
7
 
7
8
  module Savon
8
9
  class Builder
10
+ attr_reader :multipart
9
11
 
10
12
  SCHEMA_TYPES = {
11
13
  "xmlns:xsd" => "http://www.w3.org/2001/XMLSchema",
@@ -36,35 +38,27 @@ module Savon
36
38
  end
37
39
 
38
40
  def build_document
39
- xml = tag(builder, :Envelope, namespaces_with_globals) do |xml|
40
- tag(xml, :Header, header_attributes) { xml << header.to_s } unless header.empty?
41
- if @globals[:no_message_tag]
42
- tag(xml, :Body, body_attributes) { xml << message.to_s }
43
- else
44
- tag(xml, :Body, body_attributes) { xml.tag!(*namespaced_message_tag) { xml << message.to_s } }
45
- end
46
- end
41
+ xml_result = build_xml
47
42
 
48
43
  # if we have a signature sign the document
49
44
  if @signature
50
- @signature.document = xml
45
+ @signature.document = xml_result
51
46
 
52
47
  2.times do
53
48
  @header = nil
54
- @signature.document = tag(builder, :Envelope, namespaces_with_globals) do |xml|
55
- tag(xml, :Header, header_attributes) { xml << header.to_s } unless header.empty?
56
- if @globals[:no_message_tag]
57
- tag(xml, :Body, body_attributes) { xml << message.to_s }
58
- else
59
- tag(xml, :Body, body_attributes) { xml.tag!(*namespaced_message_tag) { xml << message.to_s } }
60
- end
61
- end
49
+ @signature.document = build_xml
62
50
  end
63
51
 
64
- xml = @signature.document
52
+ xml_result = @signature.document
65
53
  end
66
54
 
67
- xml
55
+ # if there are attachments for the request, we should build a multipart message according to
56
+ # https://www.w3.org/TR/SOAP-attachments
57
+ if @locals[:attachments]
58
+ build_multipart_message(xml_result)
59
+ else
60
+ xml_result
61
+ end
68
62
  end
69
63
 
70
64
  def header_attributes
@@ -117,15 +111,25 @@ module Savon
117
111
  @namespaces ||= begin
118
112
  namespaces = SCHEMA_TYPES.dup
119
113
 
120
- if namespace_identifier == nil
121
- namespaces["xmlns"] = @globals[:namespace] || @wsdl.namespace
122
- else
123
- namespaces["xmlns:#{namespace_identifier}"] = @globals[:namespace] || @wsdl.namespace
124
- end
114
+ # check namespace_identifier
115
+ namespaces["xmlns#{namespace_identifier.nil? ? '' : ":#{namespace_identifier}"}"] =
116
+ @globals[:namespace] || @wsdl.namespace
117
+
118
+ # check env_namespace
119
+ namespaces["xmlns#{env_namespace && env_namespace != "" ? ":#{env_namespace}" : ''}"] =
120
+ SOAP_NAMESPACE[@globals[:soap_version]]
121
+
122
+ if @wsdl&.document
123
+ @wsdl.parser.namespaces.each do |identifier, path|
124
+ next if identifier == 'xmlns' # Do not include xmlns namespace as this causes issues for some servers (https://github.com/savonrb/savon/issues/986)
125
125
 
126
- key = ["xmlns"]
127
- key << env_namespace if env_namespace && env_namespace != ""
128
- namespaces[key.join(":")] = SOAP_NAMESPACE[@globals[:soap_version]]
126
+ prefixed_identifier = "xmlns:#{identifier}"
127
+
128
+ next if namespaces.key?(prefixed_identifier)
129
+
130
+ namespaces[prefixed_identifier] = path
131
+ end
132
+ end
129
133
 
130
134
  namespaces
131
135
  end
@@ -141,6 +145,7 @@ module Savon
141
145
 
142
146
  def namespaced_message_tag
143
147
  tag_name = message_tag
148
+ return [tag_name] if @wsdl.document? and @wsdl.soap_input(@operation_name.to_sym).is_a?(Hash)
144
149
  if namespace_identifier == nil
145
150
  [tag_name, message_attributes]
146
151
  elsif @used_namespaces[[tag_name.to_s]]
@@ -150,18 +155,45 @@ module Savon
150
155
  end
151
156
  end
152
157
 
158
+ def serialized_message_tag
159
+ [:wsdl, @wsdl.soap_input(@operation_name.to_sym).keys.first, {}]
160
+ end
161
+
162
+ def serialized_messages
163
+ messages = ""
164
+ message_tag = serialized_message_tag[1]
165
+ @wsdl.soap_input(@operation_name.to_sym)[message_tag].each_pair do |message, type|
166
+ break if @locals[:message].nil?
167
+ message_locals = @locals[:message][StringUtils.snakecase(message).to_sym]
168
+ message_content = Message.new(message_tag, namespace_identifier, @types, @used_namespaces, message_locals, :unqualified, @globals[:convert_request_keys_to], @globals[:unwrap]).to_s
169
+ messages += "<#{message} xsi:type=\"#{type.join(':')}\">#{message_content}</#{message}>"
170
+ end
171
+ messages
172
+ end
173
+
153
174
  def message_tag
154
- message_tag = @locals[:message_tag]
155
- message_tag ||= @wsdl.soap_input(@operation_name.to_sym) if @wsdl.document?
175
+ wsdl_tag_name = @wsdl.document? && @wsdl.soap_input(@operation_name.to_sym)
176
+
177
+ message_tag = wsdl_tag_name.keys.first if wsdl_tag_name.is_a?(Hash)
178
+ message_tag ||= @locals[:message_tag]
179
+ message_tag ||= wsdl_tag_name
156
180
  message_tag ||= Gyoku.xml_tag(@operation_name, :key_converter => @globals[:convert_request_keys_to])
157
181
 
158
- @message_tag = message_tag.to_sym
182
+ message_tag.to_sym
159
183
  end
160
184
 
161
185
  def message_attributes
162
186
  @locals[:attributes] || {}
163
187
  end
164
188
 
189
+ def body_message
190
+ if @wsdl.document? and @wsdl.soap_input(@operation_name.to_sym).is_a?(Hash)
191
+ serialized_messages
192
+ else
193
+ message.to_s
194
+ end
195
+ end
196
+
165
197
  def message
166
198
  element_form_default = @globals[:element_form_default] || @wsdl.element_form_default
167
199
  # TODO: clean this up! [dh, 2012-12-17]
@@ -201,5 +233,69 @@ module Savon
201
233
  end
202
234
  end
203
235
 
236
+ def build_xml
237
+ tag(builder, :Envelope, namespaces_with_globals) do |xml|
238
+ tag(xml, :Header, header_attributes) { xml << header.to_s } unless header.empty?
239
+ tag(xml, :Body, body_attributes) do
240
+ if @globals[:no_message_tag]
241
+ xml << message.to_s
242
+ else
243
+ xml.tag!(*namespaced_message_tag) { xml << body_message }
244
+ end
245
+ end
246
+ end
247
+ end
248
+
249
+ def build_multipart_message(message_xml)
250
+ multipart_message = init_multipart_message(message_xml)
251
+ add_attachments_to_multipart_message(multipart_message)
252
+
253
+ multipart_message.ready_to_send!
254
+
255
+ # the mail.body.encoded algorithm reorders the parts, default order is [ "text/plain", "text/enriched", "text/html" ]
256
+ # should redefine the sort order, because the soap request xml should be the first
257
+ multipart_message.body.set_sort_order [ "text/xml" ]
258
+
259
+ multipart_message.body.encoded(multipart_message.content_transfer_encoding)
260
+ end
261
+
262
+ def init_multipart_message(message_xml)
263
+ multipart_message = Mail.new
264
+ xml_part = Mail::Part.new do
265
+ content_type 'text/xml'
266
+ body message_xml
267
+ # in Content-Type the start parameter is recommended (RFC 2387)
268
+ content_id '<soap-request-body@soap>'
269
+ end
270
+ multipart_message.add_part xml_part
271
+
272
+ #request.headers["Content-Type"] = "multipart/related; boundary=\"#{multipart_message.body.boundary}\"; type=\"text/xml\"; start=\"#{xml_part.content_id}\""
273
+ @multipart = {
274
+ multipart_boundary: multipart_message.body.boundary,
275
+ start: xml_part.content_id,
276
+ }
277
+
278
+ multipart_message
279
+ end
280
+
281
+ def add_attachments_to_multipart_message(multipart_message)
282
+ if @locals[:attachments].is_a? Hash
283
+ # hash example: { 'att1' => '/path/to/att1', 'att2' => '/path/to/att2' }
284
+ @locals[:attachments].each do |identifier, attachment|
285
+ add_attachment_to_multipart_message(multipart_message, attachment, identifier)
286
+ end
287
+ elsif @locals[:attachments].is_a? Array
288
+ # array example: [ '/path/to/att1', '/path/to/att2' ]
289
+ # array example: [ { filename: 'att1.xml', content: '<x/>' }, { filename: 'att2.xml', content: '<y/>' } ]
290
+ @locals[:attachments].each do |attachment|
291
+ add_attachment_to_multipart_message(multipart_message, attachment, attachment.is_a?(String) ? File.basename(attachment) : attachment[:filename])
292
+ end
293
+ end
294
+ end
295
+
296
+ def add_attachment_to_multipart_message(multipart_message, attachment, identifier)
297
+ multipart_message.add_file attachment.clone
298
+ multipart_message.parts.last.content_id = multipart_message.parts.last.content_location = identifier.to_s
299
+ end
204
300
  end
205
301
  end
data/lib/savon/client.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "savon/operation"
2
3
  require "savon/request"
3
4
  require "savon/options"
@@ -21,7 +22,7 @@ module Savon
21
22
  build_wsdl_document
22
23
  end
23
24
 
24
- attr_reader :globals
25
+ attr_reader :globals, :wsdl
25
26
 
26
27
  def operations
27
28
  raise_missing_wsdl_error! unless @wsdl.document?
@@ -60,7 +61,6 @@ module Savon
60
61
  @wsdl.document = @globals[:wsdl] if @globals.include? :wsdl
61
62
  @wsdl.endpoint = @globals[:endpoint] if @globals.include? :endpoint
62
63
  @wsdl.namespace = @globals[:namespace] if @globals.include? :namespace
63
- @wsdl.servicename = @globals[:servicename] if @globals.include? :servicename
64
64
  @wsdl.adapter = @globals[:adapter] if @globals.include? :adapter
65
65
 
66
66
  @wsdl.request = WSDLRequest.new(@globals).build
data/lib/savon/header.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "akami"
2
3
  require "gyoku"
3
4
  require "securerandom"
@@ -61,12 +62,7 @@ module Savon
61
62
  convert_to_xml({
62
63
  'wsa:Action' => @locals[:soap_action],
63
64
  'wsa:To' => @globals[:endpoint],
64
- 'wsa:MessageID' => "urn:uuid:#{SecureRandom.uuid}",
65
- attributes!: {
66
- 'wsa:MessageID' => {
67
- "xmlns:wsa" => "http://schemas.xmlsoap.org/ws/2004/08/addressing"
68
- }
69
- }
65
+ 'wsa:MessageID' => "urn:uuid:#{SecureRandom.uuid}"
70
66
  })
71
67
  end
72
68
 
@@ -1,4 +1,4 @@
1
- require "savon"
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Savon
4
4
  class HTTPError < Error
@@ -14,9 +14,9 @@ module Savon
14
14
  attr_reader :http
15
15
 
16
16
  def to_s
17
- message = "HTTP error (#{@http.code})"
18
- message << ": #{@http.body}" unless @http.body.empty?
19
- message
17
+ String.new("HTTP error (#{@http.code})").tap do |str_error|
18
+ str_error << ": #{@http.body}" unless @http.body.empty?
19
+ end
20
20
  end
21
21
 
22
22
  def to_hash
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "nokogiri"
2
3
 
3
4
  module Savon
@@ -45,7 +46,7 @@ module Savon
45
46
  end
46
47
 
47
48
  def nokogiri_options
48
- @pretty_print ? { :indent => 2 } : {}
49
+ @pretty_print ? { :indent => 2 } : { :save_with => Nokogiri::XML::Node::SaveOptions::AS_XML }
49
50
  end
50
51
 
51
52
  end
data/lib/savon/message.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "savon/qualified_message"
2
3
  require "gyoku"
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "httpi"
2
3
 
3
4
  module Savon
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "savon/mock"
2
3
 
3
4
  module Savon
data/lib/savon/mock.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Savon
2
3
  class ExpectationError < StandardError; end
3
4
  end
data/lib/savon/model.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Savon
2
3
  module Model
3
4
 
@@ -28,7 +29,7 @@ module Savon
28
29
  # Defines a class-level SOAP operation.
29
30
  def define_class_operation(operation)
30
31
  class_operation_module.module_eval %{
31
- def #{operation.to_s.snakecase}(locals = {})
32
+ def #{StringUtils.snakecase(operation.to_s)}(locals = {})
32
33
  client.call #{operation.inspect}, locals
33
34
  end
34
35
  }
@@ -37,8 +38,8 @@ module Savon
37
38
  # Defines an instance-level SOAP operation.
38
39
  def define_instance_operation(operation)
39
40
  instance_operation_module.module_eval %{
40
- def #{operation.to_s.snakecase}(locals = {})
41
- self.class.#{operation.to_s.snakecase} locals
41
+ def #{StringUtils.snakecase(operation.to_s)}(locals = {})
42
+ self.class.#{StringUtils.snakecase(operation.to_s)} locals
42
43
  end
43
44
  }
44
45
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "savon/options"
2
3
  require "savon/block_interface"
3
4
  require "savon/request"
@@ -5,10 +6,16 @@ require "savon/builder"
5
6
  require "savon/response"
6
7
  require "savon/request_logger"
7
8
  require "savon/http_error"
9
+ require "mail"
8
10
 
9
11
  module Savon
10
12
  class Operation
11
13
 
14
+ SOAP_REQUEST_TYPE = {
15
+ 1 => "text/xml",
16
+ 2 => "application/soap+xml"
17
+ }
18
+
12
19
  def self.create(operation_name, wsdl, globals)
13
20
  if wsdl.document?
14
21
  ensure_name_is_symbol! operation_name
@@ -66,21 +73,7 @@ module Savon
66
73
  private
67
74
 
68
75
  def create_response(response)
69
- if multipart_supported?
70
- Multipart::Response.new(response, @globals, @locals)
71
- else
72
- Response.new(response, @globals, @locals)
73
- end
74
- end
75
-
76
- def multipart_supported?
77
- return false unless @globals[:multipart] || @locals[:multipart]
78
-
79
- if Savon.const_defined? :Multipart
80
- true
81
- else
82
- raise 'Unable to find Savon::Multipart. Make sure the savon-multipart gem is installed and loaded.'
83
- end
76
+ Response.new(response, @globals, @locals)
84
77
  end
85
78
 
86
79
  def set_locals(locals, block)
@@ -100,12 +93,22 @@ module Savon
100
93
 
101
94
  request = SOAPRequest.new(@globals).build(
102
95
  :soap_action => soap_action,
103
- :cookies => @locals[:cookies]
96
+ :cookies => @locals[:cookies],
97
+ :headers => @locals[:headers]
104
98
  )
105
99
 
106
100
  request.url = endpoint
107
101
  request.body = builder.to_s
108
102
 
103
+ if builder.multipart
104
+ request.gzip
105
+ request.headers["Content-Type"] = ["multipart/related",
106
+ "type=\"#{SOAP_REQUEST_TYPE[@globals[:soap_version]]}\"",
107
+ "start=\"#{builder.multipart[:start]}\"",
108
+ "boundary=\"#{builder.multipart[:multipart_boundary]}\""].join("; ")
109
+ request.headers["MIME-Version"] = "1.0"
110
+ end
111
+
109
112
  # TODO: could HTTPI do this automatically in case the header
110
113
  # was not specified manually? [dh, 2013-01-04]
111
114
  request.headers["Content-Length"] = request.body.bytesize.to_s
@@ -118,11 +121,11 @@ module Savon
118
121
  return if @locals.include?(:soap_action) && !@locals[:soap_action]
119
122
 
120
123
  # get the soap_action from local options
121
- soap_action = @locals[:soap_action]
124
+ @locals[:soap_action] ||
122
125
  # with no local option, but a wsdl, ask it for the soap_action
123
- soap_action ||= @wsdl.soap_action(@name.to_sym) if @wsdl.document?
126
+ @wsdl.document? && @wsdl.soap_action(@name.to_sym) ||
124
127
  # if there is no soap_action up to this point, fallback to a simple default
125
- soap_action ||= Gyoku.xml_tag(@name, :key_converter => @globals[:convert_request_keys_to])
128
+ Gyoku.xml_tag(@name, :key_converter => @globals[:convert_request_keys_to])
126
129
  end
127
130
 
128
131
  def endpoint