savon 0.9.1 → 0.9.2

This diff has not been reviewed by any users.
Sign up to get free protection for your applications and to get access to all the features.
@@ -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