aboutsummaryrefslogtreecommitdiffstats
path: root/actionwebservice/lib
diff options
context:
space:
mode:
authorLeon Breedt <bitserf@gmail.com>2005-03-28 03:20:13 +0000
committerLeon Breedt <bitserf@gmail.com>2005-03-28 03:20:13 +0000
commit594063f23cf8e7cecd24329e801992784f420b55 (patch)
treed52e9a6fc0521d51fcc3875162adf0411ee6caa0 /actionwebservice/lib
parent439a216dcb65ac83d86ca04bb898e1797a87ce70 (diff)
downloadrails-594063f23cf8e7cecd24329e801992784f420b55.tar.gz
rails-594063f23cf8e7cecd24329e801992784f420b55.tar.bz2
rails-594063f23cf8e7cecd24329e801992784f420b55.zip
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
Diffstat (limited to 'actionwebservice/lib')
-rw-r--r--actionwebservice/lib/action_web_service/api/base.rb164
-rw-r--r--actionwebservice/lib/action_web_service/client/soap_client.rb40
-rw-r--r--actionwebservice/lib/action_web_service/client/xmlrpc_client.rb40
-rw-r--r--actionwebservice/lib/action_web_service/dispatcher/abstract.rb80
-rw-r--r--actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb37
-rw-r--r--actionwebservice/lib/action_web_service/protocol/abstract.rb16
-rw-r--r--actionwebservice/lib/action_web_service/protocol/soap_protocol.rb18
-rw-r--r--actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb20
-rw-r--r--actionwebservice/lib/action_web_service/test_invoke.rb15
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/marshaling/abstract.rb16
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb22
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb113
12 files changed, 353 insertions, 228 deletions
diff --git a/actionwebservice/lib/action_web_service/api/base.rb b/actionwebservice/lib/action_web_service/api/base.rb
index c30c833f9d..e440a8b1bd 100644
--- a/actionwebservice/lib/action_web_service/api/base.rb
+++ b/actionwebservice/lib/action_web_service/api/base.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/client/soap_client.rb b/actionwebservice/lib/action_web_service/client/soap_client.rb
index b8a20bb40e..b9eb0d11ad 100644
--- a/actionwebservice/lib/action_web_service/client/soap_client.rb
+++ b/actionwebservice/lib/action_web_service/client/soap_client.rb
@@ -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)
diff --git a/actionwebservice/lib/action_web_service/client/xmlrpc_client.rb b/actionwebservice/lib/action_web_service/client/xmlrpc_client.rb
index 27fe537404..e0b7efc864 100644
--- a/actionwebservice/lib/action_web_service/client/xmlrpc_client.rb
+++ b/actionwebservice/lib/action_web_service/client/xmlrpc_client.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/dispatcher/abstract.rb b/actionwebservice/lib/action_web_service/dispatcher/abstract.rb
index b63fe65ce1..4c184fb140 100644
--- a/actionwebservice/lib/action_web_service/dispatcher/abstract.rb
+++ b/actionwebservice/lib/action_web_service/dispatcher/abstract.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb b/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb
index 7080f813d4..5289b0b84d 100644
--- a/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb
+++ b/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/protocol/abstract.rb b/actionwebservice/lib/action_web_service/protocol/abstract.rb
index 7526539d53..ed50a6ffde 100644
--- a/actionwebservice/lib/action_web_service/protocol/abstract.rb
+++ b/actionwebservice/lib/action_web_service/protocol/abstract.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb b/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb
index 2dab7954f9..253812b5e2 100644
--- a/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb
+++ b/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb
@@ -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)
diff --git a/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb b/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb
index 8d6af246ec..f3e4a23b4b 100644
--- a/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb
+++ b/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb
@@ -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)
diff --git a/actionwebservice/lib/action_web_service/test_invoke.rb b/actionwebservice/lib/action_web_service/test_invoke.rb
index aaa03cdb30..5d364e4225 100644
--- a/actionwebservice/lib/action_web_service/test_invoke.rb
+++ b/actionwebservice/lib/action_web_service/test_invoke.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/vendor/ws/marshaling/abstract.rb b/actionwebservice/lib/action_web_service/vendor/ws/marshaling/abstract.rb
index 53120e1447..e897f62297 100644
--- a/actionwebservice/lib/action_web_service/vendor/ws/marshaling/abstract.rb
+++ b/actionwebservice/lib/action_web_service/vendor/ws/marshaling/abstract.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb b/actionwebservice/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb
index 4d29c78f2c..287a64291b 100644
--- a/actionwebservice/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb
+++ b/actionwebservice/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb b/actionwebservice/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb
index 87154f87e1..56cc7597fb 100644
--- a/actionwebservice/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb
+++ b/actionwebservice/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb
@@ -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)
+ private
+ def spec_for(param)
+ binding = param.info.data
+ binding.is_a?(XmlRpcArrayBinding) ? [binding.element_klass] : binding.klass
+ end
+
+ 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
- struct
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_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