johnreitano-savon 0.7.2.1 → 0.7.7.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +36 -4
- data/README.rdoc +78 -0
- data/Rakefile +15 -9
- data/lib/savon.rb +18 -20
- data/lib/savon/client.rb +60 -32
- data/lib/savon/core_ext.rb +8 -3
- data/lib/savon/core_ext/array.rb +31 -0
- data/lib/savon/core_ext/datetime.rb +1 -1
- data/lib/savon/core_ext/hash.rb +74 -50
- data/lib/savon/core_ext/net_http.rb +1 -2
- data/lib/savon/core_ext/object.rb +1 -3
- data/lib/savon/core_ext/string.rb +23 -4
- data/lib/savon/core_ext/uri.rb +3 -3
- data/lib/savon/logger.rb +56 -0
- data/lib/savon/request.rb +61 -60
- data/lib/savon/response.rb +68 -12
- data/lib/savon/soap.rb +180 -54
- data/lib/savon/wsdl.rb +77 -62
- data/lib/savon/wsdl_stream.rb +85 -0
- data/lib/savon/wsse.rb +45 -18
- data/spec/basic_spec_helper.rb +0 -1
- data/spec/endpoint_helper.rb +1 -0
- data/spec/fixtures/wsdl/xml/geotrust.xml +156 -0
- data/spec/http_stubs.rb +3 -0
- data/spec/savon/client_spec.rb +6 -2
- data/spec/savon/core_ext/array_spec.rb +19 -0
- data/spec/savon/core_ext/datetime_spec.rb +1 -1
- data/spec/savon/core_ext/hash_spec.rb +104 -64
- data/spec/savon/core_ext/object_spec.rb +1 -1
- data/spec/savon/core_ext/string_spec.rb +21 -2
- data/spec/savon/core_ext/uri_spec.rb +4 -0
- data/spec/savon/request_spec.rb +12 -8
- data/spec/savon/soap_spec.rb +141 -111
- data/spec/savon/wsdl_spec.rb +18 -0
- metadata +39 -19
- data/README.textile +0 -75
- data/spec/savon/savon_spec.rb +0 -23
data/CHANGELOG
CHANGED
@@ -1,10 +1,41 @@
|
|
1
|
+
== 0.7.6 (2010-03-21)
|
2
|
+
* Renamed
|
3
|
+
* Moved documentation from the Github Wiki to the actual class files and established a much nicer
|
4
|
+
documentation combining examples and implementation (using Hanna) at: http://savon.rubiii.com
|
5
|
+
* Added Savon::Client#call as a workaround for dispatching calls to SOAP actions named after
|
6
|
+
existing methods. Fix for issue #48.
|
7
|
+
* Add support for specifying attributes for duplicate tags (via Hash values as Arrays). Fix for issue #45.
|
8
|
+
* Fix for issue #41 (Escape special characters (e.g. &) for XML requests).
|
9
|
+
* Fix for issue #39 and #49. Added Savon::SOAP#xml which let's you specify completely custom SOAP request XML.
|
10
|
+
|
11
|
+
== 0.7.5 (2010-02-19)
|
12
|
+
* Fix for issue #34 (soap_actions returns empty for wsdl12).
|
13
|
+
* Fix for issue #36 (Custom WSDL actions broken).
|
14
|
+
* Added feature requested in issue #35 (Setting an attribute on an element?).
|
15
|
+
* Changed the key for specifying the order of tags from :@inorder to :order!
|
16
|
+
|
17
|
+
== 0.7.4 (2010-02-02)
|
18
|
+
* Fix for issue #33 (undefined method 'start_with?').
|
19
|
+
|
20
|
+
== 0.7.3 (2010-01-31)
|
21
|
+
* Added support for Geotrust-style WSDL documents (Julian Kornberger <github.corny@digineo.de>).
|
22
|
+
* Make HTTP requests include path and query only. This was breaking requests via proxy as scheme and host
|
23
|
+
were repeated (Adrian Mugnolo <adrian@mugnolo.com>)
|
24
|
+
* Avoid warning on 1.8.7 and 1.9.1 (Adrian Mugnolo <adrian@mugnolo.com>).
|
25
|
+
* Fix for issue #29 (WSSE Created Bug?). Default to UTC to xs:dateTime value for WSSE authentication.
|
26
|
+
* Fix for issue #28 (Undefined Method ssl? on URI::Generic).
|
27
|
+
* Fix for issue #27 (http content-type defaults to utf-8). The Content-Type now defaults to UTF-8.
|
28
|
+
* Modification to allow assignment of an Array with an input name and an optional Hash of values to soap.input.
|
29
|
+
Patches issue #30 (stanleydrew <andrewmbenton@gmail.com>).
|
30
|
+
* Fix for issue #25 (header-tag should not be sent if not set).
|
31
|
+
|
1
32
|
== 0.7.2 (2010-01-17)
|
2
33
|
* Exposed the Net::HTTP response (added by Kevin Ingolfsland). Use the "http" accessor (response.http) on your
|
3
34
|
Savon::Response to access the Net::HTTP response object.
|
4
|
-
* Fix for
|
5
|
-
* Fix for
|
6
|
-
* Fix for
|
7
|
-
* Added support for global header and namespaces. See
|
35
|
+
* Fix for issue #21 (savon is stripping ?SOAP off the end of WSDL locations).
|
36
|
+
* Fix for issue #22 (REXML::ParseException parsing 401 Unauthorized response body).
|
37
|
+
* Fix for issue #19 (Unable to set attribute in name-spaced WSSE password element).
|
38
|
+
* Added support for global header and namespaces. See issue #9 (Setting headers and namespaces).
|
8
39
|
|
9
40
|
== 0.7.1 (2010-01-10)
|
10
41
|
* The Hash of HTTP headers for SOAP calls is now public via Savon::Request#headers.
|
@@ -122,3 +153,4 @@
|
|
122
153
|
|
123
154
|
== 0.5.0 (2009-11-29)
|
124
155
|
* Complete rewrite.
|
156
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
= Savon
|
2
|
+
|
3
|
+
==== Heavy metal Ruby SOAP client library
|
4
|
+
|
5
|
+
{Documentation}[http://savon.rubiii.com] | {Metrics}[http://getcaliper.com/caliper/project?repo=git://github.com/rubiii/savon.git]
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
$ gem install savon
|
10
|
+
|
11
|
+
Savon expects you to be familiar with SOAP, WSDL and tools like soapUI.
|
12
|
+
|
13
|
+
== Instantiate a client
|
14
|
+
|
15
|
+
Instantiate Savon::Client, passing in the WSDL of your service.
|
16
|
+
|
17
|
+
client = Savon::Client.new "http://example.com/UserService?wsdl"
|
18
|
+
|
19
|
+
For production, it is highly recommended to not use Savon::WSDL. Information on {how to disable the WSDL}[http://savon.rubiii.com/docs/latest/classes/Savon/WSDL.html].
|
20
|
+
|
21
|
+
== Choose authentication methods
|
22
|
+
|
23
|
+
After the initialization of the client you can choose an authentication method to the requests.
|
24
|
+
|
25
|
+
For Basic HTTP authentication:
|
26
|
+
|
27
|
+
client.request.basic_auth("username", "password")
|
28
|
+
|
29
|
+
For NTLM HTTP authentication:
|
30
|
+
|
31
|
+
client.request.ntlm_auth("username", "password")
|
32
|
+
|
33
|
+
Choose {Basic}[http://en.wikipedia.org/wiki/Basic_access_authentication] or {NTLM}[http://en.wikipedia.org/wiki/NTLM] according your needs.
|
34
|
+
|
35
|
+
== Calling a SOAP action
|
36
|
+
|
37
|
+
Assuming your service applies to the defaults, you can now call any available SOAP action.
|
38
|
+
|
39
|
+
response = client.get_all_users
|
40
|
+
|
41
|
+
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.
|
42
|
+
|
43
|
+
== The WSDL object
|
44
|
+
|
45
|
+
Savon::WSDL represents the WSDL of your service, including information like the namespace URI and available SOAP actions.
|
46
|
+
|
47
|
+
client.wsdl.soap_actions
|
48
|
+
=> [:get_all_users, :get_user_by_id, :user_magic]
|
49
|
+
|
50
|
+
== The SOAP object
|
51
|
+
|
52
|
+
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.
|
53
|
+
|
54
|
+
response = client.get_user_by_id { |soap| soap.body = { :id => 666 } }
|
55
|
+
|
56
|
+
== The WSSE object
|
57
|
+
|
58
|
+
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.
|
59
|
+
|
60
|
+
response = client.get_user_by_id do |soap, wsse|
|
61
|
+
wsse.username = "gorilla"
|
62
|
+
wsse.password = "secret"
|
63
|
+
soap.body = { :id => 666 }
|
64
|
+
end
|
65
|
+
|
66
|
+
== The Response object
|
67
|
+
|
68
|
+
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.
|
69
|
+
|
70
|
+
== HTTP errors and SOAP faults
|
71
|
+
|
72
|
+
Savon raises a Savon::SOAPFault in case of a SOAP fault and a Savon::HTTPError in case of an HTTP error.
|
73
|
+
More information: {Errors}[http://savon.rubiii.com/docs/latest/classes/Savon/Response.html]
|
74
|
+
|
75
|
+
== Logging
|
76
|
+
|
77
|
+
Savon logs each request and response to STDOUT. But there are a couple of options to change the default behavior.
|
78
|
+
More information: {Logging}[http://savon.rubiii.com/docs/latest/classes/Savon/Request.html]
|
data/Rakefile
CHANGED
@@ -1,16 +1,14 @@
|
|
1
1
|
require "rake"
|
2
2
|
require "spec/rake/spectask"
|
3
3
|
require "spec/rake/verify_rcov"
|
4
|
-
require "rake/rdoctask"
|
5
4
|
|
6
|
-
task :default => :
|
5
|
+
task :default => :spec
|
7
6
|
|
8
7
|
Spec::Rake::SpecTask.new do |spec|
|
9
8
|
spec.spec_files = FileList["spec/{savon}/**/*_spec.rb"]
|
10
9
|
spec.spec_opts << "--color"
|
11
10
|
spec.libs += ["lib", "spec"]
|
12
11
|
spec.rcov = true
|
13
|
-
spec.rcov_dir = "rcov"
|
14
12
|
end
|
15
13
|
|
16
14
|
RCov::VerifyTask.new(:spec_verify => :spec) do |verify|
|
@@ -30,16 +28,24 @@ task :spec_integration do
|
|
30
28
|
end
|
31
29
|
end
|
32
30
|
|
33
|
-
desc "" # make this task invisible
|
31
|
+
desc "" # make this task invisible
|
34
32
|
Spec::Rake::SpecTask.new(:run_integration_spec) do |spec|
|
35
33
|
spec.spec_files = FileList["spec/{integration}/**/*_spec.rb"]
|
36
34
|
spec.spec_opts << "--color"
|
37
35
|
spec.libs += ["lib", "spec"]
|
38
36
|
end
|
39
37
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
38
|
+
begin
|
39
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "hanna", "lib")
|
40
|
+
require "hanna/rdoctask"
|
41
|
+
|
42
|
+
Rake::RDocTask.new do |rdoc|
|
43
|
+
rdoc.title = "Savon - Heavy metal Ruby SOAP client library"
|
44
|
+
rdoc.rdoc_dir = "doc"
|
45
|
+
rdoc.rdoc_files.include("**/*.rdoc").include("lib/**/*.rb")
|
46
|
+
rdoc.options << "--line-numbers"
|
47
|
+
rdoc.options << "--webcvs=http://github.com/rubiii/savon/tree/master/"
|
48
|
+
end
|
49
|
+
rescue LoadError
|
50
|
+
puts "'gem install hanna' for documentation"
|
45
51
|
end
|
data/lib/savon.rb
CHANGED
@@ -1,14 +1,5 @@
|
|
1
1
|
module Savon
|
2
2
|
|
3
|
-
# Supported SOAP versions.
|
4
|
-
SOAPVersions = [1, 2]
|
5
|
-
|
6
|
-
# SOAP xs:dateTime format.
|
7
|
-
SOAPDateTimeFormat = "%Y-%m-%dT%H:%M:%S"
|
8
|
-
|
9
|
-
# SOAP xs:dateTime Regexp.
|
10
|
-
SOAPDateTimeRegexp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/
|
11
|
-
|
12
3
|
# Raised in case of an HTTP error.
|
13
4
|
class HTTPError < StandardError; end
|
14
5
|
|
@@ -18,17 +9,24 @@ module Savon
|
|
18
9
|
end
|
19
10
|
|
20
11
|
# standard libs
|
21
|
-
|
22
|
-
|
23
|
-
|
12
|
+
require "logger"
|
13
|
+
require "net/https"
|
14
|
+
require "base64"
|
15
|
+
require "digest/sha1"
|
16
|
+
require "rexml/document"
|
24
17
|
|
25
|
-
#
|
26
|
-
require "
|
27
|
-
|
28
|
-
|
29
|
-
end
|
18
|
+
# gem dependencies
|
19
|
+
require "builder"
|
20
|
+
require "crack/xml"
|
21
|
+
require 'net/ntlm_http'
|
30
22
|
|
31
23
|
# core files
|
32
|
-
|
33
|
-
|
34
|
-
|
24
|
+
require "savon/core_ext"
|
25
|
+
require "savon/wsse"
|
26
|
+
require "savon/soap"
|
27
|
+
require "savon/logger"
|
28
|
+
require "savon/request"
|
29
|
+
require "savon/response"
|
30
|
+
require "savon/wsdl_stream"
|
31
|
+
require "savon/wsdl"
|
32
|
+
require "savon/client"
|
data/lib/savon/client.rb
CHANGED
@@ -1,13 +1,47 @@
|
|
1
1
|
module Savon
|
2
2
|
|
3
|
-
#
|
3
|
+
# = Savon::Client
|
4
4
|
#
|
5
|
-
#
|
6
|
-
#
|
5
|
+
# Savon::Client is the main object for connecting to a SOAP service. It includes methods to access
|
6
|
+
# both the Savon::WSDL and Savon::Request object.
|
7
|
+
#
|
8
|
+
# == Instantiation
|
9
|
+
#
|
10
|
+
# Depending on whether you aim to use Savon with or without Savon::WSDL, you need to instantiate
|
11
|
+
# Savon::Client by passing in the WSDL or SOAP endpoint.
|
12
|
+
#
|
13
|
+
# Client instance with a WSDL endpoint:
|
14
|
+
#
|
15
|
+
# client = Savon::Client.new "http://example.com/UserService?wsdl"
|
16
|
+
#
|
17
|
+
# Client instance with a SOAP endpoint (for using Savon without a WSDL):
|
18
|
+
#
|
19
|
+
# client = Savon::Client.new "http://example.com/UserService"
|
20
|
+
#
|
21
|
+
# It is recommended to not use Savon::WSDL for production. Please take a look at the Documentation
|
22
|
+
# for Savon::WSDL for more information about how to disable it.
|
23
|
+
#
|
24
|
+
# == Using a proxy server
|
25
|
+
#
|
26
|
+
# You can specify the URI to a proxy server via optional hash arguments.
|
27
|
+
#
|
28
|
+
# client = Savon::Client.new "http://example.com/UserService?wsdl", :proxy => "http://proxy.example.com"
|
29
|
+
#
|
30
|
+
# == Savon::WSDL
|
31
|
+
#
|
32
|
+
# You can access Savon::WSDL via:
|
33
|
+
#
|
34
|
+
# client.wsdl
|
35
|
+
#
|
36
|
+
# == Savon::Request
|
37
|
+
#
|
38
|
+
# You can also access Savon::Request via:
|
39
|
+
#
|
40
|
+
# client.request
|
7
41
|
class Client
|
8
42
|
|
9
|
-
# Expects a SOAP +endpoint+
|
10
|
-
#
|
43
|
+
# Expects a SOAP +endpoint+ string. Also accepts an optional hash of +options+ for specifying
|
44
|
+
# a +:proxy+ server to use.
|
11
45
|
def initialize(endpoint, options = {})
|
12
46
|
@request = Request.new endpoint, options
|
13
47
|
@wsdl = WSDL.new @request
|
@@ -25,34 +59,30 @@ module Savon
|
|
25
59
|
super
|
26
60
|
end
|
27
61
|
|
62
|
+
# Same as method_missing. Workaround for SOAP actions that method_missing does not catch
|
63
|
+
# because the method does exist.
|
64
|
+
def call(method, *args, &block)
|
65
|
+
method_missing method, *args, &block
|
66
|
+
end
|
67
|
+
|
28
68
|
private
|
29
69
|
|
30
70
|
# Dispatches requests to SOAP actions matching a given +method+ name.
|
31
71
|
def method_missing(method, *args, &block) #:doc:
|
32
|
-
|
33
|
-
super
|
72
|
+
soap_action = soap_action_from method.to_s
|
73
|
+
super unless @wsdl.respond_to? soap_action
|
34
74
|
|
35
|
-
setup_objects operation_from(
|
75
|
+
setup_objects *@wsdl.operation_from(soap_action), &block
|
36
76
|
Response.new @request.soap(@soap)
|
37
77
|
end
|
38
78
|
|
39
|
-
# Sets whether to use Savon::WSDL by a given +method+ name and
|
40
|
-
#
|
41
|
-
def
|
42
|
-
|
43
|
-
@wsdl.enabled = false
|
44
|
-
method[0, method.length-1].to_sym
|
45
|
-
else
|
46
|
-
@wsdl.enabled = true
|
47
|
-
method.to_sym
|
48
|
-
end
|
49
|
-
end
|
79
|
+
# Sets whether to use Savon::WSDL by a given +method+ name and returns the original method name
|
80
|
+
# without exclamation marks.
|
81
|
+
def soap_action_from(method)
|
82
|
+
@wsdl.enabled = !method.ends_with?("!")
|
50
83
|
|
51
|
-
|
52
|
-
|
53
|
-
def operation_from(soap_call)
|
54
|
-
return @wsdl.operations[soap_call] if @wsdl.enabled?
|
55
|
-
{ :action => soap_call.to_soap_key, :input => soap_call.to_soap_key }
|
84
|
+
method.chop! if method.ends_with?("!")
|
85
|
+
method.to_sym
|
56
86
|
end
|
57
87
|
|
58
88
|
# Returns the SOAP endpoint.
|
@@ -60,19 +90,17 @@ module Savon
|
|
60
90
|
@wsdl.enabled? ? @wsdl.soap_endpoint : @request.endpoint
|
61
91
|
end
|
62
92
|
|
63
|
-
# Expects a SOAP operation Hash and sets up Savon::SOAP and Savon::WSSE.
|
64
|
-
#
|
65
|
-
def setup_objects(
|
66
|
-
@soap, @wsse = SOAP.new, WSSE.new
|
67
|
-
@soap.action, @soap.input, @soap.endpoint = operation[:action], operation[:input], soap_endpoint
|
68
|
-
|
93
|
+
# Expects a SOAP operation Hash and sets up Savon::SOAP and Savon::WSSE. Yields them to a given
|
94
|
+
# +block+ in case one was given.
|
95
|
+
def setup_objects(action, input, &block)
|
96
|
+
@soap, @wsse = SOAP.new(action, input, soap_endpoint), WSSE.new
|
69
97
|
yield_objects &block if block
|
70
|
-
|
71
98
|
@soap.namespaces["xmlns:wsdl"] ||= @wsdl.namespace_uri if @wsdl.enabled?
|
72
99
|
@soap.wsse = @wsse
|
73
100
|
end
|
74
101
|
|
75
|
-
# Yields Savon::SOAP and Savon::WSSE to a given +block
|
102
|
+
# Yields either Savon::SOAP or Savon::SOAP and Savon::WSSE to a given +block+, depending on
|
103
|
+
# the number of arguments expected by the block.
|
76
104
|
def yield_objects(&block)
|
77
105
|
case block.arity
|
78
106
|
when 1 then yield @soap
|
data/lib/savon/core_ext.rb
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require "savon/core_ext/object"
|
2
|
+
require "savon/core_ext/string"
|
3
|
+
require "savon/core_ext/symbol"
|
4
|
+
require "savon/core_ext/datetime"
|
5
|
+
require "savon/core_ext/array"
|
6
|
+
require "savon/core_ext/hash"
|
7
|
+
require "savon/core_ext/uri"
|
8
|
+
require "savon/core_ext/net_http"
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Array
|
2
|
+
|
3
|
+
# Translates the Array into SOAP compatible XML. See: Hash.to_soap_xml.
|
4
|
+
def to_soap_xml(key, attributes = {})
|
5
|
+
xml = Builder::XmlMarkup.new
|
6
|
+
|
7
|
+
each_with_index do |item, index|
|
8
|
+
attrs = tag_attributes attributes, index
|
9
|
+
case item
|
10
|
+
when Hash then xml.tag!(key, attrs) { xml << item.to_soap_xml }
|
11
|
+
else xml.tag!(key, attrs) { xml << item.to_soap_value }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
xml.target!
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# Takes a Hash of +attributes+ and the +index+ for which to return attributes
|
21
|
+
# for duplicate tags.
|
22
|
+
def tag_attributes(attributes, index)
|
23
|
+
return {} if attributes.empty?
|
24
|
+
|
25
|
+
attributes.inject({}) do |hash, (key, value)|
|
26
|
+
value = value[index] if value.kind_of? Array
|
27
|
+
hash.merge key => value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/lib/savon/core_ext/hash.rb
CHANGED
@@ -1,78 +1,102 @@
|
|
1
1
|
class Hash
|
2
2
|
|
3
|
-
#
|
4
|
-
|
5
|
-
|
6
|
-
# Error message for spurious :@inorder elements.
|
7
|
-
InOrderSpurious = "Spurious elements in :@inorder %s"
|
8
|
-
|
9
|
-
# Returns the values from the 'soap:Body' element or an empty Hash
|
10
|
-
# in case the node could not be found.
|
3
|
+
# Returns the values from the soap:Body element or an empty Hash in case the soap:Body tag could
|
4
|
+
# not be found.
|
11
5
|
def find_soap_body
|
12
|
-
envelope = self[
|
6
|
+
envelope = self[keys.first] || {}
|
13
7
|
body_key = envelope.keys.find { |key| /.+:Body/ =~ key } rescue nil
|
14
8
|
body_key ? envelope[body_key].map_soap_response : {}
|
15
9
|
end
|
16
10
|
|
17
|
-
#
|
11
|
+
# Translates the Hash into SOAP request compatible XML.
|
12
|
+
#
|
13
|
+
# { :find_user => { :id => 123, "wsdl:Key" => "api" } }.to_soap_xml
|
14
|
+
# # => "<findUser><id>123</id><wsdl:Key>api</wsdl:Key></findUser>"
|
15
|
+
#
|
16
|
+
# ==== Mapping
|
17
|
+
#
|
18
|
+
# * Hash keys specified as Symbols are converted to lowerCamelCase Strings
|
19
|
+
# * Hash keys specified as Strings are not converted and may contain namespaces
|
20
|
+
# * DateTime values are converted to xs:dateTime Strings
|
21
|
+
# * Objects responding to to_datetime (except Strings) are converted to xs:dateTime Strings
|
22
|
+
# * TrueClass and FalseClass objects are converted to "true" and "false" Strings
|
23
|
+
# * All other objects are expected to be converted to Strings using to_s
|
24
|
+
#
|
25
|
+
# An example:
|
26
|
+
#
|
27
|
+
# { :magic_request => {
|
28
|
+
# :perform_move => true,
|
29
|
+
# "perform_at" => DateTime.new(2010, 11, 22, 11, 22, 33)
|
30
|
+
# }
|
31
|
+
# }.to_soap_xml
|
32
|
+
#
|
33
|
+
# <magicRequest>
|
34
|
+
# <performMove>true</performMove>
|
35
|
+
# <perform_at>2012-06-11T10:42:21</perform_at>
|
36
|
+
# </magicRequest>
|
37
|
+
#
|
38
|
+
# ==== :order!
|
39
|
+
#
|
40
|
+
# In case your service requires the tags to be in a specific order (parameterOrder), you have two
|
41
|
+
# options. The first is to specify your body as an XML string. The second is to specify the order
|
42
|
+
# through an additional array stored under the +:order!+ key.
|
18
43
|
#
|
19
|
-
#
|
20
|
-
#
|
44
|
+
# { :name => "Eve", :id => 123, :order! => [:id, :name] }.to_soap_xml
|
45
|
+
# # => "<id>123</id><name>Eve</name>"
|
21
46
|
#
|
22
|
-
#
|
47
|
+
# ==== :attributes!
|
23
48
|
#
|
24
|
-
#
|
25
|
-
#
|
49
|
+
# If you need attributes, you could either go with an XML string or add another hash under the
|
50
|
+
# +:attributes!+ key.
|
26
51
|
#
|
27
|
-
# { :
|
28
|
-
# =>
|
52
|
+
# { :person => "Eve", :attributes! => { :person => { :id => 666 } } }.to_soap_xml
|
53
|
+
# # => '<person id="666">Eve</person>'
|
29
54
|
def to_soap_xml
|
30
|
-
|
31
|
-
|
32
|
-
|
55
|
+
xml = Builder::XmlMarkup.new
|
56
|
+
attributes = delete(:attributes!) || {}
|
57
|
+
|
58
|
+
order.each do |key|
|
59
|
+
attrs = attributes[key] || {}
|
60
|
+
value = self[key]
|
61
|
+
key = key.to_soap_key
|
62
|
+
|
63
|
+
case value
|
64
|
+
when Array then xml << value.to_soap_xml(key, attrs)
|
65
|
+
when Hash then xml.tag!(key, attrs) { xml << value.to_soap_xml }
|
66
|
+
else xml.tag!(key, attrs) { xml << value.to_soap_value }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
xml.target!
|
33
71
|
end
|
34
72
|
|
35
|
-
# Maps keys and values of a Hash created from SOAP response XML to
|
36
|
-
# more convenient Ruby Objects.
|
73
|
+
# Maps keys and values of a Hash created from SOAP response XML to more convenient Ruby Objects.
|
37
74
|
def map_soap_response
|
38
75
|
inject({}) do |hash, (key, value)|
|
39
|
-
key = key.strip_namespace.snakecase.to_sym
|
40
|
-
|
41
76
|
value = case value
|
42
77
|
when Hash then value["xsi:nil"] ? nil : value.map_soap_response
|
43
|
-
when Array then value.map { |
|
78
|
+
when Array then value.map { |val| val.map_soap_response rescue val }
|
44
79
|
when String then value.map_soap_response
|
45
80
|
end
|
46
|
-
|
81
|
+
|
82
|
+
hash.merge key.strip_namespace.snakecase.to_sym => value
|
47
83
|
end
|
48
84
|
end
|
49
85
|
|
50
86
|
private
|
51
87
|
|
52
|
-
#
|
53
|
-
#
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
else
|
63
|
-
@soap_xml.tag!(key.to_soap_key) { @soap_xml << value.to_soap_value }
|
64
|
-
end
|
65
|
-
end
|
88
|
+
# Deletes and returns an Array of keys stored under the :order! key. Defaults to return the actual
|
89
|
+
# keys of this Hash if no :order! key could be found. Raises an ArgumentError in case the :order!
|
90
|
+
# Array does not match the Hash keys.
|
91
|
+
def order
|
92
|
+
order = delete :order!
|
93
|
+
order = keys unless order.kind_of? Array
|
94
|
+
|
95
|
+
missing, spurious = keys - order, order - keys
|
96
|
+
raise ArgumentError, "Missing elements in :order! #{missing.inspect}" unless missing.empty?
|
97
|
+
raise ArgumentError, "Spurious elements in :order! #{spurious.inspect}" unless spurious.empty?
|
66
98
|
|
67
|
-
|
68
|
-
# Raises an error in case an :@inorder Array does not match the Hash keys.
|
69
|
-
def inorder(hash)
|
70
|
-
inorder = hash.delete :@inorder
|
71
|
-
hash_keys = hash.keys
|
72
|
-
inorder = hash_keys unless inorder.kind_of? Array
|
73
|
-
raise InOrderMissing % (hash_keys - inorder).inspect unless (hash_keys - inorder).empty?
|
74
|
-
raise InOrderSpurious % (inorder - hash_keys).inspect unless (inorder - hash_keys).empty?
|
75
|
-
inorder
|
99
|
+
order
|
76
100
|
end
|
77
101
|
|
78
|
-
end
|
102
|
+
end
|