savon 0.9.2 → 0.9.3
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/.gitignore +1 -0
- data/.travis.yml +2 -1
- data/CHANGELOG.md +35 -1
- data/README.md +6 -5
- data/Rakefile +3 -34
- data/lib/savon/client.rb +1 -1
- data/lib/savon/core_ext/object.rb +1 -1
- data/lib/savon/core_ext/string.rb +2 -15
- data/lib/savon/global.rb +28 -2
- data/lib/savon/soap.rb +0 -3
- data/lib/savon/soap/fault.rb +1 -1
- data/lib/savon/soap/request.rb +6 -0
- data/lib/savon/soap/response.rb +21 -12
- data/lib/savon/soap/xml.rb +7 -29
- data/lib/savon/version.rb +1 -1
- data/lib/savon/wsdl/document.rb +1 -1
- data/lib/savon/wsdl/request.rb +6 -0
- data/lib/savon/wsse.rb +16 -16
- data/savon.gemspec +6 -5
- data/spec/savon/core_ext/string_spec.rb +24 -31
- data/spec/savon/soap/request_spec.rb +14 -6
- data/spec/savon/soap/response_spec.rb +45 -15
- data/spec/savon/soap/xml_spec.rb +5 -58
- data/spec/savon/soap_spec.rb +0 -5
- data/spec/savon/wsdl/request_spec.rb +9 -1
- data/spec/savon/wsse_spec.rb +3 -2
- data/spec/spec_helper.rb +5 -4
- data/spec/support/fixture.rb +3 -5
- metadata +24 -71
- data/lib/savon/core_ext/hash.rb +0 -70
- data/spec/savon/core_ext/hash_spec.rb +0 -121
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,40 @@
|
|
1
|
+
## 0.9.3 (2011-06-30)
|
2
|
+
|
3
|
+
* Fix: [issue 138](https://github.com/rubiii/savon/issues/138) -
|
4
|
+
Savon now supports setting a global SOAP header via `Savon.soap_header=`.
|
5
|
+
|
6
|
+
* Fixed the namespace for wsse message timestamps from `wsse:Timestamp`
|
7
|
+
to `wsu:Timestamp` as required by the specification.
|
8
|
+
|
9
|
+
* Change: Removed support for NTLM authentication until it's stable. If you need it, you can still
|
10
|
+
add the following line to your Gemfile:
|
11
|
+
|
12
|
+
gem "httpi", "0.9.4"
|
13
|
+
|
14
|
+
* Refactoring:
|
15
|
+
|
16
|
+
* `Hash#map_soap_response` and some of its helpers are moved to [Nori v1.0.0](http://rubygems.org/gems/nori/versions/1.0.0).
|
17
|
+
Along with replacing core extensions with a proper implementation, Nori now contains a number of methods
|
18
|
+
for configuring its default behavior:
|
19
|
+
|
20
|
+
* The option whether to strip namespaces was moved to Nori.strip_namespaces
|
21
|
+
* You can disable "advanced typecasting" for SOAP response values
|
22
|
+
* And you can configure how SOAP response keys should be converted
|
23
|
+
|
24
|
+
Please take a look at [Nori's CHANGELOG](https://github.com/rubiii/nori/blob/master/CHANGELOG.md)
|
25
|
+
for detailed information.
|
26
|
+
|
27
|
+
* `Savon::SOAP::XML.to_hash`, `Savon::SOAP::XML.parse` and `Savon::SOAP::XML.to_array` are gone.
|
28
|
+
It wasn't worth keeping them around, because they didn't do much. You can simply parse a SOAP
|
29
|
+
response and translate it to a Savon SOAP response Hash via:
|
30
|
+
|
31
|
+
Nori.parse(xml)[:envelope][:body]
|
32
|
+
|
33
|
+
* `Savon::SOAP::Response#basic_hash` is now `Savon::SOAP::Response#hash`.
|
34
|
+
|
1
35
|
## 0.9.2 (2011-04-30)
|
2
36
|
|
3
|
-
* Fix: [issue](https://github.com/rubiii/savon/pull/154) -
|
37
|
+
* Fix: [issue 154](https://github.com/rubiii/savon/pull/154) -
|
4
38
|
Timezone format used by Savon now matches the XML schema spec.
|
5
39
|
|
6
40
|
* Improvement: WSSE basic, digest and timestamp authentication are no longer mutually exclusive.
|
data/README.md
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
-
Savon
|
1
|
+
Savon [](http://travis-ci.org/rubiii/savon)
|
2
2
|
=====
|
3
3
|
|
4
4
|
Heavy metal Ruby SOAP client
|
5
5
|
|
6
|
-
[
|
6
|
+
[Documentation](http://savonrb.com) | [RDoc](http://rubydoc.info/gems/savon) |
|
7
|
+
[Mailing list](http://groups.google.com/group/savon-soap) | [Twitter](http://twitter.com/savonrb)
|
7
8
|
|
8
9
|
Installation
|
9
10
|
------------
|
@@ -33,7 +34,7 @@ response.to_hash
|
|
33
34
|
# => { :get_user_response => { :first_name => "The", :last_name => "Hoff" } }
|
34
35
|
```
|
35
36
|
|
36
|
-
|
37
|
-
|
37
|
+
Ready for more?
|
38
|
+
---------------
|
38
39
|
|
39
|
-
|
40
|
+
[Go ahead and read the official documentation](http://savonrb.com).
|
data/Rakefile
CHANGED
@@ -1,39 +1,8 @@
|
|
1
1
|
require "rake"
|
2
|
+
require "rspec/core/rake_task"
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
YARD::Rake::YardocTask.new do |t|
|
7
|
-
t.files = ["README.md", "lib/**/*.rb"]
|
8
|
-
end
|
9
|
-
rescue LoadError
|
10
|
-
desc message = %{run "bundle install" to generate documentation}
|
11
|
-
task("yard") { abort message }
|
12
|
-
end
|
13
|
-
|
14
|
-
begin
|
15
|
-
require "metric_fu"
|
16
|
-
|
17
|
-
MetricFu::Configuration.run do |c|
|
18
|
-
c.metrics = [:churn, :flog, :flay, :reek, :roodi, :saikuro] # :rcov seems to be broken
|
19
|
-
c.graphs = [:flog, :flay, :reek, :roodi]
|
20
|
-
c.flay = { :dirs_to_flay => ["lib"], :minimum_score => 20 }
|
21
|
-
c.rcov[:rcov_opts] << "-Ilib -Ispec"
|
22
|
-
end
|
23
|
-
rescue LoadError
|
24
|
-
desc message = %{run "bundle install" to generate metrics}
|
25
|
-
task("metrics:all") { abort message }
|
26
|
-
end
|
27
|
-
|
28
|
-
begin
|
29
|
-
require "rspec/core/rake_task"
|
30
|
-
|
31
|
-
RSpec::Core::RakeTask.new do |t|
|
32
|
-
t.rspec_opts = %w(-c)
|
33
|
-
end
|
34
|
-
rescue LoadError
|
35
|
-
desc message = %{run "bundle install" to run the specs}
|
36
|
-
task(:spec) { abort message }
|
4
|
+
RSpec::Core::RakeTask.new do |t|
|
5
|
+
t.rspec_opts = %w(-c)
|
37
6
|
end
|
38
7
|
|
39
8
|
task :default => :spec
|
data/lib/savon/client.rb
CHANGED
@@ -113,7 +113,7 @@ module Savon
|
|
113
113
|
soap.namespace_identifier = options[0]
|
114
114
|
soap.namespace = wsdl.namespace
|
115
115
|
soap.element_form_default = wsdl.element_form_default if wsdl.present?
|
116
|
-
soap.body = options[2].delete(:body)
|
116
|
+
soap.body = options[2].delete(:body)
|
117
117
|
|
118
118
|
set_soap_action options[1]
|
119
119
|
set_soap_input *options
|
@@ -14,7 +14,7 @@ module Savon
|
|
14
14
|
str.tr! "-", "_"
|
15
15
|
str.downcase!
|
16
16
|
str
|
17
|
-
end
|
17
|
+
end unless method_defined?(:snakecase)
|
18
18
|
|
19
19
|
# Returns the String in lowerCamelCase.
|
20
20
|
def lower_camelcase
|
@@ -29,20 +29,7 @@ module Savon
|
|
29
29
|
def starts_with?(prefix)
|
30
30
|
prefix = prefix.to_s
|
31
31
|
self[0, prefix.length] == prefix
|
32
|
-
end unless
|
33
|
-
|
34
|
-
# Returns the String without namespace.
|
35
|
-
def strip_namespace
|
36
|
-
split(":").last
|
37
|
-
end
|
38
|
-
|
39
|
-
# Translates SOAP response values to Ruby Objects.
|
40
|
-
def map_soap_response
|
41
|
-
return ::DateTime.parse(self) if Savon::SOAP::DateTimeRegexp === self
|
42
|
-
return true if self.strip.downcase == "true"
|
43
|
-
return false if self.strip.downcase == "false"
|
44
|
-
self
|
45
|
-
end
|
32
|
+
end unless method_defined?(:starts_with?)
|
46
33
|
|
47
34
|
end
|
48
35
|
end
|
data/lib/savon/global.rb
CHANGED
@@ -55,11 +55,15 @@ module Savon
|
|
55
55
|
# Returns whether to strip namespaces in a SOAP response Hash.
|
56
56
|
# Defaults to +true+.
|
57
57
|
def strip_namespaces?
|
58
|
-
|
58
|
+
Savon.deprecate("use Nori.strip_namespaces? instead of Savon.strip_namespaces?")
|
59
|
+
Nori.strip_namespaces?
|
59
60
|
end
|
60
61
|
|
61
62
|
# Sets whether to strip namespaces in a SOAP response Hash.
|
62
|
-
|
63
|
+
def strip_namespaces=(strip)
|
64
|
+
Savon.deprecate("use Nori.strip_namespaces= instead of Savon.strip_namespaces=")
|
65
|
+
Nori.strip_namespaces = strip
|
66
|
+
end
|
63
67
|
|
64
68
|
# Returns the global env_namespace.
|
65
69
|
attr_reader :env_namespace
|
@@ -67,6 +71,27 @@ module Savon
|
|
67
71
|
# Sets the global env_namespace.
|
68
72
|
attr_writer :env_namespace
|
69
73
|
|
74
|
+
# Returns the global soap_header.
|
75
|
+
attr_reader :soap_header
|
76
|
+
|
77
|
+
# Sets the global soap_header.
|
78
|
+
attr_writer :soap_header
|
79
|
+
|
80
|
+
# Expects a +message+ and raises a warning if configured.
|
81
|
+
def deprecate(message)
|
82
|
+
warn("Deprecation: #{message}") if deprecate?
|
83
|
+
end
|
84
|
+
|
85
|
+
# Sets whether to warn about deprecations.
|
86
|
+
def deprecate=(deprecate)
|
87
|
+
@deprecate = deprecate
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns whether to warn about deprecation.
|
91
|
+
def deprecate?
|
92
|
+
@deprecate != false
|
93
|
+
end
|
94
|
+
|
70
95
|
# Reset to default configuration.
|
71
96
|
def reset_config!
|
72
97
|
self.log = true
|
@@ -76,6 +101,7 @@ module Savon
|
|
76
101
|
self.soap_version = SOAP::DefaultVersion
|
77
102
|
self.strip_namespaces = true
|
78
103
|
self.env_namespace = nil
|
104
|
+
self.soap_header = {}
|
79
105
|
end
|
80
106
|
|
81
107
|
end
|
data/lib/savon/soap.rb
CHANGED
data/lib/savon/soap/fault.rb
CHANGED
data/lib/savon/soap/request.rb
CHANGED
@@ -12,6 +12,12 @@ module Savon
|
|
12
12
|
# Content-Types by SOAP version.
|
13
13
|
ContentType = { 1 => "text/xml;charset=UTF-8", 2 => "application/soap+xml;charset=UTF-8" }
|
14
14
|
|
15
|
+
# Expects an <tt>HTTPI::Request</tt> and a <tt>Savon::SOAP::XML</tt> object
|
16
|
+
# to execute a SOAP request and returns the response.
|
17
|
+
def self.execute(request, soap)
|
18
|
+
new(request, soap).response
|
19
|
+
end
|
20
|
+
|
15
21
|
# Expects an <tt>HTTPI::Request</tt> and a <tt>Savon::SOAP::XML</tt> object.
|
16
22
|
def initialize(request, soap)
|
17
23
|
self.request = setup(request, soap)
|
data/lib/savon/soap/response.rb
CHANGED
@@ -43,29 +43,38 @@ module Savon
|
|
43
43
|
@http_error ||= HTTP::Error.new http
|
44
44
|
end
|
45
45
|
|
46
|
-
#
|
47
|
-
def
|
48
|
-
|
46
|
+
# Shortcut accessor for the SOAP response body Hash.
|
47
|
+
def [](key)
|
48
|
+
body[key]
|
49
49
|
end
|
50
50
|
|
51
|
-
#
|
52
|
-
def
|
53
|
-
|
51
|
+
# Returns the SOAP response header as a Hash.
|
52
|
+
def header
|
53
|
+
hash[:envelope][:header]
|
54
54
|
end
|
55
55
|
|
56
56
|
# Returns the SOAP response body as a Hash.
|
57
|
-
def
|
58
|
-
|
57
|
+
def body
|
58
|
+
hash[:envelope][:body]
|
59
59
|
end
|
60
60
|
|
61
|
-
|
61
|
+
alias to_hash body
|
62
|
+
|
63
|
+
# Traverses the SOAP response body Hash for a given +path+ of Hash keys and returns
|
64
|
+
# the value as an Array. Defaults to return an empty Array in case the path does not
|
65
|
+
# exist or returns nil.
|
62
66
|
def to_array(*path)
|
63
|
-
|
67
|
+
result = path.inject body do |memo, key|
|
68
|
+
return [] unless memo[key]
|
69
|
+
memo[key]
|
70
|
+
end
|
71
|
+
|
72
|
+
result.kind_of?(Array) ? result.compact : [result].compact
|
64
73
|
end
|
65
74
|
|
66
75
|
# Returns the complete SOAP response XML without normalization.
|
67
|
-
def
|
68
|
-
@
|
76
|
+
def hash
|
77
|
+
@hash ||= Nori.parse http.body
|
69
78
|
end
|
70
79
|
|
71
80
|
# Returns the SOAP response XML.
|
data/lib/savon/soap/xml.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
require "builder"
|
2
|
-
require "nori"
|
3
2
|
require "gyoku"
|
3
|
+
require "nori"
|
4
4
|
|
5
5
|
require "savon/soap"
|
6
|
-
|
6
|
+
|
7
|
+
Nori.configure do |config|
|
8
|
+
config.strip_namespaces = true
|
9
|
+
config.convert_tags_to { |tag| tag.snakecase.to_sym }
|
10
|
+
end
|
7
11
|
|
8
12
|
module Savon
|
9
13
|
module SOAP
|
@@ -20,31 +24,6 @@ module Savon
|
|
20
24
|
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance"
|
21
25
|
}
|
22
26
|
|
23
|
-
# Converts the given SOAP response +value+ (XML or Hash) into a normalized Hash.
|
24
|
-
def self.to_hash(value)
|
25
|
-
value = parse value unless value.kind_of? Hash
|
26
|
-
value.find_soap_body
|
27
|
-
end
|
28
|
-
|
29
|
-
# Converts a given SOAP response +xml+ to a Hash.
|
30
|
-
def self.parse(xml)
|
31
|
-
Nori.parse(xml)
|
32
|
-
end
|
33
|
-
|
34
|
-
# Expects a SOAP response XML or Hash, traverses it for a given +path+ of Hash keys
|
35
|
-
# and returns the value as an Array. Defaults to return an empty Array in case the
|
36
|
-
# path does not exist or returns nil.
|
37
|
-
def self.to_array(object, *path)
|
38
|
-
hash = object.kind_of?(Hash) ? object : to_hash(object)
|
39
|
-
|
40
|
-
result = path.inject hash do |memo, key|
|
41
|
-
return [] unless memo[key]
|
42
|
-
memo[key]
|
43
|
-
end
|
44
|
-
|
45
|
-
result.kind_of?(Array) ? result.compact : [result].compact
|
46
|
-
end
|
47
|
-
|
48
27
|
# Accepts an +endpoint+, an +input+ tag and a SOAP +body+.
|
49
28
|
def initialize(endpoint = nil, input = nil, body = nil)
|
50
29
|
self.endpoint = endpoint if endpoint
|
@@ -74,7 +53,7 @@ module Savon
|
|
74
53
|
|
75
54
|
# Returns the SOAP +header+. Defaults to an empty Hash.
|
76
55
|
def header
|
77
|
-
@header ||= {}
|
56
|
+
@header ||= Savon.soap_header.nil? ? {} : Savon.soap_header
|
78
57
|
end
|
79
58
|
|
80
59
|
# Sets the SOAP envelope namespace.
|
@@ -181,4 +160,3 @@ module Savon
|
|
181
160
|
end
|
182
161
|
end
|
183
162
|
end
|
184
|
-
|
data/lib/savon/version.rb
CHANGED
data/lib/savon/wsdl/document.rb
CHANGED
data/lib/savon/wsdl/request.rb
CHANGED
@@ -8,6 +8,12 @@ module Savon
|
|
8
8
|
# Executes WSDL requests.
|
9
9
|
class Request
|
10
10
|
|
11
|
+
# Expects an <tt>HTTPI::Request</tt> to execute a WSDL request
|
12
|
+
# and returns the response.
|
13
|
+
def self.execute(request)
|
14
|
+
new(request).response
|
15
|
+
end
|
16
|
+
|
11
17
|
# Expects an <tt>HTTPI::Request</tt>.
|
12
18
|
def initialize(request)
|
13
19
|
self.request = request
|
data/lib/savon/wsse.rb
CHANGED
@@ -2,7 +2,6 @@ require "base64"
|
|
2
2
|
require "digest/sha1"
|
3
3
|
|
4
4
|
require "savon/core_ext/string"
|
5
|
-
require "savon/core_ext/hash"
|
6
5
|
require "savon/core_ext/time"
|
7
6
|
|
8
7
|
module Savon
|
@@ -56,20 +55,20 @@ module Savon
|
|
56
55
|
username && password
|
57
56
|
end
|
58
57
|
|
59
|
-
# Returns whether to generate a
|
58
|
+
# Returns whether to generate a wsu:Timestamp header.
|
60
59
|
def timestamp?
|
61
|
-
created_at || expires_at || @
|
60
|
+
created_at || expires_at || @wsu_timestamp
|
62
61
|
end
|
63
62
|
|
64
|
-
# Sets whether to generate a
|
63
|
+
# Sets whether to generate a wsu:Timestamp header.
|
65
64
|
def timestamp=(timestamp)
|
66
|
-
@
|
65
|
+
@wsu_timestamp = timestamp
|
67
66
|
end
|
68
67
|
|
69
68
|
# Returns the XML for a WSSE header.
|
70
69
|
def to_xml
|
71
70
|
if username_token? && timestamp?
|
72
|
-
Gyoku.xml wsse_username_token.merge!(
|
71
|
+
Gyoku.xml wsse_username_token.merge!(wsu_timestamp) {
|
73
72
|
|key, v1, v2| v1.merge!(v2) {
|
74
73
|
|key, v1, v2| v1.merge!(v2)
|
75
74
|
}
|
@@ -77,7 +76,7 @@ module Savon
|
|
77
76
|
elsif username_token?
|
78
77
|
Gyoku.xml wsse_username_token.merge!(hash)
|
79
78
|
elsif timestamp?
|
80
|
-
Gyoku.xml
|
79
|
+
Gyoku.xml wsu_timestamp.merge!(hash)
|
81
80
|
else
|
82
81
|
""
|
83
82
|
end
|
@@ -88,33 +87,34 @@ module Savon
|
|
88
87
|
# Returns a Hash containing wsse:UsernameToken details.
|
89
88
|
def wsse_username_token
|
90
89
|
if digest?
|
91
|
-
|
90
|
+
security_hash :wsse, "UsernameToken",
|
92
91
|
"wsse:Username" => username,
|
93
92
|
"wsse:Nonce" => nonce,
|
94
93
|
"wsu:Created" => timestamp,
|
95
94
|
"wsse:Password" => digest_password,
|
96
95
|
:attributes! => { "wsse:Password" => { "Type" => PasswordDigestURI } }
|
97
96
|
else
|
98
|
-
|
97
|
+
security_hash :wsse, "UsernameToken",
|
99
98
|
"wsse:Username" => username,
|
100
99
|
"wsse:Password" => password,
|
101
100
|
:attributes! => { "wsse:Password" => { "Type" => PasswordTextURI } }
|
102
101
|
end
|
103
102
|
end
|
104
103
|
|
105
|
-
# Returns a Hash containing
|
106
|
-
def
|
107
|
-
|
104
|
+
# Returns a Hash containing wsu:Timestamp details.
|
105
|
+
def wsu_timestamp
|
106
|
+
security_hash :wsu, "Timestamp",
|
108
107
|
"wsu:Created" => (created_at || Time.now).xs_datetime,
|
109
108
|
"wsu:Expires" => (expires_at || (created_at || Time.now) + 60).xs_datetime
|
110
109
|
end
|
111
110
|
|
112
|
-
# Returns a Hash containing wsse
|
113
|
-
|
111
|
+
# Returns a Hash containing wsse/wsu Security details for a given
|
112
|
+
# +namespace+, +tag+ and +hash+.
|
113
|
+
def security_hash(namespace, tag, hash)
|
114
114
|
{
|
115
115
|
"wsse:Security" => {
|
116
|
-
"
|
117
|
-
:attributes! => { "
|
116
|
+
"#{namespace}:#{tag}" => hash,
|
117
|
+
:attributes! => { "#{namespace}:#{tag}" => { "wsu:Id" => "#{tag}-#{count}", "xmlns:wsu" => WSUNamespace } }
|
118
118
|
},
|
119
119
|
:attributes! => { "wsse:Security" => { "xmlns:wsse" => WSENamespace } }
|
120
120
|
}
|