handsoap 1.1.0

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,63 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Handsoap
4
+ module Http
5
+
6
+ # Represents a HTTP Request.
7
+ class Request
8
+ attr_reader :url, :http_method, :headers, :body, :username, :password
9
+ attr_writer :body, :http_method
10
+ def initialize(url, http_method = :get)
11
+ @url = url
12
+ @http_method = http_method
13
+ @headers = {}
14
+ @body = nil
15
+ @username = nil
16
+ @password = nil
17
+ end
18
+ def set_auth(username, password)
19
+ @username = username
20
+ @password = password
21
+ end
22
+ def add_header(key, value)
23
+ if @headers[key].nil?
24
+ @headers[key] = []
25
+ end
26
+ @headers[key] << value
27
+ end
28
+ def set_header(key, value)
29
+ if value.nil?
30
+ @headers[key] = nil
31
+ else
32
+ @headers[key] = [value]
33
+ end
34
+ end
35
+ def inspect
36
+ "===============\n" +
37
+ "--- Request ---\n" +
38
+ "#{http_method.to_s.upcase} #{url}\n" +
39
+ (
40
+ if username && password
41
+ "Auth credentials: #{username}:#{password}\n"
42
+ else
43
+ ""
44
+ end
45
+ ) +
46
+ (
47
+ if headers.any?
48
+ "---\n" + headers.map { |key,values| values.map {|value| key + ": " + value + "\n" }.join("") }.join("")
49
+ else
50
+ ""
51
+ end
52
+ ) +
53
+ (
54
+ if body
55
+ "---\n" + body
56
+ else
57
+ ""
58
+ end
59
+ )
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,28 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'handsoap/http/part'
3
+
4
+ module Handsoap
5
+ module Http
6
+
7
+ # Represents a HTTP Response.
8
+ class Response < Part
9
+ attr_reader :status
10
+ def initialize(status, headers, body, parts = nil)
11
+ @status = status.to_i
12
+ super(headers, body, parts)
13
+ end
14
+ def primary_part
15
+ # Strictly speaking, the main part doesn't need to be first, but until proven otherwise, we'll just assume that.
16
+ if multipart?
17
+ parts.first
18
+ else
19
+ self
20
+ end
21
+ end
22
+ private
23
+ def inspect_head
24
+ "--- Response ---\n" + "HTTP Status: #{status}\n"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,241 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'httpclient'
3
+ require 'openssl'
4
+ require 'nokogiri'
5
+
6
+ module Handsoap
7
+ # Classes for parsing a WSDL.
8
+ #
9
+ # Used internally by the generator.
10
+ module Parser #:nodoc: all
11
+ class Interface
12
+ attr_accessor :name, :operations
13
+
14
+ def initialize(name, operations = [])
15
+ @name = name
16
+ @operations = operations || []
17
+ end
18
+ end
19
+
20
+ class Binding
21
+ attr_accessor :name, :protocol, :interface, :transport, :style, :encoding, :verb, :actions
22
+
23
+ def initialize(name, optional = {})
24
+ @name = name
25
+ @actions = optional[:actions] || []
26
+ @protocol = optional[:protocol]
27
+ @interface = optional[:interface]
28
+ @transport = optional[:transport]
29
+ @style = optional[:style]
30
+ @encoding = optional[:encoding]
31
+ @verb = optional[:verb]
32
+ end
33
+ end
34
+
35
+ class Endpoint
36
+ attr_accessor :name, :protocol, :binding, :url
37
+
38
+ def initialize(name, protocol, binding, url)
39
+ @name = name
40
+ @protocol = protocol
41
+ @binding = binding
42
+ @url = url
43
+ end
44
+ end
45
+
46
+ class Operation
47
+ attr_accessor :name, :input, :output
48
+
49
+ def initialize(name, optional = {})
50
+ @name = name
51
+ @input = optional[:input]
52
+ @output = optional[:output]
53
+ end
54
+ end
55
+
56
+ class Action
57
+ attr_accessor :name, :soap_action, :location
58
+
59
+ def initialize(name, optional = {})
60
+ @name = name
61
+ @soap_action = optional[:soap_action]
62
+ @location = optional[:location]
63
+ end
64
+ end
65
+
66
+ class Wsdl
67
+ attr_reader :url
68
+
69
+ def initialize(doc, url = "void://")
70
+ @doc = doc
71
+ @url = url
72
+ end
73
+
74
+ def self.read(url)
75
+ if url =~ /^http(s?):/
76
+ request = ::HTTPClient.new
77
+ request.ssl_config.verify_mode = ::OpenSSL::SSL::VERIFY_NONE
78
+ response = request.get(url)
79
+ xml_src = response.content
80
+ else
81
+ xml_src = Kernel.open(url).read
82
+ end
83
+ self.new(Nokogiri.XML(xml_src), url)
84
+ end
85
+
86
+ def ns
87
+ {
88
+ 'wsdl1' => "http://schemas.xmlsoap.org/wsdl/",
89
+ 'wsdl2' => "http://www.w3.org/ns/wsdl/",
90
+ 'soap11' => "http://schemas.xmlsoap.org/wsdl/soap/",
91
+ 'soap12' => "http://schemas.xmlsoap.org/wsdl/soap12/",
92
+ 'http' => "http://schemas.xmlsoap.org/wsdl/http/"
93
+ }
94
+ end
95
+ private :ns
96
+
97
+ def protocol_from_ns(node)
98
+ href = node.namespace.respond_to?(:href) ? node.namespace.href : @doc.namespaces["xmlns:#{node.namespace}"]
99
+ case href
100
+ when "http://schemas.xmlsoap.org/wsdl/soap/"
101
+ :soap11
102
+ when "http://schemas.xmlsoap.org/wsdl/soap12/"
103
+ :soap12
104
+ when "http://schemas.xmlsoap.org/wsdl/http/"
105
+ :http
106
+ else
107
+ raise "Unknown namespace '#{href}'"
108
+ end
109
+ end
110
+ private :protocol_from_ns
111
+
112
+ def is_wsdl2?(node)
113
+ href = node.namespace.respond_to?(:href) ? node.namespace.href : @doc.namespaces["xmlns:#{node.namespace}"]
114
+ case href
115
+ when "http://schemas.xmlsoap.org/wsdl/"
116
+ false
117
+ when "http://www.w3.org/ns/wsdl/"
118
+ true
119
+ else
120
+ raise "Unknown namespace '#{href}'"
121
+ end
122
+ end
123
+ private :is_wsdl2?
124
+
125
+ def service
126
+ services = @doc.xpath("//wsdl1:service|//wsdl2:service", ns)
127
+ raise "Expected exactly 1 service in WSDL" if services.length != 1
128
+ services[0][:name]
129
+ end
130
+
131
+ def interface
132
+ all_interfaces = self.interfaces
133
+ if all_interfaces.length != 1
134
+ # There are more than one portType, so we take a pick
135
+ all_bindings = self.bindings
136
+ all_interfaces.each do |interface|
137
+ b = all_bindings.find {|binding| binding.name == interface.name }
138
+ if [:soap11, :soap12].include? b.protocol
139
+ return interface
140
+ end
141
+ end
142
+ raise "Can't find a suitable soap 1.1 or 1.2 interface/portType in WSDL"
143
+ end
144
+ all_interfaces.first
145
+ end
146
+
147
+ def target_ns
148
+ @doc.root[:targetNamespace] || raise("Attribute targetNamespace not defined")
149
+ end
150
+
151
+ def preferred_protocol
152
+ e = endpoints
153
+ if e.select { |endpoint| endpoint.protocol == :soap12 }.any?
154
+ :soap12
155
+ elsif e.select { |endpoint| endpoint.protocol == :soap11 }.any?
156
+ :soap11
157
+ else
158
+ raise "Can't find any soap 1.1 or soap 1.2 endpoints"
159
+ end
160
+ end
161
+
162
+ def interfaces
163
+ @doc.xpath("//wsdl1:portType|//wsdl2:interface", ns).map do |port_type|
164
+ operations = port_type.xpath("./wsdl1:operation|./wsdl2:operation", ns).map do |operation|
165
+ if is_wsdl2?(operation)
166
+ input_node = operation.xpath("./wsdl2:input", ns).first
167
+ input = input_node ? input_node[:element] : nil
168
+ output_node = operation.xpath("./wsdl2:output", ns).first
169
+ output = output_node ? output_node[:element] : nil
170
+ else
171
+ input_node = operation.xpath("./wsdl1:input", ns).first
172
+ input = input_node ? input_node[:message] : nil
173
+ output_node = operation.xpath("./wsdl1:output", ns).first
174
+ output = output_node ? output_node[:message] : nil
175
+ end
176
+ Operation.new(operation[:name], :input => input, :output => output)
177
+ end
178
+ Interface.new(port_type[:name], operations)
179
+ end
180
+ end
181
+
182
+ def endpoints
183
+ @doc.xpath("//wsdl1:service/wsdl1:port|//wsdl2:service/wsdl2:endpoint", ns).map do |port|
184
+ binding = port[:binding]
185
+ if is_wsdl2?(port)
186
+ location = port[:address]
187
+ protocol = :binding
188
+ else
189
+ address = port.xpath("./soap11:address|./soap12:address|./http:address", ns).first
190
+ location = address[:location]
191
+ protocol = protocol_from_ns(address)
192
+ end
193
+ Endpoint.new(port[:name], protocol, binding, location)
194
+ end
195
+ end
196
+
197
+ def bindings
198
+ @doc.xpath("//wsdl1:binding|//wsdl2:binding", ns).map do |binding|
199
+ raise "WSDL 2.0 not supported" if is_wsdl2?(binding)
200
+ soap_binding = binding.xpath("./soap11:binding|./soap12:binding|./http:binding", ns).first
201
+ protocol = protocol_from_ns(soap_binding)
202
+ actions = []
203
+ style = nil
204
+ encoding = nil
205
+ actions = binding.xpath("./wsdl1:operation", ns).map do |operation|
206
+ soap_operation = operation.xpath("./soap11:operation|./soap12:operation|./http:operation", ns).first
207
+ if soap_operation[:style]
208
+ raise "Mixed styles not supported" if style && style != soap_operation[:style]
209
+ style = soap_operation[:style]
210
+ end
211
+ xquery = []
212
+ ['soap11', 'soap12', 'http'].each do |version|
213
+ ['input', 'output'].each do |message_name|
214
+ ['header', 'body'].each do |part_name|
215
+ xquery << "./wsdl1:#{message_name}/#{version}:#{part_name}"
216
+ end
217
+ end
218
+ end
219
+ operation.xpath(xquery.join('|'), ns).each do |thing|
220
+ raise "Mixed encodings not supported" if encoding && encoding != thing[:use]
221
+ encoding = thing[:use]
222
+ end
223
+ Action.new(
224
+ operation[:name],
225
+ :soap_action => soap_operation[:soapAction],
226
+ :location => soap_operation[:location])
227
+ end
228
+ Binding.new(
229
+ binding[:name],
230
+ :protocol => protocol,
231
+ :interface => binding[:type],
232
+ :transport => soap_binding[:transport],
233
+ :style => style,
234
+ :encoding => encoding,
235
+ :verb => soap_binding[:verb],
236
+ :actions => actions)
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,481 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'time'
3
+ require 'handsoap/xml_mason'
4
+ require 'handsoap/xml_query_front'
5
+ require 'handsoap/http'
6
+ require 'handsoap/deferred'
7
+
8
+ module Handsoap
9
+
10
+ def self.http_driver
11
+ @http_driver || (self.http_driver = :curb)
12
+ end
13
+
14
+ def self.http_driver=(driver)
15
+ @http_driver = driver
16
+ Handsoap::Http.drivers[driver].load!
17
+ return driver
18
+ end
19
+
20
+ def self.xml_query_driver
21
+ @xml_query_driver || (self.xml_query_driver = :nokogiri)
22
+ end
23
+
24
+ def self.xml_query_driver=(driver)
25
+ @xml_query_driver = Handsoap::XmlQueryFront.load_driver!(driver)
26
+ end
27
+
28
+ class Fault < StandardError
29
+ attr_reader :code, :reason, :details
30
+ def initialize(code, reason, details)
31
+ @code = code
32
+ @reason = reason
33
+ @details = details
34
+ end
35
+ def to_s
36
+ "Handsoap::Fault { :code => '#{@code}', :reason => '#{@reason}' }"
37
+ end
38
+ def self.from_xml(node, options = { :namespace => nil })
39
+ if not options[:namespace]
40
+ raise "Missing option :namespace"
41
+ end
42
+ ns = { 'env' => options[:namespace] }
43
+ fault_code = node.xpath('./env:Code/env:Value', ns).to_s
44
+ unless fault_code
45
+ fault_code = node.xpath('./faultcode', ns).to_s
46
+ end
47
+ reason = node.xpath('./env:Reason/env:Text[1]', ns).to_s
48
+ unless reason
49
+ reason = node.xpath('./faultstring', ns).to_s
50
+ end
51
+ details = node.xpath('./detail/*', ns)
52
+ self.new(fault_code, reason, details)
53
+ end
54
+ end
55
+
56
+ class HttpError < StandardError
57
+ attr_reader :response
58
+ def initialize(response)
59
+ @response = response
60
+ super()
61
+ end
62
+ end
63
+
64
+ class SoapResponse
65
+ attr_reader :document, :http_response
66
+ def initialize(document, http_response)
67
+ @document = document
68
+ @http_response = http_response
69
+ end
70
+ def method_missing(method, *args, &block)
71
+ if @document.respond_to?(method)
72
+ @document.__send__ method, *args, &block
73
+ else
74
+ super
75
+ end
76
+ end
77
+ end
78
+
79
+ class AsyncDispatch
80
+ attr_reader :action, :options, :request_block, :response_block
81
+ def request(action, options = { :soap_action => :auto }, &block)
82
+ @action = action
83
+ @options = options
84
+ @request_block = block
85
+ end
86
+ def response(&block)
87
+ @response_block = block
88
+ end
89
+ end
90
+
91
+ class Service
92
+ @@logger = nil
93
+ def self.logger=(io)
94
+ @@logger = io
95
+ end
96
+ # Sets the endpoint for the service.
97
+ # Arguments:
98
+ # :uri => endpoint uri of the service. Required.
99
+ # :version => 1 | 2
100
+ # :envelope_namespace => Namespace of SOAP-envelope
101
+ # :request_content_type => Content-Type of HTTP request.
102
+ # You must supply either :version or both :envelope_namspace and :request_content_type.
103
+ # :version is simply a shortcut for default values.
104
+ def self.endpoint(args = {})
105
+ @uri = args[:uri] || raise("Missing option :uri")
106
+ if args[:version]
107
+ soap_namespace = { 1 => 'http://schemas.xmlsoap.org/soap/envelope/', 2 => 'http://www.w3.org/2003/05/soap-envelope' }
108
+ raise("Unknown protocol version '#{@protocol_version.inspect}'") if soap_namespace[args[:version]].nil?
109
+ @envelope_namespace = soap_namespace[args[:version]]
110
+ @request_content_type = args[:version] == 1 ? "text/xml" : "application/soap+xml"
111
+ end
112
+ @envelope_namespace = args[:envelope_namespace] unless args[:envelope_namespace].nil?
113
+ @request_content_type = args[:request_content_type] unless args[:request_content_type].nil?
114
+ if @envelope_namespace.nil? || @request_content_type.nil?
115
+ raise("Missing option :envelope_namespace, :request_content_type or :version")
116
+ end
117
+ end
118
+ def self.envelope_namespace
119
+ @envelope_namespace
120
+ end
121
+ def self.request_content_type
122
+ @request_content_type
123
+ end
124
+ def self.uri
125
+ @uri
126
+ end
127
+ @@instance = {}
128
+ def self.instance
129
+ @@instance[self.to_s] ||= self.new
130
+ end
131
+ def self.method_missing(method, *args, &block)
132
+ if instance.respond_to?(method)
133
+ instance.__send__ method, *args, &block
134
+ else
135
+ super
136
+ end
137
+ end
138
+ def envelope_namespace
139
+ self.class.envelope_namespace
140
+ end
141
+ def request_content_type
142
+ self.class.request_content_type
143
+ end
144
+ def uri
145
+ self.class.uri
146
+ end
147
+ # Creates an XML document and sends it over HTTP.
148
+ #
149
+ # +action+ is the QName of the rootnode of the envelope.
150
+ #
151
+ # +options+ currently takes one option +:soap_action+, which can be one of:
152
+ #
153
+ # :auto sends a SOAPAction http header, deduced from the action name. (This is the default)
154
+ #
155
+ # +String+ sends a SOAPAction http header.
156
+ #
157
+ # +nil+ sends no SOAPAction http header.
158
+ def invoke(action, options = { :soap_action => :auto }, &block) # :yields: Handsoap::XmlMason::Element
159
+ if action
160
+ if options.kind_of? String
161
+ options = { :soap_action => options }
162
+ end
163
+ if options[:soap_action] == :auto
164
+ options[:soap_action] = action.gsub(/^.+:/, "")
165
+ elsif options[:soap_action] == :none
166
+ options[:soap_action] = nil
167
+ end
168
+ doc = make_envelope do |body|
169
+ body.add action
170
+ end
171
+ if block_given?
172
+ yield doc.find(action)
173
+ end
174
+ # ready to dispatch
175
+ headers = {
176
+ "Content-Type" => "#{self.request_content_type};charset=UTF-8"
177
+ }
178
+ headers["SOAPAction"] = options[:soap_action] unless options[:soap_action].nil?
179
+ on_before_dispatch
180
+ request = make_http_request(self.uri, doc.to_s, headers)
181
+ driver = Handsoap::Http.drivers[Handsoap.http_driver].new
182
+ response = driver.send_http_request(request)
183
+ parse_http_response(response)
184
+ end
185
+ end
186
+
187
+ # Async invocation
188
+ #
189
+ # Creates an XML document and sends it over HTTP.
190
+ #
191
+ # +user_block+ Block from userland
192
+ def async(user_block, &block) # :yields: Handsoap::AsyncDispatch
193
+ # Setup userland handlers
194
+ userland = Handsoap::Deferred.new
195
+ user_block.call(userland)
196
+ raise "Missing :callback" unless userland.has_callback?
197
+ raise "Missing :errback" unless userland.has_errback?
198
+ # Setup service level handlers
199
+ dispatcher = Handsoap::AsyncDispatch.new
200
+ yield dispatcher
201
+ raise "Missing :request_block" unless dispatcher.request_block
202
+ raise "Missing :response_block" unless dispatcher.response_block
203
+ # Done with the external configuration .. let's roll
204
+ action = dispatcher.action
205
+ options = dispatcher.options
206
+ if action #TODO: What if no action ?!?
207
+ if options.kind_of? String
208
+ options = { :soap_action => options }
209
+ end
210
+ if options[:soap_action] == :auto
211
+ options[:soap_action] = action.gsub(/^.+:/, "")
212
+ elsif options[:soap_action] == :none
213
+ options[:soap_action] = nil
214
+ end
215
+ doc = make_envelope do |body|
216
+ body.add action
217
+ end
218
+ dispatcher.request_block.call doc.find(action)
219
+ # ready to dispatch
220
+ headers = {
221
+ "Content-Type" => "#{self.request_content_type};charset=UTF-8"
222
+ }
223
+ headers["SOAPAction"] = options[:soap_action] unless options[:soap_action].nil?
224
+ on_before_dispatch
225
+ request = make_http_request(self.uri, doc.to_s, headers)
226
+ driver = Handsoap::Http.drivers[Handsoap.http_driver].new
227
+ if driver.respond_to? :send_http_request_async
228
+ deferred = driver.send_http_request_async(request)
229
+ else
230
+ # Fake async for sync-only drivers
231
+ deferred = Handsoap::Deferred.new
232
+ begin
233
+ deferred.trigger_callback driver.send_http_request(request)
234
+ rescue
235
+ deferred.trigger_errback $!
236
+ end
237
+ end
238
+ deferred.callback do |http_response|
239
+ begin
240
+ # Parse response
241
+ response_document = parse_http_response(http_response)
242
+ # Transform response
243
+ result = dispatcher.response_block.call(response_document)
244
+ # Yield to userland code
245
+ userland.trigger_callback(result)
246
+ rescue
247
+ userland.trigger_errback $!
248
+ end
249
+ end
250
+ # Pass driver level errors on
251
+ deferred.errback do |ex|
252
+ userland.trigger_errback(ex)
253
+ end
254
+ end
255
+ return nil
256
+ end
257
+
258
+ # Hook that is called when a new request document is created.
259
+ #
260
+ # You can override this to add namespaces and other elements that are common to all requests (Such as authentication).
261
+ def on_create_document(doc)
262
+ end
263
+ # Hook that is called before the message is dispatched.
264
+ #
265
+ # You can override this to provide filtering and logging.
266
+ def on_before_dispatch
267
+ end
268
+ # Hook that is called after the http_client is created.
269
+ #
270
+ # You can override this to customize the http_client
271
+ def on_after_create_http_request(http_request)
272
+ end
273
+ # Hook that is called when there is a response.
274
+ #
275
+ # You can override this to register common namespaces, useful for parsing the document.
276
+ def on_response_document(doc)
277
+ end
278
+ # Hook that is called if there is a HTTP level error.
279
+ #
280
+ # Default behaviour is to raise an error.
281
+ def on_http_error(response)
282
+ raise HttpError, response
283
+ end
284
+ # Hook that is called if the dispatch returns a +Fault+.
285
+ #
286
+ # Default behaviour is to raise the Fault, but you can override this to provide logging and more fine-grained handling faults.
287
+ #
288
+ # See also: parse_soap_fault
289
+ def on_fault(fault)
290
+ raise fault
291
+ end
292
+ # Hook that is called if the response does not contain a valid SOAP enevlope.
293
+ #
294
+ # Default behaviour is to raise an error
295
+ #
296
+ # Note that if your service has operations that are one-way, you shouldn't raise an error here.
297
+ # This is however a fairly exotic case, so that is why the default behaviour is to raise an error.
298
+ def on_missing_document(response)
299
+ raise "The response is not a valid SOAP envelope"
300
+ end
301
+
302
+ def debug(message = nil) #:nodoc:
303
+ if @@logger
304
+ if message
305
+ @@logger.puts(message)
306
+ end
307
+ if block_given?
308
+ yield @@logger
309
+ end
310
+ end
311
+ end
312
+
313
+ def make_http_request(uri, post_body, headers)
314
+ request = Handsoap::Http::Request.new(uri, :post)
315
+ headers.each do |key, value|
316
+ request.add_header(key, value)
317
+ end
318
+ request.body = post_body
319
+ debug do |logger|
320
+ logger.puts request.inspect
321
+ end
322
+ on_after_create_http_request(request)
323
+ request
324
+ end
325
+
326
+ # Start the parsing pipe-line.
327
+ # There are various stages and hooks for each, so that you can override those in your service classes.
328
+ def parse_http_response(response)
329
+ debug do |logger|
330
+ logger.puts(response.inspect do |body|
331
+ Handsoap.pretty_format_envelope(body).chomp
332
+ end)
333
+ end
334
+ xml_document = parse_soap_response_document(response.primary_part.body)
335
+ soap_fault = parse_soap_fault(xml_document)
336
+ # Is the response a soap-fault?
337
+ unless soap_fault.nil?
338
+ return on_fault(soap_fault)
339
+ end
340
+ # Does the http-status indicate an error?
341
+ if response.status >= 400
342
+ return on_http_error(response)
343
+ end
344
+ # Does the response contain a valid xml-document?
345
+ if xml_document.nil?
346
+ return on_missing_document(response)
347
+ end
348
+ # Everything seems in order.
349
+ on_response_document(xml_document)
350
+ return SoapResponse.new(xml_document, response)
351
+ end
352
+
353
+ # Creates a standard SOAP envelope and yields the +Body+ element.
354
+ def make_envelope # :yields: Handsoap::XmlMason::Element
355
+ doc = XmlMason::Document.new do |doc|
356
+ doc.alias 'env', self.envelope_namespace
357
+ doc.add "env:Envelope" do |env|
358
+ env.add "*:Header"
359
+ env.add "*:Body"
360
+ end
361
+ end
362
+ self.class.fire_on_create_document doc # deprecated .. use instance method
363
+ on_create_document(doc)
364
+ if block_given?
365
+ yield doc.find("Body")
366
+ end
367
+ return doc
368
+ end
369
+
370
+ # String -> [XmlDocument | nil]
371
+ def parse_soap_response_document(http_body)
372
+ begin
373
+ Handsoap::XmlQueryFront.parse_string(http_body, Handsoap.xml_query_driver)
374
+ rescue Handsoap::XmlQueryFront::ParseError => ex
375
+ nil
376
+ end
377
+ end
378
+
379
+ # XmlDocument -> [Fault | nil]
380
+ def parse_soap_fault(document)
381
+ unless document.nil?
382
+ node = document.xpath('/env:Envelope/env:Body/descendant-or-self::env:Fault', { 'env' => self.envelope_namespace }).first
383
+ Fault.from_xml(node, :namespace => self.envelope_namespace) unless node.nil?
384
+ end
385
+ end
386
+ end
387
+
388
+ def self.pretty_format_envelope(xml_string)
389
+ if /^<.*:Envelope/.match(xml_string)
390
+ begin
391
+ doc = Handsoap::XmlQueryFront.parse_string(xml_string, Handsoap.xml_query_driver)
392
+ rescue
393
+ return xml_string
394
+ end
395
+ return doc.to_xml
396
+ # return "\n\e[1;33m" + doc.to_s + "\e[0m"
397
+ end
398
+ return xml_string
399
+ end
400
+ end
401
+
402
+ # Legacy/BC code here. This shouldn't be used in new applications.
403
+ module Handsoap
404
+ class Service
405
+ # Registers a simple method mapping without any arguments and no parsing of response.
406
+ #
407
+ # This is deprecated
408
+ def self.map_method(mapping)
409
+ if @mapping.nil?
410
+ @mapping = {}
411
+ end
412
+ @mapping.merge! mapping
413
+ end
414
+ def self.get_mapping(name)
415
+ @mapping[name] if @mapping
416
+ end
417
+ def method_missing(method, *args, &block)
418
+ action = self.class.get_mapping(method)
419
+ if action
420
+ invoke(action, *args, &block)
421
+ else
422
+ super
423
+ end
424
+ end
425
+ # Registers a block to call when a request document is created.
426
+ #
427
+ # This is deprecated, in favour of #on_create_document
428
+ def self.on_create_document(&block)
429
+ @create_document_callback = block
430
+ end
431
+ def self.fire_on_create_document(doc)
432
+ if @create_document_callback
433
+ @create_document_callback.call doc
434
+ end
435
+ end
436
+ private
437
+ # Helper to serialize a node into a ruby string
438
+ #
439
+ # *deprecated*. Use Handsoap::XmlQueryFront::XmlElement#to_s
440
+ def xml_to_str(node, xquery = nil)
441
+ n = xquery ? node.xpath(xquery, ns).first : node
442
+ return if n.nil?
443
+ n.to_s
444
+ end
445
+ alias_method :xml_to_s, :xml_to_str
446
+ # Helper to serialize a node into a ruby integer
447
+ #
448
+ # *deprecated*. Use Handsoap::XmlQueryFront::XmlElement#to_i
449
+ def xml_to_int(node, xquery = nil)
450
+ n = xquery ? node.xpath(xquery, ns).first : node
451
+ return if n.nil?
452
+ n.to_s.to_i
453
+ end
454
+ alias_method :xml_to_i, :xml_to_int
455
+ # Helper to serialize a node into a ruby float
456
+ #
457
+ # *deprecated*. Use Handsoap::XmlQueryFront::XmlElement#to_f
458
+ def xml_to_float(node, xquery = nil)
459
+ n = xquery ? node.xpath(xquery, ns).first : node
460
+ return if n.nil?
461
+ n.to_s.to_f
462
+ end
463
+ alias_method :xml_to_f, :xml_to_float
464
+ # Helper to serialize a node into a ruby boolean
465
+ #
466
+ # *deprecated*. Use Handsoap::XmlQueryFront::XmlElement#to_boolean
467
+ def xml_to_bool(node, xquery = nil)
468
+ n = xquery ? node.xpath(xquery, ns).first : node
469
+ return if n.nil?
470
+ n.to_s == "true"
471
+ end
472
+ # Helper to serialize a node into a ruby Time object
473
+ #
474
+ # *deprecated*. Use Handsoap::XmlQueryFront::XmlElement#to_date
475
+ def xml_to_date(node, xquery = nil)
476
+ n = xquery ? node.xpath(xquery, ns).first : node
477
+ return if n.nil?
478
+ Time.iso8601(n.to_s)
479
+ end
480
+ end
481
+ end