actionwebservice 0.5.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.
- data/ChangeLog +47 -0
- data/MIT-LICENSE +21 -0
- data/README +238 -0
- data/Rakefile +144 -0
- data/TODO +13 -0
- data/examples/googlesearch/README +143 -0
- data/examples/googlesearch/autoloading/google_search_api.rb +50 -0
- data/examples/googlesearch/autoloading/google_search_controller.rb +57 -0
- data/examples/googlesearch/delegated/google_search_service.rb +108 -0
- data/examples/googlesearch/delegated/search_controller.rb +7 -0
- data/examples/googlesearch/direct/google_search_api.rb +50 -0
- data/examples/googlesearch/direct/search_controller.rb +58 -0
- data/examples/metaWeblog/README +16 -0
- data/examples/metaWeblog/blog_controller.rb +127 -0
- data/lib/action_web_service.rb +60 -0
- data/lib/action_web_service/api.rb +2 -0
- data/lib/action_web_service/api/abstract.rb +192 -0
- data/lib/action_web_service/api/action_controller.rb +92 -0
- data/lib/action_web_service/base.rb +41 -0
- data/lib/action_web_service/client.rb +3 -0
- data/lib/action_web_service/client/base.rb +39 -0
- data/lib/action_web_service/client/soap_client.rb +88 -0
- data/lib/action_web_service/client/xmlrpc_client.rb +77 -0
- data/lib/action_web_service/container.rb +85 -0
- data/lib/action_web_service/dispatcher.rb +2 -0
- data/lib/action_web_service/dispatcher/abstract.rb +150 -0
- data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +299 -0
- data/lib/action_web_service/invocation.rb +205 -0
- data/lib/action_web_service/protocol.rb +4 -0
- data/lib/action_web_service/protocol/abstract.rb +128 -0
- data/lib/action_web_service/protocol/registry.rb +55 -0
- data/lib/action_web_service/protocol/soap_protocol.rb +484 -0
- data/lib/action_web_service/protocol/xmlrpc_protocol.rb +168 -0
- data/lib/action_web_service/struct.rb +55 -0
- data/lib/action_web_service/support/class_inheritable_options.rb +26 -0
- data/lib/action_web_service/support/signature.rb +100 -0
- data/setup.rb +1360 -0
- data/test/abstract_client.rb +131 -0
- data/test/abstract_soap.rb +58 -0
- data/test/abstract_unit.rb +9 -0
- data/test/api_test.rb +52 -0
- data/test/base_test.rb +42 -0
- data/test/client_soap_test.rb +93 -0
- data/test/client_xmlrpc_test.rb +92 -0
- data/test/container_test.rb +53 -0
- data/test/dispatcher_action_controller_test.rb +186 -0
- data/test/gencov +3 -0
- data/test/invocation_test.rb +149 -0
- data/test/protocol_registry_test.rb +53 -0
- data/test/protocol_soap_test.rb +252 -0
- data/test/protocol_xmlrpc_test.rb +147 -0
- data/test/run +5 -0
- data/test/struct_test.rb +40 -0
- metadata +131 -0
@@ -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
|