diff options
Diffstat (limited to 'actionwebservice/lib/action_web_service/api/base.rb')
-rw-r--r-- | actionwebservice/lib/action_web_service/api/base.rb | 164 |
1 files changed, 161 insertions, 3 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 |