smacks-savon 0.0.4 → 0.0.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.
data/README.rdoc CHANGED
@@ -2,10 +2,52 @@
2
2
 
3
3
  Savon is a lightweight SOAP client.
4
4
 
5
+ Instantiate Savon::Service and pass in the WSDL of the service you would
6
+ like to use. Then just call the SOAP service method on your Savon::Service
7
+ instance (catched via method_missing) and pass in a Hash of options for the
8
+ service method to receive.
9
+
5
10
  == Install
6
11
 
7
- $ sudo gem install smacks-savon --source http://gems.github.com
12
+ $ gem install smacks-savon --s http://gems.github.com
8
13
 
9
14
  == Dependencies
10
15
 
11
- Hpricot 0.6.164 (also available for JRuby)
16
+ Hpricot 0.6.164 (also available for JRuby)
17
+
18
+ === Usage example
19
+
20
+ proxy = Savon::Service.new "http://example.com/ExampleService?wsdl"
21
+ response = proxy.findExampleById(:id => 123)
22
+
23
+ === Check for available SOAP service methods
24
+
25
+ proxy.wsdl.service_methods
26
+ => [ "findExampleById", "findExampleByName" ]
27
+
28
+ === Checking for HTTP and SOAP faults
29
+
30
+ response.success?
31
+ response.fault?
32
+
33
+ === Access the fault message and code
34
+
35
+ response.fault
36
+ response.fault_code
37
+
38
+ === Working with different response formats
39
+
40
+ # raw XML response:
41
+ response.to_s
42
+
43
+ # response as a Hash
44
+ response.to_hash
45
+
46
+ # response as a Hash starting at a custom root node (via XPath)
47
+ response.to_hash("//item")
48
+
49
+ # response as a Mash
50
+ response.to_mash
51
+
52
+ # response as a Mash starting at a custom root node (via XPath)
53
+ response.to_mash("//user/email")
@@ -1,29 +1,84 @@
1
- # -*- coding: utf-8 -*-
2
- require 'rubygems'
3
- require 'apricoteatsgorilla'
1
+ require "rubygems"
2
+ require "hpricot"
3
+ require "apricoteatsgorilla"
4
4
 
5
5
  module Savon
6
6
 
7
- # Savon::Response represents the SOAP response and includes methods for
8
- # working with the raw XML, a Hash or a Savon::Mash object.
7
+ # Savon::Response represents the HTTP response.
8
+ #
9
+ # === Checking for HTTP and SOAP faults
10
+ #
11
+ # response.success?
12
+ # response.fault?
13
+ #
14
+ # === Access the fault message and code
15
+ #
16
+ # response.fault
17
+ # response.fault_code
18
+ #
19
+ # === Different response formats
20
+ #
21
+ # # raw XML response:
22
+ # response.to_s
23
+ #
24
+ # # response as a Hash
25
+ # response.to_hash
26
+ #
27
+ # # response as a Hash starting at a custom root node (via XPath)
28
+ # response.to_hash("//item")
29
+ #
30
+ # # response as a Mash
31
+ # response.to_mash
32
+ #
33
+ # # response as a Mash starting at a custom root node (via XPath)
34
+ # response.to_mash("//user/email")
35
+ #
36
+ # ===
9
37
  class Response
10
38
 
39
+ attr_reader :fault, :fault_code
40
+
11
41
  # Initializer to set the SOAP response.
42
+ #
43
+ # === Parameters
44
+ #
45
+ # * +response+ - The Net::HTTP response.
12
46
  def initialize(response)
13
47
  @response = response
48
+ validate
49
+ end
50
+
51
+ # Returns true if the request was successful, false otherwise.
52
+ def success?
53
+ @fault_code.nil?
54
+ end
55
+
56
+ # Returns true if the request was not successful, false otherwise.
57
+ def fault?
58
+ !@fault.nil?
14
59
  end
15
60
 
16
61
  # Returns the SOAP response message as a Hash. Call with XPath expession
17
- # as parameter to define a custom root node. The root node itself will not
18
- # be included in the Hash.
62
+ # to define a custom +root_node+ to start parsing at. Defaults to "//return".
63
+ # The root node itself will not be included in the Hash.
64
+ #
65
+ # === Parameters
66
+ #
67
+ # * +root_node+ - Optional. Custom root node to start parsing at. Defaults to "//return".
19
68
  def to_hash(root_node = "//return")
20
- ApricotEatsGorilla(@response.body, root_node)
69
+ return nil if fault?
70
+ ApricotEatsGorilla[@response.body, root_node]
21
71
  end
22
72
 
23
73
  # Returns the SOAP response message as a Savon::Mash object. Call with
24
- # XPath expession as parameter to define a custom root node. The root node
25
- # itself will not be included in the Mash object.
74
+ # XPath expession to define a custom +root_node+. Defaults to "//return".
75
+ # The root node itself will not be included in the Mash object.
76
+ #
77
+ # === Parameters
78
+ #
79
+ # * +root_node+ - Optional. Custom root node to start parsing at. Defaults to "//return".
26
80
  def to_mash(root_node = "//return")
81
+ return nil if fault?
27
82
  hash = to_hash(root_node)
28
83
  Savon::Mash.new(hash)
29
84
  end
@@ -33,30 +88,19 @@ module Savon
33
88
  @response.body
34
89
  end
35
90
 
36
- end
91
+ private
37
92
 
38
- # Savon::Mash converts a given Hash into a Mash object.
39
- class Mash
40
-
41
- def initialize(hash)
42
- hash.each do |key,value|
43
- value = Savon::Mash.new value if value.is_a? Hash
44
-
45
- if value.is_a? Array
46
- value = value.map do |item|
47
- if item.is_a?(Hash) then Savon::Mash.new(item) else item end
48
- end
49
- end
50
-
51
- # Create and initialize an instance variable for this key/value pair
52
- self.instance_variable_set("@#{key}", value)
53
- # Create the getter that returns the instance variable
54
- self.class.send(:define_method, key, proc{self.instance_variable_get("@#{key}")})
55
- # Create the setter that sets the instance variable
56
- self.class.send(:define_method, "#{key}=", proc{|value| self.instance_variable_set("@#{key}", value)})
93
+ # Checks for and stores HTTP and SOAP-Fault errors.
94
+ def validate
95
+ if @response.code.to_i >= 300
96
+ @fault = @response.message
97
+ @fault_code = @response.code
98
+ else
99
+ fault = to_hash("//soap:Fault")
100
+ @fault = fault[:faultstring] unless fault.nil?
101
+ @fault_code = fault[:faultcode] unless fault.nil?
57
102
  end
58
103
  end
59
104
 
60
105
  end
61
-
62
106
  end
data/lib/savon/service.rb CHANGED
@@ -1,43 +1,58 @@
1
- # -*- coding: utf-8 -*-
2
- require 'rubygems'
3
- require 'net/http'
4
- require 'uri'
5
- require 'apricoteatsgorilla'
1
+ require "rubygems"
2
+ require "net/http"
3
+ require "uri"
4
+ require "apricoteatsgorilla"
6
5
 
7
6
  module Savon
8
7
 
9
- # Savon::Service is the actual SOAP client implementation to use.
8
+ # Savon is a lightweight SOAP client.
10
9
  #
11
10
  # Instantiate Savon::Service and pass in the WSDL of the service you would
12
- # like to work with. Then simply call the SOAP service method on your
13
- # instance (which will be catched via method_missing) and pass in a Hash
14
- # of options you would like to send.
11
+ # like to use. Then just call the SOAP service method on your Savon::Service
12
+ # instance (catched via method_missing) and pass in a Hash of options for the
13
+ # service method to receive.
14
+ #
15
+ # === Usage example
15
16
  #
16
- # Example:
17
17
  # proxy = Savon::Service.new "http://example.com/ExampleService?wsdl"
18
- # response = proxy.findExampleById(:id => "123")
18
+ # response = proxy.findExampleById(:id => 123)
19
+ #
20
+ # === Check for available SOAP service methods
21
+ #
22
+ # proxy.wsdl.service_methods
23
+ # # => [ "findExampleById", "findExampleByName" ]
19
24
  #
20
- # Get the raw response XML:
25
+ # === Working with different response formats
26
+ #
27
+ # # raw XML response:
21
28
  # response.to_s
22
29
  #
23
- # Get it as a Hash (offers optional XPath expression to set a custom root node):
30
+ # # response as a Hash
24
31
  # response.to_hash
25
- # response.to_hash("//return")
26
32
  #
27
- # Or as a Mash object (also offers specifying a custom root node):
33
+ # # response as a Hash starting at a custom root node (via XPath)
34
+ # response.to_hash("//item")
35
+ #
36
+ # # response as a Mash
28
37
  # response.to_mash
38
+ #
39
+ # # response as a Mash starting at a custom root node (via XPath)
29
40
  # response.to_mash("//user/email")
30
41
  class Service
31
42
 
32
- # Sets the HTTP connection instance.
43
+ # The Net::HTTP connection instance to use.
33
44
  attr_writer :http
34
45
 
35
- # Initializer sets the endpoint URI.
46
+ # Initializer sets the WSDL +endpoint+ URI.
47
+ #
48
+ # ==== Parameters
49
+ #
50
+ # * +endpoint+ - The WSDL endpoint URI.
36
51
  def initialize(endpoint)
37
52
  @uri = URI(endpoint)
38
53
  end
39
54
 
40
- # Returns an Wsdl instance.
55
+ # Returns an instance of the WSDL.
41
56
  def wsdl
42
57
  @wsdl = Savon::Wsdl.new(@uri, http) if @wsdl.nil?
43
58
  @wsdl
@@ -45,37 +60,34 @@ module Savon
45
60
 
46
61
  private
47
62
 
48
- # Sets up the request headers and body, makes the request and returns a
49
- # Savon::Response object.
63
+ # Prepares and processes the SOAP request. Returns a Savon::Response object.
50
64
  def call_service
51
- headers = { 'Content-Type' => 'text/xml; charset=utf-8', 'SOAPAction' => @action }
65
+ headers = { "Content-Type" => "text/xml; charset=utf-8", "SOAPAction" => @action }
52
66
  body = ApricotEatsGorilla.soap_envelope("wsdl" => wsdl.namespace_uri) do
53
- ApricotEatsGorilla("wsdl:#{@action}" => namespaced_options)
67
+ ApricotEatsGorilla["wsdl:#{@action}" => namespaced_options]
54
68
  end
55
69
  response = @http.request_post(@uri.path, body, headers)
56
70
  Savon::Response.new(response)
57
71
  end
58
72
 
59
- # Returns an HTTP connection instance.
73
+ # Returns the Net::HTTP instance to use.
60
74
  def http
61
75
  if @http.nil?
62
76
  raise ArgumentError, "Invalid endpoint URI" unless @uri.scheme
63
77
  @http = Net::HTTP.new(@uri.host, @uri.port)
64
- #@http.set_debug_output(STDOUT)
65
- #@http.read_timeout = 5
66
78
  end
67
79
  @http
68
80
  end
69
81
 
70
- # Checks if the requestion SOAP action is available.
71
- # Raises an ArgumentError in case it isn't.
82
+ # Checks if the requested SOAP service method is available.
83
+ # Raises an ArgumentError in case it is not.
72
84
  def validate_action
73
85
  unless wsdl.service_methods.include? @action
74
86
  raise ArgumentError, "Invalid service method '#{@action}'"
75
87
  end
76
88
  end
77
89
 
78
- # Checks if there were any choice elements found in the wsdl and namespaces
90
+ # Checks if there were any choice elements found in the WSDL and namespaces
79
91
  # the corresponding keys from the passed in Hash of options.
80
92
  def namespaced_options
81
93
  return @options if wsdl.choice_elements.empty?
@@ -97,7 +109,12 @@ module Savon
97
109
  options
98
110
  end
99
111
 
100
- # Catches calls to SOAP service methods.
112
+ # Intercepts calls to SOAP service methods.
113
+ #
114
+ # === Parameters
115
+ #
116
+ # * +method+ - The SOAP service method to call.
117
+ # * +options+ - Hash of options for the service method to receive.
101
118
  def method_missing(method, options = {})
102
119
  @action = method.to_s
103
120
  @options = options
data/lib/savon/wsdl.rb CHANGED
@@ -1,7 +1,6 @@
1
- # -*- coding: utf-8 -*-
2
- require 'rubygems'
3
- require 'net/http'
4
- require 'hpricot'
1
+ require "rubygems"
2
+ require "net/http"
3
+ require "hpricot"
5
4
 
6
5
  module Savon
7
6
 
@@ -11,14 +10,19 @@ module Savon
11
10
  # The namespace URI.
12
11
  attr_reader :namespace_uri
13
12
 
14
- # Available service methods.
13
+ # SOAP service methods.
15
14
  attr_reader :service_methods
16
15
 
17
16
  # Choice elements.
18
17
  attr_reader :choice_elements
19
18
 
20
- # Initializer expects an endpoint URI and an HTTP connection instance.
21
- # Gets and parses the WSDL at the given URI.
19
+ # Initializer expects an endpoint +uri+ and an +http+ connection instance,
20
+ # then gets and parses the WSDL at the given URI.
21
+ #
22
+ # === Parameters
23
+ #
24
+ # * +uri+ - The URI to access.
25
+ # * +http+ - The Net::HTTP connection instance to use.
22
26
  def initialize(uri, http)
23
27
  @uri = uri
24
28
  @http = http
@@ -42,16 +46,16 @@ module Savon
42
46
  # Parses the WSDL for the namespace URI, available service methods
43
47
  # and choice elements.
44
48
  def parse_wsdl
45
- @namespace_uri = @doc.at('//wsdl:definitions').get_attribute('targetNamespace')
49
+ @namespace_uri = @doc.at("//wsdl:definitions").get_attribute("targetNamespace")
46
50
 
47
51
  @service_methods = []
48
- @doc.search('//soap:operation').each do |operation|
49
- service_methods << operation.parent.get_attribute('name')
52
+ @doc.search("//soap:operation").each do |operation|
53
+ service_methods << operation.parent.get_attribute("name")
50
54
  end
51
55
 
52
56
  @choice_elements = []
53
- @doc.search('//xs:choice//xs:element').each do |choice|
54
- name = choice.get_attribute('ref').sub(/(.+):/, '')
57
+ @doc.search("//xs:choice//xs:element").each do |choice|
58
+ name = choice.get_attribute("ref").sub(/(.+):/, "")
55
59
  choice_elements << name unless @choice_elements.include? name
56
60
  end
57
61
  end
data/lib/savon.rb CHANGED
@@ -1,5 +1,5 @@
1
- # -*- coding: utf-8 -*-
2
- $:.unshift(File.join(File.dirname(__FILE__), 'savon'))
3
- require 'response'
4
- require 'wsdl'
5
- require 'service'
1
+ $:.unshift(File.join(File.dirname(__FILE__), "savon"))
2
+ require "service"
3
+ require "response"
4
+ require "wsdl"
5
+ require "mash"
@@ -12,4 +12,23 @@ module SoapResponseFixture
12
12
  </soap:Envelope>'
13
13
  end
14
14
 
15
+ def soap_fault_response
16
+ '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
17
+ <soap:Body>
18
+ <soap:Fault>
19
+ <faultcode>' << soap_fault_code << '</faultcode>
20
+ <faultstring>' << soap_fault << '</faultstring>
21
+ </soap:Fault>
22
+ </soap:Body>
23
+ </soap:Envelope>'
24
+ end
25
+
26
+ def soap_fault
27
+ "Failed to authenticate client."
28
+ end
29
+
30
+ def soap_fault_code
31
+ "soap:Server"
32
+ end
33
+
15
34
  end
data/tests/helper.rb CHANGED
@@ -1,13 +1,10 @@
1
- require 'rubygems'
2
- require 'test/unit'
3
- require 'mocha'
4
- require 'shoulda'
1
+ require "rubygems"
2
+ require "test/unit"
3
+ require "mocha"
4
+ require "shoulda"
5
5
  require "apricoteatsgorilla"
6
6
 
7
- ["service", "wsdl", "response"].each do |file|
8
- require File.join(File.dirname(__FILE__), "..", "lib", "savon", file)
9
- end
10
-
7
+ require File.join(File.dirname(__FILE__), "..", "lib", "savon")
11
8
  require File.join(File.dirname(__FILE__), "factories", "wsdl")
12
9
  require File.join(File.dirname(__FILE__), "fixtures", "soap_response")
13
10
 
@@ -35,10 +32,18 @@ module TestHelper
35
32
  end
36
33
 
37
34
  def response_mock(response_body)
38
- response_mock = mock('Net::HTTPResponse')
35
+ build_response_mock("200", "OK", response_body)
36
+ end
37
+
38
+ def response_fault_mock
39
+ build_response_mock("404", "NotFound")
40
+ end
41
+
42
+ def build_response_mock(code, message, body = nil)
43
+ response_mock = mock("Net::HTTPResponse")
39
44
  response_mock.stubs(
40
- :code => '200', :message => "OK", :content_type => "text/html",
41
- :body => response_body
45
+ :code => code, :message => message, :content_type => "text/html",
46
+ :body => body
42
47
  )
43
48
  response_mock
44
49
  end
@@ -0,0 +1,9 @@
1
+ require File.join(File.dirname(__FILE__), "..", "helper")
2
+
3
+ class SavonMashTest < Test::Unit::TestCase
4
+
5
+ def test_nupsi
6
+ assert true
7
+ end
8
+
9
+ end
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
1
  require File.join(File.dirname(__FILE__), "..", "helper")
3
2
 
4
3
  class SavonResponseTest < Test::Unit::TestCase
@@ -17,7 +16,7 @@ class SavonResponseTest < Test::Unit::TestCase
17
16
  end
18
17
 
19
18
  should "return a Hash equal to the response on to_hash" do
20
- assert_equal ApricotEatsGorilla(some_soap_response, "//return"), @response.to_hash
19
+ assert_equal ApricotEatsGorilla[some_soap_response, "//return"], @response.to_hash
21
20
  end
22
21
 
23
22
  should "return a Mash object on to_mash" do
@@ -31,6 +30,93 @@ class SavonResponseTest < Test::Unit::TestCase
31
30
  should "return the raw XML response on to_s" do
32
31
  assert_equal some_soap_response, @response.to_s
33
32
  end
33
+
34
+ should "return 'true' on success?" do
35
+ assert_equal true, @response.success?
36
+ end
37
+
38
+ should "return 'false' on fault?" do
39
+ assert_equal false, @response.fault?
40
+ end
41
+
42
+ should "return 'nil' for fault" do
43
+ assert_equal nil, @response.fault
44
+ end
45
+
46
+ should "return 'nil' for fault_code" do
47
+ assert_equal nil, @response.fault_code
48
+ end
49
+ end
50
+
51
+ context "Savon::Response with SOAP::Fault response" do
52
+ setup do
53
+ ApricotEatsGorilla.sort_keys = true
54
+ @response = Savon::Response.new(response_mock(soap_fault_response))
55
+ end
56
+
57
+ should "return 'nil' on to_hash" do
58
+ assert_equal nil, @response.to_hash
59
+ end
60
+
61
+ should "return 'nil' on to_mash" do
62
+ assert_equal nil, @response.to_mash
63
+ end
64
+
65
+ should "return the raw XML response on to_s" do
66
+ assert_equal soap_fault_response, @response.to_s
67
+ end
68
+
69
+ should "return 'false' on success?" do
70
+ assert_equal false, @response.success?
71
+ end
72
+
73
+ should "return 'true' on fault?" do
74
+ assert_equal true, @response.fault?
75
+ end
76
+
77
+ should "return the fault on fault" do
78
+ assert_equal soap_fault, @response.fault
79
+ end
80
+
81
+ should "return the fault_code on fault_code" do
82
+ assert_equal soap_fault_code, @response.fault_code
83
+ end
84
+ end
85
+
86
+ context "Savon::Response with 404 error" do
87
+ setup do
88
+ ApricotEatsGorilla.sort_keys = true
89
+ @response = Savon::Response.new(response_fault_mock)
90
+ end
91
+
92
+ should "return 'nil' on to_hash" do
93
+ assert_equal nil, @response.to_hash
94
+ end
95
+
96
+ should "return 'nil' on to_mash" do
97
+ assert_equal nil, @response.to_mash
98
+ end
99
+
100
+ should "return nil on to_s" do
101
+ assert_equal nil, @response.to_s
102
+ end
103
+
104
+ should "return 'false' on success?" do
105
+ assert_equal false, @response.success?
106
+ end
107
+
108
+ should "return 'true' on fault?" do
109
+ assert_equal true, @response.fault?
110
+ end
111
+
112
+ should "return the fault on fault" do
113
+ assert_equal "NotFound", @response.fault
114
+ end
115
+
116
+ should "return the fault_code on fault_code" do
117
+ assert_equal "404", @response.fault_code
118
+ end
119
+
34
120
  end
35
121
 
36
122
  end
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
1
  require File.join(File.dirname(__FILE__), "..", "helper")
3
2
 
4
3
  class SavonServiceTest < Test::Unit::TestCase
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
1
  require File.join(File.dirname(__FILE__), "..", "helper")
3
2
 
4
3
  class SavonWsdlTest < Test::Unit::TestCase
data/tests/savon_test.rb CHANGED
@@ -1,5 +1,5 @@
1
- # -*- coding: utf-8 -*-
2
1
  $:.unshift(File.join(File.dirname(__FILE__), 'savon'))
3
- require "service_test"
2
+ require "mash_test"
4
3
  require "wsdl_test"
5
- require "response_test"
4
+ require "response_test"
5
+ require "service_test"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smacks-savon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Harrington
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.2.8
33
+ version: 0.3.31
34
34
  version:
35
35
  description: Savon is a lightweight SOAP client.
36
36
  email:
@@ -79,5 +79,6 @@ test_files:
79
79
  - tests/factories/wsdl.rb
80
80
  - tests/fixtures/soap_response.rb
81
81
  - tests/savon/service_test.rb
82
- - tests/savon/wsdl_test.rb
83
82
  - tests/savon/response_test.rb
83
+ - tests/savon/wsdl_test.rb
84
+ - tests/savon/mash_test.rb