actionwebservice 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/{ChangeLog → CHANGELOG} +20 -0
  2. data/README +45 -1
  3. data/Rakefile +12 -10
  4. data/TODO +8 -9
  5. data/lib/action_web_service.rb +10 -6
  6. data/lib/action_web_service/api.rb +1 -2
  7. data/lib/action_web_service/api/{abstract.rb → base.rb} +14 -71
  8. data/lib/action_web_service/base.rb +0 -3
  9. data/lib/action_web_service/client/base.rb +1 -12
  10. data/lib/action_web_service/client/soap_client.rb +49 -17
  11. data/lib/action_web_service/client/xmlrpc_client.rb +20 -15
  12. data/lib/action_web_service/container.rb +3 -85
  13. data/lib/action_web_service/{api/action_controller.rb → container/action_controller_container.rb} +2 -2
  14. data/lib/action_web_service/container/delegated_container.rb +87 -0
  15. data/lib/action_web_service/container/direct_container.rb +70 -0
  16. data/lib/action_web_service/dispatcher/abstract.rb +100 -102
  17. data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +199 -137
  18. data/lib/action_web_service/protocol.rb +1 -1
  19. data/lib/action_web_service/protocol/abstract.rb +14 -112
  20. data/lib/action_web_service/protocol/discovery.rb +37 -0
  21. data/lib/action_web_service/protocol/soap_protocol.rb +32 -458
  22. data/lib/action_web_service/protocol/xmlrpc_protocol.rb +29 -149
  23. data/lib/action_web_service/struct.rb +2 -5
  24. data/lib/action_web_service/test_invoke.rb +130 -0
  25. data/lib/action_web_service/vendor/ws.rb +4 -0
  26. data/lib/action_web_service/vendor/ws/common.rb +8 -0
  27. data/lib/action_web_service/vendor/ws/encoding.rb +3 -0
  28. data/lib/action_web_service/vendor/ws/encoding/abstract.rb +26 -0
  29. data/lib/action_web_service/vendor/ws/encoding/soap_rpc_encoding.rb +90 -0
  30. data/lib/action_web_service/vendor/ws/encoding/xmlrpc_encoding.rb +53 -0
  31. data/lib/action_web_service/vendor/ws/marshaling.rb +3 -0
  32. data/lib/action_web_service/vendor/ws/marshaling/abstract.rb +17 -0
  33. data/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb +277 -0
  34. data/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb +116 -0
  35. data/lib/action_web_service/vendor/ws/types.rb +162 -0
  36. data/test/abstract_client.rb +8 -11
  37. data/test/abstract_dispatcher.rb +370 -0
  38. data/test/abstract_unit.rb +1 -0
  39. data/test/api_test.rb +18 -1
  40. data/test/apis/auto_load_api.rb +3 -0
  41. data/test/apis/broken_auto_load_api.rb +2 -0
  42. data/test/client_soap_test.rb +16 -3
  43. data/test/client_xmlrpc_test.rb +16 -4
  44. data/test/container_test.rb +28 -8
  45. data/test/dispatcher_action_controller_soap_test.rb +106 -0
  46. data/test/dispatcher_action_controller_xmlrpc_test.rb +44 -0
  47. data/test/gencov +1 -1
  48. data/test/invocation_test.rb +39 -3
  49. data/test/run +4 -4
  50. data/test/test_invoke_test.rb +77 -0
  51. data/test/ws/abstract_encoding.rb +68 -0
  52. data/test/ws/abstract_unit.rb +13 -0
  53. data/test/ws/gencov +3 -0
  54. data/test/ws/run +5 -0
  55. data/test/ws/soap_marshaling_test.rb +91 -0
  56. data/test/ws/soap_rpc_encoding_test.rb +47 -0
  57. data/test/ws/types_test.rb +41 -0
  58. data/test/ws/xmlrpc_encoding_test.rb +34 -0
  59. metadata +48 -19
  60. data/lib/action_web_service/protocol/registry.rb +0 -55
  61. data/lib/action_web_service/support/signature.rb +0 -100
  62. data/test/abstract_soap.rb +0 -58
  63. data/test/dispatcher_action_controller_test.rb +0 -186
  64. data/test/protocol_registry_test.rb +0 -53
  65. data/test/protocol_soap_test.rb +0 -252
  66. data/test/protocol_xmlrpc_test.rb +0 -147
@@ -0,0 +1,53 @@
1
+ require 'xmlrpc/marshal'
2
+
3
+ module WS
4
+ module Encoding
5
+ class XmlRpcError < WSError
6
+ end
7
+
8
+ class XmlRpcEncoding < AbstractEncoding
9
+ def encode_rpc_call(method_name, params)
10
+ XMLRPC::Marshal.dump_call(method_name, *params)
11
+ end
12
+
13
+ def decode_rpc_call(obj)
14
+ method_name, params = XMLRPC::Marshal.load_call(obj) rescue nil
15
+ unless method_name && params
16
+ raise(XmlRpcError, "Malformed XML-RPC request")
17
+ end
18
+ i = 0
19
+ params = params.map do |value|
20
+ param = XmlRpcDecodedParam.new("param#{i}", value)
21
+ i += 1
22
+ param
23
+ end
24
+ [method_name, params]
25
+ end
26
+
27
+ def encode_rpc_response(method_name, return_value)
28
+ if return_value.nil?
29
+ XMLRPC::Marshal.dump_response(true)
30
+ else
31
+ XMLRPC::Marshal.dump_response(return_value)
32
+ end
33
+ end
34
+
35
+ def decode_rpc_response(obj)
36
+ return_value = XMLRPC::Marshal.load_response(obj) rescue nil
37
+ if return_value.nil?
38
+ raise(XmlRpcError, "Malformed XML-RPC response")
39
+ end
40
+ [nil, XmlRpcDecodedParam.new('return', return_value)]
41
+ end
42
+ end
43
+
44
+ class XmlRpcDecodedParam
45
+ attr :param
46
+
47
+ def initialize(name, value)
48
+ info = ParamInfo.new(name, value.class)
49
+ @param = Param.new(value, info)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ require 'ws/marshaling/abstract'
2
+ require 'ws/marshaling/soap_marshaling'
3
+ require 'ws/marshaling/xmlrpc_marshaling'
@@ -0,0 +1,17 @@
1
+ module WS
2
+ module Marshaling
3
+ class AbstractMarshaler
4
+ def marshal(param)
5
+ raise NotImplementedError
6
+ end
7
+
8
+ def unmarshal(param)
9
+ raise NotImplementedError
10
+ end
11
+
12
+ def register_type(type)
13
+ nil
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,277 @@
1
+ require 'soap/mapping'
2
+ require 'xsd/ns'
3
+
4
+ module WS
5
+ module Marshaling
6
+ SoapEncodingNS = 'http://schemas.xmlsoap.org/soap/encoding/'
7
+
8
+ class SoapError < WSError
9
+ end
10
+
11
+ class SoapMarshaler < AbstractMarshaler
12
+ attr :registry
13
+ attr_accessor :type_namespace
14
+
15
+ def initialize(type_namespace='')
16
+ @type_namespace = type_namespace
17
+ @registry = SOAP::Mapping::Registry.new
18
+ @spec2binding = {}
19
+ end
20
+
21
+ def marshal(param)
22
+ annotate_arrays(param.info.data, param.value)
23
+ if param.value.is_a?(Exception)
24
+ detail = SOAP::Mapping::SOAPException.new(param.value)
25
+ soap_obj = SOAP::SOAPFault.new(
26
+ SOAP::SOAPString.new('Server'),
27
+ SOAP::SOAPString.new(param.value.to_s),
28
+ SOAP::SOAPString.new(self.class.name),
29
+ SOAP::Mapping.obj2soap(detail))
30
+ else
31
+ soap_obj = SOAP::Mapping.obj2soap(param.value, @registry)
32
+ end
33
+ SoapForeignObject.new(param, soap_obj)
34
+ end
35
+
36
+ def unmarshal(obj)
37
+ param = obj.param
38
+ soap_object = obj.soap_object
39
+ soap_type = soap_object ? soap_object.type : nil
40
+ value = soap_object ? SOAP::Mapping.soap2obj(soap_object, @registry) : nil
41
+ param.value = value
42
+ param.info.type = value.class
43
+ mapping = @registry.find_mapped_soap_class(param.info.type) rescue nil
44
+ if soap_type && soap_type.name == 'Array' && soap_type.namespace == SoapEncodingNS
45
+ param.info.data = SoapBinding.new(self, soap_object.arytype, Array, mapping)
46
+ else
47
+ param.info.data = SoapBinding.new(self, soap_type, value.class, mapping)
48
+ end
49
+ param
50
+ end
51
+
52
+ def register_type(spec)
53
+ if @spec2binding.has_key?(spec)
54
+ return @spec2binding[spec]
55
+ end
56
+
57
+ klass = BaseTypes.canonical_param_type_class(spec)
58
+ if klass.is_a?(Array)
59
+ type_class = klass[0]
60
+ else
61
+ type_class = klass
62
+ end
63
+
64
+ type_binding = nil
65
+ if (mapping = @registry.find_mapped_soap_class(type_class) rescue nil)
66
+ qname = mapping[2] ? mapping[2][:type] : nil
67
+ qname ||= soap_base_type_name(mapping[0])
68
+ type_binding = SoapBinding.new(self, qname, type_class, mapping)
69
+ else
70
+ qname = XSD::QName.new(@type_namespace, soap_type_name(type_class.name))
71
+ @registry.add(type_class,
72
+ SOAP::SOAPStruct,
73
+ typed_struct_factory(type_class),
74
+ { :type => qname })
75
+ mapping = @registry.find_mapped_soap_class(type_class)
76
+ type_binding = SoapBinding.new(self, qname, type_class, mapping)
77
+ end
78
+
79
+ array_binding = nil
80
+ if klass.is_a?(Array)
81
+ array_mapping = @registry.find_mapped_soap_class(Array) rescue nil
82
+ if (array_mapping && !array_mapping[1].is_a?(SoapTypedArrayFactory)) || array_mapping.nil?
83
+ @registry.set(Array,
84
+ SOAP::SOAPArray,
85
+ SoapTypedArrayFactory.new)
86
+ array_mapping = @registry.find_mapped_soap_class(Array)
87
+ end
88
+ qname = XSD::QName.new(@type_namespace, soap_type_name(type_class.name) + 'Array')
89
+ array_binding = SoapBinding.new(self, qname, Array, array_mapping, type_binding)
90
+ end
91
+
92
+ @spec2binding[spec] = array_binding ? array_binding : type_binding
93
+ @spec2binding[spec]
94
+ end
95
+
96
+ protected
97
+ def annotate_arrays(binding, value)
98
+ if binding.is_typed_array?
99
+ mark_typed_array(value, binding.element_binding.qname)
100
+ if binding.element_binding.is_custom_type?
101
+ value.each do |element|
102
+ annotate_arrays(register_type(element.class), element)
103
+ end
104
+ end
105
+ elsif binding.is_typed_struct?
106
+ if binding.type_class.respond_to?(:members)
107
+ binding.type_class.members.each do |name, spec|
108
+ member_binding = register_type(spec)
109
+ member_value = value.send(name)
110
+ if member_binding.is_custom_type?
111
+ annotate_arrays(member_binding, member_value)
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ def mark_typed_array(array, qname)
119
+ (class << array; self; end).class_eval do
120
+ define_method(:arytype) do
121
+ qname
122
+ end
123
+ end
124
+ end
125
+
126
+ def typed_struct_factory(type_class)
127
+ if Object.const_defined?('ActiveRecord')
128
+ if WS.derived_from?(ActiveRecord::Base, type_class)
129
+ qname = XSD::QName.new(@type_namespace, soap_type_name(type_class.name))
130
+ type_class.instance_variable_set('@qname', qname)
131
+ return SoapActiveRecordStructFactory.new
132
+ end
133
+ end
134
+ SOAP::Mapping::Registry::TypedStructFactory
135
+ end
136
+
137
+ def soap_type_name(type_name)
138
+ type_name.gsub(/::/, '..')
139
+ end
140
+
141
+ def soap_base_type_name(type)
142
+ xsd_type = type.ancestors.find{|c| c.const_defined? 'Type'}
143
+ xsd_type ? xsd_type.const_get('Type') : XSD::XSDAnySimpleType::Type
144
+ end
145
+ end
146
+
147
+ class SoapForeignObject
148
+ attr_accessor :param
149
+ attr_accessor :soap_object
150
+
151
+ def initialize(param, soap_object)
152
+ @param = param
153
+ @soap_object = soap_object
154
+ end
155
+ end
156
+
157
+ class SoapBinding
158
+ attr :qname
159
+ attr :type_class
160
+ attr :mapping
161
+ attr :element_binding
162
+
163
+ def initialize(marshaler, qname, type_class, mapping, element_binding=nil)
164
+ @marshaler = marshaler
165
+ @qname = qname
166
+ @type_class = type_class
167
+ @mapping = mapping
168
+ @element_binding = element_binding
169
+ end
170
+
171
+ def is_custom_type?
172
+ is_typed_array? || is_typed_struct?
173
+ end
174
+
175
+ def is_typed_array?
176
+ @mapping[1].is_a?(WS::Marshaling::SoapTypedArrayFactory)
177
+ end
178
+
179
+ def is_typed_struct?
180
+ @mapping[1] == SOAP::Mapping::Registry::TypedStructFactory || \
181
+ @mapping[1].is_a?(WS::Marshaling::SoapActiveRecordStructFactory)
182
+ end
183
+
184
+ def each_member(&block)
185
+ if is_typed_struct?
186
+ if @mapping[1] == SOAP::Mapping::Registry::TypedStructFactory
187
+ if @type_class.respond_to?(:members)
188
+ @type_class.members.each do |name, spec|
189
+ yield name, spec
190
+ end
191
+ end
192
+ elsif @mapping[1].is_a?(WS::Marshaling::SoapActiveRecordStructFactory)
193
+ @type_class.columns.each do |column|
194
+ yield column.name, column.klass
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ def type_name
201
+ is_custom_type? ? @qname.name : nil
202
+ end
203
+
204
+ def qualified_type_name(ns=nil)
205
+ if is_custom_type?
206
+ "#{ns ? ns : @qname.namespace}:#{@qname.name}"
207
+ else
208
+ ns = XSD::NS.new
209
+ ns.assign(XSD::Namespace, SOAP::XSDNamespaceTag)
210
+ xsd_klass = mapping[0].ancestors.find{|c| c.const_defined?('Type')}
211
+ return ns.name(XSD::AnyTypeName) unless xsd_klass
212
+ ns.name(xsd_klass.const_get('Type'))
213
+ end
214
+ end
215
+ end
216
+
217
+ class SoapActiveRecordStructFactory < SOAP::Mapping::Factory
218
+ def obj2soap(soap_class, obj, info, map)
219
+ unless obj.is_a?(ActiveRecord::Base)
220
+ return nil
221
+ end
222
+ soap_obj = soap_class.new(obj.class.instance_variable_get('@qname'))
223
+ obj.class.columns.each do |column|
224
+ key = column.name.to_s
225
+ value = obj.send(key)
226
+ soap_obj[key] = SOAP::Mapping._obj2soap(value, map)
227
+ end
228
+ soap_obj
229
+ end
230
+
231
+ def soap2obj(obj_class, node, info, map)
232
+ unless node.type == obj_class.instance_variable_get('@qname')
233
+ return false
234
+ end
235
+ obj = obj_class.new
236
+ node.each do |key, value|
237
+ obj[key] = value.data
238
+ end
239
+ obj.instance_variable_set('@new_record', false)
240
+ return true, obj
241
+ end
242
+ end
243
+
244
+ class SoapTypedArrayFactory < SOAP::Mapping::Factory
245
+ def obj2soap(soap_class, obj, info, map)
246
+ unless obj.respond_to?(:arytype)
247
+ return nil
248
+ end
249
+ soap_obj = soap_class.new(SOAP::ValueArrayName, 1, obj.arytype)
250
+ mark_marshalled_obj(obj, soap_obj)
251
+ obj.each do |item|
252
+ child = SOAP::Mapping._obj2soap(item, map)
253
+ soap_obj.add(child)
254
+ end
255
+ soap_obj
256
+ end
257
+
258
+ def soap2obj(obj_class, node, info, map)
259
+ return false
260
+ end
261
+ end
262
+
263
+ module ActiveRecordSoapMarshallable
264
+ def allocate
265
+ obj = super
266
+ attrs = {}
267
+ self.columns.each{|c| attrs[c.name.to_s] = c.default}
268
+ obj.instance_variable_set('@attributes', attrs)
269
+ obj
270
+ end
271
+ end
272
+
273
+ if Object.const_defined?('ActiveRecord')
274
+ ActiveRecord::Base.extend(ActiveRecordSoapMarshallable)
275
+ end
276
+ end
277
+ end
@@ -0,0 +1,116 @@
1
+ module WS
2
+ module Marshaling
3
+ class XmlRpcError < WSError
4
+ end
5
+
6
+ class XmlRpcMarshaler < AbstractMarshaler
7
+ def initialize
8
+ @caster = BaseTypeCaster.new
9
+ @spec2binding = {}
10
+ end
11
+
12
+ def marshal(param)
13
+ transform_outbound(param)
14
+ end
15
+
16
+ def unmarshal(obj)
17
+ obj.param.value = transform_inbound(obj.param)
18
+ obj.param
19
+ end
20
+
21
+ def typed_unmarshal(obj, spec)
22
+ param = obj.param
23
+ param.info.data = register_type(spec)
24
+ param.value = transform_inbound(param)
25
+ param
26
+ end
27
+
28
+ def register_type(spec)
29
+ if @spec2binding.has_key?(spec)
30
+ return @spec2binding[spec]
31
+ end
32
+
33
+ klass = BaseTypes.canonical_param_type_class(spec)
34
+ type_binding = nil
35
+ if klass.is_a?(Array)
36
+ type_binding = XmlRpcArrayBinding.new(klass[0])
37
+ else
38
+ type_binding = XmlRpcBinding.new(klass)
39
+ end
40
+
41
+ @spec2binding[spec] = type_binding
42
+ end
43
+
44
+ def transform_outbound(param)
45
+ binding = param.info.data
46
+ case binding
47
+ when XmlRpcArrayBinding
48
+ param.value.map{|x| cast_outbound(x, binding.element_klass)}
49
+ when XmlRpcBinding
50
+ cast_outbound(param.value, param.info.type)
51
+ end
52
+ end
53
+
54
+ def transform_inbound(param)
55
+ return param.value if param.info.data.nil?
56
+ binding = param.info.data
57
+ param.info.type = binding.klass
58
+ case binding
59
+ when XmlRpcArrayBinding
60
+ param.value.map{|x| cast_inbound(x, binding.element_klass)}
61
+ when XmlRpcBinding
62
+ cast_inbound(param.value, param.info.type)
63
+ end
64
+ end
65
+
66
+ def cast_outbound(value, klass)
67
+ if BaseTypes.base_type?(klass)
68
+ @caster.cast(value, klass)
69
+ elsif value.is_a?(Exception)
70
+ XMLRPC::FaultException.new(2, value.message)
71
+ elsif Object.const_defined?('ActiveRecord') && value.is_a?(ActiveRecord::Base)
72
+ value.attributes
73
+ else
74
+ struct = {}
75
+ value.instance_variables.each do |name|
76
+ key = name.sub(/^@/, '')
77
+ struct[key] = value.instance_variable_get(name)
78
+ end
79
+ struct
80
+ end
81
+ end
82
+
83
+ def cast_inbound(value, klass)
84
+ if BaseTypes.base_type?(klass)
85
+ value = value.to_time if value.is_a?(XMLRPC::DateTime)
86
+ @caster.cast(value, klass)
87
+ elsif value.is_a?(XMLRPC::FaultException)
88
+ value
89
+ else
90
+ obj = klass.new
91
+ value.each do |name, val|
92
+ obj.send('%s=' % name.to_s, val)
93
+ end
94
+ obj
95
+ end
96
+ end
97
+ end
98
+
99
+ class XmlRpcBinding
100
+ attr :klass
101
+
102
+ def initialize(klass)
103
+ @klass = klass
104
+ end
105
+ end
106
+
107
+ class XmlRpcArrayBinding < XmlRpcBinding
108
+ attr :element_klass
109
+
110
+ def initialize(element_klass)
111
+ super(Array)
112
+ @element_klass = element_klass
113
+ end
114
+ end
115
+ end
116
+ end