smacks-savon 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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