troelskn-handsoap 0.2.9 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +10 -3
- data/VERSION.yml +2 -2
- data/lib/handsoap/compiler.rb +29 -27
- data/lib/handsoap/parser.rb +4 -1
- data/lib/handsoap/service.rb +49 -37
- data/lib/handsoap/xml_mason.rb +36 -9
- data/lib/handsoap/xml_query_front.rb +249 -0
- data/lib/handsoap.rb +1 -0
- metadata +11 -15
data/README.markdown
CHANGED
@@ -5,7 +5,7 @@ Install
|
|
5
5
|
---
|
6
6
|
|
7
7
|
gem sources -a http://gems.github.com
|
8
|
-
sudo gem install troelskn-handsoap curb
|
8
|
+
sudo gem install troelskn-handsoap curb nokogiri
|
9
9
|
|
10
10
|
What
|
11
11
|
---
|
@@ -13,6 +13,8 @@ Handsoap is a library for creating SOAP clients in Ruby.
|
|
13
13
|
|
14
14
|
[Watch a tutorial](http://www.vimeo.com/4813848), showing how to use Handsoap. The final application can be found at: [http://github.com/troelskn/handsoap-example/tree/master](http://github.com/troelskn/handsoap-example/tree/master)
|
15
15
|
|
16
|
+
API docs are at [http://rdoc.info/projects/troelskn/handsoap](http://rdoc.info/projects/troelskn/handsoap)
|
17
|
+
|
16
18
|
![Handsoap](http://ny-image0.etsy.com/il_430xN.68558416.jpg)
|
17
19
|
|
18
20
|
Why
|
@@ -68,6 +70,11 @@ The protocol also contains a large and unwieldy specification of how to do the (
|
|
68
70
|
|
69
71
|
Handsoap only supports RPC-style SOAP. This seems to be the most common style. It's probably possible to add support for Document-style with little effort, but until I see the need I'm not going there.
|
70
72
|
|
73
|
+
API documnetation
|
74
|
+
---
|
75
|
+
|
76
|
+
In addition to this guide, there's autogenerated API documentation available at [http://rdoc.info/projects/troelskn/handsoap](http://rdoc.info/projects/troelskn/handsoap)
|
77
|
+
|
71
78
|
The toolbox
|
72
79
|
---
|
73
80
|
|
@@ -75,7 +82,7 @@ The Handsoap toolbox consists of the following components.
|
|
75
82
|
|
76
83
|
Handsoap can use either [curb](http://curb.rubyforge.org/) or [httpclient](http://dev.ctor.org/http-access2) for HTTP-connectivity. The former is recommended, and default, but for portability you might choose the latter. You usually don't need to interact at the HTTP-level, but if you do (for example, if you have to use SSL), you can.
|
77
84
|
|
78
|
-
For parsing XML, Handsoap
|
85
|
+
For parsing XML, Handsoap defaults to use [Nokogiri](http://github.com/tenderlove/nokogiri/tree/master). Handsoap has an abstraction layer, so that you can switch between REXML, Nokogiri and ruby-libxml. Besides providing portability between these parsers, Handsop also gives some helper functions that are meaningful when parsing SOAP envelopes.
|
79
86
|
|
80
87
|
There is also a library for generating XML, which you'll use when mapping from Ruby to SOAP. It's quite similar to [Builder](http://builder.rubyforge.org/), but is tailored towards being used for writing SOAP-messages. The name of this library is `XmlMason` and it is included/part of Handsoap.
|
81
88
|
|
@@ -101,7 +108,7 @@ Recommendations
|
|
101
108
|
|
102
109
|
7. Write Ruby-code (using XmlMason) to generate a request that is similar to the example from soapUI. (In your copy+paste buffer)
|
103
110
|
|
104
|
-
8. Write Ruby-code to parse the response (
|
111
|
+
8. Write Ruby-code to parse the response (an XML-document) into Ruby data types.
|
105
112
|
|
106
113
|
9. Write an integration test to verify that your method works as expected. You can use soapUI to [generate a mock-service](http://www.soapui.org/userguide/mock/getting_started.html).
|
107
114
|
|
data/VERSION.yml
CHANGED
data/lib/handsoap/compiler.rb
CHANGED
@@ -1,41 +1,43 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
module Handsoap
|
3
|
-
|
3
|
+
# Used internally to generate Ruby source code
|
4
|
+
class CodeWriter #:nodoc: all
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
def initialize
|
7
|
+
@buffer = ""
|
8
|
+
@indentation = 0
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
def begin(text)
|
12
|
+
puts(text)
|
13
|
+
indent
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
def end(str = "end")
|
17
|
+
unindent
|
18
|
+
puts(str)
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
def puts(text = "")
|
22
|
+
@buffer << text.gsub(/^(.*)$/, (" " * @indentation) + "\\1")
|
23
|
+
@buffer << "\n" # unless @buffer.match(/\n$/)
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
def indent
|
27
|
+
@indentation = @indentation + 1
|
28
|
+
end
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
def unindent
|
31
|
+
@indentation = @indentation - 1
|
32
|
+
end
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
end
|
34
|
+
def to_s
|
35
|
+
@buffer
|
36
36
|
end
|
37
|
+
end
|
37
38
|
|
38
|
-
|
39
|
+
# Used internally by the generator to generate a Service stub.
|
40
|
+
class Compiler #:nodoc: all
|
39
41
|
|
40
42
|
def initialize(wsdl, basename = nil)
|
41
43
|
@wsdl = wsdl
|
data/lib/handsoap/parser.rb
CHANGED
data/lib/handsoap/service.rb
CHANGED
@@ -1,35 +1,7 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
require 'rubygems'
|
3
|
-
require 'nokogiri'
|
4
2
|
require 'time'
|
5
3
|
require 'handsoap/xml_mason'
|
6
|
-
|
7
|
-
# Nokogiri doesn't have a way of getting plain strings out,
|
8
|
-
# so this monkeypatch adds that capability ..
|
9
|
-
module Utf8StringPatch
|
10
|
-
def to_utf8
|
11
|
-
# HTMLEntities.decode_entities(self.serialize(:encoding => 'UTF-8'))
|
12
|
-
if Gem.loaded_specs['nokogiri'].version >= Gem::Version.new('1.3.0')
|
13
|
-
self.serialize(:encoding => 'UTF-8').gsub('<', '<').gsub('>', '>').gsub('"', '"').gsub(''', "'").gsub('&', '&')
|
14
|
-
else
|
15
|
-
self.serialize('UTF-8').gsub('<', '<').gsub('>', '>').gsub('"', '"').gsub(''', "'").gsub('&', '&')
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
module Nokogiri
|
21
|
-
module XML
|
22
|
-
class Text
|
23
|
-
include Utf8StringPatch
|
24
|
-
end
|
25
|
-
class Nodeset
|
26
|
-
include Utf8StringPatch
|
27
|
-
end
|
28
|
-
class Attr
|
29
|
-
include Utf8StringPatch
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
4
|
+
require 'handsoap/xml_query_front'
|
33
5
|
|
34
6
|
module Handsoap
|
35
7
|
|
@@ -44,6 +16,14 @@ module Handsoap
|
|
44
16
|
return driver
|
45
17
|
end
|
46
18
|
|
19
|
+
def self.xml_query_driver
|
20
|
+
@xml_query_driver || (self.xml_query_driver = :nokogiri)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.xml_query_driver=(driver)
|
24
|
+
@xml_query_driver = Handsoap::XmlQueryFront.load_driver!(driver)
|
25
|
+
end
|
26
|
+
|
47
27
|
SOAP_NAMESPACE = { 1 => 'http://schemas.xmlsoap.org/soap/envelope/', 2 => 'http://www.w3.org/2001/12/soap-encoding' }
|
48
28
|
|
49
29
|
class Response
|
@@ -58,8 +38,11 @@ module Handsoap
|
|
58
38
|
end
|
59
39
|
def document
|
60
40
|
if @document == :lazy
|
61
|
-
|
62
|
-
|
41
|
+
begin
|
42
|
+
@document = Handsoap::XmlQueryFront.parse_string(@http_body, Handsoap.xml_query_driver)
|
43
|
+
rescue Handsoap::XmlQueryFront::ParseError => ex
|
44
|
+
@document = nil
|
45
|
+
end
|
63
46
|
end
|
64
47
|
return @document
|
65
48
|
end
|
@@ -160,7 +143,18 @@ module Handsoap
|
|
160
143
|
super
|
161
144
|
end
|
162
145
|
end
|
163
|
-
|
146
|
+
# Creates an XML document and sends it over HTTP.
|
147
|
+
#
|
148
|
+
# +action+ is the QName of the rootnode of the envelope.
|
149
|
+
#
|
150
|
+
# +options+ currently takes one option +:soap_action+, which can be one of:
|
151
|
+
#
|
152
|
+
# :auto sends a SOAPAction http header, deduced from the action name. (This is the default)
|
153
|
+
#
|
154
|
+
# +String+ sends a SOAPAction http header.
|
155
|
+
#
|
156
|
+
# +nil+ sends no SOAPAction http header.
|
157
|
+
def invoke(action, options = { :soap_action => :auto }, &block) # :yields: Handsoap::XmlMason::Element
|
164
158
|
if action
|
165
159
|
if options.kind_of? String
|
166
160
|
options = { :soap_action => options }
|
@@ -179,13 +173,21 @@ module Handsoap
|
|
179
173
|
dispatch(doc, options[:soap_action])
|
180
174
|
end
|
181
175
|
end
|
176
|
+
# Hook that is called before the message is dispatched.
|
177
|
+
#
|
178
|
+
# You can override this to provide filtering and logging.
|
182
179
|
def on_before_dispatch
|
183
180
|
end
|
181
|
+
# Hook that is called if the dispatch returns a +Fault+.
|
182
|
+
#
|
183
|
+
# Default behaviour is to raise the Fault, but you can override this to provide logging and more fine-grained handling faults.
|
184
184
|
def on_fault(fault)
|
185
185
|
raise fault
|
186
186
|
end
|
187
187
|
private
|
188
188
|
# Helper to serialize a node into a ruby string
|
189
|
+
#
|
190
|
+
# *deprecated*. Use Handsoap::XmlQueryFront::BaseDriver#to_s
|
189
191
|
def xml_to_str(node, xquery = nil)
|
190
192
|
n = xquery ? node.xpath(xquery, ns).first : node
|
191
193
|
return if n.nil?
|
@@ -193,6 +195,8 @@ module Handsoap
|
|
193
195
|
end
|
194
196
|
alias_method :xml_to_s, :xml_to_str
|
195
197
|
# Helper to serialize a node into a ruby integer
|
198
|
+
#
|
199
|
+
# *deprecated*. Use Handsoap::XmlQueryFront::BaseDriver#to_i
|
196
200
|
def xml_to_int(node, xquery = nil)
|
197
201
|
n = xquery ? node.xpath(xquery, ns).first : node
|
198
202
|
return if n.nil?
|
@@ -200,6 +204,8 @@ module Handsoap
|
|
200
204
|
end
|
201
205
|
alias_method :xml_to_i, :xml_to_int
|
202
206
|
# Helper to serialize a node into a ruby float
|
207
|
+
#
|
208
|
+
# *deprecated*. Use Handsoap::XmlQueryFront::BaseDriver#to_f
|
203
209
|
def xml_to_float(node, xquery = nil)
|
204
210
|
n = xquery ? node.xpath(xquery, ns).first : node
|
205
211
|
return if n.nil?
|
@@ -207,18 +213,22 @@ module Handsoap
|
|
207
213
|
end
|
208
214
|
alias_method :xml_to_f, :xml_to_float
|
209
215
|
# Helper to serialize a node into a ruby boolean
|
216
|
+
#
|
217
|
+
# *deprecated*. Use Handsoap::XmlQueryFront::BaseDriver#to_boolean
|
210
218
|
def xml_to_bool(node, xquery = nil)
|
211
219
|
n = xquery ? node.xpath(xquery, ns).first : node
|
212
220
|
return if n.nil?
|
213
221
|
n.to_s == "true"
|
214
222
|
end
|
215
223
|
# Helper to serialize a node into a ruby Time object
|
224
|
+
#
|
225
|
+
# *deprecated*. Use Handsoap::XmlQueryFront::BaseDriver#to_date
|
216
226
|
def xml_to_date(node, xquery = nil)
|
217
227
|
n = xquery ? node.xpath(xquery, ns).first : node
|
218
228
|
return if n.nil?
|
219
229
|
Time.iso8601(n.to_s)
|
220
230
|
end
|
221
|
-
def debug(message = nil)
|
231
|
+
def debug(message = nil) #:nodoc:
|
222
232
|
if @@logger
|
223
233
|
if message
|
224
234
|
@@logger.puts(message)
|
@@ -228,8 +238,9 @@ module Handsoap
|
|
228
238
|
end
|
229
239
|
end
|
230
240
|
end
|
241
|
+
# Takes care of the HTTP level dispatch.
|
231
242
|
def dispatch(doc, action)
|
232
|
-
on_before_dispatch
|
243
|
+
on_before_dispatch
|
233
244
|
headers = {
|
234
245
|
"Content-Type" => "#{self.class.request_content_type};charset=UTF-8"
|
235
246
|
}
|
@@ -273,7 +284,8 @@ module Handsoap
|
|
273
284
|
end
|
274
285
|
return soap_response
|
275
286
|
end
|
276
|
-
|
287
|
+
# Creates a standard SOAP envelope and yields the +Body+ element.
|
288
|
+
def make_envelope # :yields: Handsoap::XmlMason::Element
|
277
289
|
doc = XmlMason::Document.new do |doc|
|
278
290
|
doc.alias 'env', self.class.envelope_namespace
|
279
291
|
doc.add "env:Envelope" do |env|
|
@@ -292,11 +304,11 @@ module Handsoap
|
|
292
304
|
def self.pretty_format_envelope(xml_string)
|
293
305
|
if /^<.*:Envelope/.match(xml_string)
|
294
306
|
begin
|
295
|
-
doc =
|
307
|
+
doc = Handsoap::XmlQueryFront.parse_string(xml_string, Handsoap.xml_query_driver)
|
296
308
|
rescue Exception => ex
|
297
309
|
return "Formatting failed: " + ex.to_s
|
298
310
|
end
|
299
|
-
return doc.
|
311
|
+
return doc.to_xml
|
300
312
|
# return "\n\e[1;33m" + doc.to_s + "\e[0m"
|
301
313
|
end
|
302
314
|
return xml_string
|
data/lib/handsoap/xml_mason.rb
CHANGED
@@ -2,28 +2,44 @@
|
|
2
2
|
|
3
3
|
module Handsoap
|
4
4
|
|
5
|
+
# XmlMason is a simple XML builder.
|
5
6
|
module XmlMason
|
6
7
|
|
7
|
-
|
8
|
+
XML_ESCAPE = { '&' => '&', '"' => '"', '>' => '>', '<' => '<' }
|
8
9
|
|
9
|
-
def self.
|
10
|
-
s.to_s.gsub(/[&"><]/) { |special|
|
10
|
+
def self.xml_escape(s)
|
11
|
+
s.to_s.gsub(/[&"><]/) { |special| XML_ESCAPE[special] }
|
11
12
|
end
|
12
13
|
|
13
14
|
class Node
|
14
15
|
def initialize
|
15
16
|
@namespaces = {}
|
16
17
|
end
|
17
|
-
def add(node_name, value = nil, *flags)
|
18
|
+
def add(node_name, value = nil, *flags) # :yields: Handsoap::XmlMason::Element
|
18
19
|
prefix, name = parse_ns(node_name)
|
19
20
|
node = append_child Element.new(self, prefix, name, value, flags)
|
20
21
|
if block_given?
|
21
22
|
yield node
|
22
23
|
end
|
23
24
|
end
|
25
|
+
# Registers a prefix for a namespace.
|
26
|
+
#
|
27
|
+
# You must register a namespace, before you can refer it.
|
24
28
|
def alias(prefix, namespaces)
|
25
29
|
@namespaces[prefix] = namespaces
|
26
30
|
end
|
31
|
+
# Finds the first element whos +node_name+ equals +name+
|
32
|
+
#
|
33
|
+
# Doesn't regard namespaces/prefixes.
|
34
|
+
def find(name)
|
35
|
+
raise NotImplementedError.new
|
36
|
+
end
|
37
|
+
# Finds all elements whos +node_name+ equals +name+
|
38
|
+
#
|
39
|
+
# Doesn't regard namespaces/prefixes.
|
40
|
+
def find_all(name)
|
41
|
+
raise NotImplementedError.new
|
42
|
+
end
|
27
43
|
def parse_ns(name)
|
28
44
|
matches = name.match /^([^:]+):(.*)$/
|
29
45
|
if matches
|
@@ -36,7 +52,7 @@ module Handsoap
|
|
36
52
|
end
|
37
53
|
|
38
54
|
class Document < Node
|
39
|
-
def initialize
|
55
|
+
def initialize # :yields: Document
|
40
56
|
super
|
41
57
|
@document_element = nil
|
42
58
|
if block_given?
|
@@ -74,7 +90,7 @@ module Handsoap
|
|
74
90
|
@text = text
|
75
91
|
end
|
76
92
|
def to_s(indentation = '')
|
77
|
-
XmlMason.
|
93
|
+
XmlMason.xml_escape(@text)
|
78
94
|
end
|
79
95
|
end
|
80
96
|
|
@@ -85,7 +101,7 @@ module Handsoap
|
|
85
101
|
end
|
86
102
|
|
87
103
|
class Element < Node
|
88
|
-
def initialize(parent, prefix, node_name, value = nil, flags = [])
|
104
|
+
def initialize(parent, prefix, node_name, value = nil, flags = []) # :yields: Handsoap::XmlMason::Element
|
89
105
|
super()
|
90
106
|
# if prefix.to_s == ""
|
91
107
|
# raise "missing prefix"
|
@@ -102,12 +118,17 @@ module Handsoap
|
|
102
118
|
yield self
|
103
119
|
end
|
104
120
|
end
|
121
|
+
# Returns the document that this element belongs to, or self if this is the document.
|
105
122
|
def document
|
106
123
|
@parent.respond_to?(:document) ? @parent.document : @parent
|
107
124
|
end
|
125
|
+
# Returns the qname (prefix:nodename)
|
108
126
|
def full_name
|
109
127
|
@prefix.nil? ? @node_name : (@prefix + ":" + @node_name)
|
110
128
|
end
|
129
|
+
# Adds a child node.
|
130
|
+
#
|
131
|
+
# You usually won't need to call this method, but will rather use +add+
|
111
132
|
def append_child(node)
|
112
133
|
if value_node?
|
113
134
|
raise "Element already has a text value. Can't add nodes"
|
@@ -115,6 +136,11 @@ module Handsoap
|
|
115
136
|
@children << node
|
116
137
|
return node
|
117
138
|
end
|
139
|
+
# Sets the inner text of this element.
|
140
|
+
#
|
141
|
+
# By default the string is escaped, but you can pass the flag :raw to inject XML.
|
142
|
+
#
|
143
|
+
# You usually won't need to call this method, but will rather use +add+
|
118
144
|
def set_value(value, *flags)
|
119
145
|
if @children.length > 0
|
120
146
|
raise "Element already has children. Can't set value"
|
@@ -125,6 +151,7 @@ module Handsoap
|
|
125
151
|
@children = [TextNode.new(value)]
|
126
152
|
end
|
127
153
|
end
|
154
|
+
# Sets the value of an attribute.
|
128
155
|
def set_attr(name, value)
|
129
156
|
full_name = parse_ns(name).join(":")
|
130
157
|
@attributes[name] = value
|
@@ -171,8 +198,8 @@ module Handsoap
|
|
171
198
|
if @prefix && (not defines_namespace?(@prefix))
|
172
199
|
set_attr "xmlns:#{@prefix}", get_namespace(@prefix)
|
173
200
|
end
|
174
|
-
name = XmlMason.
|
175
|
-
attr = (@attributes.any? ? (" " + @attributes.map { |key, value| XmlMason.
|
201
|
+
name = XmlMason.xml_escape(full_name)
|
202
|
+
attr = (@attributes.any? ? (" " + @attributes.map { |key, value| XmlMason.xml_escape(key) + '="' + XmlMason.xml_escape(value) + '"' }.join(" ")) : "")
|
176
203
|
if @children.any?
|
177
204
|
if value_node?
|
178
205
|
children = @children[0].to_s(indentation + " ")
|
@@ -0,0 +1,249 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Handsoap
|
3
|
+
#
|
4
|
+
# A simple frontend for parsing XML document with Xpath.
|
5
|
+
#
|
6
|
+
# This provides a unified interface for multiple xpath-capable dom-parsers,
|
7
|
+
# allowing seamless switching between the underlying implementations.
|
8
|
+
#
|
9
|
+
# A document is loaded using the function Handsoap::XmlQueryFront.parse_string, passing
|
10
|
+
# the xml source string and a driver, which can (currently) be one of:
|
11
|
+
#
|
12
|
+
# :rexml
|
13
|
+
# :nokogiri
|
14
|
+
# :libxml
|
15
|
+
#
|
16
|
+
# The resulting object is a wrapper, of the type Handsoap::XmlQueryFront::BaseDriver.
|
17
|
+
#
|
18
|
+
module XmlQueryFront
|
19
|
+
|
20
|
+
# This error is raised if the document didn't parse
|
21
|
+
class ParseError < RuntimeError; end
|
22
|
+
|
23
|
+
# Loads requirements for a driver.
|
24
|
+
#
|
25
|
+
# This function is implicitly called by +parse_string+.
|
26
|
+
def self.load_driver!(driver)
|
27
|
+
if driver == :rexml
|
28
|
+
require 'rexml/document'
|
29
|
+
elsif driver == :nokogiri
|
30
|
+
require 'nokogiri'
|
31
|
+
elsif driver == :libxml
|
32
|
+
require 'libxml'
|
33
|
+
else
|
34
|
+
raise "Unknown driver #{driver}"
|
35
|
+
end
|
36
|
+
return driver
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns a wrapped XML parser, using the requested driver.
|
40
|
+
#
|
41
|
+
# +driver+ can be one of the following:
|
42
|
+
# :rexml
|
43
|
+
# :nokogiri
|
44
|
+
# :libxml
|
45
|
+
def self.parse_string(xml_string, driver)
|
46
|
+
load_driver!(driver)
|
47
|
+
if driver == :rexml
|
48
|
+
doc = REXML::Document.new(xml_string)
|
49
|
+
raise ParseError.new if doc.root.nil?
|
50
|
+
XmlQueryFront::REXMLDriver.new(doc)
|
51
|
+
elsif driver == :nokogiri
|
52
|
+
doc = Nokogiri::XML(xml_string)
|
53
|
+
raise ParseError.new unless (doc && doc.root && doc.errors.empty?)
|
54
|
+
XmlQueryFront::NokogiriDriver.new(doc)
|
55
|
+
elsif driver == :libxml
|
56
|
+
begin
|
57
|
+
LibXML::XML::Error.set_handler &LibXML::XML::Error::QUIET_HANDLER
|
58
|
+
doc = XmlQueryFront::LibXMLDriver.new(LibXML::XML::Parser.string(xml_string).parse)
|
59
|
+
rescue ArgumentError, LibXML::XML::Error => ex
|
60
|
+
raise ParseError.new
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Wraps the underlying (native) xml driver, and provides a uniform interface.
|
66
|
+
module BaseDriver
|
67
|
+
def initialize(element, namespaces = {})
|
68
|
+
@element = element
|
69
|
+
@namespaces = namespaces
|
70
|
+
end
|
71
|
+
# Registers a prefix to refer to a namespace.
|
72
|
+
#
|
73
|
+
# You can either register a nemspace with this function or pass it explicitly to the +xpath+ method.
|
74
|
+
def add_namespace(prefix, uri)
|
75
|
+
@namespaces[prefix] = uri
|
76
|
+
end
|
77
|
+
# Checks that an xpath-query doesn't refer to any undefined prefixes in +ns+
|
78
|
+
def assert_prefixes!(expression, ns)
|
79
|
+
expression.scan(/([a-zA-Z_][a-zA-Z0-9_.-]*):[^:]+/).map{|m| m[0] }.each do |prefix|
|
80
|
+
raise "Undefined prefix '#{prefix}'" if ns[prefix].nil?
|
81
|
+
end
|
82
|
+
end
|
83
|
+
# Returns the value of the element as an integer.
|
84
|
+
#
|
85
|
+
# See +to_s+
|
86
|
+
def to_i
|
87
|
+
t = self.to_s
|
88
|
+
return if t.nil?
|
89
|
+
t.to_i
|
90
|
+
end
|
91
|
+
# Returns the value of the element as a float.
|
92
|
+
#
|
93
|
+
# See +to_s+
|
94
|
+
def to_f
|
95
|
+
t = self.to_s
|
96
|
+
return if t.nil?
|
97
|
+
t.to_f
|
98
|
+
end
|
99
|
+
# Returns the value of the element as an boolean.
|
100
|
+
#
|
101
|
+
# See +to_s+
|
102
|
+
def to_boolean
|
103
|
+
t = self.to_s
|
104
|
+
return if t.nil?
|
105
|
+
t.downcase == 'true'
|
106
|
+
end
|
107
|
+
# Returns the value of the element as a ruby Time object.
|
108
|
+
#
|
109
|
+
# See +to_s+
|
110
|
+
def to_date
|
111
|
+
t = self.to_s
|
112
|
+
return if t.nil?
|
113
|
+
Time.iso8601(t)
|
114
|
+
end
|
115
|
+
# Returns the underlying native element.
|
116
|
+
#
|
117
|
+
# You shouldn't need to use this, since doing so would void portability.
|
118
|
+
def native_element
|
119
|
+
@element
|
120
|
+
end
|
121
|
+
# Returns the node name of the current element.
|
122
|
+
def node_name
|
123
|
+
raise NotImplementedError.new
|
124
|
+
end
|
125
|
+
# Queries the document with XPath, relative to the current element.
|
126
|
+
#
|
127
|
+
# +ns+ Should be a Hash of prefix => namespace
|
128
|
+
#
|
129
|
+
# Returns an Array of wrapped elements.
|
130
|
+
#
|
131
|
+
# See add_namespace
|
132
|
+
def xpath(expression, ns = nil)
|
133
|
+
raise NotImplementedError.new
|
134
|
+
end
|
135
|
+
# Returns the inner text content of this element, or the value (if it's an attr or textnode).
|
136
|
+
#
|
137
|
+
# The output is a UTF-8 encoded string, without xml-entities.
|
138
|
+
def to_s
|
139
|
+
raise NotImplementedError.new
|
140
|
+
end
|
141
|
+
# Returns the outer XML for this element.
|
142
|
+
def to_xml
|
143
|
+
raise NotImplementedError.new
|
144
|
+
end
|
145
|
+
# Alias for +xpath+
|
146
|
+
def /(expression)
|
147
|
+
self.xpath(expression)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Driver for +libxml+.
|
152
|
+
#
|
153
|
+
# http://libxml.rubyforge.org/
|
154
|
+
class LibXMLDriver
|
155
|
+
include BaseDriver
|
156
|
+
def node_name
|
157
|
+
@element.name
|
158
|
+
end
|
159
|
+
def xpath(expression, ns = nil)
|
160
|
+
ns = {} if ns.nil?
|
161
|
+
ns = @namespaces.merge(ns)
|
162
|
+
assert_prefixes!(expression, ns)
|
163
|
+
@element.find(expression, ns.map{|k,v| "#{k}:#{v}" }).to_a.map{|node| LibXMLDriver.new(node, ns) }
|
164
|
+
end
|
165
|
+
def to_xml
|
166
|
+
@element.to_s
|
167
|
+
end
|
168
|
+
def to_s
|
169
|
+
if @element.kind_of? LibXML::XML::Attr
|
170
|
+
@element.value
|
171
|
+
else
|
172
|
+
@element.content
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Driver for +REXML+
|
178
|
+
#
|
179
|
+
# http://www.germane-software.com/software/rexml/
|
180
|
+
class REXMLDriver
|
181
|
+
include BaseDriver
|
182
|
+
def node_name
|
183
|
+
@element.name
|
184
|
+
end
|
185
|
+
def xpath(expression, ns = nil)
|
186
|
+
ns = {} if ns.nil?
|
187
|
+
ns = @namespaces.merge(ns)
|
188
|
+
assert_prefixes!(expression, ns)
|
189
|
+
REXML::XPath.match(@element, expression, ns).map{|node| REXMLDriver.new(node, ns) }
|
190
|
+
end
|
191
|
+
def to_xml
|
192
|
+
require 'rexml/formatters/pretty'
|
193
|
+
formatter = REXML::Formatters::Pretty.new
|
194
|
+
out = String.new
|
195
|
+
formatter.write(@element, out)
|
196
|
+
out
|
197
|
+
end
|
198
|
+
def to_s
|
199
|
+
if @element.kind_of? REXML::Attribute
|
200
|
+
@element.value
|
201
|
+
else
|
202
|
+
@element.text
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Driver for +Nokogiri+
|
208
|
+
#
|
209
|
+
# http://nokogiri.rubyforge.org/nokogiri/
|
210
|
+
class NokogiriDriver
|
211
|
+
include BaseDriver
|
212
|
+
def node_name
|
213
|
+
@element.name
|
214
|
+
end
|
215
|
+
def self.serialize_args #:nodoc:
|
216
|
+
@serialize_args ||= if Gem.loaded_specs['nokogiri'].version >= Gem::Version.new('1.3.0')
|
217
|
+
{ :encoding => 'UTF-8' }
|
218
|
+
else
|
219
|
+
'UTF-8'
|
220
|
+
end
|
221
|
+
end
|
222
|
+
def xpath(expression, ns = nil)
|
223
|
+
ns = {} if ns.nil?
|
224
|
+
ns = @namespaces.merge(ns)
|
225
|
+
assert_prefixes!(expression, ns)
|
226
|
+
@element.xpath(expression, ns).map{|node| NokogiriDriver.new(node, ns) }
|
227
|
+
end
|
228
|
+
def to_xml
|
229
|
+
@element.serialize(NokogiriDriver.serialize_args)
|
230
|
+
end
|
231
|
+
def to_s
|
232
|
+
if @element.kind_of?(Nokogiri::XML::Text) || @element.kind_of?(Nokogiri::XML::CDATA)
|
233
|
+
element = @element
|
234
|
+
elsif @element.kind_of?(Nokogiri::XML::Attr)
|
235
|
+
return @element.value
|
236
|
+
else
|
237
|
+
element = @element.children.first
|
238
|
+
end
|
239
|
+
return if element.nil?
|
240
|
+
# This looks messy because it is .. Nokogiri's interface is in a flux
|
241
|
+
if element.kind_of?(Nokogiri::XML::CDATA)
|
242
|
+
element.serialize(NokogiriDriver.serialize_args).gsub(/^<!\[CDATA\[/, "").gsub(/\]\]>$/, "")
|
243
|
+
else
|
244
|
+
element.serialize(NokogiriDriver.serialize_args).gsub('<', '<').gsub('>', '>').gsub('"', '"').gsub(''', "'").gsub('&', '&')
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
data/lib/handsoap.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: troelskn-handsoap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Troels Knak-Nielsen
|
@@ -9,19 +9,10 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-06-
|
12
|
+
date: 2009-06-23 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
16
|
-
name: nokogiri
|
17
|
-
type: :runtime
|
18
|
-
version_requirement:
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 1.2.3
|
24
|
-
version:
|
14
|
+
dependencies: []
|
15
|
+
|
25
16
|
description: Handsoap is a library for creating SOAP clients in Ruby
|
26
17
|
email: troelskn@gmail.com
|
27
18
|
executables: []
|
@@ -41,6 +32,7 @@ files:
|
|
41
32
|
- lib/handsoap/parser.rb
|
42
33
|
- lib/handsoap/service.rb
|
43
34
|
- lib/handsoap/xml_mason.rb
|
35
|
+
- lib/handsoap/xml_query_front.rb
|
44
36
|
has_rdoc: true
|
45
37
|
homepage: http://github.com/troelskn/handsoap
|
46
38
|
post_install_message:
|
@@ -60,8 +52,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
60
52
|
- !ruby/object:Gem::Version
|
61
53
|
version: "0"
|
62
54
|
version:
|
63
|
-
requirements:
|
64
|
-
|
55
|
+
requirements:
|
56
|
+
- |-
|
57
|
+
You need to install either "curb" or "httpclient", using one of:
|
58
|
+
gem install curb
|
59
|
+
gem install httpclient
|
60
|
+
- It is recommended that you install either "nokogiri" or "libxml-ruby"
|
65
61
|
rubyforge_project:
|
66
62
|
rubygems_version: 1.2.0
|
67
63
|
signing_key:
|