savon 0.6.3 → 0.6.4

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 (40) hide show
  1. data/CHANGELOG +31 -7
  2. data/README.textile +0 -0
  3. data/Rakefile +1 -1
  4. data/VERSION +1 -1
  5. data/lib/savon.rb +0 -0
  6. data/lib/savon/client.rb +6 -1
  7. data/lib/savon/core_ext.rb +0 -0
  8. data/lib/savon/core_ext/datetime.rb +0 -0
  9. data/lib/savon/core_ext/hash.rb +0 -0
  10. data/lib/savon/core_ext/object.rb +0 -0
  11. data/lib/savon/core_ext/string.rb +0 -0
  12. data/lib/savon/core_ext/symbol.rb +0 -0
  13. data/lib/savon/core_ext/uri.rb +0 -0
  14. data/lib/savon/request.rb +5 -0
  15. data/lib/savon/response.rb +0 -0
  16. data/lib/savon/soap.rb +11 -9
  17. data/lib/savon/wsdl.rb +67 -31
  18. data/lib/savon/wsse.rb +0 -0
  19. data/spec/fixtures/multiple_user_response.xml +0 -0
  20. data/spec/fixtures/soap_fault.xml +0 -0
  21. data/spec/fixtures/user_fixture.rb +8 -4
  22. data/spec/fixtures/user_response.xml +0 -0
  23. data/spec/fixtures/user_wsdl.xml +0 -0
  24. data/spec/http_stubs.rb +15 -17
  25. data/spec/savon/client_spec.rb +46 -47
  26. data/spec/savon/core_ext/datetime_spec.rb +0 -0
  27. data/spec/savon/core_ext/hash_spec.rb +0 -0
  28. data/spec/savon/core_ext/object_spec.rb +0 -0
  29. data/spec/savon/core_ext/string_spec.rb +0 -0
  30. data/spec/savon/core_ext/symbol_spec.rb +0 -0
  31. data/spec/savon/core_ext/uri_spec.rb +0 -0
  32. data/spec/savon/request_spec.rb +44 -64
  33. data/spec/savon/response_spec.rb +34 -51
  34. data/spec/savon/savon_spec.rb +13 -19
  35. data/spec/savon/soap_spec.rb +61 -92
  36. data/spec/savon/wsdl_spec.rb +19 -34
  37. data/spec/savon/wsse_spec.rb +42 -58
  38. data/spec/spec_helper.rb +1 -1
  39. data/spec/{spec_helper_methods.rb → spec_helper_classes.rb} +32 -4
  40. metadata +15 -15
data/CHANGELOG CHANGED
@@ -1,10 +1,34 @@
1
+ == 0.6.4 (2009-12-13)
2
+ * Refactored specs to be less unit-like.
3
+ * Added a getter for the Savon::Request to Savon::Client and a read_timeout setter for HTTP requests.
4
+ * Replaced WSDL document with stream parsing.
5
+
6
+ Benchmarks (1000 SOAP calls):
7
+
8
+ user system total real
9
+ 0.6.4 72.180000 8.280000 80.460000 (750.799011)
10
+ 0.6.3 192.900000 19.630000 212.530000 (914.031865)
11
+
1
12
  == 0.6.3 (2009-12-11)
2
13
  * Removing 2 ruby deprecation warnings for parenthesized arguments. (Dave Woodward <dave@futuremint.com>)
3
- * Much faster XPath expressions for parsing the WSDL document.
4
14
  * Added global and per request options for disabling Savon::WSDL.
5
15
 
16
+ Benchmarks (1000 SOAP calls):
17
+
18
+ user system total real
19
+ WSDL 192.900000 19.630000 212.530000 (914.031865)
20
+ disabled WSDL 5.680000 1.340000 7.020000 (298.265318)
21
+
22
+ * Improved XPath expressions for parsing the WSDL document.
23
+
24
+ Benchmarks (1000 SOAP calls):
25
+
26
+ user system total real
27
+ 0.6.3 192.900000 19.630000 212.530000 (914.031865)
28
+ 0.6.2 574.720000 78.380000 653.100000 (1387.778539)
29
+
6
30
  == 0.6.2 (2009-12-06)
7
- * Support for changing the name of the SOAP input node.
31
+ * Added support for changing the name of the SOAP input node.
8
32
  * Added a CHANGELOG.
9
33
 
10
34
  == 0.6.1 (2009-12-06)
@@ -12,14 +36,14 @@
12
36
 
13
37
  == 0.6.0 (2009-12-06)
14
38
  * method_missing now yields the SOAP and WSSE objects to a given block.
15
- * The response_process (which previously was a block passed to method_missing) was replaced by the Savon::Response object.
16
- * Improved SOAP action handling (another problem that came up with Issue #1).
39
+ * The response_process (which previously was a block passed to method_missing) was replaced by Savon::Response.
40
+ * Improved SOAP action handling (another problem that came up with issue #1).
17
41
 
18
42
  == 0.5.3 (2009-11-30)
19
- * Fix for Issue #2 (NoMethodError: undefined method `invalid!' for Savon::WSDL)
43
+ * Patch for issue #2 (NoMethodError: undefined method `invalid!' for Savon::WSDL)
20
44
 
21
45
  == 0.5.2 (2009-11-30)
22
- * Patch for Issue #1 (Calls fail if api methods have periods in them)
46
+ * Patch for issue #1 (Calls fail if api methods have periods in them)
23
47
 
24
48
  == 0.5.1 (2009-11-29)
25
49
  * Optimized default response process.
@@ -29,4 +53,4 @@
29
53
  * Added specs
30
54
 
31
55
  == 0.5.0 (2009-11-29)
32
- * Complete rewrite.
56
+ * Complete rewrite.
File without changes
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ task :default => :spec
8
8
  Spec::Rake::SpecTask.new do |spec|
9
9
  spec.spec_files = FileList["spec/**/*_spec.rb"]
10
10
  spec.spec_opts << "--color"
11
- spec.libs << "lib"
11
+ spec.libs += ["lib", "spec"]
12
12
  spec.rcov = true
13
13
  spec.rcov_dir = "rcov"
14
14
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.3
1
+ 0.6.4
File without changes
@@ -23,6 +23,9 @@ module Savon
23
23
  # Accessor for Savon::WSDL.
24
24
  attr_accessor :wsdl
25
25
 
26
+ # Returns the Savon::Request.
27
+ attr_reader :request
28
+
26
29
  # Returns whether to use Savon::WSDL.
27
30
  def wsdl?
28
31
  self.class.wsdl && @wsdl
@@ -47,7 +50,9 @@ module Savon
47
50
  # Expects a SOAP action and sets up Savon::SOAP and Savon::WSSE.
48
51
  # Yields them to a given +block+ in case one was given.
49
52
  def setup(soap_action, &block)
50
- @soap = SOAP.new(wsdl? ? @wsdl.soap_actions[soap_action] : nil)
53
+ @soap = SOAP.new
54
+ @soap.action = @wsdl.operations[soap_action][:action] if wsdl?
55
+ @soap.input = @wsdl.operations[soap_action][:input] if wsdl?
51
56
  @wsse = WSSE.new
52
57
 
53
58
  yield_parameters &block if block
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -45,6 +45,11 @@ module Savon
45
45
  # Returns the endpoint URI.
46
46
  attr_reader :endpoint
47
47
 
48
+ # Sets the read timeout for HTTP requests.
49
+ def read_timeout=(sec)
50
+ http.read_timeout = sec
51
+ end
52
+
48
53
  # Retrieves WSDL document and returns the Net::HTTPResponse.
49
54
  def wsdl
50
55
  log "Retrieving WSDL from: #{@endpoint}"
File without changes
@@ -29,21 +29,23 @@ module Savon
29
29
 
30
30
  end
31
31
 
32
- # Expects a Hash containing the name of the SOAP action and input.
33
- def initialize(action = nil)
34
- @action = action.kind_of?(Hash) ? action[:name] : ""
35
- @input = action.kind_of?(Hash) ? action[:input] : ""
36
- end
37
-
38
32
  # Sets the WSSE options.
39
33
  attr_writer :wsse
40
34
 
41
35
  # Accessor for the SOAP action.
42
- attr_accessor :action
36
+ attr_writer :action
37
+
38
+ def action
39
+ @action ||= ""
40
+ end
43
41
 
44
42
  # Accessor for the SOAP input.
45
43
  attr_writer :input
46
44
 
45
+ def input
46
+ @input ||= ""
47
+ end
48
+
47
49
  # Sets the SOAP header. Expected to be a Hash that can be translated
48
50
  # to XML via Hash.to_soap_xml or any other Object responding to to_s.
49
51
  attr_writer :header
@@ -102,8 +104,8 @@ module Savon
102
104
  # Defaults to use the name of the SOAP action and may be an empty Array
103
105
  # in case the specified SOAP input seems invalid.
104
106
  def input_array
105
- return [@input.to_sym] if @input && !@input.empty?
106
- return [@action.to_sym] if @action && !@action.empty?
107
+ return [input.to_sym] if input && !input.empty?
108
+ return [action.to_sym] if action && !action.empty?
107
109
  []
108
110
  end
109
111
 
@@ -2,68 +2,104 @@ module Savon
2
2
 
3
3
  # Savon::WSDL
4
4
  #
5
- # Represents a WSDL document.
5
+ # Represents the WSDL document.
6
6
  class WSDL
7
7
 
8
- # Expects a Savon::Request object.
8
+ # Initializer, expects a Savon::Request.
9
9
  def initialize(request)
10
10
  @request = request
11
11
  end
12
12
 
13
- # Returns the namespace URI from the WSDL.
13
+ # Returns the namespace URI of the WSDL.
14
14
  def namespace_uri
15
- @namespace_uri ||= document.root.attributes["targetNamespace"] || ""
15
+ @namespace_uri ||= stream.namespace_uri
16
16
  end
17
17
 
18
- # Returns a Hash of available SOAP actions mapped to snake_case (keys)
19
- # and their original names and inputs in another Hash (values).
18
+ # Returns an Array of available SOAP actions.
20
19
  def soap_actions
21
- @soap_actions ||= parse_soap_operations.inject({}) do |hash, (input, action)|
22
- hash.merge input.snakecase.to_sym => { :name => action, :input => input }
23
- end
20
+ @soap_actions ||= stream.operations.keys
21
+ end
22
+
23
+ # Returns a Hash of SOAP operations including their corresponding
24
+ # SOAP actions and inputs.
25
+ def operations
26
+ @operations ||= stream.operations
24
27
  end
25
28
 
26
29
  # Returns +true+ for available methods and SOAP actions.
27
30
  def respond_to?(method)
28
- return true if soap_actions.keys.include? method
31
+ return true if soap_actions.include? method
29
32
  super
30
33
  end
31
34
 
32
- # Returns the WSDL document.
35
+ # Returns the raw WSDL document.
33
36
  def to_s
34
- wsdl_response.body
37
+ @document ||= @request.wsdl.body
35
38
  end
36
39
 
37
40
  private
38
41
 
39
- # Retrieves and returns the WSDL response. Raises an ArgumentError in
40
- # case the WSDL seems to be invalid.
41
- def wsdl_response
42
- unless @wsdl_response
43
- @wsdl_response ||= @request.wsdl
44
- raise ArgumentError, "Invalid WSDL: #{@request.endpoint}" if soap_actions.empty?
42
+ # Returns the Savon::WSDLStream.
43
+ def stream
44
+ unless @stream
45
+ @stream = WSDLStream.new
46
+ REXML::Document.parse_stream to_s, @stream
45
47
  end
46
- @wsdl_response
48
+ @stream
47
49
  end
48
50
 
49
- # Returns a REXML::Document of the WSDL.
50
- def document
51
- @document ||= REXML::Document.new wsdl_response.body
51
+ end
52
+
53
+ # Savon::WSDLStream
54
+ #
55
+ # Stream listener for parsing the WSDL document.
56
+ class WSDLStream
57
+
58
+ # Initializer, sets an empty Hash of operations.
59
+ def initialize
60
+ @operations = {}
52
61
  end
53
62
 
54
- # Parses the WSDL for available SOAP actions and inputs. Returns a Hash
55
- # containing the SOAP action inputs and corresponding SOAP actions.
56
- def parse_soap_operations
57
- wsdl_binding = document.elements["wsdl:definitions/wsdl:binding"]
58
- return {} unless wsdl_binding
63
+ # Returns the namespace URI from the WSDL document.
64
+ attr_reader :namespace_uri
59
65
 
60
- wsdl_binding.elements.inject("wsdl:operation", {}) do |hash, operation|
61
- action = operation.elements["*:operation"].attributes["soapAction"] || ""
62
- action = operation.attributes["name"] if action.empty?
66
+ # Returns the SOAP operations found in the WSDL document.
67
+ attr_reader :operations
68
+
69
+ # Hook method called when the stream parser encounters a tag.
70
+ def tag_start(name, attrs)
71
+ section_from name
72
+ @namespace_uri ||= attrs["targetNamespace"] if @section == :definitions
73
+ operation_from name, attrs if @section == :binding && /.+:operation/ === name
74
+ end
75
+
76
+ # Sets the current section of the WSDL document from a given tag +name+.
77
+ def section_from(name)
78
+ section = case name
79
+ when "wsdl:definitions" then :definitions
80
+ when "wsdl:types" then :types
81
+ when "wsdl:message" then :message
82
+ when "wsdl:portType" then :port_type
83
+ when "wsdl:binding" then :binding
84
+ when "wsdl:service" then :service
85
+ end
86
+ @section = section if section
87
+ end
63
88
 
64
- hash.merge action.split("/").last => action
89
+ # Stores available operations from a given tag +name+ and +attrs+.
90
+ def operation_from(name, attrs)
91
+ if name == "wsdl:operation"
92
+ @action = attrs["name"]
93
+ elsif /.+:operation/ === name
94
+ @action = attrs["soapAction"] if attrs["soapAction"] && !attrs["soapAction"].empty?
95
+ input = @action.split("/").last
96
+ @operations[input.snakecase.to_sym] = { :action => @action, :input => input }
65
97
  end
66
98
  end
67
99
 
100
+ # Catches calls to unimplemented hook methods.
101
+ def method_missing(method, *args)
102
+ end
103
+
68
104
  end
69
105
  end
File without changes
File without changes
File without changes
@@ -1,9 +1,9 @@
1
1
  class UserFixture
2
2
 
3
3
  @namespace_uri = "http://v1_0.ws.user.example.com"
4
- @soap_actions = {
5
- :user_find_by_id => { :name => "User.FindById", :input => "User.FindById" },
6
- :find_user => { :name => "findUser", :input => "findUser" }
4
+ @operations = {
5
+ :user_find_by_id => { :action => "User.FindById", :input => "User.FindById" },
6
+ :find_user => { :action => "findUser", :input => "findUser" }
7
7
  }
8
8
 
9
9
  @datetime_string = "2010-11-22T11:22:33"
@@ -24,9 +24,13 @@ class UserFixture
24
24
 
25
25
  class << self
26
26
 
27
- attr_accessor :namespace_uri, :soap_actions,
27
+ attr_accessor :namespace_uri, :operations,
28
28
  :datetime_string, :datetime_object, :response_hash
29
29
 
30
+ def soap_actions
31
+ @operations.keys
32
+ end
33
+
30
34
  def user_wsdl
31
35
  load_fixture :user_wsdl
32
36
  end
File without changes
File without changes
@@ -2,24 +2,22 @@ require "fakeweb"
2
2
 
3
3
  FakeWeb.allow_net_connect = false
4
4
 
5
- # Register fake WSDL and SOAP request.
6
- FakeWeb.register_uri :get, SpecHelper.some_endpoint, :body => UserFixture.user_wsdl
7
- FakeWeb.register_uri :post, SpecHelper.soap_call_endpoint, :body => UserFixture.user_response
5
+ # Some WSDL and SOAP request.
6
+ FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint, :body => UserFixture.user_wsdl
7
+ FakeWeb.register_uri :post, EndpointHelper.soap_endpoint, :body => UserFixture.user_response
8
8
 
9
- # Register fake WSDL and SOAP request with multiple "//return" nodes.
10
- FakeWeb.register_uri :get, SpecHelper.multiple_endpoint, :body => UserFixture.user_wsdl
11
- FakeWeb.register_uri :post, SpecHelper.soap_multiple_endpoint, :body => UserFixture.multiple_user_response
9
+ # WSDL and SOAP request with multiple "//return" nodes.
10
+ FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint(:multiple), :body => UserFixture.user_wsdl
11
+ FakeWeb.register_uri :post, EndpointHelper.soap_endpoint(:multiple), :body => UserFixture.multiple_user_response
12
12
 
13
- # Register fake WSDL and SOAP request for a Savon::SOAPFault.
14
- FakeWeb.register_uri :get, SpecHelper.soapfault_endpoint, :body => UserFixture.user_wsdl
15
- FakeWeb.register_uri :post, SpecHelper.soap_soapfault_endpoint, :body => UserFixture.soap_fault
13
+ # WSDL and SOAP request with a Savon::SOAPFault.
14
+ FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint(:soap_fault), :body => UserFixture.user_wsdl
15
+ FakeWeb.register_uri :post, EndpointHelper.soap_endpoint(:soap_fault), :body => UserFixture.soap_fault
16
16
 
17
- # Register fake WSDL and SOAP request for a Savon::HTTPError.
18
- FakeWeb.register_uri :get, SpecHelper.httperror_endpoint, :body => UserFixture.user_wsdl
19
- FakeWeb.register_uri :post, SpecHelper.soap_httperror_endpoint, :body => "",
20
- :status => ["404", "Not Found"]
17
+ # WSDL and SOAP request with a Savon::HTTPError.
18
+ FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint(:http_error), :body => UserFixture.user_wsdl
19
+ FakeWeb.register_uri :post, EndpointHelper.soap_endpoint(:http_error), :body => "", :status => ["404", "Not Found"]
21
20
 
22
- # Register fake WSDL and SOAP request for an invalid endpoint.
23
- FakeWeb.register_uri :get, SpecHelper.invalid_endpoint, :body => ""
24
- FakeWeb.register_uri :post, SpecHelper.soap_invalid_endpoint, :body => "",
25
- :status => ["404", "Not Found"]
21
+ # WSDL and SOAP request with an invalid endpoint.
22
+ FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint(:invalid), :body => ""
23
+ FakeWeb.register_uri :post, EndpointHelper.soap_endpoint(:invalid), :body => "", :status => ["404", "Not Found"]
@@ -1,69 +1,68 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Savon::Client do
4
- before { @client = some_client_instance }
4
+ before { @client = Savon::Client.new EndpointHelper.wsdl_endpoint }
5
5
 
6
- def some_client_instance
7
- Savon::Client.new SpecHelper.some_endpoint
6
+ it "is initialized with a SOAP endpoint String" do
7
+ Savon::Client.new EndpointHelper.wsdl_endpoint
8
8
  end
9
9
 
10
- describe "initialize" do
11
- it "expects a SOAP endpoint String" do
12
- some_client_instance
13
- end
10
+ it "raises an ArgumentError when initialized with an invalid endpoint" do
11
+ lambda { Savon::Client.new "invalid" }.should raise_error ArgumentError
12
+ end
14
13
 
15
- it "raises an ArgumentError in case of an invalid endpoint" do
16
- lambda { Savon::Client.new "invalid" }.should raise_error ArgumentError
17
- end
14
+ it "has a getter for accessing the Savon::WSDL" do
15
+ @client.wsdl.should be_a Savon::WSDL
18
16
  end
19
17
 
20
- describe "wsdl" do
21
- it "returns the Savon::WSDL" do
22
- @client.wsdl.should be_a Savon::WSDL
23
- end
18
+ it "has a getter for accessing the Savon::Request" do
19
+ @client.request.should be_a Savon::Request
24
20
  end
25
21
 
26
- describe "respond_to?" do
27
- it "returns true for available SOAP actions" do
28
- @client.respond_to?(UserFixture.soap_actions.keys.first).
29
- should be_true
30
- end
22
+ it "has a getter for returning whether to use the Savon::WSDL (global setting)" do
23
+ @client.wsdl?.should be_true
31
24
 
32
- it "still behaves like usual otherwise" do
33
- @client.respond_to?(:object_id).should be_true
34
- @client.respond_to?(:some_undefined_method).should be_false
35
- end
25
+ Savon::Client.wsdl = false
26
+ @client.wsdl?.should be_false
27
+ Savon::Client.wsdl = true
28
+
29
+ @client.wsdl = false
30
+ @client.wsdl?.should be_false
36
31
  end
37
32
 
38
- describe "method_missing" do
39
- it "dispatches SOAP requests for available SOAP actions" do
40
- @client.find_user.should be_a Savon::Response
41
- end
33
+ it "responds to SOAP actions while still behaving as usual otherwise" do
34
+ @client.respond_to?(UserFixture.soap_actions.first).should be_true
35
+ @client.respond_to?(:object_id).should be_true
36
+ @client.respond_to?(:some_undefined_method).should be_false
37
+ end
42
38
 
43
- it "raises a Savon::SOAPFault in case of a SOAP fault" do
44
- client = Savon::Client.new SpecHelper.soapfault_endpoint
45
- lambda { client.find_user }.should raise_error Savon::SOAPFault
46
- end
39
+ it "dispatches SOAP calls via method_missing and returns the Savon::Response" do
40
+ @client.find_user.should be_a Savon::Response
41
+ end
47
42
 
48
- it "raises a Savon::HTTPError in case of an HTTP error" do
49
- client = Savon::Client.new SpecHelper.httperror_endpoint
50
- lambda { client.find_user }.should raise_error Savon::HTTPError
51
- end
43
+ it "raises a Savon::SOAPFault in case of a SOAP fault" do
44
+ client = Savon::Client.new EndpointHelper.wsdl_endpoint(:soap_fault)
45
+ lambda { client.find_user }.should raise_error Savon::SOAPFault
46
+ end
52
47
 
53
- it "yields the SOAP object to a block that expects one argument" do
54
- @client.find_user { |soap| soap.should be_a Savon::SOAP }
55
- end
48
+ it "raises a Savon::HTTPError in case of an HTTP error" do
49
+ client = Savon::Client.new EndpointHelper.wsdl_endpoint(:http_error)
50
+ lambda { client.find_user }.should raise_error Savon::HTTPError
51
+ end
56
52
 
57
- it "yields the SOAP and WSSE object to a block that expects two argument" do
58
- @client.find_user do |soap, wsse|
59
- soap.should be_a Savon::SOAP
60
- wsse.should be_a Savon::WSSE
61
- end
62
- end
53
+ it "yields the SOAP object to a block when it expects one argument" do
54
+ @client.find_user { |soap| soap.should be_a Savon::SOAP }
55
+ end
63
56
 
64
- it "still raises a NoMethodError for undefined methods" do
65
- lambda { @client.some_undefined_method }.should raise_error NoMethodError
57
+ it "yields the SOAP and WSSE object to a block when it expects two argument" do
58
+ @client.find_user do |soap, wsse|
59
+ soap.should be_a Savon::SOAP
60
+ wsse.should be_a Savon::WSSE
66
61
  end
67
62
  end
68
63
 
69
- end
64
+ it "still raises a NoMethodError for undefined methods" do
65
+ lambda { @client.some_undefined_method }.should raise_error NoMethodError
66
+ end
67
+
68
+ end