savon 0.6.7 → 0.6.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/CHANGELOG +17 -4
  2. data/README.textile +14 -25
  3. data/Rakefile +7 -30
  4. data/lib/savon.rb +2 -1
  5. data/lib/savon/client.rb +13 -15
  6. data/lib/savon/core_ext.rb +1 -1
  7. data/lib/savon/core_ext/hash.rb +3 -6
  8. data/lib/savon/request.rb +26 -20
  9. data/lib/savon/soap.rb +33 -17
  10. data/lib/savon/wsdl.rb +19 -8
  11. data/spec/endpoint_helper.rb +14 -53
  12. data/spec/fixtures/response/response_fixture.rb +32 -0
  13. data/spec/fixtures/response/xml/authentication.xml +14 -0
  14. data/spec/fixtures/{soap_fault.xml → response/xml/soap_fault.xml} +0 -0
  15. data/spec/fixtures/{soap_fault12.xml → response/xml/soap_fault12.xml} +0 -0
  16. data/spec/fixtures/wsdl/wsdl_fixture.rb +37 -0
  17. data/spec/fixtures/wsdl/xml/authentication.xml +63 -0
  18. data/spec/fixtures/wsdl/xml/namespaced_actions.xml +307 -0
  19. data/spec/fixtures/wsdl/xml/no_namespace.xml +115 -0
  20. data/spec/http_stubs.rb +11 -11
  21. data/spec/savon/client_spec.rb +46 -41
  22. data/spec/savon/core_ext/datetime_spec.rb +2 -2
  23. data/spec/savon/core_ext/hash_spec.rb +10 -10
  24. data/spec/savon/core_ext/object_spec.rb +2 -2
  25. data/spec/savon/core_ext/string_spec.rb +2 -2
  26. data/spec/savon/core_ext/symbol_spec.rb +1 -1
  27. data/spec/savon/core_ext/uri_spec.rb +1 -1
  28. data/spec/savon/request_spec.rb +5 -3
  29. data/spec/savon/response_spec.rb +7 -7
  30. data/spec/savon/savon_spec.rb +3 -4
  31. data/spec/savon/soap_spec.rb +13 -5
  32. data/spec/savon/wsdl_spec.rb +63 -20
  33. data/spec/spec_helper.rb +3 -2
  34. metadata +16 -14
  35. data/VERSION +0 -1
  36. data/spec/fixtures/multiple_user_response.xml +0 -22
  37. data/spec/fixtures/user_fixture.rb +0 -62
  38. data/spec/fixtures/user_response.xml +0 -15
  39. data/spec/fixtures/user_wsdl.xml +0 -124
data/CHANGELOG CHANGED
@@ -1,11 +1,24 @@
1
- == 0.6.6 (2009-12-14)
2
- * Default to use the name of the SOAP action (the method called in a client) in lowerCamelCase for SOAP action
3
- and input when Savon::WSDL is disabled. You still need to specify soap.action and maybe soap.input in case
4
- your SOAP actions are named any different.
1
+ == 0.6.8 (2010-01-01)
2
+ * Improved specifications for various kinds of WSDL documents.
3
+ * Added support for SOAP endpoints which are different than the WSDL endpoint of a service.
4
+ * Changed how SOAP actions and inputs are retrieved from the WSDL documents. This might break a few existing
5
+ implementations, but makes Savon work well with even more services. If this change breaks your implementation,
6
+ please take a look at the +action+ and +input+ methods of the Savon::SOAP object.
7
+ One specific problem I know of is working with the createsend WSDL and its namespaced actions.
8
+ To make it work, call the SOAP action without namespace and specify the input manually:
9
+ client.get_api_key { |soap| soap.input = "User.GetApiKey" }
10
+
11
+ == 0.6.7 (2009-12-18)
5
12
  * Implemented support for a proxy server. The proxy URI can be set through an optional Hash of options passed
6
13
  to instantiating Savon::Client (Dave Woodward <dave@futuremint.com>)
7
14
  * Implemented support for SSL client authentication. Settings can be set through an optional Hash of arguments
8
15
  passed to instantiating Savon::Client (colonhyphenp)
16
+ * Patch for issue #10 (Problem with operation tags without a namespace).
17
+
18
+ == 0.6.6 (2009-12-14)
19
+ * Default to use the name of the SOAP action (the method called in a client) in lowerCamelCase for SOAP action
20
+ and input when Savon::WSDL is disabled. You still need to specify soap.action and maybe soap.input in case
21
+ your SOAP actions are named any different.
9
22
 
10
23
  == 0.6.5 (2009-12-13)
11
24
  * Added an open_timeout method to Savon::Request.
data/README.textile CHANGED
@@ -2,17 +2,23 @@ h1. Savon
2
2
 
3
3
  h4. Heavy metal Ruby SOAP client library
4
4
 
5
+ p. "RDoc":http://rdoc.info/projects/rubiii/savon | "Wiki":http://wiki.github.com/rubiii/savon | "ToDo":http://rubiii.tadalist.com/lists/1459816/public | "Metrics":http://getcaliper.com/caliper/project?repo=git://github.com/rubiii/savon.git
6
+
7
+ h2. Installation
8
+
5
9
  bc. $ gem install savon
6
10
 
7
11
  p. Savon expects you to be familiar with SOAP, WSDL and tools like soapUI.
8
12
 
9
- h3. Instantiate a client
13
+ h2. Instantiate a client
10
14
 
11
15
  p. Instantiate Savon::Client, passing in the WSDL of your service.
12
16
 
13
17
  bc. client = Savon::Client.new "http://example.com/UserService?wsdl"
14
18
 
15
- h3. Calling a SOAP action
19
+ p. More information: "Client":http://wiki.github.com/rubiii/savon/client
20
+
21
+ h2. Calling a SOAP action
16
22
 
17
23
  p. Assuming your service applies to the "Defaults":http://wiki.github.com/rubiii/savon/defaults, you can now call any available SOAP action.
18
24
 
@@ -20,7 +26,7 @@ bc. response = client.get_all_users
20
26
 
21
27
  p. Savon lets you call SOAP actions using snake_case, because even though they will propably be written in lowerCamelCase or CamelCase, it just feels much more natural.
22
28
 
23
- h3. The WSDL object
29
+ h2. The WSDL object
24
30
 
25
31
  p. Savon::WSDL represents the WSDL of your service, including information like the namespace URI and available SOAP actions.
26
32
 
@@ -29,7 +35,7 @@ bc. client.wsdl.soap_actions
29
35
 
30
36
  p. More information: "WSDL":http://wiki.github.com/rubiii/savon/wsdl
31
37
 
32
- h3. The SOAP object
38
+ h2. The SOAP object
33
39
 
34
40
  p. 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.
35
41
 
@@ -37,7 +43,7 @@ bc. response = client.get_user_by_id { |soap| soap.body = { :id => 666 } }
37
43
 
38
44
  p. More information: "SOAP":http://wiki.github.com/rubiii/savon/soap
39
45
 
40
- h3. The WSSE object
46
+ h2. The WSSE object
41
47
 
42
48
  p. Savon::WSSE represents WSSE authentication. Pass a block to your SOAP call and the WSSE object is passed to it as the second argument. The object allows setting the WSSE username, password and whether to use digest authentication.
43
49
 
@@ -49,34 +55,17 @@ end
49
55
 
50
56
  p. More information: "WSSE":http://wiki.github.com/rubiii/savon/wsse
51
57
 
52
- h3. SSL Client Authentication
53
-
54
- p. Savon::Client can accept SSL client certificates, as well as verify the validity of a server certificate. Syntax inspired by Adam Wiggin's "Rest-Client":http://github.com/adamwiggins/rest-client
55
-
56
- bc. client = Savon::Client.new(
57
- 'https://example.org/wsdl',
58
- :ssl_client_cert => OpenSSL::X509::Certificate.new(File.read("client_cert.pem")),
59
- :ssl_client_key => OpenSSL::PKey::RSA.new(File.read("client_key.pem"), "password if one exists"),
60
- :ssl_ca_file => "cacert.pem",
61
- :ssl_verify => OpenSSL::SSL::VERIFY_PEER
62
- )
63
-
64
- h3. The Response object
58
+ h2. The Response object
65
59
 
66
60
  p. Savon::Response represents the HTTP and SOAP response. It contains and raises errors in case of an HTTP error or SOAP fault (unless disabled). Also you can get the response as XML (for parsing it with an XML library) or translated into a Hash.
67
61
  More information: "Response":http://wiki.github.com/rubiii/savon/response
68
62
 
69
- h3. HTTP errors and SOAP faults
63
+ h2. HTTP errors and SOAP faults
70
64
 
71
65
  p. Savon raises a Savon::SOAPFault in case of a SOAP fault and a Savon::HTTPError in case of an HTTP error.
72
66
  More information: "Errors":http://wiki.github.com/rubiii/savon/errors
73
67
 
74
- h3. Logging
68
+ h2. Logging
75
69
 
76
70
  p. Savon logs each request and response to STDOUT. But there are a couple of options to change the default behaviour.
77
71
  More information: "Logging":http://wiki.github.com/rubiii/savon/logging
78
-
79
- h3. Documentation
80
-
81
- p. Wiki: "wiki.github.com/rubiii/savon":http://wiki.github.com/rubiii/savon
82
- RDoc: "rdoc.info/projects/rubiii/savon":http://rdoc.info/projects/rubiii/savon
data/Rakefile CHANGED
@@ -1,9 +1,10 @@
1
1
  require "rubygems"
2
2
  require "rake"
3
3
  require "spec/rake/spectask"
4
+ require "spec/rake/verify_rcov"
4
5
  require "rake/rdoctask"
5
6
 
6
- task :default => :spec
7
+ task :default => :spec_verify_rcov
7
8
 
8
9
  Spec::Rake::SpecTask.new do |spec|
9
10
  spec.spec_files = FileList["spec/**/*_spec.rb"]
@@ -13,38 +14,14 @@ Spec::Rake::SpecTask.new do |spec|
13
14
  spec.rcov_dir = "rcov"
14
15
  end
15
16
 
17
+ RCov::VerifyTask.new(:spec_verify_rcov => :spec) do |verify|
18
+ verify.threshold = 100.0
19
+ verify.index_html = "rcov/index.html"
20
+ end
21
+
16
22
  Rake::RDocTask.new do |rdoc|
17
23
  rdoc.title = "Savon"
18
24
  rdoc.rdoc_dir = "rdoc"
19
25
  rdoc.rdoc_files.include("lib/**/*.rb")
20
26
  rdoc.options = ["--line-numbers", "--inline-source"]
21
27
  end
22
-
23
- begin
24
- require "jeweler"
25
- Jeweler::Tasks.new do |spec|
26
- spec.name = "savon"
27
- spec.author = "Daniel Harrington"
28
- spec.email = "me@rubiii.com"
29
- spec.homepage = "http://github.com/rubiii/savon"
30
- spec.summary = "Heavy metal Ruby SOAP client library"
31
- spec.description = spec.summary
32
-
33
- spec.files = FileList["[A-Z]*", "{lib,spec}/**/*.{rb,xml}"]
34
-
35
- spec.rdoc_options += [
36
- "--title", "Savon",
37
- "--line-numbers",
38
- "--inline-source"
39
- ]
40
-
41
- spec.add_runtime_dependency("builder", ">= 2.1.2")
42
- spec.add_runtime_dependency("crack", ">= 0.1.4")
43
-
44
- spec.add_development_dependency("rspec", ">= 1.2.8")
45
- spec.add_development_dependency("mocha", ">= 0.9.7")
46
- spec.add_development_dependency("fakeweb", ">= 1.2.7")
47
- end
48
- rescue LoadError
49
- puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
50
- end
data/lib/savon.rb CHANGED
@@ -23,11 +23,12 @@ end
23
23
  end
24
24
 
25
25
  # gems
26
+ require "rubygems"
26
27
  %w(builder crack/xml).each do |gem|
27
28
  require gem
28
29
  end
29
30
 
30
31
  # core files
31
32
  %w(core_ext wsse soap request response wsdl client).each do |file|
32
- require "savon/#{file}"
33
+ require File.dirname(__FILE__) + "/savon/#{file}"
33
34
  end
data/lib/savon/client.rb CHANGED
@@ -21,7 +21,7 @@ module Savon
21
21
 
22
22
  # Expects a SOAP +endpoint+ String. Also accepts an optional Hash of
23
23
  # +options+ for specifying a proxy server and SSL client authentication.
24
- def initialize(endpoint,options = {})
24
+ def initialize(endpoint, options = {})
25
25
  @request = Request.new endpoint, options
26
26
  @wsdl = WSDL.new @request
27
27
  end
@@ -49,8 +49,8 @@ module Savon
49
49
  def method_missing(method, *args, &block) #:doc:
50
50
  super if wsdl? && !@wsdl.respond_to?(method)
51
51
 
52
- setup operation_from(method), &block
53
- dispatch method
52
+ setup_objects operation_from(method), &block
53
+ Response.new @request.soap(@soap)
54
54
  end
55
55
 
56
56
  # Returns a SOAP operation Hash containing the SOAP action and input
@@ -60,32 +60,30 @@ module Savon
60
60
  { :action => method.to_soap_key, :input => method.to_soap_key }
61
61
  end
62
62
 
63
+ # Returns the SOAP endpoint.
64
+ def soap_endpoint
65
+ wsdl? ? @wsdl.soap_endpoint : @request.endpoint
66
+ end
67
+
63
68
  # Expects a SOAP operation Hash and sets up Savon::SOAP and Savon::WSSE.
64
69
  # Yields them to a given +block+ in case one was given.
65
- def setup(operation, &block)
66
- @soap = SOAP.new
67
- @soap.action, @soap.input = operation[:action], operation[:input]
68
- @wsse = WSSE.new
70
+ def setup_objects(operation, &block)
71
+ @soap, @wsse = SOAP.new, WSSE.new
72
+ @soap.action, @soap.input, @soap.endpoint = operation[:action], operation[:input], soap_endpoint
69
73
 
70
- yield_parameters &block if block
74
+ yield_objects &block if block
71
75
 
72
76
  @soap.namespaces["xmlns:wsdl"] ||= @wsdl.namespace_uri if wsdl?
73
77
  @soap.wsse = @wsse
74
78
  end
75
79
 
76
80
  # Yields Savon::SOAP and Savon::WSSE to a given +block+.
77
- def yield_parameters(&block)
81
+ def yield_objects(&block)
78
82
  case block.arity
79
83
  when 1 then yield @soap
80
84
  when 2 then yield @soap, @wsse
81
85
  end
82
86
  end
83
87
 
84
- # Dispatches a given +soap_action+ and returns a Savon::Response instance.
85
- def dispatch(soap_action)
86
- response = @request.soap @soap
87
- Response.new response
88
- end
89
-
90
88
  end
91
89
  end
@@ -1,3 +1,3 @@
1
1
  %w(object string symbol datetime hash uri).each do |file|
2
- require "savon/core_ext/#{file}"
2
+ require File.dirname(__FILE__) + "/core_ext/#{file}"
3
3
  end
@@ -34,12 +34,9 @@ class Hash
34
34
  key = key.strip_namespace.snakecase.to_sym
35
35
 
36
36
  value = case value
37
- when Hash
38
- value["xsi:nil"] ? nil : value.map_soap_response
39
- when Array
40
- value.map { |a_value| a_value.map_soap_response rescue a_value }
41
- when String
42
- value.map_soap_response
37
+ when Hash then value["xsi:nil"] ? nil : value.map_soap_response
38
+ when Array then value.map { |a_value| a_value.map_soap_response rescue a_value }
39
+ when String then value.map_soap_response
43
40
  end
44
41
  hash.merge key => value
45
42
  end
data/lib/savon/request.rb CHANGED
@@ -61,15 +61,11 @@ module Savon
61
61
  # Returns the proxy URI.
62
62
  attr_reader :proxy
63
63
 
64
- # Sets the open timeout for HTTP requests.
65
- def open_timeout=(sec)
66
- http.open_timeout = sec
67
- end
64
+ # Accessor for HTTP open timeout.
65
+ attr_accessor :open_timeout
68
66
 
69
- # Sets the read timeout for HTTP requests.
70
- def read_timeout=(sec)
71
- http.read_timeout = sec
72
- end
67
+ # Accessor for HTTP read timeout.
68
+ attr_accessor :read_timeout
73
69
 
74
70
  # Retrieves WSDL document and returns the Net::HTTPResponse.
75
71
  def wsdl
@@ -83,7 +79,7 @@ module Savon
83
79
  @soap = soap
84
80
 
85
81
  log_request
86
- @response = http.request_post @endpoint.path, @soap.to_xml, http_header
82
+ @response = http(@soap.endpoint).request_post @soap.endpoint.path, @soap.to_xml, http_header
87
83
  log_response
88
84
  @response
89
85
  end
@@ -92,7 +88,7 @@ module Savon
92
88
 
93
89
  # Logs the SOAP request.
94
90
  def log_request
95
- log "SOAP request: #{@endpoint}"
91
+ log "SOAP request: #{@soap.endpoint}"
96
92
  log http_header.map { |key, value| "#{key}: #{value}" }.join( ", " )
97
93
  log @soap.to_xml
98
94
  end
@@ -103,19 +99,29 @@ module Savon
103
99
  log @response.body
104
100
  end
105
101
 
106
- # Returns a Net::HTTP instance.
107
- def http
108
- unless @http
109
- @http ||= Net::HTTP::Proxy(@proxy.host, @proxy.port).new @endpoint.host, @endpoint.port
110
- @http.use_ssl = true if @endpoint.ssl?
111
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
112
- add_ssl_authentication if @ssl
113
- end
102
+ # Returns a Net::HTTP instance for a given +endpoint+.
103
+ def http(endpoint = @endpoint)
104
+ @http = Net::HTTP::Proxy(@proxy.host, @proxy.port).new endpoint.host, endpoint.port
105
+ set_http_timeout
106
+ set_ssl_options endpoint.ssl?
107
+ set_ssl_authentication if @ssl
114
108
  @http
115
109
  end
116
110
 
117
- # Adds SSL client authentication to the +@http+ instance.
118
- def add_ssl_authentication
111
+ # Sets HTTP open and read timeout.
112
+ def set_http_timeout
113
+ @http.open_timeout = @open_timeout if @open_timeout
114
+ @http.read_timeout = @read_timeout if @read_timeout
115
+ end
116
+
117
+ # Sets basic SSL options to the +@http+ instance.
118
+ def set_ssl_options(use_ssl)
119
+ @http.use_ssl = use_ssl
120
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
121
+ end
122
+
123
+ # Sets SSL client authentication to the +@http+ instance.
124
+ def set_ssl_authentication
119
125
  @http.verify_mode = @ssl[:verify] if @ssl[:verify].kind_of? Integer
120
126
  @http.cert = @ssl[:client_cert] if @ssl[:client_cert]
121
127
  @http.key = @ssl[:client_key] if @ssl[:client_key]
data/lib/savon/soap.rb CHANGED
@@ -27,23 +27,32 @@ module Savon
27
27
  @@version = version if Savon::SOAPVersions.include? version
28
28
  end
29
29
 
30
+ def initialize
31
+ @builder = Builder::XmlMarkup.new
32
+ end
33
+
30
34
  # Sets the WSSE options.
31
35
  attr_writer :wsse
32
36
 
33
- # Accessor for the SOAP action.
37
+ # Sets the SOAP action.
34
38
  attr_writer :action
35
39
 
40
+ # Returns the SOAP action.
36
41
  def action
37
42
  @action ||= ""
38
43
  end
39
44
 
40
- # Accessor for the SOAP input.
45
+ # Sets the SOAP input.
41
46
  attr_writer :input
42
47
 
48
+ # Returns the SOAP input.
43
49
  def input
44
50
  @input ||= ""
45
51
  end
46
52
 
53
+ # Accessor for the SOAP endpoint.
54
+ attr_accessor :endpoint
55
+
47
56
  # Sets the SOAP header. Expected to be a Hash that can be translated
48
57
  # to XML via Hash.to_soap_xml or any other Object responding to to_s.
49
58
  attr_writer :header
@@ -80,17 +89,9 @@ module Savon
80
89
  # Returns the SOAP envelope XML.
81
90
  def to_xml
82
91
  unless @xml_body
83
- builder = Builder::XmlMarkup.new
84
-
85
- @xml_body = builder.env :Envelope, namespaces do |xml|
86
- xml.env(:Header) do
87
- xml << (header.to_soap_xml rescue header.to_s) + wsse_header
88
- end
89
- xml.env(:Body) do
90
- xml.tag!(:wsdl, *input_array) do
91
- xml << (@body.to_soap_xml rescue @body.to_s)
92
- end
93
- end
92
+ @xml_body = @builder.env :Envelope, namespaces do |xml|
93
+ xml_header xml
94
+ xml_body xml
94
95
  end
95
96
  end
96
97
  @xml_body
@@ -98,19 +99,34 @@ module Savon
98
99
 
99
100
  private
100
101
 
102
+ # Adds a SOAP XML header to a given +xml+ Object.
103
+ def xml_header(xml)
104
+ xml.env(:Header) do
105
+ xml << (header.to_soap_xml rescue header.to_s) + wsse_header
106
+ end
107
+ end
108
+
109
+ # Adds a SOAP XML body to a given +xml+ Object.
110
+ def xml_body(xml)
111
+ xml.env(:Body) do
112
+ xml.tag!(:wsdl, *input_array) do
113
+ xml << (@body.to_soap_xml rescue @body.to_s)
114
+ end
115
+ end
116
+ end
117
+
101
118
  # Returns an Array of SOAP input names to append to the :wsdl namespace.
102
119
  # Defaults to use the name of the SOAP action and may be an empty Array
103
120
  # in case the specified SOAP input seems invalid.
104
121
  def input_array
105
- return [input.to_sym] if input && !input.empty?
106
- return [action.to_sym] if action && !action.empty?
122
+ return [input.to_sym] unless input.blank?
123
+ return [action.to_sym] unless action.blank?
107
124
  []
108
125
  end
109
126
 
110
127
  # Returns the WSSE header or an empty String in case WSSE was not set.
111
128
  def wsse_header
112
- return "" unless @wsse.respond_to? :header
113
- @wsse.header
129
+ @wsse.respond_to?(:header) ? @wsse.header : ""
114
130
  end
115
131
 
116
132
  end