savon 0.9.1 → 0.9.2

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.
@@ -0,0 +1,7 @@
1
+ script: "rake"
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - ree
6
+ - rbx
7
+ - jruby
@@ -1,8 +1,31 @@
1
+ ## 0.9.2 (2011-04-30)
2
+
3
+ * Fix: [issue](https://github.com/rubiii/savon/pull/154) -
4
+ Timezone format used by Savon now matches the XML schema spec.
5
+
6
+ * Improvement: WSSE basic, digest and timestamp authentication are no longer mutually exclusive.
7
+ Thanks to [mleon](https://github.com/mleon) for solving [issue #142](https://github.com/rubiii/savon/issues/142).
8
+
9
+ * Improvement: Switched from using Crack to translate the SOAP response to a Hash to using
10
+ [Nori](http://rubygems.org/gems/nori). It's based on Crack and comes with pluggable parsers.
11
+ It defaults to REXML, but you can switch to Nokogiri via:
12
+
13
+ Nori.parser = :nokogiri
14
+
15
+ * Improvement: WSDL parsing now uses Nokogiri instead of REXML.
16
+
1
17
  ## 0.9.1 (2011-04-06)
2
18
 
3
19
  * Improvement: if you're only setting the local or remote address of your wsdl document, you can
4
20
  now pass an (optional) String to `Savon::Client.new` to set `wsdl.document`.
5
21
 
22
+ Savon::Client.new "http://example.com/UserService?wsdl"
23
+
24
+ * Improvement: instead of calling the `to_hash` method of your response again and again and again,
25
+ there is now a ' #[]` shortcut for you.
26
+
27
+ response[:authenticate_response][:return]
28
+
6
29
  ## 0.9.0 (2011-04-05)
7
30
 
8
31
  * Feature: issues [#158](https://github.com/rubiii/savon/issues/158),
data/README.md CHANGED
@@ -10,26 +10,28 @@ Installation
10
10
 
11
11
  Savon is available through [Rubygems](http://rubygems.org/gems/savon) and can be installed via:
12
12
 
13
- $ gem install savon
13
+ ```
14
+ $ gem install savon
15
+ ```
14
16
 
15
17
  Basic workflow
16
18
  --------------
17
19
 
18
- # Setting up a Savon::Client representing a SOAP service.
19
- client = Savon::Client.new do
20
- wsdl.document = "http://service.example.com?wsdl"
21
- end
20
+ ``` ruby
21
+ # Setting up a Savon::Client representing a SOAP service.
22
+ client = Savon::Client.new "http://service.example.com?wsdl"
22
23
 
23
- client.wsdl.soap_actions
24
- # => [:create_user, :get_user, :get_all_users]
24
+ client.wsdl.soap_actions
25
+ # => [:create_user, :get_user, :get_all_users]
25
26
 
26
- # Executing a SOAP request to call a "getUser" action.
27
- response = client.request :get_user do
28
- soap.body = { :id => 1 }
29
- end
27
+ # Executing a SOAP request to call a "getUser" action.
28
+ response = client.request :get_user do
29
+ soap.body = { :id => 1 }
30
+ end
30
31
 
31
- response.to_hash
32
- # => { :get_user_response => { :first_name => "The", :last_name => "Hoff" } }
32
+ response.to_hash
33
+ # => { :get_user_response => { :first_name => "The", :last_name => "Hoff" } }
34
+ ```
33
35
 
34
36
  Excited to learn more?
35
37
  ----------------------
data/Rakefile CHANGED
@@ -7,7 +7,7 @@ begin
7
7
  t.files = ["README.md", "lib/**/*.rb"]
8
8
  end
9
9
  rescue LoadError
10
- desc message = %{"gem install yard" to generate documentation}
10
+ desc message = %{run "bundle install" to generate documentation}
11
11
  task("yard") { abort message }
12
12
  end
13
13
 
@@ -21,7 +21,7 @@ begin
21
21
  c.rcov[:rcov_opts] << "-Ilib -Ispec"
22
22
  end
23
23
  rescue LoadError
24
- desc message = %{"gem install metric_fu" to generate metrics}
24
+ desc message = %{run "bundle install" to generate metrics}
25
25
  task("metrics:all") { abort message }
26
26
  end
27
27
 
@@ -29,10 +29,10 @@ begin
29
29
  require "rspec/core/rake_task"
30
30
 
31
31
  RSpec::Core::RakeTask.new do |t|
32
- t.rspec_opts = %w(-fd -c)
32
+ t.rspec_opts = %w(-c)
33
33
  end
34
34
  rescue LoadError
35
- desc message = %{"gem install rspec --pre" to run the specs}
35
+ desc message = %{run "bundle install" to run the specs}
36
36
  task(:spec) { abort message }
37
37
  end
38
38
 
@@ -4,7 +4,15 @@ module Savon
4
4
 
5
5
  # Returns an xs:dateTime formatted String.
6
6
  def xs_datetime
7
- strftime "%Y-%m-%dT%H:%M:%S%Z"
7
+ zone = if utc_offset < 0
8
+ "-#{"%02d" % (- utc_offset / 3600)}:#{"%02d" % ((- utc_offset % 3600) / 60)}"
9
+ elsif utc_offset > 0
10
+ "+#{"%02d" % (utc_offset / 3600)}:#{"%02d" % ((utc_offset % 3600) / 60)}"
11
+ else
12
+ "Z"
13
+ end
14
+
15
+ strftime "%Y-%m-%dT%H:%M:%S#{zone}"
8
16
  end
9
17
 
10
18
  end
@@ -1,5 +1,5 @@
1
1
  require "builder"
2
- require "crack/xml"
2
+ require "nori"
3
3
  require "gyoku"
4
4
 
5
5
  require "savon/soap"
@@ -28,7 +28,7 @@ module Savon
28
28
 
29
29
  # Converts a given SOAP response +xml+ to a Hash.
30
30
  def self.parse(xml)
31
- Crack::XML.parse(xml)
31
+ Nori.parse(xml)
32
32
  end
33
33
 
34
34
  # Expects a SOAP response XML or Hash, traverses it for a given +path+ of Hash keys
@@ -1,5 +1,5 @@
1
1
  module Savon
2
2
 
3
- Version = "0.9.1"
3
+ Version = "0.9.2"
4
4
 
5
5
  end
@@ -1,4 +1,4 @@
1
- require "rexml/document"
1
+ require "nokogiri"
2
2
 
3
3
  require "savon/wsdl/request"
4
4
  require "savon/wsdl/parser"
@@ -101,8 +101,8 @@ module Savon
101
101
  # Parses the WSDL document and returns the <tt>Savon::WSDL::Parser</tt>.
102
102
  def parser
103
103
  @parser ||= begin
104
- parser = Parser.new
105
- REXML::Document.parse_stream document, parser
104
+ parser = Parser.new(Nokogiri::XML(document))
105
+ parser.parse
106
106
  parser
107
107
  end
108
108
  end
@@ -6,13 +6,14 @@ module Savon
6
6
 
7
7
  # = Savon::WSDL::Parser
8
8
  #
9
- # Serves as a stream listener for parsing WSDL documents.
9
+ # Parses WSDL documents and remembers the parts of them we care about.
10
10
  class Parser
11
11
 
12
12
  # The main sections of a WSDL document.
13
13
  Sections = %w(definitions types message portType binding service)
14
14
 
15
- def initialize
15
+ def initialize(nokogiri_document)
16
+ @document = nokogiri_document
16
17
  @path = []
17
18
  @operations = {}
18
19
  @namespaces = {}
@@ -31,72 +32,65 @@ module Savon
31
32
  # Returns the elementFormDefault value.
32
33
  attr_reader :element_form_default
33
34
 
34
- # Hook method called when the stream parser encounters a starting tag.
35
- def tag_start(tag, attrs)
36
- # read xml namespaces if root element
37
- read_namespaces(attrs) if @path.empty?
38
-
39
- tag, namespace = tag.split(":").reverse
40
- @path << tag
41
-
42
- if @section == :types && tag == "schema"
43
- @element_form_default = attrs["elementFormDefault"].to_sym if attrs["elementFormDefault"]
44
- end
45
-
46
- if @section == :binding && tag == "binding"
47
- # ensure that we are in an wsdl/soap namespace
48
- @section = nil unless @namespaces[namespace].starts_with? "http://schemas.xmlsoap.org/wsdl/soap"
49
- end
50
-
51
- @section = tag.to_sym if Sections.include?(tag) && depth <= 2
52
-
53
- @namespace ||= attrs["targetNamespace"] if @section == :definitions
54
- @endpoint ||= URI(URI.escape(attrs["location"])) if @section == :service && tag == "address"
55
-
56
- operation_from tag, attrs if @section == :binding && tag == "operation"
57
- end
58
-
59
- # Returns our current depth in the WSDL document.
60
- def depth
61
- @path.size
35
+ def parse
36
+ parse_namespaces
37
+ parse_endpoint
38
+ parse_operations
62
39
  end
63
40
 
64
- # Reads namespace definitions from a given +attrs+ Hash.
65
- def read_namespaces(attrs)
66
- attrs.each do |key, value|
67
- @namespaces[key.strip_namespace] = value if key.starts_with? "xmlns:"
68
- end
41
+ def parse_namespaces
42
+ element_form_default = @document.at_xpath(
43
+ "s0:definitions/s0:types/xs:schema/@elementFormDefault",
44
+ "s0" => "http://schemas.xmlsoap.org/wsdl/",
45
+ "xs" => "http://www.w3.org/2001/XMLSchema")
46
+ @element_form_default = element_form_default.to_s.to_sym if element_form_default
47
+
48
+ namespace = @document.at_xpath(
49
+ "s0:definitions/@targetNamespace",
50
+ "s0" => "http://schemas.xmlsoap.org/wsdl/")
51
+ @namespace = namespace.to_s if namespace
69
52
  end
70
53
 
71
- # Hook method called when the stream parser encounters a closing tag.
72
- def tag_end(tag)
73
- @path.pop
74
-
75
- if @section == :binding && @input && tag.strip_namespace == "operation"
76
- # no soapAction attribute found till now
77
- operation_from tag, "soapAction" => @input
78
- end
79
-
80
- @section = :definitions if Sections.include?(tag) && depth <= 1
54
+ def parse_endpoint
55
+ endpoint = @document.at_xpath(
56
+ "s0:definitions/s0:service//soap11:address/@location",
57
+ "s0" => "http://schemas.xmlsoap.org/wsdl/",
58
+ "soap11" => "http://schemas.xmlsoap.org/wsdl/soap/")
59
+ endpoint ||= @document.at_xpath(
60
+ "s0:definitions/s0:service//soap12:address/@location",
61
+ "s0" => "http://schemas.xmlsoap.org/wsdl/",
62
+ "soap12" => "http://schemas.xmlsoap.org/wsdl/soap12/")
63
+ @endpoint = URI(URI.escape(endpoint.to_s)) if endpoint
81
64
  end
82
65
 
83
- # Stores available operations from a given tag +name+ and +attrs+.
84
- def operation_from(tag, attrs)
85
- @input = attrs["name"] if attrs["name"]
86
-
87
- if attrs["soapAction"]
88
- @action = !attrs["soapAction"].blank? ? attrs["soapAction"] : @input
89
- @input = @action.split("/").last if !@input || @input.empty?
90
-
91
- @operations[@input.snakecase.to_sym] = { :action => @action, :input => @input }
92
- @input, @action = nil, nil
66
+ def parse_operations
67
+ operations = @document.xpath(
68
+ "s0:definitions/s0:binding/s0:operation",
69
+ "s0" => "http://schemas.xmlsoap.org/wsdl/")
70
+ operations.each do |operation|
71
+ name = operation.attribute("name").to_s
72
+
73
+ soap_action = operation.at_xpath(".//soap11:operation/@soapAction",
74
+ "soap11" => "http://schemas.xmlsoap.org/wsdl/soap/"
75
+ )
76
+ soap_action ||= operation.at_xpath(".//soap12:operation/@soapAction",
77
+ "soap12" => "http://schemas.xmlsoap.org/wsdl/soap12/"
78
+ )
79
+ if soap_action
80
+ soap_action = soap_action.to_s
81
+
82
+ action = !soap_action.blank? ? soap_action : name
83
+ input = (!name || name.empty?) ? action.split("/").last : name
84
+
85
+ @operations[input.snakecase.to_sym] =
86
+ { :action => action, :input => input }
87
+ elsif !@operations[name.snakecase.to_sym]
88
+ @operations[name.snakecase.to_sym] =
89
+ { :action => name, :input => name }
90
+ end
93
91
  end
94
92
  end
95
93
 
96
- # Catches calls to unimplemented hook methods.
97
- def method_missing(method, *args)
98
- end
99
-
100
94
  end
101
95
  end
102
96
  end
@@ -68,7 +68,13 @@ module Savon
68
68
 
69
69
  # Returns the XML for a WSSE header.
70
70
  def to_xml
71
- if username_token?
71
+ if username_token? && timestamp?
72
+ Gyoku.xml wsse_username_token.merge!(wsse_timestamp) {
73
+ |key, v1, v2| v1.merge!(v2) {
74
+ |key, v1, v2| v1.merge!(v2)
75
+ }
76
+ }
77
+ elsif username_token?
72
78
  Gyoku.xml wsse_username_token.merge!(hash)
73
79
  elsif timestamp?
74
80
  Gyoku.xml wsse_timestamp.merge!(hash)
@@ -4,24 +4,25 @@ $:.unshift lib unless $:.include? lib
4
4
  require "savon/version"
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = "savon"
8
- s.version = Savon::Version
9
- s.authors = "Daniel Harrington"
10
- s.email = "me@rubiii.com"
11
- s.homepage = "http://savonrb.com"
12
- s.summary = "Heavy metal Ruby SOAP client"
13
- s.description = "Savon is the heavy metal Ruby SOAP client."
7
+ s.name = "savon"
8
+ s.version = Savon::Version
9
+ s.authors = "Daniel Harrington"
10
+ s.email = "me@rubiii.com"
11
+ s.homepage = "http://savonrb.com"
12
+ s.summary = "Heavy metal Ruby SOAP client"
13
+ s.description = "Savon is the heavy metal Ruby SOAP client"
14
14
 
15
15
  s.rubyforge_project = s.name
16
16
 
17
- s.add_dependency "builder", ">= 2.1.2"
18
- s.add_dependency "crack", "~> 0.1.8"
19
- s.add_dependency "httpi", ">= 0.7.8"
20
- s.add_dependency "gyoku", ">= 0.4.0"
17
+ s.add_dependency "builder", ">= 2.1.2"
18
+ s.add_dependency "nori", ">= 0.2.0"
19
+ s.add_dependency "httpi", ">= 0.7.8"
20
+ s.add_dependency "gyoku", ">= 0.4.0"
21
+ s.add_dependency "nokogiri", ">= 1.4.0"
21
22
 
22
- s.add_development_dependency "rspec", "~> 2.4.0"
23
+ s.add_development_dependency "rspec", "~> 2.4.0"
23
24
  s.add_development_dependency "autotest"
24
- s.add_development_dependency "mocha", "~> 0.9.8"
25
+ s.add_development_dependency "mocha", "~> 0.9.8"
25
26
  s.add_development_dependency "timecop", "~> 0.3.5"
26
27
 
27
28
  s.files = `git ls-files`.split("\n")
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!-- Example of a WSDL with SOAP 1.2 and no SOAP 1.1 endpoint.
3
+ Don't know whether this is widespread, but we should allow it. -->
4
+ <definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
5
+ <service name="Blog">
6
+ <port name="BlogSoap12">
7
+ <soap12:address location="http://blogsite.example.com/endpoint12"/>
8
+ </port>
9
+ </service>
10
+ </definitions>
11
+
@@ -6,7 +6,7 @@ describe Time do
6
6
  let(:time) { Time.utc(2011, 01, 04, 13, 45, 55) }
7
7
 
8
8
  it "should return an xs:dateTime formatted String" do
9
- time.xs_datetime.should == "2011-01-04T13:45:55UTC"
9
+ time.xs_datetime.should == "2011-01-04T13:45:55Z"
10
10
  end
11
11
  end
12
12
 
@@ -82,6 +82,14 @@ describe Savon::WSDL::Parser do
82
82
  end
83
83
  end
84
84
 
85
+ context "with soap12.xml" do
86
+ let(:parser) { new_parser :soap12 }
87
+
88
+ it "should return the endpoint" do
89
+ parser.endpoint.should == URI("http://blogsite.example.com/endpoint12")
90
+ end
91
+ end
92
+
85
93
  RSpec::Matchers.define :match_operations do |expected|
86
94
  match do |actual|
87
95
  actual.should have(expected.keys.size).items
@@ -91,8 +99,8 @@ describe Savon::WSDL::Parser do
91
99
  end
92
100
 
93
101
  def new_parser(fixture)
94
- parser = Savon::WSDL::Parser.new
95
- REXML::Document.parse_stream Fixture[:wsdl, fixture], parser
102
+ parser = Savon::WSDL::Parser.new(Nokogiri::XML(Fixture[:wsdl, fixture]))
103
+ parser.parse
96
104
  parser
97
105
  end
98
106
 
@@ -208,6 +208,25 @@ describe Savon::WSSE do
208
208
  wsse.to_xml.should include("<wsu:Expires>#{wsse.expires_at.xs_datetime}</wsu:Expires>")
209
209
  end
210
210
  end
211
+
212
+ context "whith credentials and timestamp" do
213
+ before do
214
+ wsse.credentials "username", "password"
215
+ wsse.timestamp = true
216
+ end
217
+
218
+ it "should contain a wsu:Created node" do
219
+ wsse.to_xml.should include("<wsu:Created>")
220
+ end
221
+
222
+ it "should contain a wsu:Expires node" do
223
+ wsse.to_xml.should include("<wsu:Expires>")
224
+ end
225
+
226
+ it "should contain the username and password" do
227
+ wsse.to_xml.should include("username", "password")
228
+ end
229
+ end
211
230
  end
212
231
 
213
232
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: savon
3
3
  version: !ruby/object:Gem::Version
4
- hash: 57
4
+ hash: 63
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 9
9
- - 1
10
- version: 0.9.1
9
+ - 2
10
+ version: 0.9.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Daniel Harrington
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-04-05 00:00:00 +02:00
18
+ date: 2011-04-30 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -35,19 +35,19 @@ dependencies:
35
35
  type: :runtime
36
36
  version_requirements: *id001
37
37
  - !ruby/object:Gem::Dependency
38
- name: crack
38
+ name: nori
39
39
  prerelease: false
40
40
  requirement: &id002 !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
- - - ~>
43
+ - - ">="
44
44
  - !ruby/object:Gem::Version
45
- hash: 11
45
+ hash: 23
46
46
  segments:
47
47
  - 0
48
- - 1
49
- - 8
50
- version: 0.1.8
48
+ - 2
49
+ - 0
50
+ version: 0.2.0
51
51
  type: :runtime
52
52
  version_requirements: *id002
53
53
  - !ruby/object:Gem::Dependency
@@ -83,9 +83,25 @@ dependencies:
83
83
  type: :runtime
84
84
  version_requirements: *id004
85
85
  - !ruby/object:Gem::Dependency
86
- name: rspec
86
+ name: nokogiri
87
87
  prerelease: false
88
88
  requirement: &id005 !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ hash: 7
94
+ segments:
95
+ - 1
96
+ - 4
97
+ - 0
98
+ version: 1.4.0
99
+ type: :runtime
100
+ version_requirements: *id005
101
+ - !ruby/object:Gem::Dependency
102
+ name: rspec
103
+ prerelease: false
104
+ requirement: &id006 !ruby/object:Gem::Requirement
89
105
  none: false
90
106
  requirements:
91
107
  - - ~>
@@ -97,11 +113,11 @@ dependencies:
97
113
  - 0
98
114
  version: 2.4.0
99
115
  type: :development
100
- version_requirements: *id005
116
+ version_requirements: *id006
101
117
  - !ruby/object:Gem::Dependency
102
118
  name: autotest
103
119
  prerelease: false
104
- requirement: &id006 !ruby/object:Gem::Requirement
120
+ requirement: &id007 !ruby/object:Gem::Requirement
105
121
  none: false
106
122
  requirements:
107
123
  - - ">="
@@ -111,11 +127,11 @@ dependencies:
111
127
  - 0
112
128
  version: "0"
113
129
  type: :development
114
- version_requirements: *id006
130
+ version_requirements: *id007
115
131
  - !ruby/object:Gem::Dependency
116
132
  name: mocha
117
133
  prerelease: false
118
- requirement: &id007 !ruby/object:Gem::Requirement
134
+ requirement: &id008 !ruby/object:Gem::Requirement
119
135
  none: false
120
136
  requirements:
121
137
  - - ~>
@@ -127,11 +143,11 @@ dependencies:
127
143
  - 8
128
144
  version: 0.9.8
129
145
  type: :development
130
- version_requirements: *id007
146
+ version_requirements: *id008
131
147
  - !ruby/object:Gem::Dependency
132
148
  name: timecop
133
149
  prerelease: false
134
- requirement: &id008 !ruby/object:Gem::Requirement
150
+ requirement: &id009 !ruby/object:Gem::Requirement
135
151
  none: false
136
152
  requirements:
137
153
  - - ~>
@@ -143,8 +159,8 @@ dependencies:
143
159
  - 5
144
160
  version: 0.3.5
145
161
  type: :development
146
- version_requirements: *id008
147
- description: Savon is the heavy metal Ruby SOAP client.
162
+ version_requirements: *id009
163
+ description: Savon is the heavy metal Ruby SOAP client
148
164
  email: me@rubiii.com
149
165
  executables: []
150
166
 
@@ -155,6 +171,7 @@ extra_rdoc_files: []
155
171
  files:
156
172
  - .gitignore
157
173
  - .rspec
174
+ - .travis.yml
158
175
  - .yardopts
159
176
  - CHANGELOG.md
160
177
  - Gemfile
@@ -193,6 +210,7 @@ files:
193
210
  - spec/fixtures/wsdl/geotrust.xml
194
211
  - spec/fixtures/wsdl/namespaced_actions.xml
195
212
  - spec/fixtures/wsdl/no_namespace.xml
213
+ - spec/fixtures/wsdl/soap12.xml
196
214
  - spec/fixtures/wsdl/two_bindings.xml
197
215
  - spec/savon/client_spec.rb
198
216
  - spec/savon/core_ext/hash_spec.rb