actionservice 0.2.99 → 0.2.100

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 CHANGED
@@ -1,3 +1,3 @@
1
- *0.2*
1
+ TBA
2
2
 
3
- * First release
3
+ * First public release
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ require 'rake/contrib/rubyforgepublisher'
8
8
 
9
9
  PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
10
10
  PKG_NAME = 'actionservice'
11
- PKG_VERSION = '0.2.99' + PKG_BUILD
11
+ PKG_VERSION = '0.2.100' + PKG_BUILD
12
12
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
13
13
 
14
14
  desc "Default Task"
data/TODO CHANGED
@@ -1,5 +1,4 @@
1
1
  = Features Remaining
2
- - XML-RPC support
3
2
  - Support for ActiveRecord model classes as structure types as well, since
4
3
  we can reflect all the details to do proper mapping from model classes.
5
4
 
@@ -21,5 +20,11 @@
21
20
  * Use the return value of the action to send back to the client
22
21
 
23
22
  = Warts
23
+ - Come up with a cleaner way for supporting XML-RPC introspection that doesn't
24
+ involve throwing a SystemExportNotHandledError exception.
25
+ - Come up with a cleaner approach for 'default_export' implementations.
26
+ But NOT method_missing!
27
+ - Create abstract SOAP test that provides helpers for doing SOAP method
28
+ calls, get rid of the copy & paste SOAP crap in the SOAP protocol tests
24
29
  - Don't have clean way to go from SOAP Class object to the xsd:NAME type
25
30
  string
@@ -8,6 +8,7 @@ module ActionService
8
8
  class_inheritable_option :export_name_mangling, true
9
9
  class_inheritable_option :report_exceptions, true
10
10
  class_inheritable_option :logger
11
+ class_inheritable_option :default_export
11
12
 
12
13
  class << self
13
14
  def export(name, options={})
@@ -19,14 +20,6 @@ module ActionService
19
20
  expects = options[:expects]
20
21
  returns = options[:returns]
21
22
  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
23
  name = name.to_sym
31
24
  public_name = public_export_name(name)
32
25
  info = { :expects => expects, :returns => returns }
@@ -36,15 +36,6 @@ module ActionService
36
36
  read_inheritable_attribute("action_services") || {}
37
37
  end
38
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
39
  private
49
40
  def call_service_definition_callbacks(container_klass, service_name, service_info)
50
41
  (read_inheritable_attribute("action_service_definition_callbacks") || []).each do |block|
@@ -54,14 +45,64 @@ module ActionService
54
45
  end
55
46
 
56
47
  module InstanceMethods
48
+ def service_object(service_name)
49
+ info = self.class.services[service_name.to_sym]
50
+ unless info
51
+ raise(ContainerError, "no such service '#{service_name}'")
52
+ end
53
+ service = info[:block]
54
+ service ? instance_eval(&service) : info[:object]
55
+ end
56
+
57
57
  private
58
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)
59
+ service = service_object(info.service_name)
60
+ service_klass = service.class
61
+ method_name = service_klass.internal_export_name(info.public_method_name)
62
+ opts = {}
63
+ params = []
64
+ if method_name
65
+ export_info = service_klass.exports[method_name]
66
+ strict = true
67
+ else
68
+ system_exports = self.class.read_inheritable_attribute('default_system_exports') || {}
69
+ method_name = system_exports[protocol.class]
70
+ if method_name
71
+ opts[:system_call] = [info.public_method_name, system_exports[protocol.class]]
72
+ params.unshift service_klass
73
+ else
74
+ method_name = service_klass.default_export
75
+ method_name = method_name.to_sym if method_name
76
+ unless method_name
77
+ raise(ContainerError, "no such method /#{info.service_name}##{info.public_method_name}()")
78
+ end
79
+ end
80
+ export_info = {}
81
+ strict = false
82
+ end
83
+ opts[:require_exported] = false unless strict
84
+ params = params + protocol.unmarshal_request(info, export_info, strict)
85
+ canceled_reason = nil
86
+ canceled_block = lambda{|r| canceled_reason = r}
87
+ perform_invoke = lambda do
88
+ service.perform_invocation(method_name, params, opts, &canceled_block)
89
+ end
90
+ begin
91
+ result = perform_invoke.call
92
+ rescue ActionService::Protocol::SystemExportNotHandledError => e
93
+ params.shift
94
+ method_name = service_klass.default_export
95
+ opts.delete(:system_call)
96
+ if method_name
97
+ method_name = method_name.to_sym
98
+ export_info = {}
99
+ strict = false
100
+ else
101
+ raise(ContainerError, "no such method /#{info.service_name}##{info.public_method_name}()")
102
+ end
103
+ result = perform_invoke.call
104
+ end
105
+ protocol.marshal_response(info, export_info, result, strict)
65
106
  end
66
107
 
67
108
  end
@@ -86,7 +86,7 @@ module ActionService
86
86
  end
87
87
  end
88
88
 
89
- module InstanceMethods # :nodoc:
89
+ module InstanceMethods
90
90
  def self.append_features(base)
91
91
  super
92
92
  base.class_eval do
@@ -95,19 +95,30 @@ module ActionService
95
95
  end
96
96
  end
97
97
 
98
- def perform_invocation_with_interception(name, args=nil, &block)
98
+ def perform_invocation_with_interception(name, args=nil, options={}, &block)
99
99
  return if before_invocation(name, args, &block) == false
100
- result = perform_invocation_without_interception(name, args)
100
+ result = perform_invocation_without_interception(name, args, options)
101
101
  after_invocation(name, args, result)
102
102
  result
103
103
  end
104
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}'"
105
+ def perform_invocation(name, args=nil, options={})
106
+ unless options[:require_exported] == false
107
+ unless self.respond_to?(name) && self.class.has_export?(name)
108
+ raise InvocationError, "no such exported method '#{name}'"
109
+ end
108
110
  end
109
111
  args ||= []
110
- self.send(name, *args)
112
+ if options[:system_call]
113
+ name, block = options[:system_call]
114
+ if block.respond_to?('call')
115
+ block.call(name, *args)
116
+ else
117
+ self.send(block, *args)
118
+ end
119
+ else
120
+ self.send(name, *args)
121
+ end
111
122
  end
112
123
 
113
124
  def before_invocation(name, args, &block)
@@ -2,6 +2,8 @@ module ActionService
2
2
  module Protocol
3
3
  class ProtocolError < ActionService::ActionServiceError
4
4
  end
5
+ class SystemExportNotHandledError < ProtocolError
6
+ end
5
7
 
6
8
  class AbstractProtocol
7
9
  attr :container_klass
@@ -10,11 +12,11 @@ module ActionService
10
12
  @container_klass = container_klass
11
13
  end
12
14
 
13
- def unmarshal_request(request_info, export_info)
15
+ def unmarshal_request(request_info, export_info, strict=true)
14
16
  raise NotImplementedError
15
17
  end
16
18
 
17
- def marshal_response(request_info, export_info, return_value)
19
+ def marshal_response(request_info, export_info, return_value, strict=true)
18
20
  raise NotImplementedError
19
21
  end
20
22
 
@@ -22,13 +24,44 @@ module ActionService
22
24
  raise NotImplementedError
23
25
  end
24
26
 
25
- def request_info(request)
27
+ def request_info
26
28
  raise NotImplementedError
27
29
  end
28
30
 
29
31
  def request_supported?(request)
30
32
  raise NotImplementedError
31
33
  end
34
+
35
+ private
36
+ def validate_types(method_name, signature, params, kind)
37
+ unless signature.length == params.length
38
+ raise(ProtocolError, "signature/parameter mismatch")
39
+ end
40
+ case kind
41
+ when :in
42
+ extra_msg = ' (Input parameter %d of method "%s")'
43
+ when :out
44
+ extra_msg = ' (Return value %d of method "%s")'
45
+ end
46
+ i = 0
47
+ signature.each do |klass|
48
+ check_compatibility(params[i].class, klass, extra_msg % [i+1, method_name])
49
+ i += 1
50
+ end
51
+ end
52
+
53
+ def check_compatibility(given_klass, signature_klass, extra_msg=nil)
54
+ if (given_klass == TrueClass or given_klass == FalseClass) and \
55
+ (signature_klass == TrueClass or signature_klass == FalseClass)
56
+ return true
57
+ end
58
+ unless given_klass.ancestors.include?(signature_klass) || \
59
+ signature_klass.ancestors.include?(given_klass)
60
+ raise(ProtocolError, "value of type #{given_klass.name} is not compatible " +
61
+ "with expected type #{signature_klass.name}" +
62
+ (extra_msg ? extra_msg : ''))
63
+ end
64
+ end
32
65
  end
33
66
 
34
67
  class ServiceRequestInfo
@@ -38,7 +71,7 @@ module ActionService
38
71
  attr :content_type
39
72
  attr :protocol_info
40
73
 
41
- def initialize(service_name, public_method_name, raw_body, content_type, protocol_info)
74
+ def initialize(service_name, public_method_name, raw_body, content_type, protocol_info=nil)
42
75
  @service_name = service_name
43
76
  @public_method_name = public_method_name
44
77
  @raw_body = raw_body
@@ -28,30 +28,52 @@ module ActionService
28
28
  end
29
29
 
30
30
  class SoapProtocol < AbstractProtocol
31
- def unmarshal_request(request_info, export_info)
31
+ def unmarshal_request(request_info, export_info, strict=true)
32
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
33
+ unmarshal_soap_message = lambda do
34
+ envelope = SOAP::Processor.unmarshal(request_info.raw_body)
35
+ request = envelope.body.request
36
+ params = request.collect{|k, v| request[k]}
37
+ soap_to_ruby_array(params)
38
+ end if expects || !strict
39
+ if expects
40
+ map_types(expects)
41
+ params = unmarshal_soap_message.call
42
+ validate_types(request_info.public_method_name, expects, params, :in) if expects
43
+ params
44
+ else
45
+ if strict
46
+ []
47
+ else
48
+ unmarshal_soap_message.call
49
+ end
50
+ end
40
51
  end
41
52
 
42
- def marshal_response(request_info, export_info, return_value)
53
+ def marshal_response(request_info, export_info, return_value, strict=true)
43
54
  returns = export_info[:returns]
55
+ create_param_def = lambda do |ret|
56
+ map_types(ret)
57
+ mapping = mapper.lookup(ret[0])
58
+ ret = [mapping.ruby_klass]
59
+ retval = fixup_array_types(mapping, return_value)
60
+ validate_types(request_info.public_method_name, ret, [retval], :out)
61
+ retval = ruby_to_soap(retval)
62
+ param_def = [['retval', 'return', mapping.registry_mapping]]
63
+ [param_def, ret, retval]
64
+ end if returns || !strict
44
65
  if returns
45
- map_types(returns)
46
- mapping = mapper.lookup(returns[0])
47
- return_value = fixup_array_types(mapping, return_value)
66
+ param_def, returns, return_value = create_param_def.call(returns)
67
+ else
68
+ if strict
69
+ param_def = []
70
+ else
71
+ param_def, returns, return_value = create_param_def.call([return_value.class])
72
+ end
48
73
  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
74
  qname = XSD::QName.new(mapper.custom_namespace, request_info.public_method_name)
53
75
  response = SOAP::RPC::SOAPMethodResponse.new(qname, param_def)
54
- response.retval = return_value
76
+ response.retval = return_value if returns
55
77
  ServiceResponseInfo.new(create_response(response), 'text/xml')
56
78
  end
57
79
 
@@ -59,19 +81,20 @@ module ActionService
59
81
  ServiceResponseInfo.new(create_exception_response(exception), 'text/xml')
60
82
  end
61
83
 
62
- def request_info(request)
84
+ def request_info
85
+ @request_info
86
+ end
87
+
88
+ def request_supported?(request)
63
89
  soap_action = extract_soap_action(request)
90
+ return false unless soap_action
64
91
  service_name = request.parameters['action']
65
92
  public_method_name = soap_action.gsub(/^[\/]+/, '').split(/[\/]+/)[-1]
66
- ServiceRequestInfo.new(service_name,
93
+ @request_info = ServiceRequestInfo.new(service_name,
67
94
  public_method_name,
68
95
  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
96
+ request.env['HTTP_CONTENT_TYPE'] || 'text/xml')
97
+ true
75
98
  end
76
99
 
77
100
  private
@@ -89,9 +112,10 @@ module ActionService
89
112
  end
90
113
  if mapping.is_a?(SoapArrayMapping)
91
114
  obj = mapping.ruby_klass.new(obj)
92
- else
93
- obj
115
+ # man, this is going to be slow for big arrays :(
116
+ obj.each{|el| fixup_array_types(mapping.element_mapping, el)}
94
117
  end
118
+ obj
95
119
  end
96
120
 
97
121
  def extract_soap_action(request)
@@ -111,36 +135,6 @@ module ActionService
111
135
  types.collect{|type| mapper.map(type)}
112
136
  end
113
137
 
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
138
  def create_response(body, is_error=false)
145
139
  header = SOAP::SOAPHeader.new
146
140
  body = SOAP::SOAPBody.new(body)
@@ -261,9 +255,9 @@ module ActionService
261
255
  end
262
256
  alias :map :lookup
263
257
 
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)
258
+ def map_container_services(container, &block)
259
+ container.class.services.each do |service_name, service_info|
260
+ object = container.service_object(service_name)
267
261
  service_klass = object.class
268
262
  service_exports = {}
269
263
  object.class.exports.each do |export_name, export_info|
@@ -286,7 +280,8 @@ module ActionService
286
280
  mapping
287
281
  end
288
282
  expects_signature = expects ? expects.map{|klass| lookup_proc.call(klass)} : nil
289
- returns_signature = export_info[:returns].map{|klass| lookup_proc.call(klass)}
283
+ returns = export_info[:returns]
284
+ returns_signature = returns ? returns.map{|klass| lookup_proc.call(klass)} : nil
290
285
  service_exports[export_name] = {
291
286
  :expects => expects_signature,
292
287
  :returns => returns_signature
@@ -380,6 +375,8 @@ module ActionService
380
375
  def type_name
381
376
  super + "Array"
382
377
  end
378
+
379
+ def each_attribute(&block); end
383
380
  end
384
381
  end
385
382
  end
@@ -0,0 +1,97 @@
1
+ require 'xmlrpc/parser'
2
+ require 'xmlrpc/utils'
3
+
4
+ module ActionService
5
+ module Protocol
6
+ module XmlRpc
7
+ def self.append_features(base)
8
+ super
9
+ base.register_protocol(BodyOnly, XmlRpcProtocol)
10
+ end
11
+
12
+ class XmlRpcProtocol < AbstractProtocol
13
+ include XMLRPC::ParserWriterChooseMixin
14
+
15
+ def initialize(container_klass)
16
+ @container_klass = container_klass
17
+ @container_klass.write_inheritable_hash('default_system_exports', XmlRpcProtocol => method(:xmlrpc_default_system_handler))
18
+ end
19
+
20
+ def unmarshal_request(request_info, export_info, strict=true)
21
+ expects = export_info[:expects]
22
+ params = request_info.protocol_info[:xmlrpc][:params]
23
+ if expects
24
+ expects = array_types(expects)
25
+ validate_types(request_info.public_method_name, expects, params, :in)
26
+ params
27
+ else
28
+ strict ? [] : params
29
+ end
30
+ end
31
+
32
+ def marshal_response(request_info, export_info, return_value, strict=true)
33
+ returns = export_info[:returns]
34
+ if returns
35
+ returns = array_types(returns)
36
+ validate_types(request_info.public_method_name, returns, [return_value], :out)
37
+ raw_response = create().methodResponse(true, return_value)
38
+ else
39
+ # XML-RPC doesn't have the concept of a void method, nor does it
40
+ # support a nil return value, so return true if we would have returned
41
+ # nil
42
+ if strict
43
+ raw_response = create().methodResponse(true, true)
44
+ else
45
+ return_value = true if return_value.nil?
46
+ raw_response = create().methodResponse(true, return_value)
47
+ end
48
+ end
49
+ ServiceResponseInfo.new(raw_response, 'text/xml')
50
+ end
51
+
52
+ def marshal_exception(exception)
53
+ raw_response = create().methodResponse(false, exception)
54
+ ServiceResponseInfo.new(raw_response, 'text/xml')
55
+ end
56
+
57
+ def request_info
58
+ @request_info
59
+ end
60
+
61
+ def request_supported?(request)
62
+ begin
63
+ service_name = request.parameters['action']
64
+ methodname, params = parser().parseMethodCall(request.raw_post)
65
+ @request_info = ServiceRequestInfo.new(service_name,
66
+ methodname,
67
+ request.raw_post,
68
+ request.env['HTTP_CONTENT_TYPE'] || 'text/xml',
69
+ :xmlrpc => { :params => params })
70
+ true
71
+ rescue
72
+ false
73
+ end
74
+ end
75
+
76
+ private
77
+ def xmlrpc_default_system_handler(name, service_klass, *args)
78
+ case name
79
+ when 'system.listMethods'
80
+ methods = []
81
+ service_klass.exports.each do |name, info|
82
+ methods << service_klass.public_export_name(name)
83
+ end
84
+ methods.sort
85
+ else
86
+ raise SystemExportNotHandledError
87
+ end
88
+ end
89
+
90
+ def array_types(signature)
91
+ signature.map{|x| x.is_a?(Array) ? Array : x}
92
+ end
93
+ end
94
+
95
+ end
96
+ end
97
+ end
@@ -1,3 +1,4 @@
1
1
  require 'action_service/protocol/abstract'
2
2
  require 'action_service/protocol/registry'
3
3
  require 'action_service/protocol/soap'
4
+ require 'action_service/protocol/xmlrpc'
@@ -19,7 +19,7 @@ module ActionService
19
19
  request_info = nil
20
20
  begin
21
21
  protocol = probe_request_protocol(self.request)
22
- request_info = protocol.request_info(self.request)
22
+ request_info = protocol.request_info
23
23
  if request_info
24
24
  log_request request_info
25
25
  response_info = dispatch_service_invocation_request(protocol, request_info)
@@ -35,7 +35,7 @@ module ActionService
35
35
  end
36
36
  rescue Exception => e
37
37
  log_error e unless logger.nil?
38
- service_object = self.class.service_object(request_info.service_name)
38
+ service_object = service_object(request_info.service_name)
39
39
  exc_response = nil
40
40
  if service_object.class.report_exceptions
41
41
  exc_response_info = protocol.marshal_exception(e) rescue nil
@@ -13,15 +13,15 @@ module ActionService
13
13
  SoapEncodingNs = 'http://schemas.xmlsoap.org/soap/encoding/'
14
14
  SoapHttpTransport = 'http://schemas.xmlsoap.org/soap/http'
15
15
 
16
- def to_wsdl(container_klass, uri, soap_action_base)
16
+ def to_wsdl(container, uri, soap_action_base)
17
17
  wsdl = ""
18
18
 
19
- namespace = container_klass.soap_mapper.custom_namespace
19
+ namespace = container.class.soap_mapper.custom_namespace
20
20
  wsdl_service_name = namespace.split(/:/)[1]
21
21
 
22
22
  mapper = ActionService::Protocol::Soap::SoapMapper.new(namespace)
23
23
  services = {}
24
- mapper.map_container_services(container_klass) do |name, klass, exports|
24
+ mapper.map_container_services(container) do |name, klass, exports|
25
25
  services[name] = [klass, exports]
26
26
  end
27
27
  custom_types = mapper.custom_types
@@ -78,7 +78,9 @@ module ActionService
78
78
  xm.message('name' => msg_name) do
79
79
  sym = nil
80
80
  if direction == :out
81
- xm.part('name' => 'return', 'type' => export_signature[:returns][0].qualified_type_name)
81
+ if export_signature[:returns]
82
+ xm.part('name' => 'return', 'type' => export_signature[:returns][0].qualified_type_name)
83
+ end
82
84
  else
83
85
  mapping_list = export_signature[:expects]
84
86
  i = 1
@@ -165,7 +167,7 @@ module ActionService
165
167
  begin
166
168
  uri = "http://#{@request.env['HTTP_HOST']||@request.env['SERVER_NAME']}/#{controller_name}/"
167
169
  soap_action_base = "/#{controller_name}"
168
- xml = self.class.to_wsdl(self.class, uri, soap_action_base)
170
+ xml = self.class.to_wsdl(self, uri, soap_action_base)
169
171
  send_data(xml, :type => 'text/xml', :disposition => 'inline')
170
172
  rescue Exception => e
171
173
  render_text('', "500 #{e.message}")
@@ -45,6 +45,7 @@ ActionController::Base.class_eval do
45
45
  include ActionService::Container
46
46
  include ActionService::Protocol::Registry
47
47
  include ActionService::Protocol::Soap
48
+ include ActionService::Protocol::XmlRpc
48
49
  include ActionService::Router::ActionController
49
50
  include ActionService::Router::Wsdl
50
51
  end
data/test/base_test.rb CHANGED
@@ -44,4 +44,8 @@ class BaseTest < Test::Unit::TestCase
44
44
  assert(BaseTestService.internal_export_name('Add') == :add)
45
45
  assert(UnmangledOrReportedService.public_export_name(:custom_name_casing) == 'custom_name_casing')
46
46
  end
47
+
48
+ def test_missing_method
49
+ assert(BaseTestService.has_public_export?('NoSuchMethod') == false)
50
+ end
47
51
  end
@@ -11,11 +11,23 @@ $immediate_service = Object.new
11
11
  $deferred_service = Object.new
12
12
 
13
13
  class TestContainer < AbstractTestContainer
14
+ attr :flag
15
+ attr :previous_flag
16
+
17
+ def initialize
18
+ @previous_flag = nil
19
+ @flag = true
20
+ end
21
+
14
22
  service :immediate_service, $immediate_service
15
- service(:deferred_service) { $deferred_service }
23
+ service(:deferred_service) { @previous_flag = @flag; @flag = false; $deferred_service }
16
24
  end
17
25
 
18
26
  class ContainerTest < Test::Unit::TestCase
27
+ def setup
28
+ @container = TestContainer.new
29
+ end
30
+
19
31
  def test_registration
20
32
  assert(TestContainer.has_service?(:immediate_service))
21
33
  assert(TestContainer.has_service?(:deferred_service))
@@ -23,7 +35,12 @@ class ContainerTest < Test::Unit::TestCase
23
35
  end
24
36
 
25
37
  def test_service_object
26
- assert(TestContainer.service_object(:immediate_service) == $immediate_service)
27
- assert(TestContainer.service_object(:deferred_service) == $deferred_service)
38
+ assert(@container.flag == true)
39
+ assert(@container.service_object(:immediate_service) == $immediate_service)
40
+ assert(@container.previous_flag.nil?)
41
+ assert(@container.flag == true)
42
+ assert(@container.service_object(:deferred_service) == $deferred_service)
43
+ assert(@container.previous_flag == true)
44
+ assert(@container.flag == false)
28
45
  end
29
46
  end
@@ -4,10 +4,13 @@ require 'soap/rpc/element'
4
4
  class SoapService < ActionService::Base
5
5
  attr :int
6
6
  attr :string
7
+ attr :values
8
+ attr :default_args
7
9
 
8
10
  def initialize
9
11
  @int = 20
10
12
  @string = "wrong string value"
13
+ @default_args = nil
11
14
  end
12
15
 
13
16
  def some_method(int, string)
@@ -16,7 +19,19 @@ class SoapService < ActionService::Base
16
19
  true
17
20
  end
18
21
 
22
+ def array_returner
23
+ @values = [1, 2, 3]
24
+ end
25
+
26
+ def default(*args)
27
+ @default_args = args
28
+ nil
29
+ end
30
+
19
31
  export :some_method, :expects => [Integer, String], :returns => [TrueClass]
32
+ export :array_returner, :returns => [[Integer]]
33
+
34
+ default_export :default
20
35
  end
21
36
 
22
37
  class SoapContainer
@@ -68,7 +83,7 @@ class ProtocolSoapTest < Test::Unit::TestCase
68
83
  test_request.env['RAW_POST_DATA'] = raw_request
69
84
  protocol = @container.request_protocol(test_request)
70
85
  assert(protocol.is_a?(ActionService::Protocol::Soap::SoapProtocol))
71
- request_info = protocol.request_info(test_request)
86
+ request_info = protocol.request_info
72
87
  assert(request_info.service_name == service_name)
73
88
  assert(request_info.public_method_name == public_method_name)
74
89
  method_name = SoapService.internal_export_name(public_method_name)
@@ -77,7 +92,7 @@ class ProtocolSoapTest < Test::Unit::TestCase
77
92
  assert(params.length == 2)
78
93
  assert(params == [int_value, string_value])
79
94
  response = @container.dispatch_request(protocol, request_info)
80
- service = SoapContainer.service_object(:soap_service)
95
+ service = @container.service_object(:soap_service)
81
96
  assert(service.int == int_value)
82
97
  assert(service.string == string_value)
83
98
  raw_response = response.raw_body
@@ -91,4 +106,76 @@ class ProtocolSoapTest < Test::Unit::TestCase
91
106
  def test_service_name
92
107
  assert(SoapContainer.soap_mapper.custom_namespace == 'urn:Test')
93
108
  end
109
+
110
+ def test_array_returning
111
+ service_name = 'soap_service'
112
+ public_method_name = 'ArrayReturner'
113
+ qname = XSD::QName.new('urn:ActionService', public_method_name)
114
+ request = SOAP::RPC::SOAPMethodRequest.new(qname)
115
+ header = SOAP::SOAPHeader.new
116
+ body = SOAP::SOAPBody.new(request)
117
+ envelope = SOAP::SOAPEnvelope.new(header, body)
118
+ raw_request = SOAP::Processor.marshal(envelope)
119
+ test_request = ActionController::TestRequest.new
120
+ test_request.request_parameters['action'] = service_name
121
+ test_request.env['REQUEST_METHOD'] = "POST"
122
+ test_request.env['HTTP_CONTENTTYPE'] = 'text/xml'
123
+ test_request.env['HTTP_SOAPACTION'] = "/soap/#{service_name}/#{public_method_name}"
124
+ test_request.env['RAW_POST_DATA'] = raw_request
125
+ protocol = @container.request_protocol(test_request)
126
+ assert(protocol.is_a?(ActionService::Protocol::Soap::SoapProtocol))
127
+ request_info = protocol.request_info
128
+ method_name = SoapService.internal_export_name(public_method_name)
129
+ export_info = SoapService.exports[method_name]
130
+ response = @container.dispatch_request(protocol, request_info)
131
+ service = @container.service_object(:soap_service)
132
+ assert(service.values == [1, 2, 3])
133
+ raw_response = response.raw_body
134
+ envelope = SOAP::Processor.unmarshal(raw_response)
135
+ assert(envelope.is_a?(SOAP::SOAPEnvelope))
136
+ resp = envelope.body.response
137
+ assert(resp.is_a?(SOAP::SOAPArray))
138
+ assert(SOAP::Mapping.soap2obj(resp) == [1, 2, 3])
139
+ end
140
+
141
+ def test_default_export
142
+ service_name = 'soap_service'
143
+ public_method_name = 'NonExistentMethodName'
144
+ qname = XSD::QName.new('urn:ActionService', public_method_name)
145
+ int_value = 50
146
+ bool_value = false
147
+ param_def = [
148
+ ['in', 'param1', [SOAP::SOAPInt]],
149
+ ['in', 'param2', [SOAP::SOAPBoolean]],
150
+ ]
151
+ request = SOAP::RPC::SOAPMethodRequest.new(qname, param_def)
152
+ request.set_param([
153
+ ['param1', SOAP::Mapping.obj2soap(int_value)],
154
+ ['param2', SOAP::Mapping.obj2soap(bool_value)]
155
+ ])
156
+ header = SOAP::SOAPHeader.new
157
+ body = SOAP::SOAPBody.new(request)
158
+ envelope = SOAP::SOAPEnvelope.new(header, body)
159
+ raw_request = SOAP::Processor.marshal(envelope)
160
+ test_request = ActionController::TestRequest.new
161
+ test_request.request_parameters['action'] = service_name
162
+ test_request.env['REQUEST_METHOD'] = "POST"
163
+ test_request.env['HTTP_CONTENTTYPE'] = 'text/xml'
164
+ test_request.env['HTTP_SOAPACTION'] = "/soap/#{service_name}/#{public_method_name}"
165
+ test_request.env['RAW_POST_DATA'] = raw_request
166
+ protocol = @container.request_protocol(test_request)
167
+ assert(protocol.is_a?(ActionService::Protocol::Soap::SoapProtocol))
168
+ request_info = protocol.request_info
169
+ method_name = SoapService.internal_export_name(public_method_name)
170
+ export_info = SoapService.exports[method_name]
171
+ service = @container.service_object(:soap_service)
172
+ assert(service.default_args.nil?)
173
+ response = @container.dispatch_request(protocol, request_info)
174
+ raw_response = response.raw_body
175
+ envelope = SOAP::Processor.unmarshal(raw_response)
176
+ assert(envelope.is_a?(SOAP::SOAPEnvelope))
177
+ resp = envelope.body.response
178
+ assert(resp.is_a?(SOAP::SOAPNil))
179
+ assert(service.default_args == [50, false])
180
+ end
94
181
  end
@@ -0,0 +1,130 @@
1
+ require File.dirname(__FILE__) + '/abstract_unit'
2
+ require 'xmlrpc/parser'
3
+ require 'xmlrpc/create'
4
+ require 'xmlrpc/config'
5
+
6
+ module XMLRPC
7
+ class XmlRpcHelper
8
+ include ParserWriterChooseMixin
9
+
10
+ def create_request(methodName, *args)
11
+ create().methodCall(methodName, *args)
12
+ end
13
+
14
+ def parse_response(response)
15
+ parser().parseMethodResponse(response)
16
+ end
17
+ end
18
+ end
19
+
20
+ class XmlRpcService < ActionService::Base
21
+ attr :result
22
+ attr :hashvalue
23
+ attr :default_args
24
+
25
+ def initialize
26
+ @result = nil
27
+ @hashvalue = nil
28
+ @default_args = nil
29
+ end
30
+
31
+ def add(a, b)
32
+ @result = a + b
33
+ end
34
+
35
+ def something_hash(hash)
36
+ @hashvalue = hash
37
+ end
38
+
39
+ def array_returner
40
+ [1, 2, 3]
41
+ end
42
+
43
+ def hash_returner
44
+ {'name' => 1, 'value' => 2}
45
+ end
46
+
47
+ def default(*args)
48
+ @default_args = args
49
+ nil
50
+ end
51
+
52
+ export :add, :expects => [Integer, Integer], :returns => [Integer]
53
+ export :hash_returner, :returns => [Hash]
54
+ export :array_returner, :returns => [[Integer]]
55
+ export :something_hash, :expects => [Hash]
56
+
57
+ default_export :default
58
+ end
59
+
60
+ $service = XmlRpcService.new
61
+
62
+ class XmlRpcContainer
63
+ include ActionService::Container
64
+ include ActionService::Protocol::Registry
65
+ include ActionService::Protocol::Soap
66
+ include ActionService::Protocol::XmlRpc
67
+
68
+ def request_protocol(request)
69
+ probe_request_protocol(request)
70
+ end
71
+
72
+ def dispatch_request(protocol, info)
73
+ dispatch_service_invocation_request(protocol, info)
74
+ end
75
+
76
+ service :xmlrpc, $service
77
+ end
78
+
79
+ class ProtocolXmlRpcTest < Test::Unit::TestCase
80
+ def setup
81
+ @helper = XMLRPC::XmlRpcHelper.new
82
+ @container = XmlRpcContainer.new
83
+ end
84
+
85
+ def test_xmlrpc_request_dispatching
86
+ retval = do_xmlrpc_call('Add', 50, 30)
87
+ assert(retval == [true, 80])
88
+ end
89
+
90
+ def test_array_returning
91
+ retval = do_xmlrpc_call('ArrayReturner')
92
+ assert(retval == [true, [1, 2, 3]])
93
+ end
94
+
95
+ def test_hash_returning
96
+ retval = do_xmlrpc_call('HashReturner')
97
+ assert(retval == [true, {'name' => 1, 'value' => 2}])
98
+ end
99
+
100
+ def test_hash_parameter
101
+ retval = do_xmlrpc_call('SomethingHash', {'name' => 1, 'value' => 2})
102
+ assert(retval == [true, true])
103
+ assert($service.hashvalue == {'name' => 1, 'value' => 2})
104
+ end
105
+
106
+ def test_default_export
107
+ retval = do_xmlrpc_call('SomeNonexistentMethod', 'test', [1, 2], {'name'=>'value'})
108
+ assert(retval == [true, true])
109
+ assert($service.default_args == ['test', [1, 2], {'name'=>'value'}])
110
+ end
111
+
112
+ def test_xmlrpc_introspection
113
+ retval = do_xmlrpc_call('system.listMethods', 'test', [1, 2], {'name'=>'value'})
114
+ assert(retval == [true, ["Add", "ArrayReturner", "HashReturner", "SomethingHash"]])
115
+ end
116
+
117
+ private
118
+ def do_xmlrpc_call(public_method_name, *args)
119
+ service_name = 'xmlrpc'
120
+ raw_request = @helper.create_request(public_method_name, *args)
121
+ test_request = ActionController::TestRequest.new
122
+ test_request.request_parameters['action'] = service_name
123
+ test_request.env['REQUEST_METHOD'] = "POST"
124
+ test_request.env['HTTP_CONTENTTYPE'] = 'text/xml'
125
+ test_request.env['RAW_POST_DATA'] = raw_request
126
+ protocol = @container.request_protocol(test_request)
127
+ response = @container.dispatch_request(protocol, protocol.request_info)
128
+ @helper.parse_response(response.raw_body)
129
+ end
130
+ end
@@ -17,8 +17,12 @@ class WsdlTestService < ActionService::Base
17
17
  []
18
18
  end
19
19
 
20
+ def nil_returner
21
+ end
22
+
20
23
  export :add, :expects => [Integer, Integer], :returns => [Integer]
21
24
  export :find_people, :returns => [[TestPerson]]
25
+ export :nil_returner
22
26
  end
23
27
 
24
28
  class WsdlController < ActionController::Base
@@ -27,7 +31,7 @@ end
27
31
 
28
32
  class RouterWsdlTest < Test::Unit::TestCase
29
33
  def test_wsdl_generation
30
- wsdl = WsdlController.to_wsdl(WsdlController, 'http://localhost:3000/test/', '/test')
34
+ wsdl = WsdlController.to_wsdl(WsdlController.new, 'http://localhost:3000/test/', '/test')
31
35
  assert(WSDL::Parser.new.parse(wsdl).is_a?(WSDL::Definitions))
32
36
  end
33
37
 
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.4
3
3
  specification_version: 1
4
4
  name: actionservice
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.99
7
- date: 2005-02-07
6
+ version: 0.2.100
7
+ date: 2005-02-08
8
8
  summary: Web service support for Action Pack.
9
9
  require_paths:
10
10
  - lib
@@ -35,38 +35,40 @@ files:
35
35
  - CHANGELOG
36
36
  - MIT-LICENSE
37
37
  - examples/soap
38
- - examples/soap/app
39
38
  - examples/soap/lib
40
39
  - examples/soap/README
40
+ - examples/soap/app
41
+ - examples/soap/lib/google_search_service.rb
41
42
  - examples/soap/app/controllers
42
43
  - examples/soap/app/controllers/search_controller.rb
43
- - examples/soap/lib/google_search_service.rb
44
44
  - lib/action_service
45
45
  - lib/action_service.rb
46
- - lib/action_service/router
47
46
  - lib/action_service/protocol
48
- - lib/action_service/invocation.rb
49
- - lib/action_service/router.rb
50
- - lib/action_service/protocol.rb
51
- - lib/action_service/container.rb
52
- - lib/action_service/struct.rb
47
+ - lib/action_service/router
53
48
  - lib/action_service/base.rb
49
+ - lib/action_service/container.rb
54
50
  - lib/action_service/support
51
+ - lib/action_service/protocol.rb
52
+ - lib/action_service/router.rb
53
+ - lib/action_service/struct.rb
54
+ - lib/action_service/invocation.rb
55
+ - lib/action_service/protocol/registry.rb
56
+ - lib/action_service/protocol/soap.rb
57
+ - lib/action_service/protocol/xmlrpc.rb
58
+ - lib/action_service/protocol/abstract.rb
55
59
  - lib/action_service/router/action_controller.rb
56
60
  - lib/action_service/router/wsdl.rb
57
- - lib/action_service/protocol/abstract.rb
58
- - lib/action_service/protocol/soap.rb
59
- - lib/action_service/protocol/registry.rb
60
61
  - lib/action_service/support/class_inheritable_options.rb
62
+ - test/struct_test.rb
63
+ - test/abstract_unit.rb
61
64
  - test/base_test.rb
62
65
  - test/container_test.rb
63
- - test/invocation_test.rb
64
- - test/router_action_controller_test.rb
65
- - test/abstract_unit.rb
66
+ - test/protocol_registry_test.rb
66
67
  - test/protocol_soap_test.rb
67
- - test/struct_test.rb
68
+ - test/invocation_test.rb
68
69
  - test/router_wsdl_test.rb
69
- - test/protocol_registry_test.rb
70
+ - test/protocol_xmlrpc_test.rb
71
+ - test/router_action_controller_test.rb
70
72
  test_files: []
71
73
  rdoc_options: []
72
74
  extra_rdoc_files: []