actionwebservice 0.5.0 → 0.6.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.
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