savon 0.7.4 → 0.7.5

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.
@@ -5,6 +5,9 @@ module Savon
5
5
  # Represents the HTTP and SOAP response.
6
6
  class Response
7
7
 
8
+ # The maximum HTTP response code considered to be OK.
9
+ MaxNonErrorResponseCode = 299
10
+
8
11
  # The global setting of whether to raise errors.
9
12
  @@raise_errors = true
10
13
 
@@ -28,7 +31,7 @@ module Savon
28
31
 
29
32
  # Returns whether there was a SOAP fault.
30
33
  def soap_fault?
31
- @soap_fault ? true : false
34
+ !@soap_fault.blank?
32
35
  end
33
36
 
34
37
  # Returns the SOAP fault message.
@@ -36,7 +39,7 @@ module Savon
36
39
 
37
40
  # Returns whether there was an HTTP error.
38
41
  def http_error?
39
- @http_error ? true : false
42
+ !@http_error.blank?
40
43
  end
41
44
 
42
45
  # Returns the HTTP error message.
@@ -88,7 +91,7 @@ module Savon
88
91
  # Handles HTTP errors. Raises a Savon::HTTPError unless the default
89
92
  # behavior of raising errors was turned off.
90
93
  def handle_http_error
91
- if @http.code.to_i >= 300
94
+ if @http.code.to_i > MaxNonErrorResponseCode
92
95
  @http_error = "#{@http.message} (#{@http.code})"
93
96
  @http_error << ": #{@http.body}" unless @http.body.empty?
94
97
  raise Savon::HTTPError, http_error if self.class.raise_errors?
@@ -6,7 +6,7 @@ module Savon
6
6
  class SOAP
7
7
 
8
8
  # SOAP namespaces by SOAP version.
9
- SOAPNamespace = {
9
+ Namespace = {
10
10
  1 => "http://schemas.xmlsoap.org/soap/envelope/",
11
11
  2 => "http://www.w3.org/2003/05/soap-envelope"
12
12
  }
@@ -14,6 +14,15 @@ module Savon
14
14
  # Content-Types by SOAP version.
15
15
  ContentType = { 1 => "text/xml", 2 => "application/soap+xml" }
16
16
 
17
+ # Supported SOAP versions.
18
+ Versions = [1, 2]
19
+
20
+ # SOAP xs:dateTime format.
21
+ DateTimeFormat = "%Y-%m-%dT%H:%M:%SZ"
22
+
23
+ # SOAP xs:dateTime Regexp.
24
+ DateTimeRegexp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/
25
+
17
26
  # The global SOAP version.
18
27
  @@version = 1
19
28
 
@@ -24,7 +33,7 @@ module Savon
24
33
 
25
34
  # Sets the global SOAP version.
26
35
  def self.version=(version)
27
- @@version = version if Savon::SOAPVersions.include? version
36
+ @@version = version if Versions.include? version
28
37
  end
29
38
 
30
39
  # Sets the global SOAP header. Expected to be a Hash that can be translated
@@ -51,7 +60,9 @@ module Savon
51
60
  end
52
61
 
53
62
  # Initialzes the SOAP object.
54
- def initialize
63
+ def initialize(operation, endpoint)
64
+ @action, @input = operation[:action], operation[:input]
65
+ @endpoint = endpoint.kind_of?(URI) ? endpoint : URI(endpoint)
55
66
  @builder = Builder::XmlMarkup.new
56
67
  end
57
68
 
@@ -97,7 +108,7 @@ module Savon
97
108
  # Returns the namespaces. A Hash containing the namespaces (keys)
98
109
  # and the corresponding URI's (values).
99
110
  def namespaces
100
- @namespaces ||= { "xmlns:env" => SOAPNamespace[version] }
111
+ @namespaces ||= { "xmlns:env" => Namespace[version] }
101
112
  end
102
113
 
103
114
  # Convenience method for setting the "xmlns:wsdl" namespace.
@@ -107,7 +118,7 @@ module Savon
107
118
 
108
119
  # Sets the SOAP version.
109
120
  def version=(version)
110
- @version = version if Savon::SOAPVersions.include? version
121
+ @version = version if Versions.include? version
111
122
  end
112
123
 
113
124
  # Returns the SOAP version. Defaults to the global default.
@@ -161,9 +172,15 @@ module Savon
161
172
  # Defaults to use the name of the SOAP action and may be an empty Array
162
173
  # in case the specified SOAP input seems invalid.
163
174
  def input_array
164
- return input.map { |i| i.is_a?(Hash) ? i : i.to_sym } unless input.blank?
165
- return [action.to_sym] unless action.blank?
166
- []
175
+ if !input.blank? && input.kind_of?(Array)
176
+ [input[0].to_sym, input[1]]
177
+ elsif !input.blank?
178
+ [input.to_sym]
179
+ elsif action.blank?
180
+ [action.to_sym]
181
+ else
182
+ []
183
+ end
167
184
  end
168
185
 
169
186
  end
@@ -7,7 +7,7 @@ module Savon
7
7
 
8
8
  # Initializer, expects a Savon::Request.
9
9
  def initialize(request)
10
- @request = request
10
+ @request, @enabled = request, true
11
11
  end
12
12
 
13
13
  # Sets whether to use the WSDL.
@@ -15,7 +15,7 @@ module Savon
15
15
 
16
16
  # Returns whether to use the WSDL. Defaults to +true+.
17
17
  def enabled?
18
- @enabled.nil? ? true : @enabled
18
+ @enabled
19
19
  end
20
20
 
21
21
  # Returns the namespace URI of the WSDL.
@@ -41,10 +41,17 @@ module Savon
41
41
 
42
42
  # Returns +true+ for available methods and SOAP actions.
43
43
  def respond_to?(method)
44
- return true if soap_actions.include? method
44
+ return true if !enabled? || soap_actions.include?(method)
45
45
  super
46
46
  end
47
47
 
48
+ # Returns a SOAP operation Hash containing the SOAP action and input
49
+ # for a given +soap_call+.
50
+ 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 }
53
+ end
54
+
48
55
  # Returns the raw WSDL document.
49
56
  def to_s
50
57
  @document ||= @request.wsdl.body
@@ -9,8 +9,7 @@ module Savon
9
9
  Sections = %w(definitions types message portType binding service)
10
10
 
11
11
  def initialize
12
- @path, @operations = [], {}
13
- @namespaces = {}
12
+ @path, @operations, @namespaces = [], {}, {}
14
13
  end
15
14
 
16
15
  # Returns the namespace URI.
@@ -27,12 +26,12 @@ module Savon
27
26
  # read xml namespaces if root element
28
27
  read_namespaces(attrs) if @path.empty?
29
28
 
30
- tag,namespace = tag.split(":").reverse
29
+ tag, namespace = tag.split(":").reverse
31
30
  @path << tag
32
31
 
33
32
  if @section == :binding && tag == "binding"
34
33
  # ensure that we are in an wsdl/soap namespace
35
- @section = nil unless @namespaces[namespace] == "http://schemas.xmlsoap.org/wsdl/soap/"
34
+ @section = nil unless @namespaces[namespace].starts_with? "http://schemas.xmlsoap.org/wsdl/soap"
36
35
  end
37
36
 
38
37
  @section = tag.to_sym if Sections.include?(tag) && depth <= 2
@@ -51,19 +50,18 @@ module Savon
51
50
  # Reads namespace definitions from a given +attrs+ Hash.
52
51
  def read_namespaces(attrs)
53
52
  attrs.each do |key, value|
54
- @namespaces[key.split(":").last] = value if key =~ /^xmlns:/
53
+ @namespaces[key.strip_namespace] = value if key.starts_with? "xmlns:"
55
54
  end
56
55
  end
57
56
 
58
57
  # Hook method called when the stream parser encounters a closing tag.
59
58
  def tag_end(tag)
60
59
  @path.pop
61
-
60
+
62
61
  if @section == :binding && @input && tag.strip_namespace == "operation"
63
62
  # no soapAction attribute found till now
64
63
  operation_from tag, "soapAction" => @input
65
64
  end
66
-
67
65
  end
68
66
 
69
67
  # Stores available operations from a given tag +name+ and +attrs+.
@@ -76,7 +74,6 @@ module Savon
76
74
 
77
75
  @operations[@input.snakecase.to_sym] = { :action => @action, :input => @input }
78
76
  @input, @action = nil, nil
79
- @input = nil
80
77
  end
81
78
  end
82
79
 
@@ -9,16 +9,16 @@ module Savon
9
9
  BaseAddress = "http://docs.oasis-open.org/wss/2004/01"
10
10
 
11
11
  # Namespace for WS Security Secext.
12
- WSENamespace = BaseAddress + "/oasis-200401-wss-wssecurity-secext-1.0.xsd"
12
+ WSENamespace = "#{BaseAddress}/oasis-200401-wss-wssecurity-secext-1.0.xsd"
13
13
 
14
14
  # Namespace for WS Security Utility.
15
- WSUNamespace = BaseAddress + "/oasis-200401-wss-wssecurity-utility-1.0.xsd"
15
+ WSUNamespace = "#{BaseAddress}/oasis-200401-wss-wssecurity-utility-1.0.xsd"
16
16
 
17
17
  # URI for "wsse:Password/@Type" #PasswordText.
18
- PasswordTextURI = BaseAddress + "/oasis-200401-wss-username-token-profile-1.0#PasswordText"
18
+ PasswordTextURI = "#{BaseAddress}/oasis-200401-wss-username-token-profile-1.0#PasswordText"
19
19
 
20
20
  # URI for "wsse:Password/@Type" #PasswordDigest.
21
- PasswordDigestURI = BaseAddress + "/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
21
+ PasswordDigestURI = "#{BaseAddress}/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
22
22
 
23
23
  # Global WSSE username.
24
24
  @@username = nil
@@ -30,8 +30,7 @@ module Savon
30
30
 
31
31
  # Sets the global WSSE username.
32
32
  def self.username=(username)
33
- @@username = username.to_s if username.respond_to? :to_s
34
- @@username = nil if username.nil?
33
+ @@username = username.nil? ? nil : username.to_s
35
34
  end
36
35
 
37
36
  # Global WSSE password.
@@ -44,8 +43,7 @@ module Savon
44
43
 
45
44
  # Sets the global WSSE password.
46
45
  def self.password=(password)
47
- @@password = password.to_s if password.respond_to? :to_s
48
- @@password = nil if password.nil?
46
+ @@password = password.nil? ? nil : password.to_s
49
47
  end
50
48
 
51
49
  # Global setting of whether to use WSSE digest.
@@ -63,8 +61,7 @@ module Savon
63
61
 
64
62
  # Sets the WSSE username per request.
65
63
  def username=(username)
66
- @username = username.to_s if username.respond_to? :to_s
67
- @username = nil if username.nil?
64
+ @username = username.nil? ? nil : username.to_s
68
65
  end
69
66
 
70
67
  # Returns the WSSE username. Defaults to the global setting.
@@ -74,8 +71,7 @@ module Savon
74
71
 
75
72
  # Sets the WSSE password per request.
76
73
  def password=(password)
77
- @password = password.to_s if password.respond_to? :to_s
78
- @password = nil if password.nil?
74
+ @password = password.nil? ? nil : password.to_s
79
75
  end
80
76
 
81
77
  # Returns the WSSE password. Defaults to the global setting.
@@ -129,7 +125,7 @@ module Savon
129
125
 
130
126
  # Returns a WSSE timestamp.
131
127
  def timestamp
132
- @timestamp ||= Time.now.strftime Savon::SOAPDateTimeFormat
128
+ @timestamp ||= Time.now.strftime Savon::SOAP::DateTimeFormat
133
129
  end
134
130
 
135
131
  end
@@ -0,0 +1,18 @@
1
+ Savon::Client is the main object to connect to a SOAP service. It includes methods to access both Savon::WSDL and Savon::Request objects.
2
+
3
+ == Instantiate Savon::Client
4
+
5
+ Depending on whether you aim to use Savon with or without Savon::WSDL, you need to instantiate Savon::Client by passing in the WSDL or SOAP endpoint.
6
+
7
+ # Client instance with a WSDL endpoint
8
+ client = Savon::Client.new "http://example.com/UserService?wsdl"
9
+
10
+ # Client instance with a SOAP endpoint (for using Savon without a WSDL)
11
+ client = Savon::Client.new "http://example.com/UserService"
12
+
13
+ == Using a proxy server
14
+
15
+ You can specify the URI to a proxy server via optional Hash arguments.
16
+
17
+ client = Savon::Client.new "http://example.com/UserService?wsdl",
18
+ :proxy => "http://proxy.example.com"
@@ -0,0 +1,11 @@
1
+ == HTTP errors
2
+
3
+ In case of an HTTP error, a Savon::HTTPError will be raised. Any HTTP response code >= 300 raises this error. The only exception to this rule is when the server also returns a SOAP fault. In this case, a Savon::SOAPFault is raised.
4
+
5
+ == SOAP faults
6
+
7
+ A Savon::SOAPFault is raised in case the server returned a SOAP fault.
8
+
9
+ == Rails tip
10
+
11
+ In case you're using Savon inside a Rails application, you can use {rescue_from}[http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html] to handle these errors
@@ -0,0 +1,11 @@
1
+ Savon logs each request and response to STDOUT. Specifying your own logger is as easy as it gets:
2
+
3
+ Savon::Request.logger = RAILS_DEFAULT_LOGGER
4
+
5
+ The default log level is set to :debug. In case you need to change this:
6
+
7
+ Savon::Request.log_level = :info
8
+
9
+ You can also disable logging if you need to:
10
+
11
+ Savon::Request.log = false
@@ -0,0 +1,21 @@
1
+ == Provide feedback
2
+
3
+ Let me know about your experiences using Savon. What's good, what's not? {Open an issue}[http://github.com/rubiii/savon/issues] if you encounter a bug. Feedback is always appreciated.
4
+
5
+ == Spread the word
6
+
7
+ Let others know about Savon by talking, tweeting or blogging about it. The more people use Savon, the more stable it becomes.
8
+
9
+ == Fix bugs or implement features
10
+
11
+ You're welcome to fork the project to fix bugs, implement features, etc. But please make sure to follow "the rules":
12
+
13
+ * Follow the coding and naming conventions of the code
14
+ * Do not edit the CHANGELOG and .gemspec files
15
+ * Test all the fucking time!
16
+
17
+ Also, if you're thinking about adding some big new feature or if you're not sure about how to implement it, please just get in touch.
18
+
19
+ Please note that Savon {does not require Rubygems}[http://gist.github.com/54177]. So make sure your environment is set up to load the package manager of your choice. In order to run autospec, you could use the following alias:
20
+
21
+ alias autospec='RUBYLIB=./lib RUBYOPT=-rubygems AUTOFEATURE=true autospec'
@@ -0,0 +1,37 @@
1
+ Savon::Request handles both WSDL and SOAP requests.
2
+
3
+ == The Net::HTTP object
4
+
5
+ You can access the Net::HTTP object used for both WSDL and SOAP requests via:
6
+
7
+ client.request.http
8
+
9
+ Here's an example of how to set open and read timeouts on the Net::HTTP object.
10
+
11
+ client.request.http.open_timeout = 30
12
+ client.request.http.read_timeout = 30
13
+
14
+ Please refer to the {official documentation}[http://www.ruby-doc.org/stdlib/libdoc/net/http/rdoc/index.html] for more information.
15
+
16
+ == HTTP basic authentication
17
+
18
+ Setting credentials for HTTP basic authentication:
19
+
20
+ client.request.basic_auth "username", "password"
21
+
22
+ == SSL client authentication
23
+
24
+ You can use the methods provided by Net::HTTP to set SSL client authentication or use this shortcut:
25
+
26
+ client.request.http.ssl_client_auth(
27
+ :cert => OpenSSL::X509::Certificate.new(File.read("client_cert.pem")),
28
+ :key => OpenSSL::PKey::RSA.new(File.read("client_key.pem"), "password if one exists"),
29
+ :ca_file => "cacert.pem",
30
+ :verify_mode => OpenSSL::SSL::VERIFY_PEER
31
+ )
32
+
33
+ == HTTP headers
34
+
35
+ There's an accessor for the Hash of HTTP headers sent with any SOAP call:
36
+
37
+ client.request.headers["custom"] = "header"
@@ -0,0 +1,46 @@
1
+ The response is wrapped in an object to give you various options of handling it.
2
+
3
+ == SOAP fault
4
+
5
+ Assuming the default behavior of raising errors is disabled, you can ask the response object if there was a SOAP fault or an HTTP error and get the SOAP fault or HTTP error message.
6
+
7
+ response.soap_fault?
8
+ => true
9
+
10
+ response.soap_fault
11
+ => "(soap:Server) Fault occurred while processing."
12
+
13
+ response.http_error?
14
+ => true
15
+
16
+ response.http_error
17
+ => "Not found (404)"
18
+
19
+ == Response as XML
20
+
21
+ To get the raw SOAP response XML, you can call to_xml or to_s on the response object.
22
+
23
+ response.to_xml
24
+ => "<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
25
+ => "..."
26
+ => "</soap:Envelope>"
27
+
28
+ == Response as a Hash
29
+
30
+ You can also let Savon translate the SOAP response body to a Hash.
31
+
32
+ response.to_hash
33
+ => { :findUserByIdResponse => {
34
+ => :id => "666",
35
+ => :username => "thedude"
36
+ => :firstname => "The",
37
+ => :lastname => "Dude",
38
+ => :active => true
39
+ => }
40
+
41
+ == Net::HTTP response
42
+
43
+ If for reason you need to access the Net::HTTP response object ... you can.
44
+
45
+ bc. response.http
46
+ => #<Net::HTTPOK:0x7f749a1aa4a8>
@@ -0,0 +1,71 @@
1
+ Savon::SOAP represents the SOAP request. Pass a block to your SOAP call and the SOAP object is passed to it as the first argument. The object allows setting the SOAP version, header, body and namespaces per request.
2
+
3
+ == Body
4
+
5
+ The body method lets you specify parameters to be received by the SOAP action. The value is expected to be a Hash (which will be translated to XML via Hash.to_soap_xml) or an XML String.
6
+
7
+ response = client.get_user_by_id do |soap|
8
+ soap.body = { :id => 666 }
9
+ end
10
+
11
+ response = client.get_user_by_id do |soap|
12
+ soap.body = "<id>666</id>"
13
+ end
14
+
15
+ In case your service requires the elements to be in a specific order, you have two options. The first is to specify your body as an XML String. The second is to add an ":order!" key containing an Array of elements in the specific order to your Hash. Here's an example:
16
+
17
+ response = client.add_user do |soap|
18
+ soap.body = { :user => { :name => "Lucy", :id => 666, :order! => [:id, :name] } }
19
+ end
20
+
21
+ == Version
22
+
23
+ Savon defaults to SOAP 1.1. In case your service uses SOAP 1.2, you can use the version method to change the default.
24
+
25
+ response = client.get_all_users do |soap|
26
+ soap.version = 2
27
+ end
28
+
29
+ You can also change the default to SOAP 1.2 for all request:
30
+
31
+ Savon::SOAP.version = 2
32
+
33
+ == Header
34
+
35
+ If you need to add custom XML into the SOAP header, you can use the header method. The value is expected to be a Hash (which will be translated to XML via Hash.to_soap_xml) or an XML String.
36
+
37
+ response = client.get_all_users do |soap|
38
+ soap.header["specialApiKey"] = "secret"
39
+ end
40
+
41
+ response = client.get_all_users do |soap|
42
+ soap.header = "<specialApiKey>secret</specialApiKey>"
43
+ end
44
+
45
+ == Namespaces
46
+
47
+ The namespaces method contains a Hash of namespaces for the SOAP envelope.
48
+
49
+ response = client.get_all_users do |soap|
50
+ soap.namespaces["xmlns:whatever"] = "http://example.com"
51
+ end
52
+
53
+ == Input
54
+
55
+ You can change the name of the SOAP input node in case you need to.
56
+
57
+ response = client.get_all_users do |soap|
58
+ soap.input = "GetAllUsersRequest"
59
+ end
60
+
61
+ The request is going to look something like this:
62
+
63
+ => <env:Envelope
64
+ => xmlns:wsdl="http://example.com/user/1.0/UserService"
65
+ => xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
66
+ => <env:Header></env:Header>
67
+ => <env:Body>
68
+ => <wsdl:GetAllUsersRequest></wsdl:GetAllUsersRequest>
69
+ => </env:Body>
70
+ => </env:Envelope>
71
+