handsoap 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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