mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
generalize casting code to be used by both SOAP and XML-RPC (previously only XML-RPC). switch
to better model for API methods, and improve the ability to generate protocol requests/response, will be required by upcoming scaffolding. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1030 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
439a216dcb
commit
594063f23c
19 changed files with 433 additions and 250 deletions
|
@ -1,3 +1,8 @@
|
|||
*0.7.0* (Unreleased)
|
||||
|
||||
* Generalize casting code to be used by both SOAP and XML-RPC (previously, it was only XML-RPC)
|
||||
|
||||
|
||||
*0.6.2* (27th March, 2005)
|
||||
|
||||
* Allow method declarations for direct dispatching to declare parameters as well. We treat an arity of < 0 or > 0 as an indication that we should send through parameters. Closes #939.
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
module ActionWebService # :nodoc:
|
||||
module API # :nodoc:
|
||||
class CastingError < ActionWebService::ActionWebServiceError
|
||||
end
|
||||
|
||||
# A web service API class specifies the methods that will be available for
|
||||
# invocation for an API. It also contains metadata such as the method type
|
||||
# signature hints.
|
||||
|
@ -77,8 +80,8 @@ module ActionWebService # :nodoc:
|
|||
end
|
||||
name = name.to_sym
|
||||
public_name = public_api_method_name(name)
|
||||
info = { :expects => expects, :returns => returns }
|
||||
write_inheritable_hash("api_methods", name => info)
|
||||
method = Method.new(name, public_name, expects, returns)
|
||||
write_inheritable_hash("api_methods", name => method)
|
||||
write_inheritable_hash("api_public_method_names", public_name => name)
|
||||
end
|
||||
|
||||
|
@ -112,7 +115,39 @@ module ActionWebService # :nodoc:
|
|||
def api_methods
|
||||
read_inheritable_attribute("api_methods") || {}
|
||||
end
|
||||
|
||||
|
||||
# The Method instance for the given public API method name, if any
|
||||
def public_api_method_instance(public_method_name)
|
||||
api_method_instance(api_method_name(public_method_name))
|
||||
end
|
||||
|
||||
# The Method instance for the given API method name, if any
|
||||
def api_method_instance(method_name)
|
||||
api_methods[method_name]
|
||||
end
|
||||
|
||||
# The Method instance for the default API method, if any
|
||||
def default_api_method_instance
|
||||
return nil unless name = default_api_method
|
||||
instance = read_inheritable_attribute("default_api_method_instance")
|
||||
if instance && instance.name == name
|
||||
return instance
|
||||
end
|
||||
instance = Method.new(name, public_api_method_name(name), nil, nil)
|
||||
write_inheritable_attribute("default_api_method_instance", instance)
|
||||
instance
|
||||
end
|
||||
|
||||
# Creates a dummy API Method instance for the given public method name
|
||||
def dummy_public_api_method_instance(public_method_name)
|
||||
Method.new(public_method_name.underscore.to_sym, public_method_name, nil, nil)
|
||||
end
|
||||
|
||||
# Creates a dummy API Method instance for the given method name
|
||||
def dummy_api_method_instance(method_name)
|
||||
Method.new(method_name, public_api_method_name(method_name), nil, nil)
|
||||
end
|
||||
|
||||
private
|
||||
def api_public_method_names
|
||||
read_inheritable_attribute("api_public_method_names") || {}
|
||||
|
@ -131,5 +166,128 @@ module ActionWebService # :nodoc:
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Represents an API method and its associated metadata, and provides functionality
|
||||
# to assist in commonly performed API method tasks.
|
||||
class Method
|
||||
attr :name
|
||||
attr :public_name
|
||||
attr :expects
|
||||
attr :returns
|
||||
|
||||
def initialize(name, public_name, expects, returns)
|
||||
@name = name
|
||||
@public_name = public_name
|
||||
@expects = expects
|
||||
@returns = returns
|
||||
end
|
||||
|
||||
# The list of parameter names for this method
|
||||
def param_names
|
||||
return [] unless @expects
|
||||
i = 0
|
||||
@expects.map{ |spec| param_name(spec, i += 1) }
|
||||
end
|
||||
|
||||
# The name for the given parameter
|
||||
def param_name(spec, i=1)
|
||||
spec.is_a?(Hash) ? spec.keys.first.to_s : "p#{i}"
|
||||
end
|
||||
|
||||
# The type of the parameter declared in +spec+. Is either
|
||||
# the Class of the parameter, or its canonical name (if its a
|
||||
# base type). Typed array specifications will return the type of
|
||||
# their elements.
|
||||
def param_type(spec)
|
||||
spec = spec.values.first if spec.is_a?(Hash)
|
||||
param_type = spec.is_a?(Array) ? spec[0] : spec
|
||||
WS::BaseTypes::class_to_type_name(param_type) rescue param_type
|
||||
end
|
||||
|
||||
# The Class of the parameter declared in +spec+.
|
||||
def param_class(spec)
|
||||
type = param_type(spec)
|
||||
type.is_a?(Symbol) ? WS::BaseTypes.type_name_to_class(type) : type
|
||||
end
|
||||
|
||||
# Registers all types known to this method with the given marshaler
|
||||
def register_types(marshaler)
|
||||
@expects.each{ |x| marshaler.register_type(x) } if @expects
|
||||
@returns.each{ |x| marshaler.register_type(x) } if @returns
|
||||
end
|
||||
|
||||
# Encodes an RPC call for this method. Casting is performed if
|
||||
# the <tt>:strict</tt> option is given.
|
||||
def encode_rpc_call(marshaler, encoder, params, options={})
|
||||
name = options[:method_name] || @public_name
|
||||
expects = @expects || []
|
||||
returns = @returns || []
|
||||
(expects + returns).each { |spec| marshaler.register_type spec }
|
||||
(0..(params.length-1)).each do |i|
|
||||
spec = expects[i] || params[i].class
|
||||
type_binding = marshaler.lookup_type(spec)
|
||||
param_info = WS::ParamInfo.create(spec, type_binding, i)
|
||||
if options[:strict]
|
||||
value = marshaler.cast_outbound_recursive(params[i], spec)
|
||||
else
|
||||
value = params[i]
|
||||
end
|
||||
param = WS::Param.new(value, param_info)
|
||||
params[i] = marshaler.marshal(param)
|
||||
end
|
||||
encoder.encode_rpc_call(name, params)
|
||||
end
|
||||
|
||||
# Encodes an RPC response for this method. Casting is performed if
|
||||
# the <tt>:strict</tt> option is given.
|
||||
def encode_rpc_response(marshaler, encoder, return_value, options={})
|
||||
if !return_value.nil? && @returns
|
||||
return_type = @returns[0]
|
||||
type_binding = marshaler.register_type(return_type)
|
||||
param_info = WS::ParamInfo.create(return_type, type_binding, 0)
|
||||
if options[:strict]
|
||||
return_value = marshaler.cast_inbound_recursive(return_value, return_type)
|
||||
end
|
||||
return_value = marshaler.marshal(WS::Param.new(return_value, param_info))
|
||||
else
|
||||
return_value = nil
|
||||
end
|
||||
encoder.encode_rpc_response(response_name(encoder), return_value)
|
||||
end
|
||||
|
||||
# Casts a set of WS::Param values into the appropriate
|
||||
# Ruby values
|
||||
def cast_expects_ws2ruby(marshaler, params)
|
||||
return [] if @expects.nil?
|
||||
i = 0
|
||||
@expects.map do |spec|
|
||||
value = marshaler.cast_inbound_recursive(params[i].value, spec)
|
||||
i += 1
|
||||
value
|
||||
end
|
||||
end
|
||||
|
||||
# Casts a set of Ruby values into the expected Ruby values
|
||||
def cast_expects(marshaler, params)
|
||||
return [] if @expects.nil?
|
||||
i = 0
|
||||
@expects.map do |spec|
|
||||
value = marshaler.cast_outbound_recursive(params[i], spec)
|
||||
i += 1
|
||||
value
|
||||
end
|
||||
end
|
||||
|
||||
# Cast a Ruby return value into the expected Ruby value
|
||||
def cast_returns(marshaler, return_value)
|
||||
return nil if @returns.nil?
|
||||
marshaler.cast_inbound_recursive(return_value, @returns[0])
|
||||
end
|
||||
|
||||
private
|
||||
def response_name(encoder)
|
||||
encoder.is_a?(WS::Encoding::SoapRpcEncoding) ? (@public_name + "Response") : @public_name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -58,7 +58,10 @@ module ActionWebService # :nodoc:
|
|||
|
||||
protected
|
||||
def perform_invocation(method_name, args)
|
||||
@driver.send(method_name, *args)
|
||||
method = @api.api_methods[method_name.to_sym]
|
||||
args = method.cast_expects(@marshaler, args)
|
||||
return_value = @driver.send(method_name, *args)
|
||||
method.cast_returns(@marshaler, return_value)
|
||||
end
|
||||
|
||||
def soap_action(method_name)
|
||||
|
@ -67,48 +70,33 @@ module ActionWebService # :nodoc:
|
|||
|
||||
private
|
||||
def create_soap_rpc_driver(api, endpoint_uri)
|
||||
register_api(@marshaler, api)
|
||||
api.api_methods.each{ |name, method| method.register_types(@marshaler) }
|
||||
driver = SoapDriver.new(endpoint_uri, nil)
|
||||
driver.mapping_registry = @marshaler.registry
|
||||
api.api_methods.each do |name, info|
|
||||
public_name = api.public_api_method_name(name)
|
||||
qname = XSD::QName.new(@method_namespace, public_name)
|
||||
action = soap_action(public_name)
|
||||
expects = info[:expects]
|
||||
returns = info[:returns]
|
||||
api.api_methods.each do |name, method|
|
||||
qname = XSD::QName.new(@method_namespace, method.public_name)
|
||||
action = soap_action(method.public_name)
|
||||
expects = method.expects
|
||||
returns = method.returns
|
||||
param_def = []
|
||||
i = 0
|
||||
if expects
|
||||
expects.each do |spec|
|
||||
param_name = spec.is_a?(Hash) ? spec.keys[0].to_s : "param#{i}"
|
||||
type_binding = @marshaler.register_type(spec)
|
||||
param_name = method.param_name(spec, i)
|
||||
type_binding = @marshaler.lookup_type(spec)
|
||||
param_def << ['in', param_name, type_binding.mapping]
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
if returns
|
||||
type_binding = @marshaler.register_type(returns[0])
|
||||
type_binding = @marshaler.lookup_type(returns[0])
|
||||
param_def << ['retval', 'return', type_binding.mapping]
|
||||
end
|
||||
driver.add_method(qname, action, name.to_s, param_def)
|
||||
driver.add_method(qname, action, method.name.to_s, param_def)
|
||||
end
|
||||
driver
|
||||
end
|
||||
|
||||
def register_api(marshaler, api)
|
||||
type_bindings = []
|
||||
api.api_methods.each do |name, info|
|
||||
expects, returns = info[:expects], info[:returns]
|
||||
if expects
|
||||
expects.each{|type| type_bindings << marshaler.register_type(type)}
|
||||
end
|
||||
if returns
|
||||
returns.each{|type| type_bindings << marshaler.register_type(type)}
|
||||
end
|
||||
end
|
||||
type_bindings
|
||||
end
|
||||
|
||||
class SoapDriver < SOAP::RPC::Driver # :nodoc:
|
||||
def add_method(qname, soapaction, name, param_def)
|
||||
@proxy.add_rpc_method(qname, soapaction, name, param_def)
|
||||
|
|
|
@ -36,43 +36,17 @@ module ActionWebService # :nodoc:
|
|||
|
||||
protected
|
||||
def perform_invocation(method_name, args)
|
||||
args = transform_outgoing_method_params(method_name, args)
|
||||
method = @api.api_methods[method_name.to_sym]
|
||||
method.register_types(@marshaler)
|
||||
if method.expects && method.expects.length != args.length
|
||||
raise(ArgumentError, "#{method.public_name}: wrong number of arguments (#{args.length} for #{method.expects.length})")
|
||||
end
|
||||
args = method.cast_expects(@marshaler, args)
|
||||
ok, return_value = @client.call2(public_name(method_name), *args)
|
||||
return transform_return_value(method_name, return_value) if ok
|
||||
return method.cast_returns(@marshaler, return_value) if ok
|
||||
raise(ClientError, "#{return_value.faultCode}: #{return_value.faultString}")
|
||||
end
|
||||
|
||||
def transform_outgoing_method_params(method_name, params)
|
||||
info = @api.api_methods[method_name.to_sym]
|
||||
expects = info[:expects]
|
||||
expects_length = expects.nil?? 0 : expects.length
|
||||
if expects_length != params.length
|
||||
raise(ClientError, "API declares #{public_name(method_name)} to accept " +
|
||||
"#{expects_length} parameters, but #{params.length} parameters " +
|
||||
"were supplied")
|
||||
end
|
||||
params = params.dup
|
||||
if expects_length > 0
|
||||
i = 0
|
||||
expects.each do |spec|
|
||||
type_binding = @marshaler.register_type(spec)
|
||||
info = WS::ParamInfo.create(spec, type_binding, i)
|
||||
params[i] = @marshaler.marshal(WS::Param.new(params[i], info))
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
params
|
||||
end
|
||||
|
||||
def transform_return_value(method_name, return_value)
|
||||
info = @api.api_methods[method_name.to_sym]
|
||||
return true unless returns = info[:returns]
|
||||
type_binding = @marshaler.register_type(returns[0])
|
||||
info = WS::ParamInfo.create(returns[0], type_binding, 0)
|
||||
info.name = 'return'
|
||||
@marshaler.transform_inbound(WS::Param.new(return_value, info))
|
||||
end
|
||||
|
||||
def public_name(method_name)
|
||||
public_name = @api.public_api_method_name(method_name)
|
||||
@handler_name ? "#{@handler_name}.#{public_name}" : public_name
|
||||
|
|
|
@ -34,30 +34,30 @@ module ActionWebService # :nodoc:
|
|||
|
||||
def web_service_direct_invoke(invocation)
|
||||
@method_params = invocation.method_ordered_params
|
||||
arity = method(invocation.api_method_name).arity rescue 0
|
||||
arity = method(invocation.api_method.name).arity rescue 0
|
||||
if arity < 0 || arity > 0
|
||||
return_value = self.__send__(invocation.api_method_name, *@method_params)
|
||||
return_value = self.__send__(invocation.api_method.name, *@method_params)
|
||||
else
|
||||
return_value = self.__send__(invocation.api_method_name)
|
||||
return_value = self.__send__(invocation.api_method.name)
|
||||
end
|
||||
if invocation.api.has_api_method?(invocation.api_method_name)
|
||||
returns = invocation.returns ? invocation.returns[0] : nil
|
||||
if invocation.api.has_api_method?(invocation.api_method.name)
|
||||
api_method = invocation.api_method
|
||||
else
|
||||
returns = return_value.class
|
||||
api_method = invocation.api_method.dup
|
||||
api_method.instance_eval{ @returns = [ return_value.class ] }
|
||||
end
|
||||
invocation.protocol.marshal_response(invocation.public_method_name, return_value, returns)
|
||||
invocation.protocol.marshal_response(api_method, return_value)
|
||||
end
|
||||
|
||||
def web_service_delegated_invoke(invocation)
|
||||
cancellation_reason = nil
|
||||
return_value = invocation.service.perform_invocation(invocation.api_method_name, invocation.method_ordered_params) do |x|
|
||||
return_value = invocation.service.perform_invocation(invocation.api_method.name, invocation.method_ordered_params) do |x|
|
||||
cancellation_reason = x
|
||||
end
|
||||
if cancellation_reason
|
||||
raise(DispatcherError, "request canceled: #{cancellation_reason}")
|
||||
end
|
||||
returns = invocation.returns ? invocation.returns[0] : nil
|
||||
invocation.protocol.marshal_response(invocation.public_method_name, return_value, returns)
|
||||
invocation.protocol.marshal_response(invocation.api_method, return_value)
|
||||
end
|
||||
|
||||
def web_service_invocation(request)
|
||||
|
@ -71,7 +71,6 @@ module ActionWebService # :nodoc:
|
|||
invocation.service_name = $1
|
||||
end
|
||||
end
|
||||
invocation.public_method_name = public_method_name
|
||||
case web_service_dispatching_mode
|
||||
when :direct
|
||||
invocation.api = self.class.web_service_api
|
||||
|
@ -83,54 +82,29 @@ module ActionWebService # :nodoc:
|
|||
end
|
||||
invocation.api = invocation.service.class.web_service_api
|
||||
end
|
||||
request.api = invocation.api
|
||||
if invocation.api.has_public_api_method?(public_method_name)
|
||||
invocation.api_method_name = invocation.api.api_method_name(public_method_name)
|
||||
invocation.api_method = invocation.api.public_api_method_instance(public_method_name)
|
||||
else
|
||||
if invocation.api.default_api_method.nil?
|
||||
raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api}")
|
||||
else
|
||||
invocation.api_method_name = invocation.api.default_api_method.to_s.to_sym
|
||||
invocation.api_method = invocation.api.default_api_method_instance
|
||||
end
|
||||
end
|
||||
unless invocation.service.respond_to?(invocation.api_method_name)
|
||||
raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api} (#{invocation.api_method_name})")
|
||||
unless invocation.service.respond_to?(invocation.api_method.name)
|
||||
raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api} (#{invocation.api_method.name})")
|
||||
end
|
||||
info = invocation.api.api_methods[invocation.api_method_name]
|
||||
invocation.expects = info ? info[:expects] : nil
|
||||
invocation.returns = info ? info[:returns] : nil
|
||||
if invocation.expects
|
||||
i = 0
|
||||
invocation.method_ordered_params = request.method_params.map do |param|
|
||||
if invocation.protocol.is_a?(Protocol::XmlRpc::XmlRpcProtocol)
|
||||
marshaler = invocation.protocol.marshaler
|
||||
decoded_param = WS::Encoding::XmlRpcDecodedParam.new(param.info.name, param.value)
|
||||
marshaled_param = marshaler.typed_unmarshal(decoded_param, invocation.expects[i]) rescue nil
|
||||
param = marshaled_param ? marshaled_param : param
|
||||
end
|
||||
i += 1
|
||||
param.value
|
||||
end
|
||||
i = 0
|
||||
params = []
|
||||
invocation.expects.each do |spec|
|
||||
type_binding = invocation.protocol.register_signature_type(spec)
|
||||
info = WS::ParamInfo.create(spec, type_binding, i)
|
||||
params << WS::Param.new(invocation.method_ordered_params[i], info)
|
||||
i += 1
|
||||
end
|
||||
invocation.method_ws_params = params
|
||||
invocation.method_named_params = {}
|
||||
invocation.method_ws_params.each do |param|
|
||||
invocation.method_named_params[param.info.name] = param.value
|
||||
end
|
||||
else
|
||||
invocation.method_ordered_params = []
|
||||
invocation.method_named_params = {}
|
||||
request.api_method = invocation.api_method
|
||||
begin
|
||||
invocation.method_ordered_params = invocation.api_method.cast_expects_ws2ruby(request.protocol.marshaler, request.method_params)
|
||||
rescue
|
||||
invocation.method_ordered_params = request.method_params.map{ |x| x.value }
|
||||
end
|
||||
if invocation.returns
|
||||
invocation.returns.each do |spec|
|
||||
invocation.protocol.register_signature_type(spec)
|
||||
end
|
||||
invocation.method_named_params = {}
|
||||
invocation.api_method.param_names.inject(0) do |m, n|
|
||||
invocation.method_named_params[n] = invocation.method_ordered_params[m]
|
||||
m + 1
|
||||
end
|
||||
invocation
|
||||
end
|
||||
|
@ -139,13 +113,9 @@ module ActionWebService # :nodoc:
|
|||
attr_accessor :protocol
|
||||
attr_accessor :service_name
|
||||
attr_accessor :api
|
||||
attr_accessor :public_method_name
|
||||
attr_accessor :api_method_name
|
||||
attr_accessor :api_method
|
||||
attr_accessor :method_ordered_params
|
||||
attr_accessor :method_named_params
|
||||
attr_accessor :method_ws_params
|
||||
attr_accessor :expects
|
||||
attr_accessor :returns
|
||||
attr_accessor :service
|
||||
end
|
||||
end
|
||||
|
|
|
@ -76,7 +76,10 @@ module ActionWebService # :nodoc:
|
|||
unless self.class.web_service_exception_reporting
|
||||
exception = DispatcherError.new("Internal server error (exception raised)")
|
||||
end
|
||||
response = request.protocol.marshal_response(request.method_name, exception, exception.class)
|
||||
api_method = request.api_method ? request.api_method.dup : nil
|
||||
api_method ||= request.api.dummy_api_method_instance(request.method_name)
|
||||
api_method.instance_eval{ @returns = [ exception.class ] }
|
||||
response = request.protocol.marshal_response(api_method, exception)
|
||||
send_web_service_response(response)
|
||||
else
|
||||
if self.class.web_service_exception_reporting
|
||||
|
@ -95,7 +98,7 @@ module ActionWebService # :nodoc:
|
|||
end
|
||||
@session ||= {}
|
||||
@assigns ||= {}
|
||||
@params['action'] = invocation.api_method_name.to_s
|
||||
@params['action'] = invocation.api_method.name.to_s
|
||||
if before_action == false
|
||||
raise(DispatcherError, "Method filtered")
|
||||
end
|
||||
|
@ -224,18 +227,18 @@ module ActionWebService # :nodoc:
|
|||
# APIs
|
||||
apis.each do |api_name, values|
|
||||
api = values[0]
|
||||
api.api_methods.each do |name, info|
|
||||
api.api_methods.each do |name, method|
|
||||
gen = lambda do |msg_name, direction|
|
||||
xm.message('name' => msg_name) do
|
||||
sym = nil
|
||||
if direction == :out
|
||||
returns = info[:returns]
|
||||
returns = method.returns
|
||||
if returns
|
||||
binding = marshaler.register_type(returns[0])
|
||||
xm.part('name' => 'return', 'type' => binding.qualified_type_name('typens'))
|
||||
end
|
||||
else
|
||||
expects = info[:expects]
|
||||
expects = method.expects
|
||||
i = 1
|
||||
expects.each do |type|
|
||||
if type.is_a?(Hash)
|
||||
|
@ -251,7 +254,7 @@ module ActionWebService # :nodoc:
|
|||
end
|
||||
end
|
||||
end
|
||||
public_name = api.public_api_method_name(name)
|
||||
public_name = method.public_name
|
||||
gen.call(public_name, :in)
|
||||
gen.call("#{public_name}Response", :out)
|
||||
end
|
||||
|
@ -259,11 +262,10 @@ module ActionWebService # :nodoc:
|
|||
# Port
|
||||
port_name = port_name_for(global_service_name, api_name)
|
||||
xm.portType('name' => port_name) do
|
||||
api.api_methods.each do |name, info|
|
||||
public_name = api.public_api_method_name(name)
|
||||
xm.operation('name' => public_name) do
|
||||
xm.input('message' => "typens:#{public_name}")
|
||||
xm.output('message' => "typens:#{public_name}Response")
|
||||
api.api_methods.each do |name, method|
|
||||
xm.operation('name' => method.public_name) do
|
||||
xm.input('message' => "typens:#{method.public_name}")
|
||||
xm.output('message' => "typens:#{method.public_name}Response")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -272,16 +274,15 @@ module ActionWebService # :nodoc:
|
|||
binding_name = binding_name_for(global_service_name, api_name)
|
||||
xm.binding('name' => binding_name, 'type' => "typens:#{port_name}") do
|
||||
xm.soap(:binding, 'style' => 'rpc', 'transport' => SoapHttpTransport)
|
||||
api.api_methods.each do |name, info|
|
||||
public_name = api.public_api_method_name(name)
|
||||
xm.operation('name' => public_name) do
|
||||
api.api_methods.each do |name, method|
|
||||
xm.operation('name' => method.public_name) do
|
||||
case web_service_dispatching_mode
|
||||
when :direct, :layered
|
||||
soap_action = soap_action_base + "/api/" + public_name
|
||||
soap_action = soap_action_base + "/api/" + method.public_name
|
||||
when :delegated
|
||||
soap_action = soap_action_base \
|
||||
+ "/" + api_name.to_s \
|
||||
+ "/" + public_name
|
||||
+ "/" + method.public_name
|
||||
end
|
||||
xm.soap(:operation, 'soapAction' => soap_action)
|
||||
xm.input do
|
||||
|
@ -337,8 +338,8 @@ module ActionWebService # :nodoc:
|
|||
end
|
||||
|
||||
def traverse_custom_types(api, marshaler, &block)
|
||||
api.api_methods.each do |name, info|
|
||||
expects, returns = info[:expects], info[:returns]
|
||||
api.api_methods.each do |name, method|
|
||||
expects, returns = method.expects, method.returns
|
||||
expects.each{|x| traverse_custom_type_spec(marshaler, x, &block)} if expects
|
||||
returns.each{|x| traverse_custom_type_spec(marshaler, x, &block)} if returns
|
||||
end
|
||||
|
|
|
@ -3,17 +3,31 @@ module ActionWebService # :nodoc:
|
|||
class ProtocolError < ActionWebServiceError # :nodoc:
|
||||
end
|
||||
|
||||
class AbstractProtocol
|
||||
attr :marshaler
|
||||
attr :encoder
|
||||
|
||||
def marshal_response(method, return_value)
|
||||
body = method.encode_rpc_response(marshaler, encoder, return_value)
|
||||
Response.new(body, 'text/xml')
|
||||
end
|
||||
end
|
||||
|
||||
class Request # :nodoc:
|
||||
attr :protocol
|
||||
attr :method_name
|
||||
attr :method_params
|
||||
attr :service_name
|
||||
attr_accessor :api
|
||||
attr_accessor :api_method
|
||||
|
||||
def initialize(protocol, method_name, method_params, service_name)
|
||||
def initialize(protocol, method_name, method_params, service_name, api=nil, api_method=nil)
|
||||
@protocol = protocol
|
||||
@method_name = method_name
|
||||
@method_params = method_params
|
||||
@service_name = service_name
|
||||
@api = api
|
||||
@api_method = api_method
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ module ActionWebService # :nodoc:
|
|||
base.class_inheritable_option(:wsdl_service_name)
|
||||
end
|
||||
|
||||
class SoapProtocol # :nodoc:
|
||||
class SoapProtocol < AbstractProtocol # :nodoc:
|
||||
def initialize
|
||||
@encoder = WS::Encoding::SoapRpcEncoding.new 'urn:ActionWebService'
|
||||
@marshaler = WS::Marshaling::SoapMarshaler.new 'urn:ActionWebService'
|
||||
|
@ -20,22 +20,6 @@ module ActionWebService # :nodoc:
|
|||
Request.new(self, method_name, params, service_name)
|
||||
end
|
||||
|
||||
def marshal_response(method_name, return_value, signature_type)
|
||||
if !return_value.nil? && signature_type
|
||||
type_binding = @marshaler.register_type(signature_type)
|
||||
info = WS::ParamInfo.create(signature_type, type_binding, 0)
|
||||
return_value = @marshaler.marshal(WS::Param.new(return_value, info))
|
||||
else
|
||||
return_value = nil
|
||||
end
|
||||
body = @encoder.encode_rpc_response(method_name + 'Response', return_value)
|
||||
Response.new(body, 'text/xml')
|
||||
end
|
||||
|
||||
def register_signature_type(spec)
|
||||
@marshaler.register_type(spec)
|
||||
end
|
||||
|
||||
def protocol_client(api, protocol_name, endpoint_uri, options)
|
||||
return nil unless protocol_name == :soap
|
||||
ActionWebService::Client::Soap.new(api, endpoint_uri, options)
|
||||
|
|
|
@ -5,9 +5,7 @@ module ActionWebService # :nodoc:
|
|||
base.register_protocol(XmlRpcProtocol)
|
||||
end
|
||||
|
||||
class XmlRpcProtocol # :nodoc:
|
||||
attr :marshaler
|
||||
|
||||
class XmlRpcProtocol < AbstractProtocol # :nodoc:
|
||||
def initialize
|
||||
@encoder = WS::Encoding::XmlRpcEncoding.new
|
||||
@marshaler = WS::Marshaling::XmlRpcMarshaler.new
|
||||
|
@ -22,22 +20,6 @@ module ActionWebService # :nodoc:
|
|||
nil
|
||||
end
|
||||
|
||||
def marshal_response(method_name, return_value, signature_type)
|
||||
if !return_value.nil? && signature_type
|
||||
type_binding = @marshaler.register_type(signature_type)
|
||||
info = WS::ParamInfo.create(signature_type, type_binding, 0)
|
||||
return_value = @marshaler.marshal(WS::Param.new(return_value, info))
|
||||
else
|
||||
return_value = nil
|
||||
end
|
||||
body = @encoder.encode_rpc_response(method_name, return_value)
|
||||
Response.new(body, 'text/xml')
|
||||
end
|
||||
|
||||
def register_signature_type(spec)
|
||||
nil
|
||||
end
|
||||
|
||||
def protocol_client(api, protocol_name, endpoint_uri, options)
|
||||
return nil unless protocol_name == :xmlrpc
|
||||
ActionWebService::Client::XmlRpc.new(api, endpoint_uri, options)
|
||||
|
|
|
@ -52,18 +52,9 @@ module Test # :nodoc:
|
|||
when :delegated, :layered
|
||||
api = @controller.web_service_object(service_name.to_sym).class.web_service_api
|
||||
end
|
||||
info = api.api_methods[api_method_name.to_sym]
|
||||
((info[:expects] || []) + (info[:returns] || [])).each do |spec|
|
||||
marshaler.register_type spec
|
||||
end
|
||||
expects = info[:expects]
|
||||
args = args.dup
|
||||
(0..(args.length-1)).each do |i|
|
||||
type_binding = marshaler.register_type(expects ? expects[i] : args[i].class)
|
||||
info = WS::ParamInfo.create(expects ? expects[i] : args[i].class, type_binding, i)
|
||||
args[i] = marshaler.marshal(WS::Param.new(args[i], info))
|
||||
end
|
||||
encoder.encode_rpc_call(public_method_name(service_name, api_method_name), args)
|
||||
method = api.api_methods[api_method_name.to_sym]
|
||||
method.register_types(marshaler)
|
||||
method.encode_rpc_call(marshaler, encoder, args.dup, :method_name => public_method_name(service_name, api_method_name))
|
||||
end
|
||||
|
||||
def decode_rpc_response
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
module WS
|
||||
module Marshaling
|
||||
class AbstractMarshaler
|
||||
def initialize
|
||||
@base_type_caster = BaseTypeCaster.new
|
||||
end
|
||||
|
||||
def marshal(param)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
@ -12,6 +16,18 @@ module WS
|
|||
def register_type(type)
|
||||
nil
|
||||
end
|
||||
alias :lookup_type :register_type
|
||||
|
||||
def cast_inbound_recursive(value, spec)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def cast_outbound_recursive(value, spec)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
attr :base_type_caster
|
||||
protected :base_type_caster
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,6 +13,7 @@ module WS
|
|||
attr_accessor :type_namespace
|
||||
|
||||
def initialize(type_namespace='')
|
||||
super()
|
||||
@type_namespace = type_namespace
|
||||
@registry = SOAP::Mapping::Registry.new
|
||||
@spec2binding = {}
|
||||
|
@ -92,6 +93,25 @@ module WS
|
|||
@spec2binding[spec] = array_binding ? array_binding : type_binding
|
||||
@spec2binding[spec]
|
||||
end
|
||||
alias :lookup_type :register_type
|
||||
|
||||
def cast_inbound_recursive(value, spec)
|
||||
binding = lookup_type(spec)
|
||||
if binding.is_custom_type?
|
||||
value
|
||||
else
|
||||
base_type_caster.cast(value, binding.type_class)
|
||||
end
|
||||
end
|
||||
|
||||
def cast_outbound_recursive(value, spec)
|
||||
binding = lookup_type(spec)
|
||||
if binding.is_custom_type?
|
||||
value
|
||||
else
|
||||
base_type_caster.cast(value, binding.type_class)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def annotate_arrays(binding, value)
|
||||
|
@ -106,7 +126,7 @@ module WS
|
|||
if binding.type_class.respond_to?(:members)
|
||||
binding.type_class.members.each do |name, spec|
|
||||
member_binding = register_type(spec)
|
||||
member_value = value.send(name)
|
||||
member_value = value.respond_to?('[]') ? value[name] : value.send(name)
|
||||
if member_binding.is_custom_type?
|
||||
annotate_arrays(member_binding, member_value)
|
||||
end
|
||||
|
|
|
@ -5,24 +5,24 @@ module WS
|
|||
|
||||
class XmlRpcMarshaler < AbstractMarshaler
|
||||
def initialize
|
||||
@caster = BaseTypeCaster.new
|
||||
super()
|
||||
@spec2binding = {}
|
||||
end
|
||||
|
||||
def marshal(param)
|
||||
transform_outbound(param)
|
||||
value = param.value
|
||||
cast_outbound_recursive(param.value, spec_for(param)) rescue value
|
||||
end
|
||||
|
||||
def unmarshal(obj)
|
||||
obj.param.value = transform_inbound(obj.param)
|
||||
obj.param
|
||||
end
|
||||
|
||||
def typed_unmarshal(obj, spec)
|
||||
param = obj.param
|
||||
param.info.data = register_type(spec)
|
||||
param.value = transform_inbound(param)
|
||||
param
|
||||
obj.param.info.data = lookup_type(spec)
|
||||
value = obj.param.value
|
||||
obj.param.value = cast_inbound_recursive(value, spec) rescue value
|
||||
obj.param
|
||||
end
|
||||
|
||||
def register_type(spec)
|
||||
|
@ -40,60 +40,87 @@ module WS
|
|||
|
||||
@spec2binding[spec] = type_binding
|
||||
end
|
||||
alias :lookup_type :register_type
|
||||
|
||||
def transform_outbound(param)
|
||||
binding = param.info.data
|
||||
def cast_inbound_recursive(value, spec)
|
||||
binding = lookup_type(spec)
|
||||
case binding
|
||||
when XmlRpcArrayBinding
|
||||
param.value.map{|x| cast_outbound(x, binding.element_klass)}
|
||||
value.map{ |x| cast_inbound(x, binding.element_klass) }
|
||||
when XmlRpcBinding
|
||||
cast_outbound(param.value, param.info.type)
|
||||
cast_inbound(value, binding.klass)
|
||||
end
|
||||
end
|
||||
|
||||
def transform_inbound(param)
|
||||
return param.value if param.info.data.nil?
|
||||
binding = param.info.data
|
||||
param.info.type = binding.klass
|
||||
def cast_outbound_recursive(value, spec)
|
||||
binding = lookup_type(spec)
|
||||
case binding
|
||||
when XmlRpcArrayBinding
|
||||
param.value.map{|x| cast_inbound(x, binding.element_klass)}
|
||||
value.map{ |x| cast_outbound(x, binding.element_klass) }
|
||||
when XmlRpcBinding
|
||||
cast_inbound(param.value, param.info.type)
|
||||
cast_outbound(value, binding.klass)
|
||||
end
|
||||
end
|
||||
|
||||
def cast_outbound(value, klass)
|
||||
if BaseTypes.base_type?(klass)
|
||||
@caster.cast(value, klass)
|
||||
elsif value.is_a?(Exception)
|
||||
XMLRPC::FaultException.new(2, value.message)
|
||||
elsif Object.const_defined?('ActiveRecord') && value.is_a?(ActiveRecord::Base)
|
||||
value.attributes
|
||||
else
|
||||
struct = {}
|
||||
value.instance_variables.each do |name|
|
||||
key = name.sub(/^@/, '')
|
||||
struct[key] = value.instance_variable_get(name)
|
||||
end
|
||||
struct
|
||||
private
|
||||
def spec_for(param)
|
||||
binding = param.info.data
|
||||
binding.is_a?(XmlRpcArrayBinding) ? [binding.element_klass] : binding.klass
|
||||
end
|
||||
end
|
||||
|
||||
def cast_inbound(value, klass)
|
||||
if BaseTypes.base_type?(klass)
|
||||
value = value.to_time if value.is_a?(XMLRPC::DateTime)
|
||||
@caster.cast(value, klass)
|
||||
elsif value.is_a?(XMLRPC::FaultException)
|
||||
value
|
||||
else
|
||||
obj = klass.new
|
||||
value.each do |name, val|
|
||||
obj.send('%s=' % name.to_s, val)
|
||||
def cast_inbound(value, klass)
|
||||
if BaseTypes.base_type?(klass)
|
||||
value = value.to_time if value.is_a?(XMLRPC::DateTime)
|
||||
base_type_caster.cast(value, klass)
|
||||
elsif value.is_a?(XMLRPC::FaultException)
|
||||
value
|
||||
elsif klass.ancestors.include?(ActionWebService::Struct)
|
||||
obj = klass.new
|
||||
klass.members.each do |name, klass|
|
||||
name = name.to_s
|
||||
obj.send('%s=' % name, cast_inbound_recursive(value[name], klass))
|
||||
end
|
||||
obj
|
||||
else
|
||||
obj = klass.new
|
||||
if obj.respond_to?(:update)
|
||||
obj.update(value)
|
||||
else
|
||||
value.each do |name, val|
|
||||
obj.send('%s=' % name.to_s, val)
|
||||
end
|
||||
end
|
||||
obj
|
||||
end
|
||||
end
|
||||
|
||||
def cast_outbound(value, klass)
|
||||
if BaseTypes.base_type?(klass)
|
||||
base_type_caster.cast(value, klass)
|
||||
elsif value.is_a?(Exception)
|
||||
XMLRPC::FaultException.new(2, value.message)
|
||||
elsif Object.const_defined?('ActiveRecord') && value.is_a?(ActiveRecord::Base)
|
||||
value.attributes
|
||||
elsif value.is_a?(ActionWebService::Struct)
|
||||
struct = {}
|
||||
value.class.members.each do |name, klass|
|
||||
name = name.to_s
|
||||
struct[name] = cast_outbound_recursive(value[name], klass)
|
||||
end
|
||||
struct
|
||||
else
|
||||
struct = {}
|
||||
if value.respond_to?(:each_pair)
|
||||
value.each_pair{ |key, value| struct[key] = value }
|
||||
else
|
||||
value.instance_variables.each do |name|
|
||||
key = name.sub(/^@/, '')
|
||||
struct[key] = value.instance_variable_get(name)
|
||||
end
|
||||
end
|
||||
struct
|
||||
end
|
||||
obj
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class XmlRpcBinding
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
|
||||
class ActionController::Base; def rescue_action(e) raise e end; end
|
||||
|
||||
module DispatcherTest
|
||||
class Node < ActiveRecord::Base
|
||||
def initialize(*args)
|
||||
|
@ -29,6 +31,10 @@ module DispatcherTest
|
|||
class Person < ActionWebService::Struct
|
||||
member :id, :int
|
||||
member :name, :string
|
||||
|
||||
def ==(other)
|
||||
self.id == other.id && self.name == other.name
|
||||
end
|
||||
end
|
||||
|
||||
class API < ActionWebService::API::Base
|
||||
|
@ -44,6 +50,7 @@ module DispatcherTest
|
|||
api_method :before_filtered
|
||||
api_method :after_filtered, :returns => [[:int]]
|
||||
api_method :struct_return, :returns => [[Node]]
|
||||
api_method :struct_pass, :expects => [Person]
|
||||
api_method :base_struct_return, :returns => [[Person]]
|
||||
api_method :thrower
|
||||
api_method :void
|
||||
|
@ -148,6 +155,7 @@ module DispatcherTest
|
|||
attr :after_filter_called
|
||||
attr :after_filter_target_called
|
||||
attr :void_called
|
||||
attr :struct_pass_value
|
||||
|
||||
def initialize
|
||||
@before_filter_called = false
|
||||
|
@ -155,6 +163,7 @@ module DispatcherTest
|
|||
@after_filter_called = false
|
||||
@after_filter_target_called = false
|
||||
@void_called = false
|
||||
@struct_pass_value = false
|
||||
end
|
||||
|
||||
def add
|
||||
|
@ -184,6 +193,10 @@ module DispatcherTest
|
|||
[n1, n2]
|
||||
end
|
||||
|
||||
def struct_pass(person)
|
||||
@struct_pass_value = person
|
||||
end
|
||||
|
||||
def base_struct_return
|
||||
p1 = Person.new('id' => 1, 'name' => 'person1')
|
||||
p2 = Person.new('id' => 2, 'name' => 'person2')
|
||||
|
@ -328,6 +341,26 @@ module DispatcherCommonTests
|
|||
end
|
||||
end
|
||||
|
||||
def test_casting
|
||||
assert_equal 70, do_method_call(@direct_controller, 'Add', "50", "20")
|
||||
assert_equal false, @direct_controller.struct_pass_value
|
||||
person = DispatcherTest::Person.new(:id => 1, :name => 'test')
|
||||
result = do_method_call(@direct_controller, 'StructPass', person)
|
||||
assert(nil == result || true == result)
|
||||
assert_equal person, @direct_controller.struct_pass_value
|
||||
assert !person.equal?(@direct_controller.struct_pass_value)
|
||||
result = do_method_call(@direct_controller, 'StructPass', {'id' => '1', 'name' => 'test'})
|
||||
case @encoder
|
||||
when WS::Encoding::SoapRpcEncoding
|
||||
# We don't cast complex types for SOAP. SOAP clients should have used the WSDL to
|
||||
# send the correct types.
|
||||
assert_equal({'id' => '1', 'name' => 'test'}, @direct_controller.struct_pass_value)
|
||||
when WS::Encoding::XmlRpcEncoding
|
||||
assert_equal(person, @direct_controller.struct_pass_value)
|
||||
assert !person.equal?(@direct_controller.struct_pass_value)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def service_name(container)
|
||||
raise NotImplementedError
|
||||
|
@ -355,24 +388,20 @@ module DispatcherCommonTests
|
|||
end
|
||||
api = container.web_service_object(service_name.to_sym).class.web_service_api
|
||||
end
|
||||
info = api.api_methods[method_name] || {}
|
||||
params = params.dup
|
||||
((info[:expects] || []) + (info[:returns] || [])).each do |spec|
|
||||
@marshaler.register_type(spec)
|
||||
end
|
||||
expects = info[:expects]
|
||||
(0..(params.length-1)).each do |i|
|
||||
type_binding = @marshaler.register_type(expects ? expects[i] : params[i].class)
|
||||
info = WS::ParamInfo.create(expects ? expects[i] : params[i].class, type_binding, i)
|
||||
params[i] = @marshaler.marshal(WS::Param.new(params[i], info))
|
||||
end
|
||||
body = @encoder.encode_rpc_call(public_method_name, params)
|
||||
method = api.public_api_method_instance(public_method_name)
|
||||
method ||= api.dummy_public_api_method_instance(public_method_name)
|
||||
# we turn off strict so we can test our own handling of incorrectly typed parameters
|
||||
body = method.encode_rpc_call(@marshaler, @encoder, params.dup, :strict => false)
|
||||
# puts body
|
||||
ap_request = create_ap_request(container, body, public_method_name, *params)
|
||||
ap_response = ActionController::TestResponse.new
|
||||
container.process(ap_request, ap_response)
|
||||
# puts ap_response.body
|
||||
public_method_name, return_value = @encoder.decode_rpc_response(ap_response.body)
|
||||
if @encoder.is_a?(WS::Encoding::SoapRpcEncoding)
|
||||
# http://dev.rubyonrails.com/changeset/920
|
||||
assert_match(/Response$/, public_method_name) unless public_method_name == "fault"
|
||||
end
|
||||
@marshaler.unmarshal(return_value).value
|
||||
end
|
||||
end
|
||||
|
|
|
@ -35,13 +35,20 @@ class TC_API < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_signature_canonicalization
|
||||
assert_equal({:expects=>nil, :returns=>nil}, API.api_methods[:void])
|
||||
assert_equal({:expects=>[String], :returns=>[String]}, API.api_methods[:expects_and_returns])
|
||||
assert_equal({:expects=>[Integer, TrueClass], :returns=>nil}, API.api_methods[:expects])
|
||||
assert_equal({:expects=>nil, :returns=>[Integer, [String]]}, API.api_methods[:returns])
|
||||
assert_equal({:expects=>[{:appkey=>Integer}, {:publish=>TrueClass}], :returns=>nil}, API.api_methods[:named_signature])
|
||||
assert_equal({:expects=>[Integer, String, TrueClass], :returns=>nil}, API.api_methods[:string_types])
|
||||
assert_equal({:expects=>[TrueClass, Integer, String], :returns=>nil}, API.api_methods[:class_types])
|
||||
assert_equal(nil, API.api_methods[:void].expects)
|
||||
assert_equal(nil, API.api_methods[:void].returns)
|
||||
assert_equal([String], API.api_methods[:expects_and_returns].expects)
|
||||
assert_equal([String], API.api_methods[:expects_and_returns].returns)
|
||||
assert_equal([Integer, TrueClass], API.api_methods[:expects].expects)
|
||||
assert_equal(nil, API.api_methods[:expects].returns)
|
||||
assert_equal(nil, API.api_methods[:returns].expects)
|
||||
assert_equal([Integer, [String]], API.api_methods[:returns].returns)
|
||||
assert_equal([{:appkey=>Integer}, {:publish=>TrueClass}], API.api_methods[:named_signature].expects)
|
||||
assert_equal(nil, API.api_methods[:named_signature].returns)
|
||||
assert_equal([Integer, String, TrueClass], API.api_methods[:string_types].expects)
|
||||
assert_equal(nil, API.api_methods[:string_types].returns)
|
||||
assert_equal([TrueClass, Integer, String], API.api_methods[:class_types].expects)
|
||||
assert_equal(nil, API.api_methods[:class_types].returns)
|
||||
end
|
||||
|
||||
def test_not_instantiable
|
||||
|
|
|
@ -66,6 +66,11 @@ class TC_ClientSoap < Test::Unit::TestCase
|
|||
assert(@container.value_normal.nil?)
|
||||
assert_equal(5, @client.normal(5, 6))
|
||||
assert_equal([5, 6], @container.value_normal)
|
||||
assert_equal(5, @client.normal("7", "8"))
|
||||
assert_equal([7, 8], @container.value_normal)
|
||||
assert_raises(TypeError) do
|
||||
assert_equal(5, @client.normal(true, false))
|
||||
end
|
||||
end
|
||||
|
||||
def test_array_return
|
||||
|
|
|
@ -60,6 +60,11 @@ class TC_ClientXmlRpc < Test::Unit::TestCase
|
|||
assert(@container.value_normal.nil?)
|
||||
assert_equal(5, @client.normal(5, 6))
|
||||
assert_equal([5, 6], @container.value_normal)
|
||||
assert_equal(5, @client.normal("7", "8"))
|
||||
assert_equal([7, 8], @container.value_normal)
|
||||
assert_raises(TypeError) do
|
||||
assert_equal(5, @client.normal(true, false))
|
||||
end
|
||||
end
|
||||
|
||||
def test_array_return
|
||||
|
@ -86,7 +91,7 @@ class TC_ClientXmlRpc < Test::Unit::TestCase
|
|||
|
||||
def test_named_parameters
|
||||
assert(@container.value_named_parameters.nil?)
|
||||
assert_equal(true, @client.named_parameters("xxx", 7))
|
||||
assert_equal(nil, @client.named_parameters("xxx", 7))
|
||||
assert_equal(["xxx", 7], @container.value_named_parameters)
|
||||
end
|
||||
|
||||
|
@ -97,7 +102,7 @@ class TC_ClientXmlRpc < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_invalid_signature
|
||||
assert_raises(ActionWebService::Client::ClientError) do
|
||||
assert_raises(ArgumentError) do
|
||||
@client.normal
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env ruby
|
||||
require 'test/unit'
|
||||
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
||||
args = Dir[File.join(File.dirname(__FILE__), '*_test.rb')] + Dir[File.join(File.dirname(__FILE__), 'ws/*_test.rb')]
|
||||
(r = Test::Unit::AutoRunner.new(true)).process_args(args)
|
||||
exit r.run
|
||||
|
|
|
@ -30,6 +30,12 @@ class SoapMarshalingTest < Test::Unit::TestCase
|
|||
marshaler.unmarshal(nil)
|
||||
end
|
||||
assert_equal(nil, marshaler.register_type(nil))
|
||||
assert_raises(NotImplementedError) do
|
||||
marshaler.cast_inbound_recursive(nil, nil)
|
||||
end
|
||||
assert_raises(NotImplementedError) do
|
||||
marshaler.cast_outbound_recursive(nil, nil)
|
||||
end
|
||||
end
|
||||
|
||||
def test_marshaling
|
||||
|
|
Loading…
Reference in a new issue