actionservice 0.2.99 → 0.2.100

Sign up to get free protection for your applications and to get access to all the features.
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: []