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
@@ -1,167 +1,47 @@
1
- require 'xmlrpc/parser'
2
- require 'xmlrpc/create'
3
- require 'xmlrpc/config'
4
- require 'xmlrpc/utils'
5
- require 'singleton'
6
-
7
- module XMLRPC # :nodoc:
8
- class XmlRpcHelper # :nodoc:
9
- include Singleton
10
- include ParserWriterChooseMixin
11
-
12
- def parse_method_call(message)
13
- parser().parseMethodCall(message)
14
- end
15
-
16
- def create_method_response(successful, return_value)
17
- create().methodResponse(successful, return_value)
18
- end
19
- end
20
- end
21
-
22
1
  module ActionWebService # :nodoc:
23
2
  module Protocol # :nodoc:
24
3
  module XmlRpc # :nodoc:
25
- def self.append_features(base) # :nodoc:
26
- super
27
- base.register_protocol(BodyOnly, XmlRpcProtocol)
4
+ def self.included(base)
5
+ base.register_protocol(XmlRpcProtocol)
28
6
  end
7
+
8
+ class XmlRpcProtocol # :nodoc:
9
+ attr :marshaler
29
10
 
30
- class XmlRpcProtocol < AbstractProtocol # :nodoc:
31
- def self.create_protocol_request(container_class, action_pack_request)
32
- helper = XMLRPC::XmlRpcHelper.instance
33
- service_name = action_pack_request.parameters['action']
34
- methodname, params = helper.parse_method_call(action_pack_request.raw_post)
35
- methodname.gsub!(/^[^\.]+\./, '') unless methodname =~ /^system\./ # XXX
36
- protocol = XmlRpcProtocol.new(container_class)
37
- content_type = action_pack_request.env['HTTP_CONTENT_TYPE']
38
- content_type ||= 'text/xml'
39
- request = ProtocolRequest.new(protocol,
40
- action_pack_request.raw_post,
41
- service_name.to_sym,
42
- methodname,
43
- content_type,
44
- :xmlrpc_values => params)
45
- request
46
- rescue
47
- nil
48
- end
49
-
50
- def self.create_protocol_client(api, protocol_name, endpoint_uri, options)
51
- return nil unless protocol_name.to_s.downcase.to_sym == :xmlrpc
52
- ActionWebService::Client::XmlRpc.new(api, endpoint_uri, options)
11
+ def initialize
12
+ @encoder = WS::Encoding::XmlRpcEncoding.new
13
+ @marshaler = WS::Marshaling::XmlRpcMarshaler.new
53
14
  end
54
15
 
55
- def initialize(container_class)
56
- super(container_class)
16
+ def unmarshal_request(ap_request)
17
+ method_name, params = @encoder.decode_rpc_call(ap_request.raw_post)
18
+ params = params.map{|x| @marshaler.unmarshal(x)}
19
+ service_name = ap_request.parameters['action']
20
+ Request.new(self, method_name, params, service_name)
21
+ rescue
22
+ nil
57
23
  end
58
24
 
59
- def unmarshal_request(protocol_request)
60
- values = protocol_request.options[:xmlrpc_values]
61
- signature = protocol_request.signature
62
- if signature
63
- values = self.class.transform_incoming_method_params(self.class.transform_array_types(signature), values)
64
- protocol_request.check_parameter_types(values, check_array_types(signature))
65
- values
66
- else
67
- protocol_request.checked? ? [] : values
68
- end
69
- end
70
-
71
- def marshal_response(protocol_request, return_value)
72
- helper = XMLRPC::XmlRpcHelper.instance
73
- signature = protocol_request.return_signature
74
- if signature
75
- protocol_request.check_parameter_types([return_value], check_array_types(signature))
76
- return_value = self.class.transform_return_value(self.class.transform_array_types(signature), return_value)
77
- raw_response = helper.create_method_response(true, return_value)
25
+ def marshal_response(method_name, return_value, signature_type)
26
+ if !return_value.nil? && signature_type
27
+ type_binding = @marshaler.register_type(signature_type)
28
+ info = WS::ParamInfo.create(signature_type, type_binding, 0)
29
+ return_value = @marshaler.marshal(WS::Param.new(return_value, info))
78
30
  else
79
- # XML-RPC doesn't have the concept of a void method, nor does it
80
- # support a nil return value, so return true if we would have returned
81
- # nil
82
- if protocol_request.checked?
83
- raw_response = helper.create_method_response(true, true)
84
- else
85
- return_value = true if return_value.nil?
86
- raw_response = helper.create_method_response(true, return_value)
87
- end
31
+ return_value = nil
88
32
  end
89
- ProtocolResponse.new(self, raw_response, 'text/xml')
33
+ body = @encoder.encode_rpc_response(method_name, return_value)
34
+ Response.new(body, 'text/xml')
90
35
  end
91
-
92
- def marshal_exception(exception)
93
- helper = XMLRPC::XmlRpcHelper.instance
94
- exception = XMLRPC::FaultException.new(1, exception.message)
95
- raw_response = helper.create_method_response(false, exception)
96
- ProtocolResponse.new(self, raw_response, 'text/xml')
97
- end
98
-
99
- class << self
100
- def transform_incoming_method_params(signature, params)
101
- (1..signature.size).each do |i|
102
- i -= 1
103
- params[i] = xmlrpc_to_ruby(params[i], signature[i])
104
- end
105
- params
106
- end
107
-
108
- def transform_return_value(signature, return_value)
109
- ruby_to_xmlrpc(return_value, signature[0])
110
- end
111
-
112
- def ruby_to_xmlrpc(param, param_class)
113
- if param_class.is_a?(XmlRpcArray)
114
- param.map{|p| ruby_to_xmlrpc(p, param_class.klass)}
115
- elsif param_class.ancestors.include?(ActiveRecord::Base)
116
- param.instance_variable_get('@attributes')
117
- elsif param_class.ancestors.include?(ActionWebService::Struct)
118
- struct = {}
119
- param_class.members.each do |name, klass|
120
- value = param.send(name)
121
- next if value.nil?
122
- struct[name.to_s] = value
123
- end
124
- struct
125
- else
126
- param
127
- end
128
- end
129
36
 
130
- def xmlrpc_to_ruby(param, param_class)
131
- if param_class.is_a?(XmlRpcArray)
132
- param.map{|p| xmlrpc_to_ruby(p, param_class.klass)}
133
- elsif param_class.ancestors.include?(ActiveRecord::Base)
134
- raise(ProtocolError, "incoming ActiveRecord::Base types are not allowed")
135
- elsif param_class.ancestors.include?(ActionWebService::Struct)
136
- unless param.is_a?(Hash)
137
- raise(ProtocolError, "expected parameter to be a Hash")
138
- end
139
- new_param = param_class.new
140
- param_class.members.each do |name, klass|
141
- new_param.send('%s=' % name.to_s, param[name.to_s])
142
- end
143
- new_param
144
- else
145
- param
146
- end
147
- end
37
+ def register_signature_type(spec)
38
+ nil
39
+ end
148
40
 
149
- def transform_array_types(signature)
150
- signature.map{|x| x.is_a?(Array) ? XmlRpcArray.new(x[0]) : x}
151
- end
41
+ def protocol_client(api, protocol_name, endpoint_uri, options)
42
+ return nil unless protocol_name == :xmlrpc
43
+ ActionWebService::Client::XmlRpc.new(api, endpoint_uri, options)
152
44
  end
153
-
154
- private
155
- def check_array_types(signature)
156
- signature.map{|x| x.is_a?(Array) ? Array : x}
157
- end
158
-
159
- class XmlRpcArray
160
- attr :klass
161
- def initialize(klass)
162
- @klass = klass
163
- end
164
- end
165
45
  end
166
46
  end
167
47
  end
@@ -17,8 +17,7 @@ module ActionWebService
17
17
  # person = Person.new(:id => 5, :firstname => 'john', :lastname => 'doe')
18
18
  #
19
19
  # Active Record model classes are already implicitly supported for method
20
- # return signatures. A structure containing its columns as members will be
21
- # automatically generated if its present in a signature.
20
+ # return signatures.
22
21
  class Struct
23
22
 
24
23
  # If a Hash is given as argument to an ActionWebService::Struct constructor,
@@ -35,12 +34,10 @@ module ActionWebService
35
34
  end
36
35
 
37
36
  class << self
38
- include ActionWebService::Signature
39
-
40
37
  # Creates a structure member with the specified +name+ and +type+. Generates
41
38
  # accessor methods for reading and writing the member value.
42
39
  def member(name, type)
43
- write_inheritable_hash("struct_members", name => signature_parameter_class(type))
40
+ write_inheritable_hash("struct_members", name => WS::BaseTypes.canonical_param_type_class(type))
44
41
  class_eval <<-END
45
42
  def #{name}; @#{name}; end
46
43
  def #{name}=(value); @#{name} = value; end
@@ -0,0 +1,130 @@
1
+ require 'test/unit'
2
+
3
+ module Test
4
+ module Unit
5
+ class TestCase # :nodoc:
6
+ private
7
+ # invoke the specified API method
8
+ def invoke_direct(method_name, *args)
9
+ prepare_request('api', 'api', method_name, *args)
10
+ @controller.process(@request, @response)
11
+ decode_rpc_response
12
+ end
13
+ alias_method :invoke, :invoke_direct
14
+
15
+ # invoke the specified API method on the specified service
16
+ def invoke_delegated(service_name, method_name, *args)
17
+ prepare_request(service_name.to_s, service_name, method_name, *args)
18
+ @controller.process(@request, @response)
19
+ decode_rpc_response
20
+ end
21
+
22
+ # invoke the specified layered API method on the correct service
23
+ def invoke_layered(service_name, method_name, *args)
24
+ if protocol == :soap
25
+ raise "SOAP protocol support for :layered dispatching mode is not available"
26
+ end
27
+ prepare_request('api', service_name, method_name, *args)
28
+ @controller.process(@request, @response)
29
+ decode_rpc_response
30
+ end
31
+
32
+ # ---------------------- internal ---------------------------
33
+
34
+ def prepare_request(action, service_name, api_method_name, *args)
35
+ @request.request_parameters['action'] = action
36
+ @request.env['REQUEST_METHOD'] = 'POST'
37
+ @request.env['HTTP_CONTENT_TYPE'] = 'text/xml'
38
+ @request.env['RAW_POST_DATA'] = encode_rpc_call(service_name, api_method_name, *args)
39
+ case protocol
40
+ when :soap
41
+ soap_action = "/#{@controller.controller_name}/#{service_name}/#{public_method_name(service_name, api_method_name)}"
42
+ @request.env['HTTP_SOAPACTION'] = soap_action
43
+ when :xmlrpc
44
+ @request.env.delete('HTTP_SOAPACTION')
45
+ end
46
+ end
47
+
48
+ def encode_rpc_call(service_name, api_method_name, *args)
49
+ case @controller.web_service_dispatching_mode
50
+ when :direct
51
+ api = @controller.class.web_service_api
52
+ when :delegated, :layered
53
+ api = @controller.web_service_object(service_name.to_sym).class.web_service_api
54
+ end
55
+ info = api.api_methods[api_method_name.to_sym]
56
+ ((info[:expects] || []) + (info[:returns] || [])).each do |spec|
57
+ marshaler.register_type spec
58
+ end
59
+ expects = info[:expects]
60
+ args = args.dup
61
+ (0..(args.length-1)).each do |i|
62
+ type_binding = marshaler.register_type(expects ? expects[i] : args[i].class)
63
+ info = WS::ParamInfo.create(expects ? expects[i] : args[i].class, type_binding, i)
64
+ args[i] = marshaler.marshal(WS::Param.new(args[i], info))
65
+ end
66
+ encoder.encode_rpc_call(public_method_name(service_name, api_method_name), args)
67
+ end
68
+
69
+ def decode_rpc_response
70
+ public_method_name, return_value = encoder.decode_rpc_response(@response.body)
71
+ result = marshaler.unmarshal(return_value).value
72
+ unless @return_exceptions
73
+ exception = is_exception?(result)
74
+ raise exception if exception
75
+ end
76
+ result
77
+ end
78
+
79
+ def public_method_name(service_name, api_method_name)
80
+ public_name = service_api(service_name).public_api_method_name(api_method_name)
81
+ if @controller.web_service_dispatching_mode == :layered
82
+ '%s.%s' % [service_name.to_s, public_name]
83
+ else
84
+ public_name
85
+ end
86
+ end
87
+
88
+ def service_api(service_name)
89
+ case @controller.web_service_dispatching_mode
90
+ when :direct
91
+ @controller.class.web_service_api
92
+ when :delegated, :layered
93
+ @controller.web_service_object(service_name.to_sym).class.web_service_api
94
+ end
95
+ end
96
+
97
+ def protocol
98
+ @protocol ||= :soap
99
+ end
100
+
101
+ def marshaler
102
+ case protocol
103
+ when :soap
104
+ @soap_marshaler ||= WS::Marshaling::SoapMarshaler.new 'urn:ActionWebService'
105
+ when :xmlrpc
106
+ @xmlrpc_marshaler ||= WS::Marshaling::XmlRpcMarshaler.new
107
+ end
108
+ end
109
+
110
+ def encoder
111
+ case protocol
112
+ when :soap
113
+ @soap_encoder ||= WS::Encoding::SoapRpcEncoding.new 'urn:ActionWebService'
114
+ when :xmlrpc
115
+ @xmlrpc_encoder ||= WS::Encoding::XmlRpcEncoding.new
116
+ end
117
+ end
118
+
119
+ def is_exception?(obj)
120
+ case protocol
121
+ when :soap
122
+ (obj.respond_to?(:detail) && obj.detail.respond_to?(:cause) && \
123
+ obj.detail.cause.is_a?(Exception)) ? obj.detail.cause : nil
124
+ when :xmlrpc
125
+ obj.is_a?(XMLRPC::FaultException) ? obj : nil
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,4 @@
1
+ require 'ws/common'
2
+ require 'ws/types'
3
+ require 'ws/marshaling'
4
+ require 'ws/encoding'
@@ -0,0 +1,8 @@
1
+ module WS
2
+ class WSError < StandardError
3
+ end
4
+
5
+ def self.derived_from?(ancestor, child)
6
+ child.ancestors.include?(ancestor)
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ require 'ws/encoding/abstract'
2
+ require 'ws/encoding/soap_rpc_encoding'
3
+ require 'ws/encoding/xmlrpc_encoding'
@@ -0,0 +1,26 @@
1
+ module WS
2
+ module Encoding
3
+ # Encoders operate on _foreign_ objects. That is, Ruby object
4
+ # instances that are the _marshaling format specific_ representation
5
+ # of objects. In other words, objects that have not yet been marshaled, but
6
+ # are in protocol-specific form (such as an AST or DOM element), and not
7
+ # native Ruby form.
8
+ class AbstractEncoding
9
+ def encode_rpc_call(method_name, params)
10
+ raise NotImplementedError
11
+ end
12
+
13
+ def decode_rpc_call(obj)
14
+ raise NotImplementedError
15
+ end
16
+
17
+ def encode_rpc_response(method_name, return_value)
18
+ raise NotImplementedError
19
+ end
20
+
21
+ def decode_rpc_response(obj)
22
+ raise NotImplementedError
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,90 @@
1
+ require 'soap/processor'
2
+ require 'soap/mapping'
3
+ require 'soap/rpc/element'
4
+
5
+ module WS
6
+ module Encoding
7
+ class SoapRpcError < WSError
8
+ end
9
+
10
+ class SoapRpcEncoding < AbstractEncoding
11
+ attr_accessor :method_namespace
12
+
13
+ def initialize(method_namespace='')
14
+ @method_namespace = method_namespace
15
+ end
16
+
17
+ def encode_rpc_call(method_name, foreign_params)
18
+ qname = create_method_qname(method_name)
19
+ param_def = []
20
+ params = foreign_params.map do |p|
21
+ param_def << ['in', p.param.info.name, p.param.info.data.mapping]
22
+ [p.param.info.name, p.soap_object]
23
+ end
24
+ request = SOAP::RPC::SOAPMethodRequest.new(qname, param_def)
25
+ request.set_param(params)
26
+ envelope = create_soap_envelope(request)
27
+ SOAP::Processor.marshal(envelope)
28
+ end
29
+
30
+ def decode_rpc_call(obj)
31
+ envelope = SOAP::Processor.unmarshal(obj)
32
+ unless envelope
33
+ raise(SoapRpcError, "Malformed SOAP request")
34
+ end
35
+ request = envelope.body.request
36
+ method_name = request.elename.name
37
+ params = request.collect do |key, value|
38
+ info = ParamInfo.new(key, nil, nil)
39
+ param = Param.new(nil, info)
40
+ Marshaling::SoapForeignObject.new(param, request[key])
41
+ end
42
+ [method_name, params]
43
+ end
44
+
45
+ def encode_rpc_response(method_name, return_value)
46
+ response = nil
47
+ qname = create_method_qname(method_name)
48
+ if return_value.nil?
49
+ response = SOAP::RPC::SOAPMethodResponse.new(qname, nil)
50
+ else
51
+ param = return_value.param
52
+ soap_object = return_value.soap_object
53
+ param_def = [['retval', 'return', param.info.data.mapping]]
54
+ if soap_object.is_a?(SOAP::SOAPFault)
55
+ response = soap_object
56
+ else
57
+ response = SOAP::RPC::SOAPMethodResponse.new(qname, param_def)
58
+ response.retval = soap_object
59
+ end
60
+ end
61
+ envelope = create_soap_envelope(response)
62
+ SOAP::Processor.marshal(envelope)
63
+ end
64
+
65
+ def decode_rpc_response(obj)
66
+ envelope = SOAP::Processor.unmarshal(obj)
67
+ unless envelope
68
+ raise(SoapRpcError, "Malformed SOAP response")
69
+ end
70
+ method_name = envelope.body.request.elename.name
71
+ return_value = envelope.body.response
72
+ info = ParamInfo.new('return', nil, nil)
73
+ param = Param.new(nil, info)
74
+ return_value = Marshaling::SoapForeignObject.new(param, return_value)
75
+ [method_name, return_value]
76
+ end
77
+
78
+ private
79
+ def create_soap_envelope(body)
80
+ header = SOAP::SOAPHeader.new
81
+ body = SOAP::SOAPBody.new(body)
82
+ SOAP::SOAPEnvelope.new(header, body)
83
+ end
84
+
85
+ def create_method_qname(method_name)
86
+ XSD::QName.new(@method_namespace, method_name)
87
+ end
88
+ end
89
+ end
90
+ end