actionwebservice 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/ChangeLog +47 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +238 -0
  4. data/Rakefile +144 -0
  5. data/TODO +13 -0
  6. data/examples/googlesearch/README +143 -0
  7. data/examples/googlesearch/autoloading/google_search_api.rb +50 -0
  8. data/examples/googlesearch/autoloading/google_search_controller.rb +57 -0
  9. data/examples/googlesearch/delegated/google_search_service.rb +108 -0
  10. data/examples/googlesearch/delegated/search_controller.rb +7 -0
  11. data/examples/googlesearch/direct/google_search_api.rb +50 -0
  12. data/examples/googlesearch/direct/search_controller.rb +58 -0
  13. data/examples/metaWeblog/README +16 -0
  14. data/examples/metaWeblog/blog_controller.rb +127 -0
  15. data/lib/action_web_service.rb +60 -0
  16. data/lib/action_web_service/api.rb +2 -0
  17. data/lib/action_web_service/api/abstract.rb +192 -0
  18. data/lib/action_web_service/api/action_controller.rb +92 -0
  19. data/lib/action_web_service/base.rb +41 -0
  20. data/lib/action_web_service/client.rb +3 -0
  21. data/lib/action_web_service/client/base.rb +39 -0
  22. data/lib/action_web_service/client/soap_client.rb +88 -0
  23. data/lib/action_web_service/client/xmlrpc_client.rb +77 -0
  24. data/lib/action_web_service/container.rb +85 -0
  25. data/lib/action_web_service/dispatcher.rb +2 -0
  26. data/lib/action_web_service/dispatcher/abstract.rb +150 -0
  27. data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +299 -0
  28. data/lib/action_web_service/invocation.rb +205 -0
  29. data/lib/action_web_service/protocol.rb +4 -0
  30. data/lib/action_web_service/protocol/abstract.rb +128 -0
  31. data/lib/action_web_service/protocol/registry.rb +55 -0
  32. data/lib/action_web_service/protocol/soap_protocol.rb +484 -0
  33. data/lib/action_web_service/protocol/xmlrpc_protocol.rb +168 -0
  34. data/lib/action_web_service/struct.rb +55 -0
  35. data/lib/action_web_service/support/class_inheritable_options.rb +26 -0
  36. data/lib/action_web_service/support/signature.rb +100 -0
  37. data/setup.rb +1360 -0
  38. data/test/abstract_client.rb +131 -0
  39. data/test/abstract_soap.rb +58 -0
  40. data/test/abstract_unit.rb +9 -0
  41. data/test/api_test.rb +52 -0
  42. data/test/base_test.rb +42 -0
  43. data/test/client_soap_test.rb +93 -0
  44. data/test/client_xmlrpc_test.rb +92 -0
  45. data/test/container_test.rb +53 -0
  46. data/test/dispatcher_action_controller_test.rb +186 -0
  47. data/test/gencov +3 -0
  48. data/test/invocation_test.rb +149 -0
  49. data/test/protocol_registry_test.rb +53 -0
  50. data/test/protocol_soap_test.rb +252 -0
  51. data/test/protocol_xmlrpc_test.rb +147 -0
  52. data/test/run +5 -0
  53. data/test/struct_test.rb +40 -0
  54. metadata +131 -0
@@ -0,0 +1,4 @@
1
+ require 'action_web_service/protocol/abstract'
2
+ require 'action_web_service/protocol/registry'
3
+ require 'action_web_service/protocol/soap_protocol'
4
+ require 'action_web_service/protocol/xmlrpc_protocol'
@@ -0,0 +1,128 @@
1
+ module ActionWebService # :nodoc:
2
+ module Protocol # :nodoc:
3
+ CheckedMessage = :checked
4
+ UncheckedMessage = :unchecked
5
+
6
+ class ProtocolError < ActionWebService::ActionWebServiceError # :nodoc:
7
+ end
8
+
9
+ class AbstractProtocol # :nodoc:
10
+ attr :container_class
11
+
12
+ def initialize(container_class)
13
+ @container_class = container_class
14
+ end
15
+
16
+ def unmarshal_request(protocol_request)
17
+ raise NotImplementedError
18
+ end
19
+
20
+ def marshal_response(protocol_request, return_value)
21
+ raise NotImplementedError
22
+ end
23
+
24
+ def marshal_exception(exception)
25
+ raise NotImplementedError
26
+ end
27
+
28
+ def self.create_protocol_request(container_class, action_pack_request)
29
+ nil
30
+ end
31
+
32
+ def self.create_protocol_client(api, protocol_name, endpoint_uri, options)
33
+ nil
34
+ end
35
+ end
36
+
37
+ class AbstractProtocolMessage # :nodoc:
38
+ attr_accessor :signature
39
+ attr_accessor :return_signature
40
+ attr_accessor :type
41
+ attr :options
42
+
43
+ def initialize(options={})
44
+ @signature = @return_signature = nil
45
+ @options = options
46
+ @type = @options[:type] || CheckedMessage
47
+ end
48
+
49
+ def signature=(value)
50
+ return if value.nil?
51
+ @signature = []
52
+ value.each do |klass|
53
+ if klass.is_a?(Hash)
54
+ @signature << klass.values.shift
55
+ else
56
+ @signature << klass
57
+ end
58
+ end
59
+ @signature
60
+ end
61
+
62
+ def checked?
63
+ @type == CheckedMessage
64
+ end
65
+
66
+ def check_parameter_types(values, signature)
67
+ return unless checked? && signature
68
+ unless signature.length == values.length
69
+ raise(ProtocolError, "Signature and parameter lengths mismatch")
70
+ end
71
+ (1..signature.length).each do |i|
72
+ check_compatibility(signature[i-1], values[i-1].class)
73
+ end
74
+ end
75
+
76
+ def check_compatibility(expected_class, received_class)
77
+ return if \
78
+ (expected_class == TrueClass or expected_class == FalseClass) and \
79
+ (received_class == TrueClass or received_class == FalseClass)
80
+ unless received_class.ancestors.include?(expected_class) or \
81
+ expected_class.ancestors.include?(received_class)
82
+ raise(ProtocolError, "value of type #{received_class.name} is not " +
83
+ "compatible with expected type #{expected_class.name}")
84
+ end
85
+ end
86
+ end
87
+
88
+ class ProtocolRequest < AbstractProtocolMessage # :nodoc:
89
+ attr :protocol
90
+ attr :raw_body
91
+
92
+ attr_accessor :web_service_name
93
+ attr_accessor :public_method_name
94
+ attr_accessor :content_type
95
+
96
+ def initialize(protocol, raw_body, web_service_name, public_method_name, content_type, options={})
97
+ super(options)
98
+ @protocol = protocol
99
+ @raw_body = raw_body
100
+ @web_service_name = web_service_name
101
+ @public_method_name = public_method_name
102
+ @content_type = content_type
103
+ end
104
+
105
+ def unmarshal
106
+ @protocol.unmarshal_request(self)
107
+ end
108
+
109
+ def marshal(return_value)
110
+ @protocol.marshal_response(self, return_value)
111
+ end
112
+ end
113
+
114
+ class ProtocolResponse < AbstractProtocolMessage # :nodoc:
115
+ attr :protocol
116
+ attr :raw_body
117
+
118
+ attr_accessor :content_type
119
+
120
+ def initialize(protocol, raw_body, content_type, options={})
121
+ super(options)
122
+ @protocol = protocol
123
+ @raw_body = raw_body
124
+ @content_type = content_type
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,55 @@
1
+ module ActionWebService # :nodoc:
2
+ module Protocol # :nodoc:
3
+ HeaderAndBody = :header_and_body
4
+ BodyOnly = :body_only
5
+
6
+ module Registry # :nodoc:
7
+ def self.append_features(base) # :nodoc:
8
+ super
9
+ base.extend(ClassMethods)
10
+ base.send(:include, ActionWebService::Protocol::Registry::InstanceMethods)
11
+ end
12
+
13
+ module ClassMethods # :nodoc:
14
+ def register_protocol(type, klass) # :nodoc:
15
+ case type
16
+ when HeaderAndBody
17
+ write_inheritable_array("header_and_body_protocols", [klass])
18
+ when BodyOnly
19
+ write_inheritable_array("body_only_protocols", [klass])
20
+ else
21
+ raise(ProtocolError, "unknown protocol type #{type}")
22
+ end
23
+ end
24
+ end
25
+
26
+ module InstanceMethods # :nodoc:
27
+ private
28
+ def probe_request_protocol(action_pack_request)
29
+ (header_and_body_protocols + body_only_protocols).each do |protocol|
30
+ protocol_request = protocol.create_protocol_request(self.class, action_pack_request)
31
+ return protocol_request if protocol_request
32
+ end
33
+ raise(ProtocolError, "unsupported request message format")
34
+ end
35
+
36
+ def probe_protocol_client(api, protocol_name, endpoint_uri, options)
37
+ (header_and_body_protocols + body_only_protocols).each do |protocol|
38
+ protocol_client = protocol.create_protocol_client(api, protocol_name, endpoint_uri, options)
39
+ return protocol_client if protocol_client
40
+ end
41
+ raise(ProtocolError, "unsupported client protocol :#{protocol_name}")
42
+ end
43
+
44
+ def header_and_body_protocols
45
+ self.class.read_inheritable_attribute("header_and_body_protocols") || []
46
+ end
47
+
48
+ def body_only_protocols
49
+ self.class.read_inheritable_attribute("body_only_protocols") || []
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,484 @@
1
+ require 'soap/processor'
2
+ require 'soap/mapping'
3
+ require 'soap/rpc/element'
4
+ require 'xsd/datatypes'
5
+ require 'xsd/ns'
6
+ require 'singleton'
7
+
8
+ module ActionWebService # :nodoc:
9
+ module Protocol # :nodoc:
10
+ module Soap # :nodoc:
11
+ class ProtocolError < ActionWebService::ActionWebServiceError # :nodoc:
12
+ end
13
+
14
+ def self.append_features(base) # :nodoc:
15
+ super
16
+ base.register_protocol(HeaderAndBody, SoapProtocol)
17
+ base.extend(ClassMethods)
18
+ base.wsdl_service_name('ActionWebService')
19
+ end
20
+
21
+ module ClassMethods
22
+ # Specifies the WSDL service name to use when generating WSDL. Highly
23
+ # recommended that you set this value, or code generators may generate
24
+ # classes with very generic names.
25
+ #
26
+ # === Example
27
+ # class MyController < ActionController::Base
28
+ # wsdl_service_name 'MyService'
29
+ # end
30
+ def wsdl_service_name(name)
31
+ write_inheritable_attribute("soap_mapper", SoapMapper.new("urn:#{name}"))
32
+ end
33
+
34
+ def soap_mapper # :nodoc:
35
+ read_inheritable_attribute("soap_mapper")
36
+ end
37
+ end
38
+
39
+ class SoapProtocol < AbstractProtocol # :nodoc:
40
+ attr :mapper
41
+
42
+ def initialize(mapper)
43
+ @mapper = mapper
44
+ end
45
+
46
+ def self.create_protocol_request(container_class, action_pack_request)
47
+ soap_action = extract_soap_action(action_pack_request)
48
+ return nil unless soap_action
49
+ service_name = action_pack_request.parameters['action']
50
+ public_method_name = soap_action.gsub(/^[\/]+/, '').split(/[\/]+/)[-1]
51
+ content_type = action_pack_request.env['HTTP_CONTENT_TYPE']
52
+ content_type ||= 'text/xml'
53
+ protocol = SoapProtocol.new(container_class.soap_mapper)
54
+ ProtocolRequest.new(protocol,
55
+ action_pack_request.raw_post,
56
+ service_name.to_sym,
57
+ public_method_name,
58
+ content_type)
59
+ end
60
+
61
+ def self.create_protocol_client(api, protocol_name, endpoint_uri, options)
62
+ return nil unless protocol_name.to_s.downcase.to_sym == :soap
63
+ ActionWebService::Client::Soap.new(api, endpoint_uri, options)
64
+ end
65
+
66
+ def unmarshal_request(protocol_request)
67
+ unmarshal = lambda do
68
+ envelope = SOAP::Processor.unmarshal(protocol_request.raw_body)
69
+ request = envelope.body.request
70
+ values = request.collect{|k, v| request[k]}
71
+ soap_to_ruby_array(values)
72
+ end
73
+ signature = protocol_request.signature
74
+ if signature
75
+ map_signature_types(signature)
76
+ values = unmarshal.call
77
+ signature = signature.map{|x|mapper.lookup(x).ruby_klass}
78
+ protocol_request.check_parameter_types(values, signature)
79
+ values
80
+ else
81
+ if protocol_request.checked?
82
+ []
83
+ else
84
+ unmarshal.call
85
+ end
86
+ end
87
+ end
88
+
89
+ def marshal_response(protocol_request, return_value)
90
+ marshal = lambda do |signature|
91
+ mapping = mapper.lookup(signature[0])
92
+ return_value = fixup_array_types(mapping, return_value)
93
+ signature = signature.map{|x|mapper.lookup(x).ruby_klass}
94
+ protocol_request.check_parameter_types([return_value], signature)
95
+ param_def = [['retval', 'return', mapping.registry_mapping]]
96
+ [param_def, ruby_to_soap(return_value)]
97
+ end
98
+ signature = protocol_request.return_signature
99
+ param_def = nil
100
+ if signature
101
+ param_def, return_value = marshal.call(signature)
102
+ else
103
+ if protocol_request.checked?
104
+ param_def, return_value = nil, nil
105
+ else
106
+ param_def, return_value = marshal.call([return_value.class])
107
+ end
108
+ end
109
+ qname = XSD::QName.new(mapper.custom_namespace,
110
+ protocol_request.public_method_name)
111
+ response = SOAP::RPC::SOAPMethodResponse.new(qname, param_def)
112
+ response.retval = return_value unless return_value.nil?
113
+ ProtocolResponse.new(self, create_response(response), 'text/xml')
114
+ end
115
+
116
+ def marshal_exception(exc)
117
+ ProtocolResponse.new(self, create_exception_response(exc), 'text/xml')
118
+ end
119
+
120
+ private
121
+ def self.extract_soap_action(request)
122
+ return nil unless request.method == :post
123
+ content_type = request.env['HTTP_CONTENT_TYPE'] || 'text/xml'
124
+ return nil unless content_type
125
+ soap_action = request.env['HTTP_SOAPACTION']
126
+ return nil unless soap_action
127
+ soap_action.gsub!(/^"/, '')
128
+ soap_action.gsub!(/"$/, '')
129
+ soap_action.strip!
130
+ return nil if soap_action.empty?
131
+ soap_action
132
+ end
133
+
134
+ def fixup_array_types(mapping, obj)
135
+ mapping.each_attribute do |name, type, attr_mapping|
136
+ if attr_mapping.custom_type?
137
+ attr_obj = obj.send(name)
138
+ new_obj = fixup_array_types(attr_mapping, attr_obj)
139
+ obj.send("#{name}=", new_obj) unless new_obj.equal?(attr_obj)
140
+ end
141
+ end
142
+ if mapping.is_a?(SoapArrayMapping)
143
+ obj = mapping.ruby_klass.new(obj)
144
+ # man, this is going to be slow for big arrays :(
145
+ (1..obj.size).each do |i|
146
+ i -= 1
147
+ obj[i] = fixup_array_types(mapping.element_mapping, obj[i])
148
+ end
149
+ else
150
+ if !mapping.generated_klass.nil? && mapping.generated_klass.respond_to?(:members)
151
+ # have to map the publically visible structure of the class
152
+ new_obj = mapping.generated_klass.new
153
+ mapping.generated_klass.members.each do |name, klass|
154
+ new_obj.send("#{name}=", obj.send(name))
155
+ end
156
+ obj = new_obj
157
+ end
158
+ end
159
+ obj
160
+ end
161
+
162
+ def map_signature_types(types)
163
+ types.collect{|type| mapper.map(type)}
164
+ end
165
+
166
+ def create_response(body)
167
+ header = SOAP::SOAPHeader.new
168
+ body = SOAP::SOAPBody.new(body)
169
+ envelope = SOAP::SOAPEnvelope.new(header, body)
170
+ SOAP::Processor.marshal(envelope)
171
+ end
172
+
173
+ def create_exception_response(exc)
174
+ detail = SOAP::Mapping::SOAPException.new(exc)
175
+ body = SOAP::SOAPFault.new(
176
+ SOAP::SOAPString.new('Server'),
177
+ SOAP::SOAPString.new(exc.to_s),
178
+ SOAP::SOAPString.new(self.class.name),
179
+ SOAP::Mapping.obj2soap(detail))
180
+ create_response(body)
181
+ end
182
+
183
+ def ruby_to_soap(obj)
184
+ SOAP::Mapping.obj2soap(obj, mapper.registry)
185
+ end
186
+
187
+ def soap_to_ruby(obj)
188
+ SOAP::Mapping.soap2obj(obj, mapper.registry)
189
+ end
190
+
191
+ def soap_to_ruby_array(array)
192
+ array.map{|x| soap_to_ruby(x)}
193
+ end
194
+ end
195
+
196
+ class SoapMapper # :nodoc:
197
+ attr :registry
198
+ attr :custom_namespace
199
+ attr :custom_types
200
+
201
+ def initialize(custom_namespace)
202
+ @custom_namespace = custom_namespace
203
+ @registry = SOAP::Mapping::Registry.new
204
+ @klass2map = {}
205
+ @custom_types = {}
206
+ @ar2klass = {}
207
+ end
208
+
209
+ def lookup(klass)
210
+ lookup_klass = klass.is_a?(Array) ? klass[0] : klass
211
+ generated_klass = nil
212
+ unless lookup_klass.respond_to?(:ancestors)
213
+ raise(ProtocolError, "expected parameter type definition to be a Class")
214
+ end
215
+ if lookup_klass.ancestors.include?(ActiveRecord::Base)
216
+ generated_klass = @ar2klass.has_key?(klass) ? @ar2klass[klass] : nil
217
+ klass = generated_klass if generated_klass
218
+ end
219
+ return @klass2map[klass] if @klass2map.has_key?(klass)
220
+
221
+ custom_type = false
222
+
223
+ ruby_klass = select_class(lookup_klass)
224
+ generated_klass = @ar2klass[lookup_klass] if @ar2klass.has_key?(lookup_klass)
225
+ type_name = ruby_klass.name
226
+
227
+ # Array signatures generate a double-mapping and require generation
228
+ # of an Array subclass to represent the mapping in the SOAP
229
+ # registry
230
+ array_klass = nil
231
+ if klass.is_a?(Array)
232
+ array_klass = Class.new(Array) do
233
+ module_eval <<-END
234
+ def self.name
235
+ "#{type_name}Array"
236
+ end
237
+ END
238
+ end
239
+ end
240
+
241
+ mapping = @registry.find_mapped_soap_class(ruby_klass) rescue nil
242
+ unless mapping
243
+ # Custom structured type, generate a mapping
244
+ info = { :type => XSD::QName.new(@custom_namespace, type_name) }
245
+ @registry.add(ruby_klass,
246
+ SOAP::SOAPStruct,
247
+ SOAP::Mapping::Registry::TypedStructFactory,
248
+ info)
249
+ mapping = ensure_mapped(ruby_klass)
250
+ custom_type = true
251
+ end
252
+
253
+ array_mapping = nil
254
+ if array_klass
255
+ # Typed array always requires a custom type. The info of the array
256
+ # is the info of its element type (in mapping[2]), falling back
257
+ # to SOAP base types.
258
+ info = mapping[2]
259
+ info ||= {}
260
+ info[:type] ||= soap_base_type_qname(mapping[0])
261
+ @registry.add(array_klass,
262
+ SOAP::SOAPArray,
263
+ SOAP::Mapping::Registry::TypedArrayFactory,
264
+ info)
265
+ array_mapping = ensure_mapped(array_klass)
266
+ end
267
+
268
+ if array_mapping
269
+ @klass2map[ruby_klass] = SoapMapping.new(self,
270
+ type_name,
271
+ ruby_klass,
272
+ generated_klass,
273
+ mapping[0],
274
+ mapping,
275
+ custom_type)
276
+ @klass2map[klass] = SoapArrayMapping.new(self,
277
+ type_name,
278
+ array_klass,
279
+ array_mapping[0],
280
+ array_mapping,
281
+ @klass2map[ruby_klass])
282
+ @custom_types[klass] = @klass2map[klass]
283
+ @custom_types[ruby_klass] = @klass2map[ruby_klass] if custom_type
284
+ else
285
+ @klass2map[klass] = SoapMapping.new(self,
286
+ type_name,
287
+ ruby_klass,
288
+ generated_klass,
289
+ mapping[0],
290
+ mapping,
291
+ custom_type)
292
+ @custom_types[klass] = @klass2map[klass] if custom_type
293
+ end
294
+
295
+ @klass2map[klass]
296
+ end
297
+ alias :map :lookup
298
+
299
+ def map_container_services(container, &block)
300
+ dispatching_mode = container.web_service_dispatching_mode
301
+ web_services = nil
302
+ case dispatching_mode
303
+ when :direct
304
+ api = container.class.web_service_api
305
+ if container.respond_to?(:controller_class_name)
306
+ web_service_name = container.controller_class_name.sub(/Controller$/, '').underscore
307
+ else
308
+ web_service_name = container.class.name.demodulize.underscore
309
+ end
310
+ web_services = { web_service_name => api }
311
+ when :delegated
312
+ web_services = {}
313
+ container.class.web_services.each do |web_service_name, web_service_info|
314
+ begin
315
+ object = container.web_service_object(web_service_name)
316
+ rescue Exception => e
317
+ raise(ProtocolError, "failed to retrieve web service object for web service '#{web_service_name}': #{e.message}")
318
+ end
319
+ web_services[web_service_name] = object.class.web_service_api
320
+ end
321
+ end
322
+ web_services.each do |web_service_name, api|
323
+ if api.nil?
324
+ raise(ProtocolError, "no web service API set while in :#{dispatching_mode} mode")
325
+ end
326
+ map_api(api) do |api_methods|
327
+ yield web_service_name, api, api_methods if block_given?
328
+ end
329
+ end
330
+ end
331
+
332
+ def map_api(api, &block)
333
+ lookup_proc = lambda do |klass|
334
+ mapping = lookup(klass)
335
+ custom_mapping = nil
336
+ if mapping.respond_to?(:element_mapping)
337
+ custom_mapping = mapping.element_mapping
338
+ else
339
+ custom_mapping = mapping
340
+ end
341
+ if custom_mapping && custom_mapping.custom_type?
342
+ # What gives? This is required so that structure types
343
+ # referenced only by structures (and not signatures) still
344
+ # have a custom type mapping in the registry (needed for WSDL
345
+ # generation).
346
+ custom_mapping.each_attribute{}
347
+ end
348
+ mapping
349
+ end
350
+ api_methods = block.nil?? nil : {}
351
+ api.api_methods.each do |method_name, method_info|
352
+ expects = method_info[:expects]
353
+ expects_signature = nil
354
+ if expects
355
+ expects_signature = block ? [] : nil
356
+ expects.each do |klass|
357
+ lookup_klass = nil
358
+ if klass.is_a?(Hash)
359
+ lookup_klass = lookup_proc.call(klass.values[0])
360
+ expects_signature << {klass.keys[0]=>lookup_klass} if block
361
+ else
362
+ lookup_klass = lookup_proc.call(klass)
363
+ expects_signature << lookup_klass if block
364
+ end
365
+ end
366
+ end
367
+ returns = method_info[:returns]
368
+ returns_signature = returns ? returns.map{|klass| lookup_proc.call(klass)} : nil
369
+ if block
370
+ api_methods[method_name] = {
371
+ :expects => expects_signature,
372
+ :returns => returns_signature
373
+ }
374
+ end
375
+ end
376
+ yield api_methods if block
377
+ end
378
+
379
+ private
380
+ def select_class(klass)
381
+ return Integer if klass == Fixnum
382
+ if klass.ancestors.include?(ActiveRecord::Base)
383
+ new_klass = Class.new(ActionWebService::Struct)
384
+ new_klass.class_eval <<-EOS
385
+ def self.name
386
+ "#{klass.name}"
387
+ end
388
+ EOS
389
+ klass.columns.each do |column|
390
+ next if column.klass.nil?
391
+ new_klass.send(:member, column.name.to_sym, column.klass)
392
+ end
393
+ @ar2klass[klass] = new_klass
394
+ return new_klass
395
+ end
396
+ klass
397
+ end
398
+
399
+ def ensure_mapped(klass)
400
+ mapping = @registry.find_mapped_soap_class(klass) rescue nil
401
+ raise(ProtocolError, "failed to register #{klass.name}") unless mapping
402
+ mapping
403
+ end
404
+
405
+ def soap_base_type_qname(base_type)
406
+ xsd_type = base_type.ancestors.find{|c| c.const_defined? 'Type'}
407
+ xsd_type ? xsd_type.const_get('Type') : XSD::XSDAnySimpleType::Type
408
+ end
409
+ end
410
+
411
+ class SoapMapping # :nodoc:
412
+ attr :ruby_klass
413
+ attr :generated_klass
414
+ attr :soap_klass
415
+ attr :registry_mapping
416
+
417
+ def initialize(mapper, type_name, ruby_klass, generated_klass, soap_klass, registry_mapping,
418
+ custom_type=false)
419
+ @mapper = mapper
420
+ @type_name = type_name
421
+ @ruby_klass = ruby_klass
422
+ @generated_klass = generated_klass
423
+ @soap_klass = soap_klass
424
+ @registry_mapping = registry_mapping
425
+ @custom_type = custom_type
426
+ end
427
+
428
+ def type_name
429
+ @type_name
430
+ end
431
+
432
+ def custom_type?
433
+ @custom_type
434
+ end
435
+
436
+ def qualified_type_name
437
+ name = type_name
438
+ if custom_type?
439
+ "typens:#{name}"
440
+ else
441
+ xsd_type_for(@soap_klass)
442
+ end
443
+ end
444
+
445
+ def each_attribute(&block)
446
+ if @ruby_klass.respond_to?(:members)
447
+ @ruby_klass.members.each do |name, klass|
448
+ name = name.to_s
449
+ mapping = @mapper.lookup(klass)
450
+ yield name, mapping.qualified_type_name, mapping
451
+ end
452
+ end
453
+ end
454
+
455
+ def is_xsd_type?(klass)
456
+ klass.ancestors.include?(XSD::NSDBase)
457
+ end
458
+
459
+ def xsd_type_for(klass)
460
+ ns = XSD::NS.new
461
+ ns.assign(XSD::Namespace, SOAP::XSDNamespaceTag)
462
+ xsd_klass = klass.ancestors.find{|c| c.const_defined?('Type')}
463
+ return ns.name(XSD::AnyTypeName) unless xsd_klass
464
+ ns.name(xsd_klass.const_get('Type'))
465
+ end
466
+ end
467
+
468
+ class SoapArrayMapping < SoapMapping # :nodoc:
469
+ attr :element_mapping
470
+
471
+ def initialize(mapper, type_name, ruby_klass, soap_klass, registry_mapping, element_mapping)
472
+ super(mapper, type_name, ruby_klass, nil, soap_klass, registry_mapping, true)
473
+ @element_mapping = element_mapping
474
+ end
475
+
476
+ def type_name
477
+ super + "Array"
478
+ end
479
+
480
+ def each_attribute(&block); end
481
+ end
482
+ end
483
+ end
484
+ end