actionservice 0.2.99

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,74 @@
1
+ require 'action_service/support/class_inheritable_options'
2
+
3
+ module ActionService
4
+ class ActionServiceError < StandardError
5
+ end
6
+
7
+ class Base
8
+ class_inheritable_option :export_name_mangling, true
9
+ class_inheritable_option :report_exceptions, true
10
+ class_inheritable_option :logger
11
+
12
+ class << self
13
+ def export(name, options={})
14
+ validate_options([:expects, :returns, :expects_and_returns], options.keys)
15
+ if options[:expects_and_returns]
16
+ expects = options[:expects_and_returns]
17
+ returns = options[:expects_and_returns]
18
+ else
19
+ expects = options[:expects]
20
+ returns = options[:returns]
21
+ end
22
+ if expects.nil?
23
+ arity = instance_method(name).arity
24
+ expects = [Object] * arity if arity > 0
25
+ end
26
+ if returns.nil?
27
+ returns = [NilClass]
28
+ end
29
+
30
+ name = name.to_sym
31
+ public_name = public_export_name(name)
32
+ info = { :expects => expects, :returns => returns }
33
+ write_inheritable_hash("action_service_exports", name => info)
34
+ write_inheritable_hash("action_service_public_exports", public_name => name)
35
+ end
36
+
37
+ def has_export?(name)
38
+ exports.has_key?(name)
39
+ end
40
+
41
+ def has_public_export?(name)
42
+ public_exports.has_key?(name)
43
+ end
44
+
45
+ def public_export_name(export_name)
46
+ if export_name_mangling
47
+ Inflector.camelize(export_name.to_s)
48
+ else
49
+ export_name.to_s
50
+ end
51
+ end
52
+
53
+ def internal_export_name(public_name)
54
+ public_exports[public_name]
55
+ end
56
+
57
+ def exports
58
+ read_inheritable_attribute("action_service_exports") || {}
59
+ end
60
+
61
+ private
62
+ def public_exports
63
+ read_inheritable_attribute("action_service_public_exports") || {}
64
+ end
65
+
66
+ def validate_options(valid_option_keys, supplied_option_keys)
67
+ unknown_option_keys = supplied_option_keys - valid_option_keys
68
+ unless unknown_option_keys.empty?
69
+ raise(ActionServiceError, "Unknown options: #{unknown_option_keys}")
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,69 @@
1
+ module ActionService
2
+ module Container
3
+ class ContainerError < ActionService::ActionServiceError
4
+ end
5
+
6
+ def self.append_features(base)
7
+ super
8
+ base.extend(ClassMethods)
9
+ base.send(:include, ActionService::Container::InstanceMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ def service(name, object=nil, options={}, &block)
14
+ if (object && block_given?) || (object.nil? && block.nil?)
15
+ raise(ContainerError, "either service, or a block must be given")
16
+ end
17
+ name = name.to_sym
18
+ if block_given?
19
+ info = { name => { :block => block } }
20
+ else
21
+ info = { name => { :object => object } }
22
+ end
23
+ write_inheritable_hash("action_services", info)
24
+ call_service_definition_callbacks(self, name, info)
25
+ end
26
+
27
+ def add_service_definition_callback(&block)
28
+ write_inheritable_array("action_service_definition_callbacks", [block])
29
+ end
30
+
31
+ def has_service?(name)
32
+ services.has_key?(name.to_sym)
33
+ end
34
+
35
+ def services
36
+ read_inheritable_attribute("action_services") || {}
37
+ end
38
+
39
+ def service_object(service_name)
40
+ info = services[service_name.to_sym]
41
+ unless info
42
+ raise(ContainerError, "no such service '#{service_name}'")
43
+ end
44
+ service = info[:block]
45
+ service ? instance_eval(&service) : info[:object]
46
+ end
47
+
48
+ private
49
+ def call_service_definition_callbacks(container_klass, service_name, service_info)
50
+ (read_inheritable_attribute("action_service_definition_callbacks") || []).each do |block|
51
+ block.call(container_klass, service_name, service_info)
52
+ end
53
+ end
54
+ end
55
+
56
+ module InstanceMethods
57
+ private
58
+ def dispatch_service_invocation_request(protocol, info)
59
+ service = self.class.service_object(info.service_name)
60
+ method_name = service.class.internal_export_name(info.public_method_name)
61
+ export_info = service.class.exports[method_name]
62
+ params = protocol.unmarshal_request(info, export_info)
63
+ result = service.perform_invocation(method_name, params)
64
+ protocol.marshal_response(info, export_info, result)
65
+ end
66
+
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,172 @@
1
+ module ActionService
2
+ module Invocation
3
+ class InvocationError < ActionService::ActionServiceError
4
+ end
5
+
6
+ def self.append_features(base) # :nodoc:
7
+ super
8
+ base.extend(ClassMethods)
9
+ base.send(:include, ActionService::Invocation::InstanceMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ def append_before_invocation(*interceptors, &block)
14
+ conditions = extract_conditions!(interceptors)
15
+ interceptors << block if block_given?
16
+ add_interception_conditions(interceptors, conditions)
17
+ append_interceptors_to_chain("before", interceptors)
18
+ end
19
+
20
+ def prepend_before_invocation(*interceptors, &block)
21
+ conditions = extract_conditions!(interceptors)
22
+ interceptors << block if block_given?
23
+ add_interception_conditions(interceptors, conditions)
24
+ prepend_interceptors_to_chain("before", interceptors)
25
+ end
26
+
27
+ alias :before_invocation :append_before_invocation
28
+
29
+ def append_after_invocation(*interceptors, &block)
30
+ conditions = extract_conditions!(interceptors)
31
+ interceptors << block if block_given?
32
+ add_interception_conditions(interceptors, conditions)
33
+ append_interceptors_to_chain("after", interceptors)
34
+ end
35
+
36
+ def prepend_after_invocation(*interceptors, &block)
37
+ conditions = extract_conditions!(interceptors)
38
+ interceptors << block if block_given?
39
+ add_interception_conditions(interceptors, conditions)
40
+ prepend_interceptors_to_chain("after", interceptors)
41
+ end
42
+
43
+ alias :after_invocation :append_after_invocation
44
+
45
+ def before_invocation_interceptors
46
+ read_inheritable_attribute("before_invocation_interceptors")
47
+ end
48
+
49
+ def after_invocation_interceptors
50
+ read_inheritable_attribute("after_invocation_interceptors")
51
+ end
52
+
53
+ def included_intercepted_methods
54
+ read_inheritable_attribute("included_intercepted_methods") || {}
55
+ end
56
+
57
+ def excluded_intercepted_methods
58
+ read_inheritable_attribute("excluded_intercepted_methods") || {}
59
+ end
60
+
61
+ private
62
+
63
+ def append_interceptors_to_chain(condition, interceptors)
64
+ write_inheritable_array("#{condition}_invocation_interceptors", interceptors)
65
+ end
66
+
67
+ def prepend_interceptors_to_chain(condition, interceptors)
68
+ interceptors = interceptors + read_inheritable_attribute("#{condition}_invocation_interceptors")
69
+ write_inheritable_attribute("#{condition}_invocation_interceptors", interceptors)
70
+ end
71
+
72
+ def extract_conditions!(interceptors)
73
+ return nil unless interceptors.last.is_a? Hash
74
+ interceptors.pop
75
+ end
76
+
77
+ def add_interception_conditions(interceptors, conditions)
78
+ return unless conditions
79
+ included, excluded = conditions[:only], conditions[:except]
80
+ write_inheritable_hash("included_intercepted_methods", condition_hash(interceptors, included)) && return if included
81
+ write_inheritable_hash("excluded_intercepted_methods", condition_hash(interceptors, excluded)) if excluded
82
+ end
83
+
84
+ def condition_hash(interceptors, *methods)
85
+ interceptors.inject({}) {|hash, interceptor| hash.merge(interceptor => methods.flatten.map {|method| method.to_s})}
86
+ end
87
+ end
88
+
89
+ module InstanceMethods # :nodoc:
90
+ def self.append_features(base)
91
+ super
92
+ base.class_eval do
93
+ alias_method :perform_invocation_without_interception, :perform_invocation
94
+ alias_method :perform_invocation, :perform_invocation_with_interception
95
+ end
96
+ end
97
+
98
+ def perform_invocation_with_interception(name, args=nil, &block)
99
+ return if before_invocation(name, args, &block) == false
100
+ result = perform_invocation_without_interception(name, args)
101
+ after_invocation(name, args, result)
102
+ result
103
+ end
104
+
105
+ def perform_invocation(name, args=nil)
106
+ unless self.respond_to?(name) and self.class.has_export?(name)
107
+ raise InvocationError, "no such exported method '#{name}'"
108
+ end
109
+ args ||= []
110
+ self.send(name, *args)
111
+ end
112
+
113
+ def before_invocation(name, args, &block)
114
+ call_interceptors(self.class.before_invocation_interceptors, [name, args], &block)
115
+ end
116
+
117
+ def after_invocation(name, args, result)
118
+ call_interceptors(self.class.after_invocation_interceptors, [name, args, result])
119
+ end
120
+
121
+ private
122
+
123
+ def call_interceptors(interceptors, interceptor_args, &block)
124
+ if interceptors and not interceptors.empty?
125
+ interceptors.each do |interceptor|
126
+ next if method_exempted?(interceptor, interceptor_args[0].to_s)
127
+ result = case
128
+ when interceptor.is_a?(Symbol)
129
+ self.send(interceptor, *interceptor_args)
130
+ when interceptor_block?(interceptor)
131
+ interceptor.call(self, *interceptor_args)
132
+ when interceptor_class?(interceptor)
133
+ interceptor.intercept(self, *interceptor_args)
134
+ else
135
+ raise(
136
+ InvocationError,
137
+ "Interceptors need to be either a symbol, proc/method, or a class implementing a static intercept method"
138
+ )
139
+ end
140
+ reason = nil
141
+ if result.is_a?(Array)
142
+ reason = result[1] if result[1]
143
+ result = result[0]
144
+ end
145
+ if result == false
146
+ block.call(reason) if block && reason
147
+ return false
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ def interceptor_block?(interceptor)
154
+ interceptor.respond_to?("call") && (interceptor.arity == 3 || interceptor.arity == -1)
155
+ end
156
+
157
+ def interceptor_class?(interceptor)
158
+ interceptor.respond_to?("intercept")
159
+ end
160
+
161
+ def method_exempted?(interceptor, method_name)
162
+ case
163
+ when self.class.included_intercepted_methods[interceptor]
164
+ !self.class.included_intercepted_methods[interceptor].include?(method_name)
165
+ when self.class.excluded_intercepted_methods[interceptor]
166
+ self.class.excluded_intercepted_methods[interceptor].include?(method_name)
167
+ end
168
+ end
169
+
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,61 @@
1
+ module ActionService
2
+ module Protocol
3
+ class ProtocolError < ActionService::ActionServiceError
4
+ end
5
+
6
+ class AbstractProtocol
7
+ attr :container_klass
8
+
9
+ def initialize(container_klass)
10
+ @container_klass = container_klass
11
+ end
12
+
13
+ def unmarshal_request(request_info, export_info)
14
+ raise NotImplementedError
15
+ end
16
+
17
+ def marshal_response(request_info, export_info, return_value)
18
+ raise NotImplementedError
19
+ end
20
+
21
+ def marshal_exception(exception)
22
+ raise NotImplementedError
23
+ end
24
+
25
+ def request_info(request)
26
+ raise NotImplementedError
27
+ end
28
+
29
+ def request_supported?(request)
30
+ raise NotImplementedError
31
+ end
32
+ end
33
+
34
+ class ServiceRequestInfo
35
+ attr :service_name
36
+ attr :public_method_name
37
+ attr :raw_body
38
+ attr :content_type
39
+ attr :protocol_info
40
+
41
+ def initialize(service_name, public_method_name, raw_body, content_type, protocol_info)
42
+ @service_name = service_name
43
+ @public_method_name = public_method_name
44
+ @raw_body = raw_body
45
+ @content_type = content_type
46
+ @protocol_info = protocol_info
47
+ end
48
+ end
49
+
50
+ class ServiceResponseInfo
51
+ attr :raw_body
52
+ attr :content_type
53
+
54
+ def initialize(raw_body, content_type)
55
+ @raw_body = raw_body
56
+ @content_type = content_type
57
+ end
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,49 @@
1
+ module ActionService
2
+ module Protocol
3
+ HeaderAndBody = :header_and_body
4
+ BodyOnly = :body_only
5
+
6
+ module Registry
7
+ def self.append_features(base)
8
+ super
9
+ base.extend(ClassMethods)
10
+ base.send(:include, ActionService::Protocol::Registry::InstanceMethods)
11
+ end
12
+
13
+ module ClassMethods
14
+ def register_protocol(type, klass)
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
27
+ private
28
+ def probe_request_protocol(action_pack_request)
29
+ (header_and_body_protocols + body_only_protocols).each do |klass|
30
+ protocol = klass.new(self.class)
31
+ if protocol.request_supported?(action_pack_request)
32
+ return protocol
33
+ end
34
+ end
35
+ raise(ProtocolError, "unsupported request message format")
36
+ end
37
+
38
+ def header_and_body_protocols
39
+ self.class.read_inheritable_attribute("header_and_body_protocols") || []
40
+ end
41
+
42
+ def body_only_protocols
43
+ self.class.read_inheritable_attribute("body_only_protocols") || []
44
+ end
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,386 @@
1
+ require 'soap/processor'
2
+ require 'soap/mapping'
3
+ require 'soap/rpc/element'
4
+ require 'xsd/datatypes'
5
+ require 'xsd/ns'
6
+
7
+ module ActionService
8
+ module Protocol
9
+ module Soap
10
+ class ProtocolError < ActionService::ActionServiceError
11
+ end
12
+
13
+ def self.append_features(base)
14
+ super
15
+ base.register_protocol(HeaderAndBody, SoapProtocol)
16
+ base.extend(ClassMethods)
17
+ base.wsdl_service_name('ActionService')
18
+ end
19
+
20
+ module ClassMethods
21
+ def wsdl_service_name(name)
22
+ write_inheritable_attribute("soap_mapper", SoapMapper.new("urn:#{name}"))
23
+ end
24
+
25
+ def soap_mapper
26
+ read_inheritable_attribute("soap_mapper")
27
+ end
28
+ end
29
+
30
+ class SoapProtocol < AbstractProtocol
31
+ def unmarshal_request(request_info, export_info)
32
+ expects = export_info[:expects]
33
+ map_types(expects) if expects
34
+ envelope = SOAP::Processor.unmarshal(request_info.raw_body)
35
+ request = envelope.body.request
36
+ params = request.collect{|k, v| request[k]}
37
+ params = soap_to_ruby_array(params)
38
+ validate_types(request_info.public_method_name, expects, params, :in) if expects
39
+ params
40
+ end
41
+
42
+ def marshal_response(request_info, export_info, return_value)
43
+ returns = export_info[:returns]
44
+ if returns
45
+ map_types(returns)
46
+ mapping = mapper.lookup(returns[0])
47
+ return_value = fixup_array_types(mapping, return_value)
48
+ end
49
+ validate_types(request_info.public_method_name, returns, [return_value], :out)
50
+ return_value = ruby_to_soap(return_value)
51
+ param_def = returns ? [['retval', 'return', mapping.registry_mapping]] : []
52
+ qname = XSD::QName.new(mapper.custom_namespace, request_info.public_method_name)
53
+ response = SOAP::RPC::SOAPMethodResponse.new(qname, param_def)
54
+ response.retval = return_value
55
+ ServiceResponseInfo.new(create_response(response), 'text/xml')
56
+ end
57
+
58
+ def marshal_exception(exception)
59
+ ServiceResponseInfo.new(create_exception_response(exception), 'text/xml')
60
+ end
61
+
62
+ def request_info(request)
63
+ soap_action = extract_soap_action(request)
64
+ service_name = request.parameters['action']
65
+ public_method_name = soap_action.gsub(/^[\/]+/, '').split(/[\/]+/)[-1]
66
+ ServiceRequestInfo.new(service_name,
67
+ public_method_name,
68
+ request.raw_post,
69
+ request.env['HTTP_CONTENT_TYPE'] || 'text/xml',
70
+ :soap => { :action => soap_action })
71
+ end
72
+
73
+ def request_supported?(request)
74
+ extract_soap_action(request) ? true : false
75
+ end
76
+
77
+ private
78
+ def mapper
79
+ container_klass.soap_mapper
80
+ end
81
+
82
+ def fixup_array_types(mapping, obj)
83
+ mapping.each_attribute do |name, type, attr_mapping|
84
+ if attr_mapping.custom_type?
85
+ attr_obj = obj.send(name)
86
+ new_obj = fixup_array_types(attr_mapping, attr_obj)
87
+ obj.send("#{name}=", new_obj) unless new_obj.equal?(attr_obj)
88
+ end
89
+ end
90
+ if mapping.is_a?(SoapArrayMapping)
91
+ obj = mapping.ruby_klass.new(obj)
92
+ else
93
+ obj
94
+ end
95
+ end
96
+
97
+ def extract_soap_action(request)
98
+ return nil unless request.method == :post
99
+ content_type = request.env['HTTP_CONTENT_TYPE'] || 'text/xml'
100
+ return nil unless content_type
101
+ soap_action = request.env['HTTP_SOAPACTION']
102
+ return nil unless soap_action
103
+ soap_action.gsub!(/^"/, '')
104
+ soap_action.gsub!(/"$/, '')
105
+ soap_action.strip!
106
+ return nil if soap_action.empty?
107
+ soap_action
108
+ end
109
+
110
+ def map_types(types)
111
+ types.collect{|type| mapper.map(type)}
112
+ end
113
+
114
+ def validate_types(method_name, signature, params, kind)
115
+ unless signature.length == params.length
116
+ raise(ProtocolError, "signature/parameter mismatch")
117
+ end
118
+ case kind
119
+ when :in
120
+ extra_msg = ' (Input parameter %d of method "%s")'
121
+ when :out
122
+ extra_msg = ' (Return value %d of method "%s")'
123
+ end
124
+ i = 0
125
+ signature.each do |klass|
126
+ check_compatibility(params[i].class, klass, extra_msg % [i, method_name])
127
+ i += 1
128
+ end
129
+ end
130
+
131
+ def check_compatibility(klass1, klass2, extra_msg=nil)
132
+ if (klass1 == TrueClass or klass1 == FalseClass) and \
133
+ (klass2 == TrueClass or klass2 == FalseClass)
134
+ return true
135
+ end
136
+ unless klass1.ancestors.include?(klass2) || \
137
+ klass2.ancestors.include?(klass1)
138
+ raise(ProtocolError, "value of type #{klass1.name} is not compatible " +
139
+ "with expected type #{klass2.name}" +
140
+ (extra_msg ? extra_msg : ''))
141
+ end
142
+ end
143
+
144
+ def create_response(body, is_error=false)
145
+ header = SOAP::SOAPHeader.new
146
+ body = SOAP::SOAPBody.new(body)
147
+ envelope = SOAP::SOAPEnvelope.new(header, body)
148
+ message = SOAP::Processor.marshal(envelope)
149
+ end
150
+
151
+ def create_exception_response(exc)
152
+ detail = SOAP::Mapping::SOAPException.new(exc)
153
+ body = SOAP::SOAPFault.new(
154
+ SOAP::SOAPString.new('Server'),
155
+ SOAP::SOAPString.new(exc.to_s),
156
+ SOAP::SOAPString.new(self.class.name),
157
+ SOAP::Mapping.obj2soap(detail))
158
+ create_response(body, true)
159
+ end
160
+
161
+ def ruby_to_soap(obj)
162
+ SOAP::Mapping.obj2soap(obj, mapper.registry)
163
+ end
164
+
165
+ def soap_to_ruby(obj)
166
+ SOAP::Mapping.soap2obj(obj, mapper.registry)
167
+ end
168
+
169
+ def soap_to_ruby_array(array)
170
+ array.map{|x| soap_to_ruby(x)}
171
+ end
172
+ end
173
+
174
+ class SoapMapper
175
+ attr :registry
176
+ attr :custom_namespace
177
+ attr :custom_types
178
+
179
+ def initialize(custom_namespace)
180
+ @custom_namespace = custom_namespace
181
+ @registry = SOAP::Mapping::Registry.new
182
+ @klass2map = {}
183
+ @custom_types = {}
184
+ end
185
+
186
+ def lookup(klass)
187
+ return @klass2map[klass] if @klass2map.has_key?(klass)
188
+
189
+ custom_type = false
190
+
191
+ ruby_klass = select_class(klass.is_a?(Array) ? klass[0] : klass)
192
+ type_name = Inflector.camelize(ruby_klass.name.split(/::/)[-1])
193
+
194
+ # Array signatures generate a double-mapping and require generation
195
+ # of an Array subclass to represent the mapping in the SOAP
196
+ # registry
197
+ array_klass = nil
198
+ if klass.is_a?(Array)
199
+ array_klass = Class.new(Array) do
200
+ module_eval <<-END
201
+ def self.name
202
+ "#{type_name}Array"
203
+ end
204
+ END
205
+ end
206
+ end
207
+
208
+ mapping = @registry.find_mapped_soap_class(ruby_klass) rescue nil
209
+ unless mapping
210
+ # Custom structured type, generate a mapping
211
+ info = { :type => XSD::QName.new(@custom_namespace, type_name) }
212
+ @registry.add(ruby_klass,
213
+ SOAP::SOAPStruct,
214
+ SOAP::Mapping::Registry::TypedStructFactory,
215
+ info)
216
+ mapping = ensure_mapped(ruby_klass)
217
+ custom_type = true
218
+ end
219
+
220
+ array_mapping = nil
221
+ if array_klass
222
+ # Typed array always requires a custom type. The info of the array
223
+ # is the info of its element type (in mapping[2]), falling back
224
+ # to SOAP base types.
225
+ info = mapping[2]
226
+ info ||= {}
227
+ info[:type] ||= soap_base_type_qname(mapping[0])
228
+ @registry.add(array_klass,
229
+ SOAP::SOAPArray,
230
+ SOAP::Mapping::Registry::TypedArrayFactory,
231
+ info)
232
+ array_mapping = ensure_mapped(array_klass)
233
+ end
234
+
235
+ if array_mapping
236
+ @klass2map[ruby_klass] = SoapMapping.new(self,
237
+ type_name,
238
+ ruby_klass,
239
+ mapping[0],
240
+ mapping,
241
+ custom_type)
242
+ @klass2map[klass] = SoapArrayMapping.new(self,
243
+ type_name,
244
+ array_klass,
245
+ array_mapping[0],
246
+ array_mapping,
247
+ @klass2map[ruby_klass])
248
+ @custom_types[klass] = @klass2map[klass]
249
+ @custom_types[ruby_klass] = @klass2map[ruby_klass] if custom_type
250
+ else
251
+ @klass2map[klass] = SoapMapping.new(self,
252
+ type_name,
253
+ ruby_klass,
254
+ mapping[0],
255
+ mapping,
256
+ custom_type)
257
+ @custom_types[klass] = @klass2map[klass] if custom_type
258
+ end
259
+
260
+ @klass2map[klass]
261
+ end
262
+ alias :map :lookup
263
+
264
+ def map_container_services(container_klass, &block)
265
+ container_klass.services.each do |service_name, service_info|
266
+ object = container_klass.service_object(service_name)
267
+ service_klass = object.class
268
+ service_exports = {}
269
+ object.class.exports.each do |export_name, export_info|
270
+ expects = export_info[:expects]
271
+ lookup_proc = lambda do |klass|
272
+ mapping = lookup(klass)
273
+ custom_mapping = nil
274
+ if mapping.respond_to?(:element_mapping)
275
+ custom_mapping = mapping.element_mapping
276
+ else
277
+ custom_mapping = mapping
278
+ end
279
+ if custom_mapping && custom_mapping.custom_type?
280
+ # What gives? This is required so that structure types
281
+ # referenced only by structures (and not signatures) still
282
+ # have a custom type mapping in the registry (needed for WSDL
283
+ # generation).
284
+ custom_mapping.each_attribute{}
285
+ end
286
+ mapping
287
+ end
288
+ expects_signature = expects ? expects.map{|klass| lookup_proc.call(klass)} : nil
289
+ returns_signature = export_info[:returns].map{|klass| lookup_proc.call(klass)}
290
+ service_exports[export_name] = {
291
+ :expects => expects_signature,
292
+ :returns => returns_signature
293
+ }
294
+ end
295
+ yield service_name, service_klass, service_exports if block_given?
296
+ end
297
+ end
298
+
299
+ private
300
+ def select_class(klass)
301
+ return Integer if klass == Fixnum
302
+ klass
303
+ end
304
+
305
+ def ensure_mapped(klass)
306
+ mapping = @registry.find_mapped_soap_class(klass) rescue nil
307
+ raise(ProtocolError, "failed to register #{klass.name}") unless mapping
308
+ mapping
309
+ end
310
+
311
+ def soap_base_type_qname(base_type)
312
+ xsd_type = base_type.ancestors.find{|c| c.const_defined? 'Type'}
313
+ xsd_type ? xsd_type.const_get('Type') : XSD::XSDAnySimpleType::Type
314
+ end
315
+ end
316
+
317
+ class SoapMapping
318
+ attr :ruby_klass
319
+ attr :soap_klass
320
+ attr :registry_mapping
321
+
322
+ def initialize(mapper, type_name, ruby_klass, soap_klass, registry_mapping,
323
+ custom_type=false)
324
+ @mapper = mapper
325
+ @type_name = type_name
326
+ @ruby_klass = ruby_klass
327
+ @soap_klass = soap_klass
328
+ @registry_mapping = registry_mapping
329
+ @custom_type = custom_type
330
+ end
331
+
332
+ def type_name
333
+ @type_name
334
+ end
335
+
336
+ def custom_type?
337
+ @custom_type
338
+ end
339
+
340
+ def qualified_type_name
341
+ name = type_name
342
+ if custom_type?
343
+ "typens:#{name}"
344
+ else
345
+ xsd_type_for(@soap_klass)
346
+ end
347
+ end
348
+
349
+ def each_attribute(&block)
350
+ if @ruby_klass.respond_to?(:members)
351
+ @ruby_klass.members.each do |name, klass|
352
+ name = name.to_s
353
+ mapping = @mapper.lookup(klass)
354
+ yield name, mapping.qualified_type_name, mapping
355
+ end
356
+ end
357
+ end
358
+
359
+ def is_xsd_type?(klass)
360
+ klass.ancestors.include?(XSD::NSDBase)
361
+ end
362
+
363
+ def xsd_type_for(klass)
364
+ ns = XSD::NS.new
365
+ ns.assign(XSD::Namespace, SOAP::XSDNamespaceTag)
366
+ xsd_klass = klass.ancestors.find{|c| c.const_defined?('Type')}
367
+ return ns.name(XSD::AnyTypeName) unless xsd_klass
368
+ ns.name(xsd_klass.const_get('Type'))
369
+ end
370
+ end
371
+
372
+ class SoapArrayMapping < SoapMapping
373
+ attr :element_mapping
374
+
375
+ def initialize(mapper, type_name, ruby_klass, soap_klass, registry_mapping, element_mapping)
376
+ super(mapper, type_name, ruby_klass, soap_klass, registry_mapping, true)
377
+ @element_mapping = element_mapping
378
+ end
379
+
380
+ def type_name
381
+ super + "Array"
382
+ end
383
+ end
384
+ end
385
+ end
386
+ end