savon 0.7.5 → 0.7.6

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 (62) hide show
  1. data/CHANGELOG +11 -0
  2. data/README.rdoc +5 -3
  3. data/Rakefile +2 -0
  4. data/lib/savon.rb +1 -0
  5. data/lib/savon/client.rb +54 -13
  6. data/lib/savon/core_ext.rb +0 -0
  7. data/lib/savon/core_ext/array.rb +20 -6
  8. data/lib/savon/core_ext/datetime.rb +0 -0
  9. data/lib/savon/core_ext/hash.rb +36 -15
  10. data/lib/savon/core_ext/net_http.rb +1 -2
  11. data/lib/savon/core_ext/object.rb +1 -3
  12. data/lib/savon/core_ext/string.rb +9 -2
  13. data/lib/savon/core_ext/symbol.rb +0 -0
  14. data/lib/savon/core_ext/uri.rb +1 -1
  15. data/lib/savon/logger.rb +56 -0
  16. data/lib/savon/request.rb +42 -50
  17. data/lib/savon/response.rb +62 -9
  18. data/lib/savon/soap.rb +157 -42
  19. data/lib/savon/wsdl.rb +71 -6
  20. data/lib/savon/wsdl_stream.rb +2 -2
  21. data/lib/savon/wsse.rb +36 -5
  22. data/spec/basic_spec_helper.rb +0 -0
  23. data/spec/endpoint_helper.rb +0 -0
  24. data/spec/fixtures/response/response_fixture.rb +0 -0
  25. data/spec/fixtures/response/xml/authentication.xml +0 -0
  26. data/spec/fixtures/response/xml/multi_ref.xml +0 -0
  27. data/spec/fixtures/response/xml/soap_fault.xml +0 -0
  28. data/spec/fixtures/response/xml/soap_fault12.xml +0 -0
  29. data/spec/fixtures/wsdl/wsdl_fixture.rb +0 -0
  30. data/spec/fixtures/wsdl/xml/authentication.xml +0 -0
  31. data/spec/fixtures/wsdl/xml/geotrust.xml +0 -0
  32. data/spec/fixtures/wsdl/xml/namespaced_actions.xml +0 -0
  33. data/spec/fixtures/wsdl/xml/no_namespace.xml +0 -0
  34. data/spec/http_stubs.rb +0 -0
  35. data/spec/integration/http_basic_auth_spec.rb +0 -0
  36. data/spec/integration/server.rb +0 -0
  37. data/spec/savon/client_spec.rb +5 -1
  38. data/spec/savon/core_ext/array_spec.rb +0 -0
  39. data/spec/savon/core_ext/datetime_spec.rb +0 -0
  40. data/spec/savon/core_ext/hash_spec.rb +10 -1
  41. data/spec/savon/core_ext/net_http_spec.rb +0 -0
  42. data/spec/savon/core_ext/object_spec.rb +0 -0
  43. data/spec/savon/core_ext/string_spec.rb +6 -2
  44. data/spec/savon/core_ext/symbol_spec.rb +0 -0
  45. data/spec/savon/core_ext/uri_spec.rb +4 -0
  46. data/spec/savon/request_spec.rb +5 -4
  47. data/spec/savon/response_spec.rb +0 -0
  48. data/spec/savon/soap_spec.rb +124 -130
  49. data/spec/savon/wsdl_spec.rb +0 -0
  50. data/spec/savon/wsse_spec.rb +0 -0
  51. data/spec/spec_helper.rb +0 -0
  52. metadata +55 -37
  53. data/readme/client.rdoc +0 -18
  54. data/readme/errors.rdoc +0 -11
  55. data/readme/logging.rdoc +0 -11
  56. data/readme/participate.rdoc +0 -21
  57. data/readme/request.rdoc +0 -37
  58. data/readme/response.rdoc +0 -46
  59. data/readme/soap.rdoc +0 -71
  60. data/readme/value_mapping.rdoc +0 -49
  61. data/readme/wsdl.rdoc +0 -39
  62. data/readme/wsse.rdoc +0 -28
@@ -1,8 +1,61 @@
1
1
  module Savon
2
2
 
3
- # == Savon::Response
3
+ # = Savon::Response
4
4
  #
5
- # Represents the HTTP and SOAP response.
5
+ # Savon::Response represents both HTTP and SOAP response.
6
+ #
7
+ # == SOAP fault
8
+ #
9
+ # Assuming the default behavior of raising errors is disabled, you can ask the response object
10
+ # if there was a SOAP fault or an HTTP error and get the SOAP fault or HTTP error message.
11
+ #
12
+ # response.soap_fault?
13
+ # # => true
14
+ #
15
+ # response.soap_fault
16
+ # # => "(soap:Server) Fault occurred while processing."
17
+ #
18
+ # response.http_error?
19
+ # # => true
20
+ #
21
+ # response.http_error
22
+ # # => "Not found (404)"
23
+ #
24
+ # == Response as XML
25
+ #
26
+ # To get the raw SOAP response XML, you can call to_xml or to_s on the response object.
27
+ #
28
+ # response.to_xml
29
+ # => "<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
30
+ # => "..."
31
+ # => "</soap:Envelope>"
32
+ #
33
+ # == Response as a Hash
34
+ #
35
+ # You can also let Savon translate the SOAP response body to a Hash.
36
+ #
37
+ # response.to_hash
38
+ # => { :findUserByIdResponse => {
39
+ # => :id => "123",
40
+ # => :username => "eve"
41
+ # => :active => true
42
+ # => }
43
+ #
44
+ # When translating the SOAP response to a Hash, some XML tags and values are converted to more
45
+ # convenient Ruby objects. Translation is done through John Nunemaker's {Crack}[http://github.com/jnunemaker/crack]
46
+ # library along with some custom mapping.
47
+ #
48
+ # * XML tags (Hash keys) are converted to snake_case Symbols and namespaces are stripped off
49
+ # * SOAP xs:nil values are converted to nil objects
50
+ # * XML values specified in xs:DateTime format are converted to DateTime objects
51
+ # * XML values of "true" and "false" are converted to TrueClass and FalseClass
52
+ #
53
+ # == Net::HTTP response
54
+ #
55
+ # If for some reason you need to access the Net::HTTP response object ... you can.
56
+ #
57
+ # bc. response.http
58
+ # => #<Net::HTTPOK:0x7f749a1aa4a8>
6
59
  class Response
7
60
 
8
61
  # The maximum HTTP response code considered to be OK.
@@ -57,13 +110,13 @@ module Savon
57
110
 
58
111
  # Returns the HTTP response object.
59
112
  attr_reader :http
60
-
113
+
61
114
  alias :to_s :to_xml
62
115
 
63
116
  private
64
117
 
65
- # Handles SOAP faults. Raises a Savon::SOAPFault unless the default
66
- # behavior of raising errors was turned off.
118
+ # Handles SOAP faults. Raises a Savon::SOAPFault unless the default behavior of raising errors
119
+ # was turned off.
67
120
  def handle_soap_fault
68
121
  if soap_fault_message
69
122
  @soap_fault = soap_fault_message
@@ -76,8 +129,8 @@ module Savon
76
129
  @soap_fault_message ||= soap_fault_message_by_version to_hash[:fault]
77
130
  end
78
131
 
79
- # Expects a Hash that might contain information about a SOAP fault.
80
- # Returns the SOAP fault message in case one was found.
132
+ # Expects a Hash that might contain information about a SOAP fault. Returns the SOAP fault
133
+ # message in case one was found.
81
134
  def soap_fault_message_by_version(soap_fault)
82
135
  return unless soap_fault
83
136
 
@@ -88,8 +141,8 @@ module Savon
88
141
  end
89
142
  end
90
143
 
91
- # Handles HTTP errors. Raises a Savon::HTTPError unless the default
92
- # behavior of raising errors was turned off.
144
+ # Handles HTTP errors. Raises a Savon::HTTPError unless the default behavior of raising errors
145
+ # was turned off.
93
146
  def handle_http_error
94
147
  if @http.code.to_i > MaxNonErrorResponseCode
95
148
  @http_error = "#{@http.message} (#{@http.code})"
@@ -1,10 +1,123 @@
1
1
  module Savon
2
2
 
3
- # == Savon::SOAP
3
+ # = Savon::SOAP
4
4
  #
5
- # Represents the SOAP parameters and envelope.
5
+ # Savon::SOAP represents the SOAP request. Pass a block to your SOAP call and the SOAP object is
6
+ # passed to it as the first argument. The object allows setting the SOAP version, header, body
7
+ # and namespaces per request.
8
+ #
9
+ # == Body
10
+ #
11
+ # The body method lets you specify parameters to be received by the SOAP action.
12
+ #
13
+ # You can either pass in a hash (which will be translated to XML via Hash.to_soap_xml):
14
+ #
15
+ # response = client.get_user_by_id do |soap|
16
+ # soap.body = { :id => 123 }
17
+ # end
18
+ #
19
+ # Or a string containing the raw XML:
20
+ #
21
+ # response = client.get_user_by_id do |soap|
22
+ # soap.body = "<id>123</id>"
23
+ # end
24
+ #
25
+ # Request output:
26
+ #
27
+ # <env:Envelope
28
+ # xmlns:wsdl="http://example.com/user/1.0/UserService"
29
+ # xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
30
+ # <env:Body>
31
+ # <wsdl:getUserById><id>123</id></wsdl:getUserById>
32
+ # </env:Body>
33
+ # </env:Envelope>
34
+ #
35
+ # Please look at the documentation of Hash.to_soap_xml for some more information.
36
+ #
37
+ # == Version
38
+ #
39
+ # Savon defaults to SOAP 1.1. In case your service uses SOAP 1.2, you can use the version method
40
+ # to change the default per request.
41
+ #
42
+ # response = client.get_all_users do |soap|
43
+ # soap.version = 2
44
+ # end
45
+ #
46
+ # You can also change the default to SOAP 1.2 for all request:
47
+ #
48
+ # Savon::SOAP.version = 2
49
+ #
50
+ # == Header
51
+ #
52
+ # If you need to add custom XML into the SOAP header, you can use the header method.
53
+ #
54
+ # The value is expected to be a hash (which will be translated to XML via Hash.to_soap_xml):
55
+ #
56
+ # response = client.get_all_users do |soap|
57
+ # soap.header["specialApiKey"] = "secret"
58
+ # end
59
+ #
60
+ # Or a string containing the raw XML:
61
+ #
62
+ # response = client.get_all_users do |soap|
63
+ # soap.header = "<specialApiKey>secret</specialApiKey>"
64
+ # end
65
+ #
66
+ # Request output:
67
+ #
68
+ # <env:Envelope
69
+ # xmlns:wsdl="http://example.com/user/1.0/UserService"
70
+ # xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
71
+ # <env:Header>
72
+ # <specialApiKey>secret</specialApiKey>
73
+ # </env:Header>
74
+ # <env:Body>
75
+ # <wsdl:getAllUsers></wsdl:getAllUsers>
76
+ # </env:Body>
77
+ # </env:Envelope>
78
+ #
79
+ # == Namespaces
80
+ #
81
+ # The namespaces method contains a hash of attributes for the SOAP envelope. You can overwrite it
82
+ # or add additional attributes.
83
+ #
84
+ # response = client.get_all_users do |soap|
85
+ # soap.namespaces["xmlns:domains"] = "http://domains.example.com"
86
+ # end
87
+ #
88
+ # Request output:
89
+ #
90
+ # <env:Envelope
91
+ # xmlns:wsdl="http://example.com/user/1.0/UserService"
92
+ # xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
93
+ # xmlns:domains="http://domains.example.com">
94
+ # <env:Body>
95
+ # <wsdl:getAllUsers></wsdl:getAllUsers>
96
+ # </env:Body>
97
+ # </env:Envelope>
98
+ #
99
+ # == Input
100
+ #
101
+ # You can change the name of the SOAP input tag in case you need to.
102
+ #
103
+ # response = client.get_all_users do |soap|
104
+ # soap.input = "GetAllUsersRequest"
105
+ # end
106
+ #
107
+ # Request output:
108
+ #
109
+ # <env:Envelope
110
+ # xmlns:wsdl="http://example.com/user/1.0/UserService"
111
+ # xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
112
+ # <env:Body>
113
+ # <wsdl:GetAllUsersRequest></wsdl:GetAllUsersRequest>
114
+ # </env:Body>
115
+ # </env:Envelope>
6
116
  class SOAP
7
117
 
118
+ # Supported SOAP versions.
119
+ Versions = [1, 2]
120
+
8
121
  # SOAP namespaces by SOAP version.
9
122
  Namespace = {
10
123
  1 => "http://schemas.xmlsoap.org/soap/envelope/",
@@ -14,9 +127,6 @@ module Savon
14
127
  # Content-Types by SOAP version.
15
128
  ContentType = { 1 => "text/xml", 2 => "application/soap+xml" }
16
129
 
17
- # Supported SOAP versions.
18
- Versions = [1, 2]
19
-
20
130
  # SOAP xs:dateTime format.
21
131
  DateTimeFormat = "%Y-%m-%dT%H:%M:%SZ"
22
132
 
@@ -36,8 +146,8 @@ module Savon
36
146
  @@version = version if Versions.include? version
37
147
  end
38
148
 
39
- # Sets the global SOAP header. Expected to be a Hash that can be translated
40
- # to XML via Hash.to_soap_xml or any other Object responding to to_s.
149
+ # Sets the global SOAP header. Expected to be a Hash that can be translated to XML via
150
+ # Hash.to_soap_xml or any other Object responding to to_s.
41
151
  def self.header=(header)
42
152
  @@header = header
43
153
  end
@@ -47,21 +157,21 @@ module Savon
47
157
  @@header ||= {}
48
158
  end
49
159
 
50
- # Sets the global namespaces. Expected to be a Hash containing the
51
- # namespaces (keys) and the corresponding URI's (values).
160
+ # Sets the global namespaces. Expected to be a Hash containing the namespaces (keys) and the
161
+ # corresponding URI's (values).
52
162
  def self.namespaces=(namespaces)
53
163
  @@namespaces = namespaces if namespaces.kind_of? Hash
54
164
  end
55
165
 
56
- # Returns the global namespaces. A Hash containing the namespaces (keys)
57
- # and the corresponding URI's (values).
166
+ # Returns the global namespaces. A Hash containing the namespaces (keys) and the corresponding
167
+ # URI's (values).
58
168
  def self.namespaces
59
169
  @@namespaces ||= {}
60
170
  end
61
171
 
62
- # Initialzes the SOAP object.
63
- def initialize(operation, endpoint)
64
- @action, @input = operation[:action], operation[:input]
172
+ # Initialzes the SOAP object. Expects a SOAP +operation+ Hash along with an +endpoint+.
173
+ def initialize(action, input, endpoint)
174
+ @action, @input = action, input
65
175
  @endpoint = endpoint.kind_of?(URI) ? endpoint : URI(endpoint)
66
176
  @builder = Builder::XmlMarkup.new
67
177
  end
@@ -88,8 +198,8 @@ module Savon
88
198
  # Accessor for the SOAP endpoint.
89
199
  attr_accessor :endpoint
90
200
 
91
- # Sets the SOAP header. Expected to be a Hash that can be translated
92
- # to XML via Hash.to_soap_xml or any other Object responding to to_s.
201
+ # Sets the SOAP header. Expected to be a Hash that can be translated to XML via Hash.to_soap_xml
202
+ # or any other Object responding to to_s.
93
203
  attr_writer :header
94
204
 
95
205
  # Returns the SOAP header. Defaults to an empty Hash.
@@ -97,21 +207,25 @@ module Savon
97
207
  @header ||= {}
98
208
  end
99
209
 
100
- # Sets the SOAP body. Expected to be a Hash that can be translated to
101
- # XML via Hash.to_soap_xml or any other Object responding to to_s.
102
- attr_writer :body
210
+ # Accessor for the SOAP body. Expected to be a Hash that can be translated to XML via Hash.to_soap_xml
211
+ # or any other Object responding to to_s.
212
+ attr_accessor :body
103
213
 
104
- # Sets the namespaces. Expected to be a Hash containing the namespaces
105
- # (keys) and the corresponding URI's (values).
214
+ # Accessor for overwriting the default SOAP request. Let's you specify completely custom XML.
215
+ attr_accessor :xml
216
+
217
+ # Sets the namespaces. Expected to be a Hash containing the namespaces (keys) and the
218
+ # corresponding URI's (values).
106
219
  attr_writer :namespaces
107
220
 
108
- # Returns the namespaces. A Hash containing the namespaces (keys)
109
- # and the corresponding URI's (values).
221
+ # Returns the namespaces. A Hash containing the namespaces (keys) and the corresponding URI's
222
+ # (values). Defaults to a Hash containing an +xmlns:env+ key and the namespace for the current
223
+ # SOAP version.
110
224
  def namespaces
111
225
  @namespaces ||= { "xmlns:env" => Namespace[version] }
112
226
  end
113
227
 
114
- # Convenience method for setting the "xmlns:wsdl" namespace.
228
+ # Convenience method for setting the +xmlns:wsdl+ namespace.
115
229
  def namespace=(namespace)
116
230
  namespaces["xmlns:wsdl"] = namespace
117
231
  end
@@ -128,25 +242,28 @@ module Savon
128
242
 
129
243
  # Returns the SOAP envelope XML.
130
244
  def to_xml
131
- unless @xml_body
132
- @xml_body = @builder.env :Envelope, all_namespaces do |xml|
133
- xml.env(:Header) { xml << all_header } unless all_header.empty?
245
+ unless @xml
246
+ @builder.instruct!
247
+ @xml = @builder.env :Envelope, merged_namespaces do |xml|
248
+ xml.env(:Header) { xml << merged_header } unless merged_header.empty?
134
249
  xml_body xml
135
250
  end
136
251
  end
137
- @xml_body
252
+ @xml
138
253
  end
139
254
 
140
255
  private
141
256
 
142
257
  # Returns a String containing the global and per request header.
143
- def all_header
258
+ def merged_header
144
259
  if self.class.header.kind_of?(Hash) && header.kind_of?(Hash)
145
- custom_header = self.class.header.merge(header).to_soap_xml
260
+ merged_header = self.class.header.merge(header).to_soap_xml
146
261
  else
147
- custom_header = self.class.header.to_s + header.to_s
262
+ global_header = self.class.header.to_soap_xml rescue self.class.header.to_s
263
+ request_header = header.to_soap_xml rescue header.to_s
264
+ merged_header = global_header + request_header
148
265
  end
149
- custom_header + wsse_header
266
+ merged_header + wsse_header
150
267
  end
151
268
 
152
269
  # Returns the WSSE header or an empty String in case WSSE was not set.
@@ -157,26 +274,24 @@ module Savon
157
274
  # Adds a SOAP XML body to a given +xml+ Object.
158
275
  def xml_body(xml)
159
276
  xml.env(:Body) do
160
- xml.tag!(:wsdl, *input_array) do
161
- xml << (@body.to_soap_xml rescue @body.to_s)
162
- end
277
+ xml.tag!(:wsdl, *input_array) { xml << (@body.to_soap_xml rescue @body.to_s) }
163
278
  end
164
279
  end
165
280
 
166
281
  # Returns a Hash containing the global and per request namespaces.
167
- def all_namespaces
282
+ def merged_namespaces
168
283
  self.class.namespaces.merge namespaces
169
284
  end
170
285
 
171
- # Returns an Array of SOAP input names to append to the :wsdl namespace.
172
- # Defaults to use the name of the SOAP action and may be an empty Array
173
- # in case the specified SOAP input seems invalid.
286
+ # Returns an Array of SOAP input names to append to the wsdl namespace. Defaults to use the
287
+ # name of the SOAP action. May return an empty Array in case the specified SOAP input seems
288
+ # to be invalid.
174
289
  def input_array
175
- if !input.blank? && input.kind_of?(Array)
290
+ if input.kind_of?(Array) && !input.blank?
176
291
  [input[0].to_sym, input[1]]
177
292
  elsif !input.blank?
178
293
  [input.to_sym]
179
- elsif action.blank?
294
+ elsif !action.blank?
180
295
  [action.to_sym]
181
296
  else
182
297
  []
@@ -184,4 +299,4 @@ module Savon
184
299
  end
185
300
 
186
301
  end
187
- end
302
+ end
@@ -1,8 +1,74 @@
1
1
  module Savon
2
2
 
3
- # Savon::WSDL
3
+ # = Savon::WSDL
4
4
  #
5
- # Represents the WSDL document.
5
+ # Savon::WSDL represents the WSDL of your service, including information like the namespace URI,
6
+ # the SOAP endpoint and available SOAP actions.
7
+ #
8
+ # == The WSDL document
9
+ #
10
+ # Retrieve the raw WSDL document:
11
+ #
12
+ # client.wsdl.to_s
13
+ #
14
+ # == Available SOAP actions
15
+ #
16
+ # Get an array of available SOAP actions:
17
+ #
18
+ # client.wsdl.soap_actions
19
+ # # => [:get_all_users, :get_user_by_id]
20
+ #
21
+ # == Namespace URI
22
+ #
23
+ # Get the namespace URI:
24
+ #
25
+ # client.wsdl.namespace_uri
26
+ # # => "http://ws.userservice.example.com"
27
+ #
28
+ # == SOAP endpoint
29
+ #
30
+ # Get the SOAP endpoint:
31
+ #
32
+ # client.wsdl.soap_endpoint
33
+ # # => "http://example.com"
34
+ #
35
+ # == Disable Savon::WSDL
36
+ #
37
+ # Especially with large services (i.e. Ebay), getting and parsing the WSDL document can really
38
+ # slow down your request. The WSDL is great for exploring a service, but it's recommended to
39
+ # disable it for production.
40
+ #
41
+ # When disabling the WSDL, you need to pay attention to certain differences:
42
+ #
43
+ # 1. You instantiate Savon::Client with the actual SOAP endpoint instead of pointing it to the
44
+ # WSDL of your service.
45
+ # 2. You also need to manually specify the SOAP.namespace.
46
+ # 3. Append an exclamation mark (!) to your SOAP call:
47
+ #
48
+ # client = Savon::Client.new "http://example.com"
49
+ #
50
+ # client.get_user_by_id! do |soap|
51
+ # soap.namespace = "http://example.com/UserService"
52
+ # soap.body = { :id => 666 }
53
+ # end
54
+ #
55
+ # Without the WSDL, Savon also has to guess the name of the SOAP action and input tag. It takes
56
+ # the name of the method called on its client instance, converts it from snake_case to lowerCamelCase
57
+ # and uses the result.
58
+ #
59
+ # The example above expects a SOAP action with an original name of "getUserById". If you service
60
+ # uses UpperCamelCase method names, you can just use the original name:
61
+ #
62
+ # client.GetAllUsers!
63
+ #
64
+ # For special cases, you could also specify the SOAP.action and SOAP.input inside the block:
65
+ #
66
+ # client.get_user_by_id! do |soap|
67
+ # soap.namespace = "http://example.com/UserService"
68
+ # soap.action = "GetUserById"
69
+ # soap.input = "GetUserByIdRequest"
70
+ # soap.body = { :id => 123 }
71
+ # end
6
72
  class WSDL
7
73
 
8
74
  # Initializer, expects a Savon::Request.
@@ -45,11 +111,10 @@ module Savon
45
111
  super
46
112
  end
47
113
 
48
- # Returns a SOAP operation Hash containing the SOAP action and input
49
- # for a given +soap_call+.
114
+ # Returns an Array containg the SOAP action and input for a given +soap_call+.
50
115
  def operation_from(soap_action)
51
- return operations[soap_action] if enabled?
52
- { :action => soap_action.to_soap_key, :input => soap_action.to_soap_key }
116
+ return [soap_action.to_soap_key, soap_action.to_soap_key] unless enabled?
117
+ [operations[soap_action][:action], operations[soap_action][:input]]
53
118
  end
54
119
 
55
120
  # Returns the raw WSDL document.