mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
merged the changes for the upcoming 0.6.0:
seperate out protocol marshaling into a small 'ws' library in vendor, so that AWS itself only does integration with ActionPack, and so we can keep protocol specific code in AWS proper to a minimum. refactor unit tests to get 95% code coverage (for a baseline). be far more relaxed about the types given to us by the remote side, don't do any poor man's type checking, just try to cast and marshal to the correct types if possible, and if not, return what they gave us anyway. this should make interoperating with fuzzy XML-RPC clients easier. if exception reporting is turned on, do best-effort error responses, so that we can avoid "Internal protocol error" with no details if there is a bug in AWS itself. also perform extensive cleanups on AWS proper. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@800 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
10faf204b7
commit
6f5a7b2004
62 changed files with 2187 additions and 2086 deletions
|
@ -1,3 +1,12 @@
|
|||
*0.6.0* (Unreleased)
|
||||
|
||||
* lib/*, test/*: refactored SOAP and XML-RPC protocol specifics into
|
||||
a small seperate library named 'ws', and drop it in vendor. be
|
||||
more relaxed about the type of received parameters, perform casting
|
||||
for XML-RPC if possible, but fallback to the received parameters.
|
||||
performed extensive cleanup of the way we use SOAP, so that marshaling
|
||||
of custom and array types should somewhat faster.
|
||||
|
||||
*0.5.0* (24th February, 2005)
|
||||
|
||||
* lib/action_service/dispatcher*: replace "router" fragments with
|
||||
|
|
|
@ -9,7 +9,7 @@ require 'fileutils'
|
|||
|
||||
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
||||
PKG_NAME = 'actionwebservice'
|
||||
PKG_VERSION = '0.5.0' + PKG_BUILD
|
||||
PKG_VERSION = '0.6.0' + PKG_BUILD
|
||||
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
||||
PKG_DESTINATION = ENV["RAILS_PKG_DESTINATION"] || "../#{PKG_NAME}"
|
||||
|
||||
|
@ -20,7 +20,7 @@ task :default => [ :test ]
|
|||
# Run the unit tests
|
||||
Rake::TestTask.new { |t|
|
||||
t.libs << "test"
|
||||
t.pattern = 'test/*_test.rb'
|
||||
t.test_files = Dir['test/*_test.rb'] + Dir['test/ws/*_test.rb']
|
||||
t.verbose = true
|
||||
}
|
||||
|
||||
|
@ -54,9 +54,9 @@ spec = Gem::Specification.new do |s|
|
|||
s.rubyforge_project = "aws"
|
||||
s.homepage = "http://www.rubyonrails.org"
|
||||
|
||||
s.add_dependency('actionpack', '= 1.5.0' + PKG_BUILD)
|
||||
s.add_dependency('activerecord', '= 1.7.0' + PKG_BUILD)
|
||||
s.add_dependency('activesupport', '= 1.0.0' + PKG_BUILD)
|
||||
s.add_dependency('actionpack', '>= 1.5.0' + PKG_BUILD)
|
||||
s.add_dependency('activerecord', '>= 1.7.0' + PKG_BUILD)
|
||||
s.add_dependency('activesupport', '>= 1.0.0' + PKG_BUILD)
|
||||
|
||||
s.has_rdoc = true
|
||||
s.requirements << 'none'
|
||||
|
@ -94,7 +94,7 @@ def each_source_file(*args)
|
|||
prefix ||= File.dirname(__FILE__)
|
||||
open_file = true if open_file.nil?
|
||||
includes ||= %w[lib\/action_web_service\.rb$ lib\/action_web_service\/.*\.rb$]
|
||||
excludes ||= %w[]
|
||||
excludes ||= %w[lib\/action_web_service\/vendor]
|
||||
Find.find(prefix) do |file_name|
|
||||
next if file_name =~ /\.svn/
|
||||
file_name.gsub!(/^\.\//, '')
|
||||
|
@ -123,7 +123,7 @@ def each_source_file(*args)
|
|||
end
|
||||
end
|
||||
|
||||
desc "Count lines of the source code"
|
||||
desc "Count lines of the AWS source code"
|
||||
task :lines do
|
||||
total_lines = total_loc = 0
|
||||
puts "Per File:"
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
= Low priority tasks
|
||||
- add better type mapping tests for XML-RPC
|
||||
- add tests for ActiveRecord support (with mock objects?)
|
||||
= 0.6.0 Tasks
|
||||
- finish off tickets #676, #677, #678
|
||||
|
||||
= Refactoring
|
||||
- Find an alternative way to map interesting types for SOAP (like ActiveRecord
|
||||
model classes) that doesn't require creation of a sanitized copy object with data
|
||||
copied from the real one. Ideally this would let us get rid of
|
||||
ActionWebService::Struct altogether and provide a block that would yield the
|
||||
attributes and values. "Filters" ? Not sure how to integrate with SOAP though.
|
||||
|
||||
- Don't have clean way to go from SOAP Class object to the xsd:NAME type
|
||||
string -- NaHi possibly looking at remedying this situation
|
||||
|
|
|
@ -32,7 +32,10 @@ rescue LoadError
|
|||
require_gem 'activerecord', '>= 1.6.0'
|
||||
end
|
||||
|
||||
$:.unshift(File.dirname(__FILE__))
|
||||
$:.unshift(File.dirname(__FILE__) + "/action_web_service/vendor/")
|
||||
|
||||
require 'action_web_service/support/class_inheritable_options'
|
||||
require 'action_web_service/vendor/ws'
|
||||
|
||||
require 'action_web_service/base'
|
||||
require 'action_web_service/client'
|
||||
|
@ -41,20 +44,21 @@ require 'action_web_service/api'
|
|||
require 'action_web_service/struct'
|
||||
require 'action_web_service/container'
|
||||
require 'action_web_service/protocol'
|
||||
require 'action_web_service/struct'
|
||||
require 'action_web_service/dispatcher'
|
||||
|
||||
ActionWebService::Base.class_eval do
|
||||
include ActionWebService::API
|
||||
include ActionWebService::Container::Direct
|
||||
include ActionWebService::Invocation
|
||||
end
|
||||
|
||||
ActionController::Base.class_eval do
|
||||
include ActionWebService::Container
|
||||
include ActionWebService::Protocol::Registry
|
||||
include ActionWebService::Protocol::Discovery
|
||||
include ActionWebService::Protocol::Soap
|
||||
include ActionWebService::Protocol::XmlRpc
|
||||
include ActionWebService::API
|
||||
include ActionWebService::API::ActionController
|
||||
include ActionWebService::Container::Direct
|
||||
include ActionWebService::Container::Delegated
|
||||
include ActionWebService::Container::ActionController
|
||||
include ActionWebService::Dispatcher
|
||||
include ActionWebService::Dispatcher::ActionController
|
||||
end
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
require 'action_web_service/api/abstract'
|
||||
require 'action_web_service/api/action_controller'
|
||||
require 'action_web_service/api/base'
|
||||
|
|
|
@ -1,70 +1,5 @@
|
|||
module ActionWebService # :nodoc:
|
||||
module API # :nodoc:
|
||||
class APIError < ActionWebService::ActionWebServiceError # :nodoc:
|
||||
end
|
||||
|
||||
def self.append_features(base) # :nodoc:
|
||||
super
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Attaches ActionWebService API +definition+ to the calling class.
|
||||
#
|
||||
# Action Controllers can have a default associated API, removing the need
|
||||
# to call this method if you follow the Action Web Service naming conventions.
|
||||
#
|
||||
# A controller with a class name of GoogleSearchController will
|
||||
# implicitly load <tt>app/apis/google_search_api.rb</tt>, and expect the
|
||||
# API definition class to be named <tt>GoogleSearchAPI</tt> or
|
||||
# <tt>GoogleSearchApi</tt>.
|
||||
#
|
||||
# ==== Service class example
|
||||
#
|
||||
# class MyService < ActionWebService::Base
|
||||
# web_service_api MyAPI
|
||||
# end
|
||||
#
|
||||
# class MyAPI < ActionWebService::API::Base
|
||||
# ...
|
||||
# end
|
||||
#
|
||||
# ==== Controller class example
|
||||
#
|
||||
# class MyController < ActionController::Base
|
||||
# web_service_api MyAPI
|
||||
# end
|
||||
#
|
||||
# class MyAPI < ActionWebService::API::Base
|
||||
# ...
|
||||
# end
|
||||
def web_service_api(definition=nil)
|
||||
if definition.nil?
|
||||
read_inheritable_attribute("web_service_api")
|
||||
else
|
||||
if definition.is_a?(Symbol)
|
||||
raise(APIError, "symbols can only be used for #web_service_api inside of a controller")
|
||||
end
|
||||
unless definition.respond_to?(:ancestors) && definition.ancestors.include?(Base)
|
||||
raise(APIError, "#{definition.to_s} is not a valid API definition")
|
||||
end
|
||||
write_inheritable_attribute("web_service_api", definition)
|
||||
call_web_service_api_callbacks(self, definition)
|
||||
end
|
||||
end
|
||||
|
||||
def add_web_service_api_callback(&block) # :nodoc:
|
||||
write_inheritable_array("web_service_api_callbacks", [block])
|
||||
end
|
||||
|
||||
private
|
||||
def call_web_service_api_callbacks(container_class, definition)
|
||||
(read_inheritable_attribute("web_service_api_callbacks") || []).each do |block|
|
||||
block.call(container_class, definition)
|
||||
end
|
||||
end
|
||||
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.
|
||||
|
@ -87,8 +22,6 @@ module ActionWebService # :nodoc:
|
|||
private_class_method :new, :allocate
|
||||
|
||||
class << self
|
||||
include ActionWebService::Signature
|
||||
|
||||
# API methods have a +name+, which must be the Ruby method name to use when
|
||||
# performing the invocation on the web service object.
|
||||
#
|
||||
|
@ -125,11 +58,11 @@ module ActionWebService # :nodoc:
|
|||
expects = options[:expects]
|
||||
returns = options[:returns]
|
||||
end
|
||||
expects = canonical_signature(expects) if expects
|
||||
returns = canonical_signature(returns) if returns
|
||||
expects = canonical_signature(expects)
|
||||
returns = canonical_signature(returns)
|
||||
if expects
|
||||
expects.each do |param|
|
||||
klass = signature_parameter_class(param)
|
||||
klass = WS::BaseTypes.canonical_param_type_class(param)
|
||||
klass = klass[0] if klass.is_a?(Array)
|
||||
if klass.ancestors.include?(ActiveRecord::Base)
|
||||
raise(ActionWebServiceError, "ActiveRecord model classes not allowed in :expects")
|
||||
|
@ -186,6 +119,10 @@ module ActionWebService # :nodoc:
|
|||
end
|
||||
end
|
||||
|
||||
def canonical_signature(signature)
|
||||
return nil if signature.nil?
|
||||
signature.map{|spec| WS::BaseTypes.canonical_param_type_spec(spec)}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,6 +1,3 @@
|
|||
require 'action_web_service/support/class_inheritable_options'
|
||||
require 'action_web_service/support/signature'
|
||||
|
||||
module ActionWebService # :nodoc:
|
||||
class ActionWebServiceError < StandardError # :nodoc:
|
||||
end
|
||||
|
|
|
@ -12,28 +12,17 @@ module ActionWebService # :nodoc:
|
|||
def method_missing(name, *args) # :nodoc:
|
||||
call_name = method_name(name)
|
||||
return super(name, *args) if call_name.nil?
|
||||
perform_invocation(call_name, args)
|
||||
self.perform_invocation(call_name, args)
|
||||
end
|
||||
|
||||
protected
|
||||
def perform_invocation(method_name, args) # :nodoc:
|
||||
raise NotImplementedError, "use a protocol-specific client"
|
||||
end
|
||||
|
||||
private
|
||||
def method_name(name)
|
||||
if @api.has_api_method?(name.to_sym)
|
||||
name.to_s
|
||||
elsif @api.has_public_api_method?(name.to_s)
|
||||
@api.api_method_name(name.to_s).to_s
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def lookup_class(klass)
|
||||
klass.is_a?(Hash) ? klass.values[0] : klass
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,10 +28,10 @@ module ActionWebService # :nodoc:
|
|||
# option, you must specify it here
|
||||
def initialize(api, endpoint_uri, options={})
|
||||
super(api, endpoint_uri)
|
||||
@service_name = options[:service_name] || 'ActionWebService'
|
||||
@namespace = "urn:#{@service_name}"
|
||||
@mapper = ActionWebService::Protocol::Soap::SoapMapper.new(@namespace)
|
||||
@protocol = ActionWebService::Protocol::Soap::SoapProtocol.new(@mapper)
|
||||
@service_name = options[:service_name]
|
||||
@namespace = @service_name ? '' : "urn:#{@service_name}"
|
||||
@marshaler = WS::Marshaling::SoapMarshaler.new
|
||||
@encoder = WS::Encoding::SoapRpcEncoding.new
|
||||
@soap_action_base = options[:soap_action_base]
|
||||
@soap_action_base ||= URI.parse(endpoint_uri).path
|
||||
@driver = create_soap_rpc_driver(api, endpoint_uri)
|
||||
|
@ -48,9 +48,9 @@ module ActionWebService # :nodoc:
|
|||
|
||||
private
|
||||
def create_soap_rpc_driver(api, endpoint_uri)
|
||||
@mapper.map_api(api)
|
||||
register_api(@marshaler, api)
|
||||
driver = SoapDriver.new(endpoint_uri, nil)
|
||||
driver.mapping_registry = @mapper.registry
|
||||
driver.mapping_registry = @marshaler.registry
|
||||
api.api_methods.each do |name, info|
|
||||
public_name = api.public_api_method_name(name)
|
||||
qname = XSD::QName.new(@namespace, public_name)
|
||||
|
@ -58,25 +58,38 @@ module ActionWebService # :nodoc:
|
|||
expects = info[:expects]
|
||||
returns = info[:returns]
|
||||
param_def = []
|
||||
i = 1
|
||||
i = 0
|
||||
if expects
|
||||
expects.each do |klass|
|
||||
param_name = klass.is_a?(Hash) ? klass.keys[0] : "param#{i}"
|
||||
param_klass = lookup_class(klass)
|
||||
mapping = @mapper.lookup(param_klass)
|
||||
param_def << ['in', param_name, mapping.registry_mapping]
|
||||
expects.each do |spec|
|
||||
param_name = spec.is_a?(Hash) ? spec.keys[0].to_s : "param#{i}"
|
||||
type_binding = @marshaler.register_type(spec)
|
||||
param_def << ['in', param_name, type_binding.mapping]
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
if returns
|
||||
mapping = @mapper.lookup(lookup_class(returns[0]))
|
||||
param_def << ['retval', 'return', mapping.registry_mapping]
|
||||
type_binding = @marshaler.register_type(returns[0])
|
||||
param_def << ['retval', 'return', type_binding.mapping]
|
||||
end
|
||||
driver.add_method(qname, action, 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)
|
||||
|
|
|
@ -31,6 +31,7 @@ module ActionWebService # :nodoc:
|
|||
@api = api
|
||||
@handler_name = options[:handler_name]
|
||||
@client = XMLRPC::Client.new2(endpoint_uri, options[:proxy], options[:timeout])
|
||||
@marshaler = WS::Marshaling::XmlRpcMarshaler.new
|
||||
end
|
||||
|
||||
protected
|
||||
|
@ -43,18 +44,21 @@ module ActionWebService # :nodoc:
|
|||
|
||||
def transform_outgoing_method_params(method_name, params)
|
||||
info = @api.api_methods[method_name.to_sym]
|
||||
signature = info[:expects]
|
||||
signature_length = signature.nil?? 0 : signature.length
|
||||
if signature_length != params.length
|
||||
raise(ProtocolError, "API declares #{public_name(method_name)} to accept " +
|
||||
"#{signature_length} parameters, but #{params.length} parameters " +
|
||||
"were supplied")
|
||||
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
|
||||
if signature_length > 0
|
||||
signature = Protocol::XmlRpc::XmlRpcProtocol.transform_array_types(signature)
|
||||
(1..signature.size).each do |i|
|
||||
i -= 1
|
||||
params[i] = Protocol::XmlRpc::XmlRpcProtocol.ruby_to_xmlrpc(params[i], lookup_class(signature[i]))
|
||||
params = params.dup
|
||||
if expects_length > 0
|
||||
i = 0
|
||||
expects.each do |spec|
|
||||
type_binding = @marshaler.register_type(spec)
|
||||
info = WS::ParamInfo.create(spec, i, type_binding)
|
||||
params[i] = @marshaler.marshal(WS::Param.new(params[i], info))
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
params
|
||||
|
@ -62,10 +66,11 @@ module ActionWebService # :nodoc:
|
|||
|
||||
def transform_return_value(method_name, return_value)
|
||||
info = @api.api_methods[method_name.to_sym]
|
||||
return true unless signature = info[:returns]
|
||||
param_klass = lookup_class(signature[0])
|
||||
signature = Protocol::XmlRpc::XmlRpcProtocol.transform_array_types([param_klass])
|
||||
Protocol::XmlRpc::XmlRpcProtocol.xmlrpc_to_ruby(return_value, signature[0])
|
||||
return true unless returns = info[:returns]
|
||||
type_binding = @marshaler.register_type(returns[0])
|
||||
info = WS::ParamInfo.create(returns[0], 0, type_binding)
|
||||
info.name = 'return'
|
||||
@marshaler.transform_inbound(WS::Param.new(return_value, info))
|
||||
end
|
||||
|
||||
def public_name(method_name)
|
||||
|
|
|
@ -1,85 +1,3 @@
|
|||
module ActionWebService # :nodoc:
|
||||
module Container # :nodoc:
|
||||
class ContainerError < ActionWebService::ActionWebServiceError # :nodoc:
|
||||
end
|
||||
|
||||
def self.append_features(base) # :nodoc:
|
||||
super
|
||||
base.extend(ClassMethods)
|
||||
base.send(:include, ActionWebService::Container::InstanceMethods)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Declares a web service that will provides access to the API of the given
|
||||
# +object+. +object+ must be an ActionWebService::Base derivative.
|
||||
#
|
||||
# Web service object creation can either be _immediate_, where the object
|
||||
# instance is given at class definition time, or _deferred_, where
|
||||
# object instantiation is delayed until request time.
|
||||
#
|
||||
# ==== Immediate web service object example
|
||||
#
|
||||
# class ApiController < ApplicationController
|
||||
# web_service_dispatching_mode :delegated
|
||||
#
|
||||
# web_service :person, PersonService.new
|
||||
# end
|
||||
#
|
||||
# For deferred instantiation, a block should be given instead of an
|
||||
# object instance. This block will be executed in controller instance
|
||||
# context, so it can rely on controller instance variables being present.
|
||||
#
|
||||
# ==== Deferred web service object example
|
||||
#
|
||||
# class ApiController < ApplicationController
|
||||
# web_service_dispatching_mode :delegated
|
||||
#
|
||||
# web_service(:person) { PersonService.new(@request.env) }
|
||||
# end
|
||||
def web_service(name, object=nil, &block)
|
||||
if (object && block_given?) || (object.nil? && block.nil?)
|
||||
raise(ContainerError, "either service, or a block must be given")
|
||||
end
|
||||
name = name.to_sym
|
||||
if block_given?
|
||||
info = { name => { :block => block } }
|
||||
else
|
||||
info = { name => { :object => object } }
|
||||
end
|
||||
write_inheritable_hash("web_services", info)
|
||||
call_web_service_definition_callbacks(self, name, info)
|
||||
end
|
||||
|
||||
# Whether this service contains a service with the given +name+
|
||||
def has_web_service?(name)
|
||||
web_services.has_key?(name.to_sym)
|
||||
end
|
||||
|
||||
def web_services # :nodoc:
|
||||
read_inheritable_attribute("web_services") || {}
|
||||
end
|
||||
|
||||
def add_web_service_definition_callback(&block) # :nodoc:
|
||||
write_inheritable_array("web_service_definition_callbacks", [block])
|
||||
end
|
||||
|
||||
private
|
||||
def call_web_service_definition_callbacks(container_class, web_service_name, service_info)
|
||||
(read_inheritable_attribute("web_service_definition_callbacks") || []).each do |block|
|
||||
block.call(container_class, web_service_name, service_info)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods # :nodoc:
|
||||
def web_service_object(web_service_name)
|
||||
info = self.class.web_services[web_service_name.to_sym]
|
||||
unless info
|
||||
raise(ContainerError, "no such web service '#{web_service_name}'")
|
||||
end
|
||||
service = info[:block]
|
||||
service ? instance_eval(&service) : info[:object]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
require 'action_web_service/container/direct_container'
|
||||
require 'action_web_service/container/delegated_container'
|
||||
require 'action_web_service/container/action_controller_container'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
module ActionWebService # :nodoc:
|
||||
module API # :nodoc:
|
||||
module Container # :nodoc:
|
||||
module ActionController # :nodoc:
|
||||
def self.append_features(base) # :nodoc:
|
||||
base.class_eval do
|
||||
|
@ -36,7 +36,7 @@ module ActionWebService # :nodoc:
|
|||
api_klass = options.delete(:api) || require_web_service_api(name)
|
||||
class_eval do
|
||||
define_method(name) do
|
||||
probe_protocol_client(api_klass, protocol, endpoint_uri, options)
|
||||
create_web_service_client(api_klass, protocol, endpoint_uri, options)
|
||||
end
|
||||
protected name
|
||||
end
|
|
@ -0,0 +1,87 @@
|
|||
module ActionWebService # :nodoc:
|
||||
module Container # :nodoc:
|
||||
module Delegated # :nodoc:
|
||||
class ContainerError < ActionWebServiceError # :nodoc:
|
||||
end
|
||||
|
||||
def self.append_features(base) # :nodoc:
|
||||
super
|
||||
base.extend(ClassMethods)
|
||||
base.send(:include, ActionWebService::Container::Delegated::InstanceMethods)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Declares a web service that will provides access to the API of the given
|
||||
# +object+. +object+ must be an ActionWebService::Base derivative.
|
||||
#
|
||||
# Web service object creation can either be _immediate_, where the object
|
||||
# instance is given at class definition time, or _deferred_, where
|
||||
# object instantiation is delayed until request time.
|
||||
#
|
||||
# ==== Immediate web service object example
|
||||
#
|
||||
# class ApiController < ApplicationController
|
||||
# web_service_dispatching_mode :delegated
|
||||
#
|
||||
# web_service :person, PersonService.new
|
||||
# end
|
||||
#
|
||||
# For deferred instantiation, a block should be given instead of an
|
||||
# object instance. This block will be executed in controller instance
|
||||
# context, so it can rely on controller instance variables being present.
|
||||
#
|
||||
# ==== Deferred web service object example
|
||||
#
|
||||
# class ApiController < ApplicationController
|
||||
# web_service_dispatching_mode :delegated
|
||||
#
|
||||
# web_service(:person) { PersonService.new(@request.env) }
|
||||
# end
|
||||
def web_service(name, object=nil, &block)
|
||||
if (object && block_given?) || (object.nil? && block.nil?)
|
||||
raise(ContainerError, "either service, or a block must be given")
|
||||
end
|
||||
name = name.to_sym
|
||||
if block_given?
|
||||
info = { name => { :block => block } }
|
||||
else
|
||||
info = { name => { :object => object } }
|
||||
end
|
||||
write_inheritable_hash("web_services", info)
|
||||
call_web_service_definition_callbacks(self, name, info)
|
||||
end
|
||||
|
||||
# Whether this service contains a service with the given +name+
|
||||
def has_web_service?(name)
|
||||
web_services.has_key?(name.to_sym)
|
||||
end
|
||||
|
||||
def web_services # :nodoc:
|
||||
read_inheritable_attribute("web_services") || {}
|
||||
end
|
||||
|
||||
def add_web_service_definition_callback(&block) # :nodoc:
|
||||
write_inheritable_array("web_service_definition_callbacks", [block])
|
||||
end
|
||||
|
||||
private
|
||||
def call_web_service_definition_callbacks(container_class, web_service_name, service_info)
|
||||
(read_inheritable_attribute("web_service_definition_callbacks") || []).each do |block|
|
||||
block.call(container_class, web_service_name, service_info)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods # :nodoc:
|
||||
def web_service_object(web_service_name)
|
||||
info = self.class.web_services[web_service_name.to_sym]
|
||||
unless info
|
||||
raise(ContainerError, "no such web service '#{web_service_name}'")
|
||||
end
|
||||
service = info[:block]
|
||||
service ? instance_eval(&service) : info[:object]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,70 @@
|
|||
module ActionWebService # :nodoc:
|
||||
module Container # :nodoc:
|
||||
module Direct # :nodoc:
|
||||
class ContainerError < ActionWebServiceError # :nodoc:
|
||||
end
|
||||
|
||||
def self.append_features(base) # :nodoc:
|
||||
super
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Attaches ActionWebService API +definition+ to the calling class.
|
||||
#
|
||||
# Action Controllers can have a default associated API, removing the need
|
||||
# to call this method if you follow the Action Web Service naming conventions.
|
||||
#
|
||||
# A controller with a class name of GoogleSearchController will
|
||||
# implicitly load <tt>app/apis/google_search_api.rb</tt>, and expect the
|
||||
# API definition class to be named <tt>GoogleSearchAPI</tt> or
|
||||
# <tt>GoogleSearchApi</tt>.
|
||||
#
|
||||
# ==== Service class example
|
||||
#
|
||||
# class MyService < ActionWebService::Base
|
||||
# web_service_api MyAPI
|
||||
# end
|
||||
#
|
||||
# class MyAPI < ActionWebService::API::Base
|
||||
# ...
|
||||
# end
|
||||
#
|
||||
# ==== Controller class example
|
||||
#
|
||||
# class MyController < ActionController::Base
|
||||
# web_service_api MyAPI
|
||||
# end
|
||||
#
|
||||
# class MyAPI < ActionWebService::API::Base
|
||||
# ...
|
||||
# end
|
||||
def web_service_api(definition=nil)
|
||||
if definition.nil?
|
||||
read_inheritable_attribute("web_service_api")
|
||||
else
|
||||
if definition.is_a?(Symbol)
|
||||
raise(ContainerError, "symbols can only be used for #web_service_api inside of a controller")
|
||||
end
|
||||
unless definition.respond_to?(:ancestors) && definition.ancestors.include?(ActionWebService::API::Base)
|
||||
raise(ContainerError, "#{definition.to_s} is not a valid API definition")
|
||||
end
|
||||
write_inheritable_attribute("web_service_api", definition)
|
||||
call_web_service_api_callbacks(self, definition)
|
||||
end
|
||||
end
|
||||
|
||||
def add_web_service_api_callback(&block) # :nodoc:
|
||||
write_inheritable_array("web_service_api_callbacks", [block])
|
||||
end
|
||||
|
||||
private
|
||||
def call_web_service_api_callbacks(container_class, definition)
|
||||
(read_inheritable_attribute("web_service_api_callbacks") || []).each do |block|
|
||||
block.call(container_class, definition)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -14,136 +14,103 @@ module ActionWebService # :nodoc:
|
|||
|
||||
module InstanceMethods # :nodoc:
|
||||
private
|
||||
def dispatch_web_service_request(action_pack_request)
|
||||
protocol_request = protocol_response = nil
|
||||
bm = Benchmark.measure do
|
||||
protocol_request = probe_request_protocol(action_pack_request)
|
||||
protocol_response = dispatch_protocol_request(protocol_request)
|
||||
end
|
||||
[protocol_request, protocol_response, bm.real, nil]
|
||||
rescue Exception => e
|
||||
protocol_response = prepare_exception_response(protocol_request, e)
|
||||
[protocol_request, prepare_exception_response(protocol_request, e), nil, e]
|
||||
end
|
||||
|
||||
def dispatch_protocol_request(protocol_request)
|
||||
def invoke_web_service_request(protocol_request)
|
||||
invocation = web_service_invocation(protocol_request)
|
||||
case web_service_dispatching_mode
|
||||
when :direct
|
||||
dispatch_direct_request(protocol_request)
|
||||
web_service_direct_invoke(invocation)
|
||||
when :delegated
|
||||
dispatch_delegated_request(protocol_request)
|
||||
else
|
||||
raise(ContainerError, "unsupported dispatching mode :#{web_service_dispatching_mode}")
|
||||
web_service_delegated_invoke(invocation)
|
||||
end
|
||||
end
|
||||
|
||||
def dispatch_direct_request(protocol_request)
|
||||
request = prepare_dispatch_request(protocol_request)
|
||||
return_value = direct_invoke(request)
|
||||
protocol_request.marshal(return_value)
|
||||
|
||||
def web_service_direct_invoke(invocation)
|
||||
@method_params = invocation.method_ordered_params
|
||||
return_value = self.__send__(invocation.api_method_name)
|
||||
returns = invocation.returns ? invocation.returns[0] : nil
|
||||
invocation.protocol.marshal_response(invocation.public_method_name, return_value, returns)
|
||||
end
|
||||
|
||||
def dispatch_delegated_request(protocol_request)
|
||||
request = prepare_dispatch_request(protocol_request)
|
||||
return_value = delegated_invoke(request)
|
||||
protocol_request.marshal(return_value)
|
||||
end
|
||||
|
||||
def direct_invoke(request)
|
||||
return nil unless before_direct_invoke(request)
|
||||
return_value = send(request.method_name)
|
||||
after_direct_invoke(request)
|
||||
return_value
|
||||
end
|
||||
|
||||
def before_direct_invoke(request)
|
||||
@method_params = request.params
|
||||
end
|
||||
|
||||
def after_direct_invoke(request)
|
||||
end
|
||||
|
||||
def delegated_invoke(request)
|
||||
def web_service_delegated_invoke(invocation)
|
||||
cancellation_reason = nil
|
||||
web_service = request.web_service
|
||||
return_value = web_service.perform_invocation(request.method_name, request.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
|
||||
return_value
|
||||
returns = invocation.returns ? invocation.returns[0] : nil
|
||||
invocation.protocol.marshal_response(invocation.public_method_name, return_value, returns)
|
||||
end
|
||||
|
||||
def prepare_dispatch_request(protocol_request)
|
||||
api = method_name = web_service_name = web_service = params = nil
|
||||
public_method_name = protocol_request.public_method_name
|
||||
def web_service_invocation(request)
|
||||
invocation = Invocation.new
|
||||
invocation.protocol = request.protocol
|
||||
invocation.service_name = request.service_name
|
||||
case web_service_dispatching_mode
|
||||
when :direct
|
||||
api = self.class.web_service_api
|
||||
invocation.api = self.class.web_service_api
|
||||
invocation.service = self
|
||||
when :delegated
|
||||
web_service_name = protocol_request.web_service_name
|
||||
web_service = web_service_object(web_service_name)
|
||||
api = web_service.class.web_service_api
|
||||
end
|
||||
method_name = api.api_method_name(public_method_name)
|
||||
signature = nil
|
||||
if method_name
|
||||
signature = api.api_methods[method_name]
|
||||
protocol_request.type = Protocol::CheckedMessage
|
||||
protocol_request.signature = signature[:expects]
|
||||
protocol_request.return_signature = signature[:returns]
|
||||
else
|
||||
method_name = api.default_api_method
|
||||
if method_name
|
||||
protocol_request.type = Protocol::UncheckedMessage
|
||||
else
|
||||
raise(DispatcherError, "no such method #{web_service_name}##{public_method_name}")
|
||||
invocation.service = web_service_object(request.service_name) rescue nil
|
||||
unless invocation.service
|
||||
raise(DispatcherError, "failed to instantiate service #{invocation.service_name}")
|
||||
end
|
||||
invocation.api = invocation.service.class.web_service_api
|
||||
end
|
||||
params = protocol_request.unmarshal
|
||||
DispatchRequest.new(
|
||||
:api => api,
|
||||
:public_method_name => public_method_name,
|
||||
:method_name => method_name,
|
||||
:signature => signature,
|
||||
:web_service_name => web_service_name,
|
||||
:web_service => web_service,
|
||||
:params => params)
|
||||
end
|
||||
|
||||
def prepare_exception_response(protocol_request, exception)
|
||||
if protocol_request && exception
|
||||
case web_service_dispatching_mode
|
||||
when :direct
|
||||
if web_service_exception_reporting
|
||||
return protocol_request.protocol.marshal_exception(exception)
|
||||
end
|
||||
when :delegated
|
||||
web_service = web_service_object(protocol_request.web_service_name)
|
||||
if web_service && web_service.class.web_service_exception_reporting
|
||||
return protocol_request.protocol.marshal_exception(exception)
|
||||
public_method_name = request.method_name
|
||||
unless invocation.api.has_public_api_method?(public_method_name)
|
||||
raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api}")
|
||||
end
|
||||
invocation.public_method_name = public_method_name
|
||||
invocation.api_method_name = invocation.api.api_method_name(public_method_name)
|
||||
info = invocation.api.api_methods[invocation.api_method_name]
|
||||
invocation.expects = info[:expects]
|
||||
invocation.returns = info[:returns]
|
||||
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, i, type_binding)
|
||||
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
|
||||
protocol_request.protocol.marshal_exception(RuntimeError.new("missing protocol request or exception"))
|
||||
invocation.method_ordered_params = []
|
||||
invocation.method_named_params = {}
|
||||
end
|
||||
rescue Exception
|
||||
nil
|
||||
invocation
|
||||
end
|
||||
|
||||
class DispatchRequest
|
||||
attr :api
|
||||
attr :public_method_name
|
||||
attr :method_name
|
||||
attr :signature
|
||||
attr :web_service_name
|
||||
attr :web_service
|
||||
attr :params
|
||||
|
||||
def initialize(values={})
|
||||
values.each{|k,v| instance_variable_set("@#{k.to_s}", v)}
|
||||
end
|
||||
class Invocation
|
||||
attr_accessor :protocol
|
||||
attr_accessor :service_name
|
||||
attr_accessor :api
|
||||
attr_accessor :public_method_name
|
||||
attr_accessor :api_method_name
|
||||
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
|
||||
end
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
require 'benchmark'
|
||||
require 'builder/xmlmarkup'
|
||||
|
||||
module ActionWebService # :nodoc:
|
||||
module Dispatcher # :nodoc:
|
||||
module ActionController # :nodoc:
|
||||
|
@ -7,106 +10,121 @@ module ActionWebService # :nodoc:
|
|||
class << self
|
||||
alias_method :inherited_without_action_controller, :inherited
|
||||
end
|
||||
alias_method :before_direct_invoke_without_action_controller, :before_direct_invoke
|
||||
alias_method :after_direct_invoke_without_action_controller, :after_direct_invoke
|
||||
alias_method :web_service_direct_invoke_without_controller, :web_service_direct_invoke
|
||||
end
|
||||
base.add_web_service_api_callback do |klass, api|
|
||||
if klass.web_service_dispatching_mode == :direct
|
||||
klass.class_eval <<-EOS
|
||||
def api
|
||||
controller_dispatch_web_service_request
|
||||
end
|
||||
EOS
|
||||
klass.class_eval 'def api; dispatch_web_service_request; end'
|
||||
end
|
||||
end
|
||||
base.add_web_service_definition_callback do |klass, name, info|
|
||||
if klass.web_service_dispatching_mode == :delegated
|
||||
klass.class_eval <<-EOS
|
||||
def #{name}
|
||||
controller_dispatch_web_service_request
|
||||
end
|
||||
EOS
|
||||
klass.class_eval "def #{name}; dispatch_web_service_request; end"
|
||||
end
|
||||
end
|
||||
base.extend(ClassMethods)
|
||||
base.send(:include, ActionWebService::Dispatcher::ActionController::Invocation)
|
||||
base.send(:include, ActionWebService::Dispatcher::ActionController::InstanceMethods)
|
||||
end
|
||||
|
||||
module ClassMethods # :nodoc:
|
||||
module ClassMethods
|
||||
def inherited(child)
|
||||
inherited_without_action_controller(child)
|
||||
child.send(:include, ActionWebService::Dispatcher::ActionController::WsdlGeneration)
|
||||
child.send(:include, ActionWebService::Dispatcher::ActionController::WsdlAction)
|
||||
end
|
||||
end
|
||||
|
||||
module Invocation # :nodoc:
|
||||
module InstanceMethods
|
||||
private
|
||||
def controller_dispatch_web_service_request
|
||||
request, response, elapsed, exception = dispatch_web_service_request(@request)
|
||||
if response
|
||||
begin
|
||||
log_request(request)
|
||||
log_error(exception) if exception && logger
|
||||
log_response(response, elapsed)
|
||||
response_options = { :type => response.content_type, :disposition => 'inline' }
|
||||
send_data(response.raw_body, response_options)
|
||||
rescue Exception => e
|
||||
log_error(e) unless logger.nil?
|
||||
render_text("Internal protocol error", "500 Internal Server Error")
|
||||
end
|
||||
else
|
||||
logger.error("No response available") unless logger.nil?
|
||||
render_text("Internal protocol error", "500 Internal Server Error")
|
||||
end
|
||||
end
|
||||
|
||||
def before_direct_invoke(request)
|
||||
before_direct_invoke_without_action_controller(request)
|
||||
@params ||= {}
|
||||
signature = request.signature
|
||||
if signature && (expects = request.signature[:expects])
|
||||
(0..(@method_params.size-1)).each do |i|
|
||||
if expects[i].is_a?(Hash)
|
||||
@params[expects[i].keys[0].to_s] = @method_params[i]
|
||||
else
|
||||
@params['param%d' % i] = @method_params[i]
|
||||
def dispatch_web_service_request
|
||||
request = discover_web_service_request(@request)
|
||||
if request
|
||||
log_request(request, @request.raw_post)
|
||||
response = nil
|
||||
exception = nil
|
||||
bm = Benchmark.measure do
|
||||
begin
|
||||
response = invoke_web_service_request(request)
|
||||
rescue Exception => e
|
||||
exception = e
|
||||
end
|
||||
end
|
||||
if exception
|
||||
log_error(exception) unless logger.nil?
|
||||
send_web_service_error_response(request, exception)
|
||||
else
|
||||
send_web_service_response(response, bm.real)
|
||||
end
|
||||
else
|
||||
exception = DispatcherError.new("Malformed SOAP or XML-RPC protocol message")
|
||||
send_web_service_error_response(request, exception)
|
||||
end
|
||||
rescue Exception => e
|
||||
log_error(e) unless logger.nil?
|
||||
send_web_service_error_response(request, e)
|
||||
end
|
||||
|
||||
def send_web_service_response(response, elapsed=nil)
|
||||
log_response(response, elapsed)
|
||||
options = { :type => response.content_type, :disposition => 'inline' }
|
||||
send_data(response.body, options)
|
||||
end
|
||||
|
||||
def send_web_service_error_response(request, exception)
|
||||
if request
|
||||
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)
|
||||
send_web_service_response(response)
|
||||
else
|
||||
if self.class.web_service_exception_reporting
|
||||
message = exception.message
|
||||
else
|
||||
message = "Exception raised"
|
||||
end
|
||||
render_text("Internal protocol error: #{message}", "500 #{message}")
|
||||
end
|
||||
end
|
||||
|
||||
def web_service_direct_invoke(invocation)
|
||||
@params ||= {}
|
||||
invocation.method_named_params.each do |name, value|
|
||||
@params[name] = value
|
||||
end
|
||||
@params['action'] = request.method_name.to_s
|
||||
@session ||= {}
|
||||
@assigns ||= {}
|
||||
return nil if before_action == false
|
||||
true
|
||||
end
|
||||
|
||||
def after_direct_invoke(request)
|
||||
after_direct_invoke_without_action_controller(request)
|
||||
@params['action'] = invocation.api_method_name.to_s
|
||||
if before_action == false
|
||||
raise(DispatcherError, "Method filtered")
|
||||
end
|
||||
return_value = web_service_direct_invoke_without_controller(invocation)
|
||||
after_action
|
||||
return_value
|
||||
end
|
||||
|
||||
def log_request(request)
|
||||
unless logger.nil? || request.nil?
|
||||
logger.debug("\nWeb Service Request:")
|
||||
indented = request.raw_body.split(/\n/).map{|x| " #{x}"}.join("\n")
|
||||
logger.debug(indented)
|
||||
def log_request(request, body)
|
||||
unless logger.nil?
|
||||
name = request.method_name
|
||||
params = request.method_params.map{|x| x.value.inspect}
|
||||
service = request.service_name
|
||||
logger.debug("\nWeb Service Request: #{name}(#{params}) #{service}")
|
||||
logger.debug(indent(body))
|
||||
end
|
||||
end
|
||||
|
||||
def log_response(response, elapsed)
|
||||
unless logger.nil? || response.nil?
|
||||
logger.debug("\nWeb Service Response" + (elapsed ? " (%f):" % elapsed : ":"))
|
||||
indented = response.raw_body.split(/\n/).map{|x| " #{x}"}.join("\n")
|
||||
logger.debug(indented)
|
||||
def log_response(response, elapsed=nil)
|
||||
unless logger.nil?
|
||||
logger.debug("\nWeb Service Response (%f):" + (elapsed ? " (%f):" % elapsed : ":"))
|
||||
logger.debug(indent(response.body))
|
||||
end
|
||||
end
|
||||
|
||||
unless method_defined?(:logger)
|
||||
def logger; @logger; end
|
||||
def indent(body)
|
||||
body.split(/\n/).map{|x| " #{x}"}.join("\n")
|
||||
end
|
||||
end
|
||||
|
||||
module WsdlGeneration # :nodoc:
|
||||
module WsdlAction
|
||||
XsdNs = 'http://www.w3.org/2001/XMLSchema'
|
||||
WsdlNs = 'http://schemas.xmlsoap.org/wsdl/'
|
||||
SoapNs = 'http://schemas.xmlsoap.org/wsdl/soap/'
|
||||
|
@ -117,40 +135,53 @@ module ActionWebService # :nodoc:
|
|||
case @request.method
|
||||
when :get
|
||||
begin
|
||||
host_name = @request.env['HTTP_HOST'] || @request.env['SERVER_NAME']
|
||||
uri = "http://#{host_name}/#{controller_name}/"
|
||||
soap_action_base = "/#{controller_name}"
|
||||
xml = to_wsdl(self, uri, soap_action_base)
|
||||
send_data(xml, :type => 'text/xml', :disposition => 'inline')
|
||||
options = { :type => 'text/xml', :disposition => 'inline' }
|
||||
send_data(to_wsdl, options)
|
||||
rescue Exception => e
|
||||
log_error e unless logger.nil?
|
||||
render_text('', "500 #{e.message}")
|
||||
log_error(e) unless logger.nil?
|
||||
end
|
||||
when :post
|
||||
render_text('', "500 POST not supported")
|
||||
render_text('POST not supported', '500 POST not supported')
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def to_wsdl(container, uri, soap_action_base)
|
||||
wsdl = ""
|
||||
|
||||
web_service_dispatching_mode = container.web_service_dispatching_mode
|
||||
mapper = container.class.soap_mapper
|
||||
namespace = mapper.custom_namespace
|
||||
wsdl_service_name = namespace.split(/:/)[1]
|
||||
|
||||
services = {}
|
||||
mapper.map_container_services(container) do |name, api, api_methods|
|
||||
services[name] = [api, api_methods]
|
||||
def base_uri
|
||||
host = @request ? (@request.env['HTTP_HOST'] || @request.env['SERVER_NAME']) : 'localhost'
|
||||
'http://%s/%s/' % [host, controller_name]
|
||||
end
|
||||
|
||||
def to_wsdl
|
||||
xml = ''
|
||||
dispatching_mode = web_service_dispatching_mode
|
||||
global_service_name = wsdl_service_name
|
||||
namespace = "urn:#{global_service_name}"
|
||||
soap_action_base = "/#{controller_name}"
|
||||
|
||||
marshaler = WS::Marshaling::SoapMarshaler.new(namespace)
|
||||
apis = {}
|
||||
case dispatching_mode
|
||||
when :direct
|
||||
api = self.class.web_service_api
|
||||
web_service_name = controller_class_name.sub(/Controller$/, '').underscore
|
||||
apis[web_service_name] = [api, register_api(marshaler, api)]
|
||||
when :delegated
|
||||
self.class.web_services.each do |web_service_name, info|
|
||||
service = web_service_object(web_service_name)
|
||||
api = service.class.web_service_api
|
||||
apis[web_service_name] = [api, register_api(marshaler, api)]
|
||||
end
|
||||
end
|
||||
custom_types = mapper.custom_types
|
||||
|
||||
|
||||
xm = Builder::XmlMarkup.new(:target => wsdl, :indent => 2)
|
||||
custom_types = []
|
||||
apis.values.each do |api, bindings|
|
||||
bindings.each do |b|
|
||||
custom_types << b if b.is_custom_type?
|
||||
end
|
||||
end
|
||||
|
||||
xm = Builder::XmlMarkup.new(:target => xml, :indent => 2)
|
||||
xm.instruct!
|
||||
|
||||
xm.definitions('name' => wsdl_service_name,
|
||||
xm.definitions('name' => wsdl_service_name,
|
||||
'targetNamespace' => namespace,
|
||||
'xmlns:typens' => namespace,
|
||||
'xmlns:xsd' => XsdNs,
|
||||
|
@ -158,95 +189,95 @@ module ActionWebService # :nodoc:
|
|||
'xmlns:soapenc' => SoapEncodingNs,
|
||||
'xmlns:wsdl' => WsdlNs,
|
||||
'xmlns' => WsdlNs) do
|
||||
|
||||
# Custom type XSD generation
|
||||
# Generate XSD
|
||||
if custom_types.size > 0
|
||||
xm.types do
|
||||
xm.xsd(:schema, 'xmlns' => XsdNs, 'targetNamespace' => namespace) do
|
||||
custom_types.each do |klass, mapping|
|
||||
custom_types.each do |binding|
|
||||
case
|
||||
when mapping.is_a?(ActionWebService::Protocol::Soap::SoapArrayMapping)
|
||||
xm.xsd(:complexType, 'name' => mapping.type_name) do
|
||||
when binding.is_typed_array?
|
||||
xm.xsd(:complexType, 'name' => binding.type_name) do
|
||||
xm.xsd(:complexContent) do
|
||||
xm.xsd(:restriction, 'base' => 'soapenc:Array') do
|
||||
xm.xsd(:attribute, 'ref' => 'soapenc:arrayType',
|
||||
'wsdl:arrayType' => mapping.element_mapping.qualified_type_name + '[]')
|
||||
'wsdl:arrayType' => binding.element_binding.qualified_type_name + '[]')
|
||||
end
|
||||
end
|
||||
end
|
||||
when mapping.is_a?(ActionWebService::Protocol::Soap::SoapMapping)
|
||||
xm.xsd(:complexType, 'name' => mapping.type_name) do
|
||||
when binding.is_typed_struct?
|
||||
xm.xsd(:complexType, 'name' => binding.type_name) do
|
||||
xm.xsd(:all) do
|
||||
mapping.each_attribute do |name, type_name|
|
||||
binding.each_member do |name, type_name|
|
||||
xm.xsd(:element, 'name' => name, 'type' => type_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
raise(WsdlError, "unsupported mapping type #{mapping.class.name}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
services.each do |service_name, service_values|
|
||||
service_api, api_methods = service_values
|
||||
# Parameter list message definitions
|
||||
api_methods.each do |method_name, method_signature|
|
||||
|
||||
# APIs
|
||||
apis.each do |api_name, values|
|
||||
api = values[0]
|
||||
api.api_methods.each do |name, info|
|
||||
gen = lambda do |msg_name, direction|
|
||||
xm.message('name' => msg_name) do
|
||||
sym = nil
|
||||
if direction == :out
|
||||
if method_signature[:returns]
|
||||
xm.part('name' => 'return', 'type' => method_signature[:returns][0].qualified_type_name)
|
||||
returns = info[:returns]
|
||||
if returns
|
||||
binding = marshaler.register_type(returns[0])
|
||||
xm.part('name' => 'return', 'type' => binding.qualified_type_name)
|
||||
end
|
||||
else
|
||||
mapping_list = method_signature[:expects]
|
||||
expects = info[:expects]
|
||||
i = 1
|
||||
mapping_list.each do |mapping|
|
||||
if mapping.is_a?(Hash)
|
||||
param_name = mapping.keys.shift
|
||||
mapping = mapping.values.shift
|
||||
expects.each do |type|
|
||||
if type.is_a?(Hash)
|
||||
param_name = type.keys.shift
|
||||
type = type.values.shift
|
||||
else
|
||||
param_name = "param#{i}"
|
||||
end
|
||||
xm.part('name' => param_name, 'type' => mapping.qualified_type_name)
|
||||
binding = marshaler.register_type(type)
|
||||
xm.part('name' => param_name, 'type' => binding.qualified_type_name)
|
||||
i += 1
|
||||
end if mapping_list
|
||||
end if expects
|
||||
end
|
||||
end
|
||||
end
|
||||
public_name = service_api.public_api_method_name(method_name)
|
||||
public_name = api.public_api_method_name(name)
|
||||
gen.call(public_name, :in)
|
||||
gen.call("#{public_name}Response", :out)
|
||||
end
|
||||
|
||||
# Declare the port
|
||||
port_name = port_name_for(wsdl_service_name, service_name)
|
||||
|
||||
# Port
|
||||
port_name = port_name_for(global_service_name, api_name)
|
||||
xm.portType('name' => port_name) do
|
||||
api_methods.each do |method_name, method_signature|
|
||||
public_name = service_api.public_api_method_name(method_name)
|
||||
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")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Bind the port to SOAP
|
||||
binding_name = binding_name_for(wsdl_service_name, service_name)
|
||||
|
||||
# Bind it
|
||||
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_methods.each do |method_name, method_signature|
|
||||
public_name = service_api.public_api_method_name(method_name)
|
||||
api.api_methods.each do |name, info|
|
||||
public_name = api.public_api_method_name(name)
|
||||
xm.operation('name' => public_name) do
|
||||
case web_service_dispatching_mode
|
||||
when :direct
|
||||
soap_action = soap_action_base + "/api/" + public_name
|
||||
when :delegated
|
||||
soap_action = soap_action_base \
|
||||
+ "/" + service_name.to_s \
|
||||
+ "/" + api_name.to_s \
|
||||
+ "/" + public_name
|
||||
end
|
||||
xm.soap(:operation, 'soapAction' => soap_action)
|
||||
|
@ -266,32 +297,46 @@ module ActionWebService # :nodoc:
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Define the service
|
||||
xm.service('name' => "#{wsdl_service_name}Service") do
|
||||
services.each do |service_name, service_values|
|
||||
port_name = port_name_for(wsdl_service_name, service_name)
|
||||
binding_name = binding_name_for(wsdl_service_name, service_name)
|
||||
|
||||
# Define it
|
||||
xm.service('name' => "#{global_service_name}Service") do
|
||||
apis.each do |api_name, values|
|
||||
port_name = port_name_for(global_service_name, api_name)
|
||||
binding_name = binding_name_for(global_service_name, api_name)
|
||||
case web_service_dispatching_mode
|
||||
when :direct
|
||||
binding_target = 'api'
|
||||
when :delegated
|
||||
binding_target = service_name.to_s
|
||||
binding_target = api_name.to_s
|
||||
end
|
||||
xm.port('name' => port_name, 'binding' => "typens:#{binding_name}") do
|
||||
xm.soap(:address, 'location' => "#{uri}#{binding_target}")
|
||||
xm.soap(:address, 'location' => "#{base_uri}#{binding_target}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def port_name_for(wsdl_service_name, service_name)
|
||||
"#{wsdl_service_name}#{service_name.to_s.camelize}Port"
|
||||
def port_name_for(global_service, service)
|
||||
"#{global_service}#{service.to_s.camelize}Port"
|
||||
end
|
||||
|
||||
def binding_name_for(wsdl_service_name, service_name)
|
||||
"#{wsdl_service_name}#{service_name.to_s.camelize}Binding"
|
||||
def binding_name_for(global_service, service)
|
||||
"#{global_service}#{service.to_s.camelize}Binding"
|
||||
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
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
require 'action_web_service/protocol/abstract'
|
||||
require 'action_web_service/protocol/registry'
|
||||
require 'action_web_service/protocol/discovery'
|
||||
require 'action_web_service/protocol/soap_protocol'
|
||||
require 'action_web_service/protocol/xmlrpc_protocol'
|
||||
|
|
|
@ -1,126 +1,28 @@
|
|||
module ActionWebService # :nodoc:
|
||||
module Protocol # :nodoc:
|
||||
CheckedMessage = :checked
|
||||
UncheckedMessage = :unchecked
|
||||
|
||||
class ProtocolError < ActionWebService::ActionWebServiceError # :nodoc:
|
||||
class ProtocolError < ActionWebService::ActionWebServiceError
|
||||
end
|
||||
|
||||
class AbstractProtocol # :nodoc:
|
||||
attr :container_class
|
||||
|
||||
def initialize(container_class)
|
||||
@container_class = container_class
|
||||
end
|
||||
|
||||
def unmarshal_request(protocol_request)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def marshal_response(protocol_request, return_value)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def marshal_exception(exception)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def self.create_protocol_request(container_class, action_pack_request)
|
||||
nil
|
||||
end
|
||||
|
||||
def self.create_protocol_client(api, protocol_name, endpoint_uri, options)
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
class AbstractProtocolMessage # :nodoc:
|
||||
attr_accessor :signature
|
||||
attr_accessor :return_signature
|
||||
attr_accessor :type
|
||||
attr :options
|
||||
|
||||
def initialize(options={})
|
||||
@signature = @return_signature = nil
|
||||
@options = options
|
||||
@type = @options[:type] || CheckedMessage
|
||||
end
|
||||
|
||||
def signature=(value)
|
||||
return if value.nil?
|
||||
@signature = []
|
||||
value.each do |klass|
|
||||
if klass.is_a?(Hash)
|
||||
@signature << klass.values.shift
|
||||
else
|
||||
@signature << klass
|
||||
end
|
||||
end
|
||||
@signature
|
||||
end
|
||||
|
||||
def checked?
|
||||
@type == CheckedMessage
|
||||
end
|
||||
|
||||
def check_parameter_types(values, signature)
|
||||
return unless checked? && signature
|
||||
unless signature.length == values.length
|
||||
raise(ProtocolError, "Signature and parameter lengths mismatch")
|
||||
end
|
||||
(1..signature.length).each do |i|
|
||||
check_compatibility(signature[i-1], values[i-1].class)
|
||||
end
|
||||
end
|
||||
|
||||
def check_compatibility(expected_class, received_class)
|
||||
return if \
|
||||
(expected_class == TrueClass or expected_class == FalseClass) and \
|
||||
(received_class == TrueClass or received_class == FalseClass)
|
||||
unless received_class.ancestors.include?(expected_class) or \
|
||||
expected_class.ancestors.include?(received_class)
|
||||
raise(ProtocolError, "value of type #{received_class.name} is not " +
|
||||
"compatible with expected type #{expected_class.name}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ProtocolRequest < AbstractProtocolMessage # :nodoc:
|
||||
class Request
|
||||
attr :protocol
|
||||
attr :raw_body
|
||||
attr :method_name
|
||||
attr :method_params
|
||||
attr :service_name
|
||||
|
||||
attr_accessor :web_service_name
|
||||
attr_accessor :public_method_name
|
||||
attr_accessor :content_type
|
||||
|
||||
def initialize(protocol, raw_body, web_service_name, public_method_name, content_type, options={})
|
||||
super(options)
|
||||
def initialize(protocol, method_name, method_params, service_name)
|
||||
@protocol = protocol
|
||||
@raw_body = raw_body
|
||||
@web_service_name = web_service_name
|
||||
@public_method_name = public_method_name
|
||||
@content_type = content_type
|
||||
end
|
||||
|
||||
def unmarshal
|
||||
@protocol.unmarshal_request(self)
|
||||
end
|
||||
|
||||
def marshal(return_value)
|
||||
@protocol.marshal_response(self, return_value)
|
||||
@method_name = method_name
|
||||
@method_params = method_params
|
||||
@service_name = service_name
|
||||
end
|
||||
end
|
||||
|
||||
class ProtocolResponse < AbstractProtocolMessage # :nodoc:
|
||||
attr :protocol
|
||||
attr :raw_body
|
||||
class Response
|
||||
attr :body
|
||||
attr :content_type
|
||||
|
||||
attr_accessor :content_type
|
||||
|
||||
def initialize(protocol, raw_body, content_type, options={})
|
||||
super(options)
|
||||
@protocol = protocol
|
||||
@raw_body = raw_body
|
||||
def initialize(body, content_type)
|
||||
@body = body
|
||||
@content_type = content_type
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
module ActionWebService
|
||||
module Protocol
|
||||
module Discovery
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
base.send(:include, ActionWebService::Protocol::Discovery::InstanceMethods)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def register_protocol(klass)
|
||||
write_inheritable_array("web_service_protocols", [klass])
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
private
|
||||
def discover_web_service_request(ap_request)
|
||||
(self.class.read_inheritable_attribute("web_service_protocols") || []).each do |protocol|
|
||||
protocol = protocol.new
|
||||
request = protocol.unmarshal_request(ap_request)
|
||||
return request unless request.nil?
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def create_web_service_client(api, protocol_name, endpoint_uri, options)
|
||||
(self.class.read_inheritable_attribute("web_service_protocols") || []).each do |protocol|
|
||||
protocol = protocol.new
|
||||
client = protocol.protocol_client(api, protocol_name, endpoint_uri, options)
|
||||
return client unless client.nil?
|
||||
end
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,55 +0,0 @@
|
|||
module ActionWebService # :nodoc:
|
||||
module Protocol # :nodoc:
|
||||
HeaderAndBody = :header_and_body
|
||||
BodyOnly = :body_only
|
||||
|
||||
module Registry # :nodoc:
|
||||
def self.append_features(base) # :nodoc:
|
||||
super
|
||||
base.extend(ClassMethods)
|
||||
base.send(:include, ActionWebService::Protocol::Registry::InstanceMethods)
|
||||
end
|
||||
|
||||
module ClassMethods # :nodoc:
|
||||
def register_protocol(type, klass) # :nodoc:
|
||||
case type
|
||||
when HeaderAndBody
|
||||
write_inheritable_array("header_and_body_protocols", [klass])
|
||||
when BodyOnly
|
||||
write_inheritable_array("body_only_protocols", [klass])
|
||||
else
|
||||
raise(ProtocolError, "unknown protocol type #{type}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods # :nodoc:
|
||||
private
|
||||
def probe_request_protocol(action_pack_request)
|
||||
(header_and_body_protocols + body_only_protocols).each do |protocol|
|
||||
protocol_request = protocol.create_protocol_request(self.class, action_pack_request)
|
||||
return protocol_request if protocol_request
|
||||
end
|
||||
raise(ProtocolError, "unsupported request message format")
|
||||
end
|
||||
|
||||
def probe_protocol_client(api, protocol_name, endpoint_uri, options)
|
||||
(header_and_body_protocols + body_only_protocols).each do |protocol|
|
||||
protocol_client = protocol.create_protocol_client(api, protocol_name, endpoint_uri, options)
|
||||
return protocol_client if protocol_client
|
||||
end
|
||||
raise(ProtocolError, "unsupported client protocol :#{protocol_name}")
|
||||
end
|
||||
|
||||
def header_and_body_protocols
|
||||
self.class.read_inheritable_attribute("header_and_body_protocols") || []
|
||||
end
|
||||
|
||||
def body_only_protocols
|
||||
self.class.read_inheritable_attribute("body_only_protocols") || []
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,127 +1,49 @@
|
|||
require 'soap/processor'
|
||||
require 'soap/mapping'
|
||||
require 'soap/rpc/element'
|
||||
require 'xsd/datatypes'
|
||||
require 'xsd/ns'
|
||||
require 'singleton'
|
||||
|
||||
module ActionWebService # :nodoc:
|
||||
module Protocol # :nodoc:
|
||||
module Soap # :nodoc:
|
||||
class ProtocolError < ActionWebService::ActionWebServiceError # :nodoc:
|
||||
module ActionWebService
|
||||
module Protocol
|
||||
module Soap
|
||||
def self.included(base)
|
||||
base.register_protocol(SoapProtocol)
|
||||
base.class_inheritable_option(:wsdl_service_name)
|
||||
end
|
||||
|
||||
def self.append_features(base) # :nodoc:
|
||||
super
|
||||
base.register_protocol(HeaderAndBody, SoapProtocol)
|
||||
base.extend(ClassMethods)
|
||||
base.wsdl_service_name('ActionWebService')
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Specifies the WSDL service name to use when generating WSDL. Highly
|
||||
# recommended that you set this value, or code generators may generate
|
||||
# classes with very generic names.
|
||||
#
|
||||
# === Example
|
||||
# class MyController < ActionController::Base
|
||||
# wsdl_service_name 'MyService'
|
||||
# end
|
||||
def wsdl_service_name(name)
|
||||
write_inheritable_attribute("soap_mapper", SoapMapper.new("urn:#{name}"))
|
||||
|
||||
class SoapProtocol
|
||||
def initialize
|
||||
@encoder = WS::Encoding::SoapRpcEncoding.new
|
||||
@marshaler = WS::Marshaling::SoapMarshaler.new
|
||||
end
|
||||
|
||||
def soap_mapper # :nodoc:
|
||||
read_inheritable_attribute("soap_mapper")
|
||||
end
|
||||
end
|
||||
|
||||
class SoapProtocol < AbstractProtocol # :nodoc:
|
||||
attr :mapper
|
||||
|
||||
def initialize(mapper)
|
||||
@mapper = mapper
|
||||
def unmarshal_request(ap_request)
|
||||
return nil unless has_valid_soap_action?(ap_request)
|
||||
method_name, params = @encoder.decode_rpc_call(ap_request.raw_post)
|
||||
params = params.map{|x| @marshaler.unmarshal(x)}
|
||||
service_name = ap_request.parameters['action']
|
||||
Request.new(self, method_name, params, service_name)
|
||||
end
|
||||
|
||||
def self.create_protocol_request(container_class, action_pack_request)
|
||||
soap_action = extract_soap_action(action_pack_request)
|
||||
return nil unless soap_action
|
||||
service_name = action_pack_request.parameters['action']
|
||||
public_method_name = soap_action.gsub(/^[\/]+/, '').split(/[\/]+/)[-1]
|
||||
content_type = action_pack_request.env['HTTP_CONTENT_TYPE']
|
||||
content_type ||= 'text/xml'
|
||||
protocol = SoapProtocol.new(container_class.soap_mapper)
|
||||
ProtocolRequest.new(protocol,
|
||||
action_pack_request.raw_post,
|
||||
service_name.to_sym,
|
||||
public_method_name,
|
||||
content_type)
|
||||
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, 0, type_binding)
|
||||
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 self.create_protocol_client(api, protocol_name, endpoint_uri, options)
|
||||
return nil unless protocol_name.to_s.downcase.to_sym == :soap
|
||||
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)
|
||||
end
|
||||
|
||||
def unmarshal_request(protocol_request)
|
||||
unmarshal = lambda do
|
||||
envelope = SOAP::Processor.unmarshal(protocol_request.raw_body)
|
||||
request = envelope.body.request
|
||||
values = request.collect{|k, v| request[k]}
|
||||
soap_to_ruby_array(values)
|
||||
end
|
||||
signature = protocol_request.signature
|
||||
if signature
|
||||
map_signature_types(signature)
|
||||
values = unmarshal.call
|
||||
signature = signature.map{|x|mapper.lookup(x).ruby_klass}
|
||||
protocol_request.check_parameter_types(values, signature)
|
||||
values
|
||||
else
|
||||
if protocol_request.checked?
|
||||
[]
|
||||
else
|
||||
unmarshal.call
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def marshal_response(protocol_request, return_value)
|
||||
marshal = lambda do |signature|
|
||||
mapping = mapper.lookup(signature[0])
|
||||
return_value = fixup_array_types(mapping, return_value)
|
||||
signature = signature.map{|x|mapper.lookup(x).ruby_klass}
|
||||
protocol_request.check_parameter_types([return_value], signature)
|
||||
param_def = [['retval', 'return', mapping.registry_mapping]]
|
||||
[param_def, ruby_to_soap(return_value)]
|
||||
end
|
||||
signature = protocol_request.return_signature
|
||||
param_def = nil
|
||||
if signature
|
||||
param_def, return_value = marshal.call(signature)
|
||||
else
|
||||
if protocol_request.checked?
|
||||
param_def, return_value = nil, nil
|
||||
else
|
||||
param_def, return_value = marshal.call([return_value.class])
|
||||
end
|
||||
end
|
||||
qname = XSD::QName.new(mapper.custom_namespace,
|
||||
protocol_request.public_method_name)
|
||||
response = SOAP::RPC::SOAPMethodResponse.new(qname, param_def)
|
||||
response.retval = return_value unless return_value.nil?
|
||||
ProtocolResponse.new(self, create_response(response), 'text/xml')
|
||||
end
|
||||
|
||||
def marshal_exception(exc)
|
||||
ProtocolResponse.new(self, create_exception_response(exc), 'text/xml')
|
||||
end
|
||||
|
||||
private
|
||||
def self.extract_soap_action(request)
|
||||
def has_valid_soap_action?(request)
|
||||
return nil unless request.method == :post
|
||||
content_type = request.env['HTTP_CONTENT_TYPE'] || 'text/xml'
|
||||
return nil unless content_type
|
||||
soap_action = request.env['HTTP_SOAPACTION']
|
||||
return nil unless soap_action
|
||||
soap_action.gsub!(/^"/, '')
|
||||
|
@ -130,355 +52,7 @@ module ActionWebService # :nodoc:
|
|||
return nil if soap_action.empty?
|
||||
soap_action
|
||||
end
|
||||
|
||||
def fixup_array_types(mapping, obj)
|
||||
mapping.each_attribute do |name, type, attr_mapping|
|
||||
if attr_mapping.custom_type?
|
||||
attr_obj = obj.send(name)
|
||||
new_obj = fixup_array_types(attr_mapping, attr_obj)
|
||||
obj.send("#{name}=", new_obj) unless new_obj.equal?(attr_obj)
|
||||
end
|
||||
end
|
||||
if mapping.is_a?(SoapArrayMapping)
|
||||
obj = mapping.ruby_klass.new(obj)
|
||||
# man, this is going to be slow for big arrays :(
|
||||
(1..obj.size).each do |i|
|
||||
i -= 1
|
||||
obj[i] = fixup_array_types(mapping.element_mapping, obj[i])
|
||||
end
|
||||
else
|
||||
if !mapping.generated_klass.nil? && mapping.generated_klass.respond_to?(:members)
|
||||
# have to map the publically visible structure of the class
|
||||
new_obj = mapping.generated_klass.new
|
||||
mapping.generated_klass.members.each do |name, klass|
|
||||
new_obj.send("#{name}=", obj.send(name))
|
||||
end
|
||||
obj = new_obj
|
||||
end
|
||||
end
|
||||
obj
|
||||
end
|
||||
|
||||
def map_signature_types(types)
|
||||
types.collect{|type| mapper.map(type)}
|
||||
end
|
||||
|
||||
def create_response(body)
|
||||
header = SOAP::SOAPHeader.new
|
||||
body = SOAP::SOAPBody.new(body)
|
||||
envelope = SOAP::SOAPEnvelope.new(header, body)
|
||||
SOAP::Processor.marshal(envelope)
|
||||
end
|
||||
|
||||
def create_exception_response(exc)
|
||||
detail = SOAP::Mapping::SOAPException.new(exc)
|
||||
body = SOAP::SOAPFault.new(
|
||||
SOAP::SOAPString.new('Server'),
|
||||
SOAP::SOAPString.new(exc.to_s),
|
||||
SOAP::SOAPString.new(self.class.name),
|
||||
SOAP::Mapping.obj2soap(detail))
|
||||
create_response(body)
|
||||
end
|
||||
|
||||
def ruby_to_soap(obj)
|
||||
SOAP::Mapping.obj2soap(obj, mapper.registry)
|
||||
end
|
||||
|
||||
def soap_to_ruby(obj)
|
||||
SOAP::Mapping.soap2obj(obj, mapper.registry)
|
||||
end
|
||||
|
||||
def soap_to_ruby_array(array)
|
||||
array.map{|x| soap_to_ruby(x)}
|
||||
end
|
||||
end
|
||||
|
||||
class SoapMapper # :nodoc:
|
||||
attr :registry
|
||||
attr :custom_namespace
|
||||
attr :custom_types
|
||||
|
||||
def initialize(custom_namespace)
|
||||
@custom_namespace = custom_namespace
|
||||
@registry = SOAP::Mapping::Registry.new
|
||||
@klass2map = {}
|
||||
@custom_types = {}
|
||||
@ar2klass = {}
|
||||
end
|
||||
|
||||
def lookup(klass)
|
||||
lookup_klass = klass.is_a?(Array) ? klass[0] : klass
|
||||
generated_klass = nil
|
||||
unless lookup_klass.respond_to?(:ancestors)
|
||||
raise(ProtocolError, "expected parameter type definition to be a Class")
|
||||
end
|
||||
if lookup_klass.ancestors.include?(ActiveRecord::Base)
|
||||
generated_klass = @ar2klass.has_key?(klass) ? @ar2klass[klass] : nil
|
||||
klass = generated_klass if generated_klass
|
||||
end
|
||||
return @klass2map[klass] if @klass2map.has_key?(klass)
|
||||
|
||||
custom_type = false
|
||||
|
||||
ruby_klass = select_class(lookup_klass)
|
||||
generated_klass = @ar2klass[lookup_klass] if @ar2klass.has_key?(lookup_klass)
|
||||
type_name = ruby_klass.name
|
||||
|
||||
# Array signatures generate a double-mapping and require generation
|
||||
# of an Array subclass to represent the mapping in the SOAP
|
||||
# registry
|
||||
array_klass = nil
|
||||
if klass.is_a?(Array)
|
||||
array_klass = Class.new(Array) do
|
||||
module_eval <<-END
|
||||
def self.name
|
||||
"#{type_name}Array"
|
||||
end
|
||||
END
|
||||
end
|
||||
end
|
||||
|
||||
mapping = @registry.find_mapped_soap_class(ruby_klass) rescue nil
|
||||
unless mapping
|
||||
# Custom structured type, generate a mapping
|
||||
info = { :type => XSD::QName.new(@custom_namespace, type_name) }
|
||||
@registry.add(ruby_klass,
|
||||
SOAP::SOAPStruct,
|
||||
SOAP::Mapping::Registry::TypedStructFactory,
|
||||
info)
|
||||
mapping = ensure_mapped(ruby_klass)
|
||||
custom_type = true
|
||||
end
|
||||
|
||||
array_mapping = nil
|
||||
if array_klass
|
||||
# Typed array always requires a custom type. The info of the array
|
||||
# is the info of its element type (in mapping[2]), falling back
|
||||
# to SOAP base types.
|
||||
info = mapping[2]
|
||||
info ||= {}
|
||||
info[:type] ||= soap_base_type_qname(mapping[0])
|
||||
@registry.add(array_klass,
|
||||
SOAP::SOAPArray,
|
||||
SOAP::Mapping::Registry::TypedArrayFactory,
|
||||
info)
|
||||
array_mapping = ensure_mapped(array_klass)
|
||||
end
|
||||
|
||||
if array_mapping
|
||||
@klass2map[ruby_klass] = SoapMapping.new(self,
|
||||
type_name,
|
||||
ruby_klass,
|
||||
generated_klass,
|
||||
mapping[0],
|
||||
mapping,
|
||||
custom_type)
|
||||
@klass2map[klass] = SoapArrayMapping.new(self,
|
||||
type_name,
|
||||
array_klass,
|
||||
array_mapping[0],
|
||||
array_mapping,
|
||||
@klass2map[ruby_klass])
|
||||
@custom_types[klass] = @klass2map[klass]
|
||||
@custom_types[ruby_klass] = @klass2map[ruby_klass] if custom_type
|
||||
else
|
||||
@klass2map[klass] = SoapMapping.new(self,
|
||||
type_name,
|
||||
ruby_klass,
|
||||
generated_klass,
|
||||
mapping[0],
|
||||
mapping,
|
||||
custom_type)
|
||||
@custom_types[klass] = @klass2map[klass] if custom_type
|
||||
end
|
||||
|
||||
@klass2map[klass]
|
||||
end
|
||||
alias :map :lookup
|
||||
|
||||
def map_container_services(container, &block)
|
||||
dispatching_mode = container.web_service_dispatching_mode
|
||||
web_services = nil
|
||||
case dispatching_mode
|
||||
when :direct
|
||||
api = container.class.web_service_api
|
||||
if container.respond_to?(:controller_class_name)
|
||||
web_service_name = container.controller_class_name.sub(/Controller$/, '').underscore
|
||||
else
|
||||
web_service_name = container.class.name.demodulize.underscore
|
||||
end
|
||||
web_services = { web_service_name => api }
|
||||
when :delegated
|
||||
web_services = {}
|
||||
container.class.web_services.each do |web_service_name, web_service_info|
|
||||
begin
|
||||
object = container.web_service_object(web_service_name)
|
||||
rescue Exception => e
|
||||
raise(ProtocolError, "failed to retrieve web service object for web service '#{web_service_name}': #{e.message}")
|
||||
end
|
||||
web_services[web_service_name] = object.class.web_service_api
|
||||
end
|
||||
end
|
||||
web_services.each do |web_service_name, api|
|
||||
if api.nil?
|
||||
raise(ProtocolError, "no web service API set while in :#{dispatching_mode} mode")
|
||||
end
|
||||
map_api(api) do |api_methods|
|
||||
yield web_service_name, api, api_methods if block_given?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def map_api(api, &block)
|
||||
lookup_proc = lambda do |klass|
|
||||
mapping = lookup(klass)
|
||||
custom_mapping = nil
|
||||
if mapping.respond_to?(:element_mapping)
|
||||
custom_mapping = mapping.element_mapping
|
||||
else
|
||||
custom_mapping = mapping
|
||||
end
|
||||
if custom_mapping && custom_mapping.custom_type?
|
||||
# What gives? This is required so that structure types
|
||||
# referenced only by structures (and not signatures) still
|
||||
# have a custom type mapping in the registry (needed for WSDL
|
||||
# generation).
|
||||
custom_mapping.each_attribute{}
|
||||
end
|
||||
mapping
|
||||
end
|
||||
api_methods = block.nil?? nil : {}
|
||||
api.api_methods.each do |method_name, method_info|
|
||||
expects = method_info[:expects]
|
||||
expects_signature = nil
|
||||
if expects
|
||||
expects_signature = block ? [] : nil
|
||||
expects.each do |klass|
|
||||
lookup_klass = nil
|
||||
if klass.is_a?(Hash)
|
||||
lookup_klass = lookup_proc.call(klass.values[0])
|
||||
expects_signature << {klass.keys[0]=>lookup_klass} if block
|
||||
else
|
||||
lookup_klass = lookup_proc.call(klass)
|
||||
expects_signature << lookup_klass if block
|
||||
end
|
||||
end
|
||||
end
|
||||
returns = method_info[:returns]
|
||||
returns_signature = returns ? returns.map{|klass| lookup_proc.call(klass)} : nil
|
||||
if block
|
||||
api_methods[method_name] = {
|
||||
:expects => expects_signature,
|
||||
:returns => returns_signature
|
||||
}
|
||||
end
|
||||
end
|
||||
yield api_methods if block
|
||||
end
|
||||
|
||||
private
|
||||
def select_class(klass)
|
||||
return Integer if klass == Fixnum
|
||||
if klass.ancestors.include?(ActiveRecord::Base)
|
||||
new_klass = Class.new(ActionWebService::Struct)
|
||||
new_klass.class_eval <<-EOS
|
||||
def self.name
|
||||
"#{klass.name}"
|
||||
end
|
||||
EOS
|
||||
klass.columns.each do |column|
|
||||
next if column.klass.nil?
|
||||
new_klass.send(:member, column.name.to_sym, column.klass)
|
||||
end
|
||||
@ar2klass[klass] = new_klass
|
||||
return new_klass
|
||||
end
|
||||
klass
|
||||
end
|
||||
|
||||
def ensure_mapped(klass)
|
||||
mapping = @registry.find_mapped_soap_class(klass) rescue nil
|
||||
raise(ProtocolError, "failed to register #{klass.name}") unless mapping
|
||||
mapping
|
||||
end
|
||||
|
||||
def soap_base_type_qname(base_type)
|
||||
xsd_type = base_type.ancestors.find{|c| c.const_defined? 'Type'}
|
||||
xsd_type ? xsd_type.const_get('Type') : XSD::XSDAnySimpleType::Type
|
||||
end
|
||||
end
|
||||
|
||||
class SoapMapping # :nodoc:
|
||||
attr :ruby_klass
|
||||
attr :generated_klass
|
||||
attr :soap_klass
|
||||
attr :registry_mapping
|
||||
|
||||
def initialize(mapper, type_name, ruby_klass, generated_klass, soap_klass, registry_mapping,
|
||||
custom_type=false)
|
||||
@mapper = mapper
|
||||
@type_name = type_name
|
||||
@ruby_klass = ruby_klass
|
||||
@generated_klass = generated_klass
|
||||
@soap_klass = soap_klass
|
||||
@registry_mapping = registry_mapping
|
||||
@custom_type = custom_type
|
||||
end
|
||||
|
||||
def type_name
|
||||
@type_name
|
||||
end
|
||||
|
||||
def custom_type?
|
||||
@custom_type
|
||||
end
|
||||
|
||||
def qualified_type_name
|
||||
name = type_name
|
||||
if custom_type?
|
||||
"typens:#{name}"
|
||||
else
|
||||
xsd_type_for(@soap_klass)
|
||||
end
|
||||
end
|
||||
|
||||
def each_attribute(&block)
|
||||
if @ruby_klass.respond_to?(:members)
|
||||
@ruby_klass.members.each do |name, klass|
|
||||
name = name.to_s
|
||||
mapping = @mapper.lookup(klass)
|
||||
yield name, mapping.qualified_type_name, mapping
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def is_xsd_type?(klass)
|
||||
klass.ancestors.include?(XSD::NSDBase)
|
||||
end
|
||||
|
||||
def xsd_type_for(klass)
|
||||
ns = XSD::NS.new
|
||||
ns.assign(XSD::Namespace, SOAP::XSDNamespaceTag)
|
||||
xsd_klass = klass.ancestors.find{|c| c.const_defined?('Type')}
|
||||
return ns.name(XSD::AnyTypeName) unless xsd_klass
|
||||
ns.name(xsd_klass.const_get('Type'))
|
||||
end
|
||||
end
|
||||
|
||||
class SoapArrayMapping < SoapMapping # :nodoc:
|
||||
attr :element_mapping
|
||||
|
||||
def initialize(mapper, type_name, ruby_klass, soap_klass, registry_mapping, element_mapping)
|
||||
super(mapper, type_name, ruby_klass, nil, soap_klass, registry_mapping, true)
|
||||
@element_mapping = element_mapping
|
||||
end
|
||||
|
||||
def type_name
|
||||
super + "Array"
|
||||
end
|
||||
|
||||
def each_attribute(&block); end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,167 +1,47 @@
|
|||
require 'xmlrpc/parser'
|
||||
require 'xmlrpc/create'
|
||||
require 'xmlrpc/config'
|
||||
require 'xmlrpc/utils'
|
||||
require 'singleton'
|
||||
|
||||
module XMLRPC # :nodoc:
|
||||
class XmlRpcHelper # :nodoc:
|
||||
include Singleton
|
||||
include ParserWriterChooseMixin
|
||||
|
||||
def parse_method_call(message)
|
||||
parser().parseMethodCall(message)
|
||||
end
|
||||
|
||||
def create_method_response(successful, return_value)
|
||||
create().methodResponse(successful, return_value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ActionWebService # :nodoc:
|
||||
module Protocol # :nodoc:
|
||||
module XmlRpc # :nodoc:
|
||||
def self.append_features(base) # :nodoc:
|
||||
super
|
||||
base.register_protocol(BodyOnly, XmlRpcProtocol)
|
||||
module ActionWebService
|
||||
module Protocol
|
||||
module XmlRpc
|
||||
def self.included(base)
|
||||
base.register_protocol(XmlRpcProtocol)
|
||||
end
|
||||
|
||||
class XmlRpcProtocol
|
||||
attr :marshaler
|
||||
|
||||
class XmlRpcProtocol < AbstractProtocol # :nodoc:
|
||||
def self.create_protocol_request(container_class, action_pack_request)
|
||||
helper = XMLRPC::XmlRpcHelper.instance
|
||||
service_name = action_pack_request.parameters['action']
|
||||
methodname, params = helper.parse_method_call(action_pack_request.raw_post)
|
||||
methodname.gsub!(/^[^\.]+\./, '') unless methodname =~ /^system\./ # XXX
|
||||
protocol = XmlRpcProtocol.new(container_class)
|
||||
content_type = action_pack_request.env['HTTP_CONTENT_TYPE']
|
||||
content_type ||= 'text/xml'
|
||||
request = ProtocolRequest.new(protocol,
|
||||
action_pack_request.raw_post,
|
||||
service_name.to_sym,
|
||||
methodname,
|
||||
content_type,
|
||||
:xmlrpc_values => params)
|
||||
request
|
||||
def initialize
|
||||
@encoder = WS::Encoding::XmlRpcEncoding.new
|
||||
@marshaler = WS::Marshaling::XmlRpcMarshaler.new
|
||||
end
|
||||
|
||||
def unmarshal_request(ap_request)
|
||||
method_name, params = @encoder.decode_rpc_call(ap_request.raw_post)
|
||||
params = params.map{|x| @marshaler.unmarshal(x)}
|
||||
service_name = ap_request.parameters['action']
|
||||
Request.new(self, method_name, params, service_name)
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
def self.create_protocol_client(api, protocol_name, endpoint_uri, options)
|
||||
return nil unless protocol_name.to_s.downcase.to_sym == :xmlrpc
|
||||
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, 0, type_binding)
|
||||
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)
|
||||
end
|
||||
|
||||
def initialize(container_class)
|
||||
super(container_class)
|
||||
end
|
||||
|
||||
def unmarshal_request(protocol_request)
|
||||
values = protocol_request.options[:xmlrpc_values]
|
||||
signature = protocol_request.signature
|
||||
if signature
|
||||
values = self.class.transform_incoming_method_params(self.class.transform_array_types(signature), values)
|
||||
protocol_request.check_parameter_types(values, check_array_types(signature))
|
||||
values
|
||||
else
|
||||
protocol_request.checked? ? [] : values
|
||||
end
|
||||
end
|
||||
|
||||
def marshal_response(protocol_request, return_value)
|
||||
helper = XMLRPC::XmlRpcHelper.instance
|
||||
signature = protocol_request.return_signature
|
||||
if signature
|
||||
protocol_request.check_parameter_types([return_value], check_array_types(signature))
|
||||
return_value = self.class.transform_return_value(self.class.transform_array_types(signature), return_value)
|
||||
raw_response = helper.create_method_response(true, return_value)
|
||||
else
|
||||
# XML-RPC doesn't have the concept of a void method, nor does it
|
||||
# support a nil return value, so return true if we would have returned
|
||||
# nil
|
||||
if protocol_request.checked?
|
||||
raw_response = helper.create_method_response(true, true)
|
||||
else
|
||||
return_value = true if return_value.nil?
|
||||
raw_response = helper.create_method_response(true, return_value)
|
||||
end
|
||||
end
|
||||
ProtocolResponse.new(self, raw_response, 'text/xml')
|
||||
end
|
||||
|
||||
def marshal_exception(exception)
|
||||
helper = XMLRPC::XmlRpcHelper.instance
|
||||
exception = XMLRPC::FaultException.new(1, exception.message)
|
||||
raw_response = helper.create_method_response(false, exception)
|
||||
ProtocolResponse.new(self, raw_response, 'text/xml')
|
||||
end
|
||||
|
||||
class << self
|
||||
def transform_incoming_method_params(signature, params)
|
||||
(1..signature.size).each do |i|
|
||||
i -= 1
|
||||
params[i] = xmlrpc_to_ruby(params[i], signature[i])
|
||||
end
|
||||
params
|
||||
end
|
||||
|
||||
def transform_return_value(signature, return_value)
|
||||
ruby_to_xmlrpc(return_value, signature[0])
|
||||
end
|
||||
|
||||
def ruby_to_xmlrpc(param, param_class)
|
||||
if param_class.is_a?(XmlRpcArray)
|
||||
param.map{|p| ruby_to_xmlrpc(p, param_class.klass)}
|
||||
elsif param_class.ancestors.include?(ActiveRecord::Base)
|
||||
param.instance_variable_get('@attributes')
|
||||
elsif param_class.ancestors.include?(ActionWebService::Struct)
|
||||
struct = {}
|
||||
param_class.members.each do |name, klass|
|
||||
value = param.send(name)
|
||||
next if value.nil?
|
||||
struct[name.to_s] = value
|
||||
end
|
||||
struct
|
||||
else
|
||||
param
|
||||
end
|
||||
end
|
||||
|
||||
def xmlrpc_to_ruby(param, param_class)
|
||||
if param_class.is_a?(XmlRpcArray)
|
||||
param.map{|p| xmlrpc_to_ruby(p, param_class.klass)}
|
||||
elsif param_class.ancestors.include?(ActiveRecord::Base)
|
||||
raise(ProtocolError, "incoming ActiveRecord::Base types are not allowed")
|
||||
elsif param_class.ancestors.include?(ActionWebService::Struct)
|
||||
unless param.is_a?(Hash)
|
||||
raise(ProtocolError, "expected parameter to be a Hash")
|
||||
end
|
||||
new_param = param_class.new
|
||||
param_class.members.each do |name, klass|
|
||||
new_param.send('%s=' % name.to_s, param[name.to_s])
|
||||
end
|
||||
new_param
|
||||
else
|
||||
param
|
||||
end
|
||||
end
|
||||
|
||||
def transform_array_types(signature)
|
||||
signature.map{|x| x.is_a?(Array) ? XmlRpcArray.new(x[0]) : x}
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def check_array_types(signature)
|
||||
signature.map{|x| x.is_a?(Array) ? Array : x}
|
||||
end
|
||||
|
||||
class XmlRpcArray
|
||||
attr :klass
|
||||
def initialize(klass)
|
||||
@klass = klass
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -35,12 +35,10 @@ module ActionWebService
|
|||
end
|
||||
|
||||
class << self
|
||||
include ActionWebService::Signature
|
||||
|
||||
# Creates a structure member with the specified +name+ and +type+. Generates
|
||||
# accessor methods for reading and writing the member value.
|
||||
def member(name, type)
|
||||
write_inheritable_hash("struct_members", name => signature_parameter_class(type))
|
||||
write_inheritable_hash("struct_members", name => WS::BaseTypes.canonical_param_type_class(type))
|
||||
class_eval <<-END
|
||||
def #{name}; @#{name}; end
|
||||
def #{name}=(value); @#{name} = value; end
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
module ActionWebService # :nodoc:
|
||||
# Action Web Service parameter specifiers may contain symbols or strings
|
||||
# instead of Class objects, for a limited set of base types.
|
||||
#
|
||||
# This provides an unambiguous way to specify that a given parameter
|
||||
# contains an integer or boolean value, for example.
|
||||
#
|
||||
# The allowed set of symbol/string aliases:
|
||||
#
|
||||
# [<tt>:int</tt>] any integer value
|
||||
# [<tt>:float</tt>] any floating point value
|
||||
# [<tt>:string</tt>] any string value
|
||||
# [<tt>:bool</tt>] any boolean value
|
||||
# [<tt>:time</tt>] any value containing both date and time
|
||||
# [<tt>:date</tt>] any value containing only a date
|
||||
module Signature
|
||||
class SignatureError < StandardError # :nodoc:
|
||||
end
|
||||
|
||||
private
|
||||
def canonical_signature(params)
|
||||
return nil if params.nil?
|
||||
params.map do |param|
|
||||
klass = signature_parameter_class(param)
|
||||
if param.is_a?(Hash)
|
||||
param[param.keys[0]] = klass
|
||||
param
|
||||
else
|
||||
klass
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def signature_parameter_class(param)
|
||||
param = param.is_a?(Hash) ? param.values[0] : param
|
||||
is_array = param.is_a?(Array)
|
||||
param = is_array ? param[0] : param
|
||||
param = param.is_a?(String) ? param.to_sym : param
|
||||
param = param.is_a?(Symbol) ? signature_ruby_class(param) : param
|
||||
is_array ? [param] : param
|
||||
end
|
||||
|
||||
|
||||
def canonical_signature_base_type(base_type)
|
||||
base_type = base_type.to_sym
|
||||
case base_type
|
||||
when :int, :integer, :fixnum, :bignum
|
||||
:int
|
||||
when :string, :base64
|
||||
:string
|
||||
when :bool, :boolean
|
||||
:bool
|
||||
when :float, :double
|
||||
:float
|
||||
when :time, :datetime, :timestamp
|
||||
:time
|
||||
when :date
|
||||
:date
|
||||
else
|
||||
raise(SignatureError, ":#{base_type} is not an ActionWebService base type")
|
||||
end
|
||||
end
|
||||
|
||||
def signature_ruby_class(base_type)
|
||||
case canonical_signature_base_type(base_type)
|
||||
when :int
|
||||
Integer
|
||||
when :string
|
||||
String
|
||||
when :bool
|
||||
TrueClass
|
||||
when :float
|
||||
Float
|
||||
when :time
|
||||
Time
|
||||
when :date
|
||||
Date
|
||||
end
|
||||
end
|
||||
|
||||
def signature_base_type(ruby_class)
|
||||
case ruby_class
|
||||
when Bignum, Integer, Fixnum
|
||||
:int
|
||||
when String
|
||||
:string
|
||||
when TrueClass, FalseClass
|
||||
:bool
|
||||
when Float, Numeric, Precision
|
||||
:float
|
||||
when Time, DateTime
|
||||
:time
|
||||
when Date
|
||||
:date
|
||||
else
|
||||
raise(SignatureError, "#{ruby_class.name} is not an ActionWebService base type")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
4
actionwebservice/lib/action_web_service/vendor/ws.rb
vendored
Normal file
4
actionwebservice/lib/action_web_service/vendor/ws.rb
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
require 'ws/common'
|
||||
require 'ws/types'
|
||||
require 'ws/marshaling'
|
||||
require 'ws/encoding'
|
8
actionwebservice/lib/action_web_service/vendor/ws/common.rb
vendored
Normal file
8
actionwebservice/lib/action_web_service/vendor/ws/common.rb
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
module WS
|
||||
class WSError < StandardError
|
||||
end
|
||||
|
||||
def self.derived_from?(ancestor, child)
|
||||
child.ancestors.include?(ancestor)
|
||||
end
|
||||
end
|
3
actionwebservice/lib/action_web_service/vendor/ws/encoding.rb
vendored
Normal file
3
actionwebservice/lib/action_web_service/vendor/ws/encoding.rb
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
require 'ws/encoding/abstract'
|
||||
require 'ws/encoding/soap_rpc_encoding'
|
||||
require 'ws/encoding/xmlrpc_encoding'
|
26
actionwebservice/lib/action_web_service/vendor/ws/encoding/abstract.rb
vendored
Normal file
26
actionwebservice/lib/action_web_service/vendor/ws/encoding/abstract.rb
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
module WS
|
||||
module Encoding
|
||||
# Encoders operate on _foreign_ objects. That is, Ruby object
|
||||
# instances that are the _marshaling format specific_ representation
|
||||
# of objects. In other words, objects that have not yet been marshaled, but
|
||||
# are in protocol-specific form (such as an AST or DOM element), and not
|
||||
# native Ruby form.
|
||||
class AbstractEncoding
|
||||
def encode_rpc_call(method_name, params)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def decode_rpc_call(obj)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def encode_rpc_response(method_name, return_value)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def decode_rpc_response(obj)
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
90
actionwebservice/lib/action_web_service/vendor/ws/encoding/soap_rpc_encoding.rb
vendored
Normal file
90
actionwebservice/lib/action_web_service/vendor/ws/encoding/soap_rpc_encoding.rb
vendored
Normal file
|
@ -0,0 +1,90 @@
|
|||
require 'soap/processor'
|
||||
require 'soap/mapping'
|
||||
require 'soap/rpc/element'
|
||||
|
||||
module WS
|
||||
module Encoding
|
||||
class SoapRpcError < WSError
|
||||
end
|
||||
|
||||
class SoapRpcEncoding < AbstractEncoding
|
||||
attr_accessor :method_namespace
|
||||
|
||||
def initialize(method_namespace='')
|
||||
@method_namespace = method_namespace
|
||||
end
|
||||
|
||||
def encode_rpc_call(method_name, foreign_params)
|
||||
qname = create_method_qname(method_name)
|
||||
param_def = []
|
||||
params = foreign_params.map do |p|
|
||||
param_def << ['in', p.param.info.name, p.param.info.data.mapping]
|
||||
[p.param.info.name, p.soap_object]
|
||||
end
|
||||
request = SOAP::RPC::SOAPMethodRequest.new(qname, param_def)
|
||||
request.set_param(params)
|
||||
envelope = create_soap_envelope(request)
|
||||
SOAP::Processor.marshal(envelope)
|
||||
end
|
||||
|
||||
def decode_rpc_call(obj)
|
||||
envelope = SOAP::Processor.unmarshal(obj)
|
||||
unless envelope
|
||||
raise(SoapRpcError, "Malformed SOAP request")
|
||||
end
|
||||
request = envelope.body.request
|
||||
method_name = request.elename.name
|
||||
params = request.collect do |key, value|
|
||||
info = ParamInfo.new(key, nil, nil)
|
||||
param = Param.new(nil, info)
|
||||
Marshaling::SoapForeignObject.new(param, request[key])
|
||||
end
|
||||
[method_name, params]
|
||||
end
|
||||
|
||||
def encode_rpc_response(method_name, return_value)
|
||||
response = nil
|
||||
qname = create_method_qname(method_name)
|
||||
if return_value.nil?
|
||||
response = SOAP::RPC::SOAPMethodResponse.new(qname, nil)
|
||||
else
|
||||
param = return_value.param
|
||||
soap_object = return_value.soap_object
|
||||
param_def = [['retval', 'return', param.info.data.mapping]]
|
||||
if soap_object.is_a?(SOAP::SOAPFault)
|
||||
response = soap_object
|
||||
else
|
||||
response = SOAP::RPC::SOAPMethodResponse.new(qname, param_def)
|
||||
response.retval = soap_object
|
||||
end
|
||||
end
|
||||
envelope = create_soap_envelope(response)
|
||||
SOAP::Processor.marshal(envelope)
|
||||
end
|
||||
|
||||
def decode_rpc_response(obj)
|
||||
envelope = SOAP::Processor.unmarshal(obj)
|
||||
unless envelope
|
||||
raise(SoapRpcError, "Malformed SOAP response")
|
||||
end
|
||||
method_name = envelope.body.request.elename.name
|
||||
return_value = envelope.body.response
|
||||
info = ParamInfo.new('return', nil, nil)
|
||||
param = Param.new(nil, info)
|
||||
return_value = Marshaling::SoapForeignObject.new(param, return_value)
|
||||
[method_name, return_value]
|
||||
end
|
||||
|
||||
private
|
||||
def create_soap_envelope(body)
|
||||
header = SOAP::SOAPHeader.new
|
||||
body = SOAP::SOAPBody.new(body)
|
||||
SOAP::SOAPEnvelope.new(header, body)
|
||||
end
|
||||
|
||||
def create_method_qname(method_name)
|
||||
XSD::QName.new(@method_namespace, method_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
53
actionwebservice/lib/action_web_service/vendor/ws/encoding/xmlrpc_encoding.rb
vendored
Normal file
53
actionwebservice/lib/action_web_service/vendor/ws/encoding/xmlrpc_encoding.rb
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
require 'xmlrpc/marshal'
|
||||
|
||||
module WS
|
||||
module Encoding
|
||||
class XmlRpcError < WSError
|
||||
end
|
||||
|
||||
class XmlRpcEncoding < AbstractEncoding
|
||||
def encode_rpc_call(method_name, params)
|
||||
XMLRPC::Marshal.dump_call(method_name, *params)
|
||||
end
|
||||
|
||||
def decode_rpc_call(obj)
|
||||
method_name, params = XMLRPC::Marshal.load_call(obj) rescue nil
|
||||
unless method_name && params
|
||||
raise(XmlRpcError, "Malformed XML-RPC request")
|
||||
end
|
||||
i = 0
|
||||
params = params.map do |value|
|
||||
param = XmlRpcDecodedParam.new("param#{i}", value)
|
||||
i += 1
|
||||
param
|
||||
end
|
||||
[method_name, params]
|
||||
end
|
||||
|
||||
def encode_rpc_response(method_name, return_value)
|
||||
if return_value.nil?
|
||||
XMLRPC::Marshal.dump_response(true)
|
||||
else
|
||||
XMLRPC::Marshal.dump_response(return_value)
|
||||
end
|
||||
end
|
||||
|
||||
def decode_rpc_response(obj)
|
||||
return_value = XMLRPC::Marshal.load_response(obj) rescue nil
|
||||
if return_value.nil?
|
||||
raise(XmlRpcError, "Malformed XML-RPC response")
|
||||
end
|
||||
[nil, XmlRpcDecodedParam.new('return', return_value)]
|
||||
end
|
||||
end
|
||||
|
||||
class XmlRpcDecodedParam
|
||||
attr :param
|
||||
|
||||
def initialize(name, value)
|
||||
info = ParamInfo.new(name, value.class)
|
||||
@param = Param.new(value, info)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
3
actionwebservice/lib/action_web_service/vendor/ws/marshaling.rb
vendored
Normal file
3
actionwebservice/lib/action_web_service/vendor/ws/marshaling.rb
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
require 'ws/marshaling/abstract'
|
||||
require 'ws/marshaling/soap_marshaling'
|
||||
require 'ws/marshaling/xmlrpc_marshaling'
|
17
actionwebservice/lib/action_web_service/vendor/ws/marshaling/abstract.rb
vendored
Normal file
17
actionwebservice/lib/action_web_service/vendor/ws/marshaling/abstract.rb
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
module WS
|
||||
module Marshaling
|
||||
class AbstractMarshaler
|
||||
def marshal(param)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def unmarshal(param)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def register_type(type)
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
224
actionwebservice/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb
vendored
Normal file
224
actionwebservice/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb
vendored
Normal file
|
@ -0,0 +1,224 @@
|
|||
require 'soap/mapping'
|
||||
require 'xsd/ns'
|
||||
|
||||
module WS
|
||||
module Marshaling
|
||||
SoapEncodingNS = 'http://schemas.xmlsoap.org/soap/encoding/'
|
||||
|
||||
class SoapError < WSError
|
||||
end
|
||||
|
||||
class SoapMarshaler < AbstractMarshaler
|
||||
attr :registry
|
||||
attr_accessor :type_namespace
|
||||
|
||||
def initialize(type_namespace='')
|
||||
@type_namespace = type_namespace
|
||||
@registry = SOAP::Mapping::Registry.new
|
||||
@spec2binding = {}
|
||||
end
|
||||
|
||||
def marshal(param)
|
||||
if param.info.type.is_a?(Array)
|
||||
(class << param.value; self; end).class_eval do
|
||||
define_method(:arytype) do
|
||||
param.info.data.qname
|
||||
end
|
||||
end
|
||||
end
|
||||
if param.value.is_a?(Exception)
|
||||
detail = SOAP::Mapping::SOAPException.new(param.value)
|
||||
soap_obj = SOAP::SOAPFault.new(
|
||||
SOAP::SOAPString.new('Server'),
|
||||
SOAP::SOAPString.new(param.value.to_s),
|
||||
SOAP::SOAPString.new(self.class.name),
|
||||
SOAP::Mapping.obj2soap(detail))
|
||||
else
|
||||
soap_obj = SOAP::Mapping.obj2soap(param.value, @registry)
|
||||
end
|
||||
SoapForeignObject.new(param, soap_obj)
|
||||
end
|
||||
|
||||
def unmarshal(obj)
|
||||
param = obj.param
|
||||
soap_object = obj.soap_object
|
||||
soap_type = soap_object ? soap_object.type : nil
|
||||
value = soap_object ? SOAP::Mapping.soap2obj(soap_object, @registry) : nil
|
||||
param.value = value
|
||||
param.info.type = value.class
|
||||
mapping = @registry.find_mapped_soap_class(param.info.type) rescue nil
|
||||
if soap_type && soap_type.name == 'Array' && soap_type.namespace == SoapEncodingNS
|
||||
param.info.data = SoapBinding.new(soap_object.arytype, mapping)
|
||||
else
|
||||
param.info.data = SoapBinding.new(soap_type, mapping)
|
||||
end
|
||||
param
|
||||
end
|
||||
|
||||
def register_type(spec)
|
||||
if @spec2binding.has_key?(spec)
|
||||
return @spec2binding[spec]
|
||||
end
|
||||
|
||||
klass = BaseTypes.canonical_param_type_class(spec)
|
||||
if klass.is_a?(Array)
|
||||
type_class = klass[0]
|
||||
else
|
||||
type_class = klass
|
||||
end
|
||||
|
||||
type_binding = nil
|
||||
if (mapping = @registry.find_mapped_soap_class(type_class) rescue nil)
|
||||
qname = mapping[2] ? mapping[2][:type] : nil
|
||||
qname ||= soap_base_type_name(mapping[0])
|
||||
type_binding = SoapBinding.new(qname, mapping)
|
||||
else
|
||||
qname = XSD::QName.new(@type_namespace, soap_type_name(type_class.name))
|
||||
@registry.add(type_class,
|
||||
SOAP::SOAPStruct,
|
||||
typed_struct_factory(type_class),
|
||||
{ :type => qname })
|
||||
mapping = @registry.find_mapped_soap_class(type_class)
|
||||
type_binding = SoapBinding.new(qname, mapping)
|
||||
end
|
||||
|
||||
array_binding = nil
|
||||
if klass.is_a?(Array)
|
||||
array_mapping = @registry.find_mapped_soap_class(Array) rescue nil
|
||||
if (array_mapping && !array_mapping[1].is_a?(SoapTypedArrayFactory)) || array_mapping.nil?
|
||||
@registry.set(Array,
|
||||
SOAP::SOAPArray,
|
||||
SoapTypedArrayFactory.new)
|
||||
array_mapping = @registry.find_mapped_soap_class(Array)
|
||||
end
|
||||
qname = XSD::QName.new(@type_namespace, soap_type_name(type_class.name) + 'Array')
|
||||
array_binding = SoapBinding.new(qname, array_mapping, type_binding)
|
||||
end
|
||||
|
||||
@spec2binding[spec] = array_binding ? array_binding : type_binding
|
||||
end
|
||||
|
||||
protected
|
||||
def typed_struct_factory(type_class)
|
||||
if Object.const_defined?('ActiveRecord')
|
||||
if WS.derived_from?(ActiveRecord::Base, type_class)
|
||||
qname = XSD::QName.new(@type_namespace, soap_type_name(type_class.name))
|
||||
type_class.instance_variable_set('@qname', qname)
|
||||
return SoapActiveRecordStructFactory.new
|
||||
end
|
||||
end
|
||||
SOAP::Mapping::Registry::TypedStructFactory
|
||||
end
|
||||
|
||||
def soap_type_name(type_name)
|
||||
type_name.gsub(/::/, '..')
|
||||
end
|
||||
|
||||
def soap_base_type_name(type)
|
||||
xsd_type = type.ancestors.find{|c| c.const_defined? 'Type'}
|
||||
xsd_type ? xsd_type.const_get('Type') : XSD::XSDAnySimpleType::Type
|
||||
end
|
||||
end
|
||||
|
||||
class SoapForeignObject
|
||||
attr_accessor :param
|
||||
attr_accessor :soap_object
|
||||
|
||||
def initialize(param, soap_object)
|
||||
@param = param
|
||||
@soap_object = soap_object
|
||||
end
|
||||
end
|
||||
|
||||
class SoapBinding
|
||||
attr :qname
|
||||
attr :mapping
|
||||
attr :element_binding
|
||||
|
||||
def initialize(qname, mapping, element_binding=nil)
|
||||
@qname = qname
|
||||
@mapping = mapping
|
||||
@element_binding = element_binding
|
||||
end
|
||||
|
||||
def is_custom_type?
|
||||
is_typed_array? || is_typed_struct?
|
||||
end
|
||||
|
||||
def is_typed_array?
|
||||
@mapping[1].is_a?(WS::Marshaling::SoapTypedArrayFactory)
|
||||
end
|
||||
|
||||
def is_typed_struct?
|
||||
@mapping[1] == SOAP::Mapping::Registry::TypedStructFactory || \
|
||||
@mapping[1].is_a?(WS::Marshaling::SoapActiveRecordStructFactory)
|
||||
end
|
||||
|
||||
def each_member(&block)
|
||||
unless is_typed_struct?
|
||||
raise(SoapError, "not a structured type")
|
||||
end
|
||||
end
|
||||
|
||||
def type_name
|
||||
is_custom_type? ? @qname.name : nil
|
||||
end
|
||||
|
||||
def qualified_type_name(ns=nil)
|
||||
if is_custom_type?
|
||||
"#{ns ? ns : @qname.namespace}:#{@qname.name}"
|
||||
else
|
||||
ns = XSD::NS.new
|
||||
ns.assign(XSD::Namespace, SOAP::XSDNamespaceTag)
|
||||
xsd_klass = mapping[0].ancestors.find{|c| c.const_defined?('Type')}
|
||||
return ns.name(XSD::AnyTypeName) unless xsd_klass
|
||||
ns.name(xsd_klass.const_get('Type'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class SoapActiveRecordStructFactory < SOAP::Mapping::Factory
|
||||
def obj2soap(soap_class, obj, info, map)
|
||||
unless obj.is_a?(ActiveRecord::Base)
|
||||
return nil
|
||||
end
|
||||
soap_obj = soap_class.new(obj.class.instance_variable_get('@qname'))
|
||||
obj.attributes.each do |key, value|
|
||||
soap_obj[key] = SOAP::Mapping._obj2soap(value, map)
|
||||
end
|
||||
soap_obj
|
||||
end
|
||||
|
||||
def soap2obj(obj_class, node, info, map)
|
||||
unless node.type == obj_class.instance_variable_get('@qname')
|
||||
return false
|
||||
end
|
||||
obj = obj_class.new
|
||||
node.each do |key, value|
|
||||
obj[key] = value.data
|
||||
end
|
||||
obj.instance_variable_set('@new_record', false)
|
||||
return true, obj
|
||||
end
|
||||
end
|
||||
|
||||
class SoapTypedArrayFactory < SOAP::Mapping::Factory
|
||||
def obj2soap(soap_class, obj, info, map)
|
||||
unless obj.respond_to?(:arytype)
|
||||
return nil
|
||||
end
|
||||
soap_obj = soap_class.new(SOAP::ValueArrayName, 1, obj.arytype)
|
||||
mark_marshalled_obj(obj, soap_obj)
|
||||
obj.each do |item|
|
||||
child = SOAP::Mapping._obj2soap(item, map)
|
||||
soap_obj.add(child)
|
||||
end
|
||||
soap_obj
|
||||
end
|
||||
|
||||
def soap2obj(obj_class, node, info, map)
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
116
actionwebservice/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb
vendored
Normal file
116
actionwebservice/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb
vendored
Normal file
|
@ -0,0 +1,116 @@
|
|||
module WS
|
||||
module Marshaling
|
||||
class XmlRpcError < WSError
|
||||
end
|
||||
|
||||
class XmlRpcMarshaler < AbstractMarshaler
|
||||
def initialize
|
||||
@caster = BaseTypeCaster.new
|
||||
@spec2binding = {}
|
||||
end
|
||||
|
||||
def marshal(param)
|
||||
transform_outbound(param)
|
||||
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
|
||||
end
|
||||
|
||||
def register_type(spec)
|
||||
if @spec2binding.has_key?(spec)
|
||||
return @spec2binding[spec]
|
||||
end
|
||||
|
||||
klass = BaseTypes.canonical_param_type_class(spec)
|
||||
type_binding = nil
|
||||
if klass.is_a?(Array)
|
||||
type_binding = XmlRpcArrayBinding.new(klass[0])
|
||||
else
|
||||
type_binding = XmlRpcBinding.new(klass)
|
||||
end
|
||||
|
||||
@spec2binding[spec] = type_binding
|
||||
end
|
||||
|
||||
def transform_outbound(param)
|
||||
binding = param.info.data
|
||||
case binding
|
||||
when XmlRpcArrayBinding
|
||||
param.value.map{|x| cast_outbound(x, binding.element_klass)}
|
||||
when XmlRpcBinding
|
||||
cast_outbound(param.value, param.info.type)
|
||||
end
|
||||
end
|
||||
|
||||
def transform_inbound(param)
|
||||
return param.value if param.info.data.nil?
|
||||
binding = param.info.data
|
||||
param.info.type = binding.klass
|
||||
case binding
|
||||
when XmlRpcArrayBinding
|
||||
param.value.map{|x| cast_inbound(x, binding.element_klass)}
|
||||
when XmlRpcBinding
|
||||
cast_inbound(param.value, param.info.type)
|
||||
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
|
||||
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)
|
||||
end
|
||||
obj
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class XmlRpcBinding
|
||||
attr :klass
|
||||
|
||||
def initialize(klass)
|
||||
@klass = klass
|
||||
end
|
||||
end
|
||||
|
||||
class XmlRpcArrayBinding < XmlRpcBinding
|
||||
attr :element_klass
|
||||
|
||||
def initialize(element_klass)
|
||||
super(Array)
|
||||
@element_klass = element_klass
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
162
actionwebservice/lib/action_web_service/vendor/ws/types.rb
vendored
Normal file
162
actionwebservice/lib/action_web_service/vendor/ws/types.rb
vendored
Normal file
|
@ -0,0 +1,162 @@
|
|||
require 'time'
|
||||
require 'date'
|
||||
|
||||
module WS
|
||||
module BaseTypes
|
||||
class << self
|
||||
def type_name_to_class(name)
|
||||
case canonical_type_name(name)
|
||||
when :int
|
||||
Integer
|
||||
when :string
|
||||
String
|
||||
when :bool
|
||||
TrueClass
|
||||
when :float
|
||||
Float
|
||||
when :time
|
||||
Time
|
||||
when :date
|
||||
Date
|
||||
end
|
||||
end
|
||||
|
||||
def class_to_type_name(klass)
|
||||
if WS.derived_from?(Integer, klass) || WS.derived_from?(Fixnum, klass) || WS.derived_from?(Bignum, klass)
|
||||
:int
|
||||
elsif klass == String
|
||||
:string
|
||||
elsif klass == TrueClass || klass == FalseClass
|
||||
:bool
|
||||
elsif WS.derived_from?(Float, klass) || WS.derived_from?(Precision, klass) || WS.derived_from?(Numeric, klass)
|
||||
:float
|
||||
elsif klass == Time || klass == DateTime
|
||||
:time
|
||||
elsif klass == Date
|
||||
:date
|
||||
else
|
||||
raise(TypeError, "#{klass} is not a valid base type")
|
||||
end
|
||||
end
|
||||
|
||||
def base_type?(klass)
|
||||
!(canonical_type_class(klass) rescue nil).nil?
|
||||
end
|
||||
|
||||
def canonical_type_class(klass)
|
||||
type_name_to_class(class_to_type_name(klass))
|
||||
end
|
||||
|
||||
def canonical_param_type_class(spec)
|
||||
klass = spec.is_a?(Hash) ? spec.values[0] : spec
|
||||
array_element_class = klass.is_a?(Array) ? klass[0] : nil
|
||||
klass = array_element_class ? array_element_class : klass
|
||||
klass = type_name_to_class(klass) if klass.is_a?(Symbol) || klass.is_a?(String)
|
||||
base_class = canonical_type_class(klass) rescue nil
|
||||
klass = base_class unless base_class.nil?
|
||||
array_element_class ? [klass] : klass
|
||||
end
|
||||
|
||||
def canonical_param_type_spec(spec)
|
||||
klass = canonical_param_type_class(spec)
|
||||
spec.is_a?(Hash) ? {spec.keys[0]=>klass} : klass
|
||||
end
|
||||
|
||||
def canonical_type_name(name)
|
||||
name = name.to_sym
|
||||
case name
|
||||
when :int, :integer, :fixnum, :bignum
|
||||
:int
|
||||
when :string, :base64
|
||||
:string
|
||||
when :bool, :boolean
|
||||
:bool
|
||||
when :float, :double
|
||||
:float
|
||||
when :time, :datetime, :timestamp
|
||||
:time
|
||||
when :date
|
||||
:date
|
||||
else
|
||||
raise(TypeError, "#{name} is not a valid base type")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Param
|
||||
attr_accessor :value
|
||||
attr_accessor :info
|
||||
|
||||
def initialize(value, info)
|
||||
@value = value
|
||||
@info = info
|
||||
end
|
||||
end
|
||||
|
||||
class ParamInfo
|
||||
attr_accessor :name
|
||||
attr_accessor :type
|
||||
attr_accessor :data
|
||||
|
||||
def initialize(name, type, data=nil)
|
||||
@name = name
|
||||
@type = type
|
||||
@data = data
|
||||
end
|
||||
|
||||
def self.create(spec, index=nil, data=nil)
|
||||
name = spec.is_a?(Hash) ? spec.keys[0].to_s : (index ? "param#{index}" : nil)
|
||||
type = BaseTypes.canonical_param_type_class(spec)
|
||||
ParamInfo.new(name, type, data)
|
||||
end
|
||||
end
|
||||
|
||||
class BaseTypeCaster
|
||||
def initialize
|
||||
@handlers = {}
|
||||
install_handlers
|
||||
end
|
||||
|
||||
def cast(value, klass)
|
||||
type_class = BaseTypes.canonical_type_class(klass)
|
||||
return value unless type_class
|
||||
@handlers[type_class].call(value, type_class)
|
||||
end
|
||||
|
||||
protected
|
||||
def install_handlers
|
||||
handler = method(:cast_base_type)
|
||||
[:int, :string, :bool, :float, :time, :date].each do |name|
|
||||
type = BaseTypes.type_name_to_class(name)
|
||||
@handlers[type] = handler
|
||||
end
|
||||
@handlers[Fixnum] = handler
|
||||
end
|
||||
|
||||
def cast_base_type(value, type_class)
|
||||
desired_class = BaseTypes.canonical_type_class(type_class)
|
||||
value_class = BaseTypes.canonical_type_class(value.class)
|
||||
return value if desired_class == value_class
|
||||
desired_name = BaseTypes.class_to_type_name(desired_class)
|
||||
case desired_name
|
||||
when :int
|
||||
Integer(value)
|
||||
when :string
|
||||
value.to_s
|
||||
when :bool
|
||||
return false if value.nil?
|
||||
value = value.to_s
|
||||
return true if value == 'true'
|
||||
return false if value == 'false'
|
||||
raise(TypeError, "can't convert #{value} to boolean")
|
||||
when :float
|
||||
Float(value)
|
||||
when :time
|
||||
Time.parse(value.to_s)
|
||||
when :date
|
||||
Date.parse(value.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -20,6 +20,7 @@ module ClientTest
|
|||
api_method :struct_pass, :expects => [[Person]], :returns => [:bool]
|
||||
api_method :client_container, :returns => [:int]
|
||||
api_method :named_parameters, :expects => [{:key=>:string}, {:id=>:int}]
|
||||
api_method :thrower
|
||||
end
|
||||
|
||||
class NullLogOut
|
||||
|
@ -29,11 +30,11 @@ module ClientTest
|
|||
class Container < ActionController::Base
|
||||
web_service_api API
|
||||
|
||||
attr :value_void
|
||||
attr :value_normal
|
||||
attr :value_array_return
|
||||
attr :value_struct_pass
|
||||
attr :value_named_parameters
|
||||
attr_accessor :value_void
|
||||
attr_accessor :value_normal
|
||||
attr_accessor :value_array_return
|
||||
attr_accessor :value_struct_pass
|
||||
attr_accessor :value_named_parameters
|
||||
|
||||
def initialize
|
||||
@session = @assigns = {}
|
||||
|
@ -73,12 +74,8 @@ module ClientTest
|
|||
@value_named_parameters = @method_params
|
||||
end
|
||||
|
||||
def protocol_request(request)
|
||||
probe_request_protocol(request)
|
||||
end
|
||||
|
||||
def dispatch_request(protocol_request)
|
||||
dispatch_protocol_request(protocol_request)
|
||||
def thrower
|
||||
raise "Hi"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
294
actionwebservice/test/abstract_dispatcher.rb
Normal file
294
actionwebservice/test/abstract_dispatcher.rb
Normal file
|
@ -0,0 +1,294 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
|
||||
module DispatcherTest
|
||||
class Node < ActiveRecord::Base
|
||||
def initialize(*args)
|
||||
super(*args)
|
||||
@new_record = false
|
||||
end
|
||||
|
||||
class << self
|
||||
def name
|
||||
"Node"
|
||||
end
|
||||
|
||||
def columns(*args)
|
||||
[
|
||||
ActiveRecord::ConnectionAdapters::Column.new('id', 0, 'int'),
|
||||
ActiveRecord::ConnectionAdapters::Column.new('name', nil, 'string'),
|
||||
ActiveRecord::ConnectionAdapters::Column.new('description', nil, 'string'),
|
||||
]
|
||||
end
|
||||
|
||||
def connection
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class API < ActionWebService::API::Base
|
||||
api_method :add, :expects => [:int, :int], :returns => [:int]
|
||||
api_method :interceptee
|
||||
api_method :struct_return, :returns => [[Node]]
|
||||
api_method :void
|
||||
end
|
||||
|
||||
class DirectAPI < ActionWebService::API::Base
|
||||
api_method :add, :expects => [{:a=>:int}, {:b=>:int}], :returns => [:int]
|
||||
api_method :before_filtered
|
||||
api_method :after_filtered, :returns => [[:int]]
|
||||
api_method :struct_return, :returns => [[Node]]
|
||||
api_method :thrower
|
||||
api_method :void
|
||||
end
|
||||
|
||||
class Service < ActionWebService::Base
|
||||
web_service_api API
|
||||
|
||||
before_invocation :do_intercept, :only => [:interceptee]
|
||||
|
||||
attr :added
|
||||
attr :intercepted
|
||||
attr :void_called
|
||||
|
||||
def initialize
|
||||
@void_called = false
|
||||
end
|
||||
|
||||
def add(a, b)
|
||||
@added = a + b
|
||||
end
|
||||
|
||||
def interceptee
|
||||
@intercepted = false
|
||||
end
|
||||
|
||||
def struct_return
|
||||
n1 = Node.new('id' => 1, 'name' => 'node1', 'description' => 'Node 1')
|
||||
n2 = Node.new('id' => 2, 'name' => 'node2', 'description' => 'Node 2')
|
||||
[n1, n2]
|
||||
end
|
||||
|
||||
def void(*args)
|
||||
@void_called = args
|
||||
end
|
||||
|
||||
def do_intercept(name, args)
|
||||
[false, "permission denied"]
|
||||
end
|
||||
end
|
||||
|
||||
class AbstractController < ActionController::Base
|
||||
def generate_wsdl
|
||||
to_wsdl
|
||||
end
|
||||
end
|
||||
|
||||
class DelegatedController < AbstractController
|
||||
web_service_dispatching_mode :delegated
|
||||
|
||||
web_service(:test_service) { @service ||= Service.new; @service }
|
||||
end
|
||||
|
||||
class DirectController < AbstractController
|
||||
web_service_api DirectAPI
|
||||
web_service_dispatching_mode :direct
|
||||
|
||||
before_filter :alwaysfail, :only => [:before_filtered]
|
||||
after_filter :alwaysok, :only => [:after_filtered]
|
||||
|
||||
attr :added
|
||||
attr :before_filter_called
|
||||
attr :before_filter_target_called
|
||||
attr :after_filter_called
|
||||
attr :after_filter_target_called
|
||||
attr :void_called
|
||||
|
||||
def initialize
|
||||
@before_filter_called = false
|
||||
@before_filter_target_called = false
|
||||
@after_filter_called = false
|
||||
@after_filter_target_called = false
|
||||
@void_called = false
|
||||
end
|
||||
|
||||
def add
|
||||
@added = @params['a'] + @params['b']
|
||||
end
|
||||
|
||||
def before_filtered
|
||||
@before_filter_target_called = true
|
||||
end
|
||||
|
||||
def after_filtered
|
||||
@after_filter_target_called = true
|
||||
[5, 6, 7]
|
||||
end
|
||||
|
||||
def thrower
|
||||
raise "Hi, I'm an exception"
|
||||
end
|
||||
|
||||
def struct_return
|
||||
n1 = Node.new('id' => 1, 'name' => 'node1', 'description' => 'Node 1')
|
||||
n2 = Node.new('id' => 2, 'name' => 'node2', 'description' => 'Node 2')
|
||||
[n1, n2]
|
||||
end
|
||||
|
||||
def void
|
||||
@void_called = @method_params
|
||||
end
|
||||
|
||||
protected
|
||||
def alwaysfail
|
||||
@before_filter_called = true
|
||||
false
|
||||
end
|
||||
|
||||
def alwaysok
|
||||
@after_filter_called = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module DispatcherCommonTests
|
||||
def test_direct_dispatching
|
||||
assert_equal(70, do_method_call(@direct_controller, 'Add', 20, 50))
|
||||
assert_equal(70, @direct_controller.added)
|
||||
assert(@direct_controller.void_called == false)
|
||||
case @encoder
|
||||
when WS::Encoding::SoapRpcEncoding
|
||||
assert(do_method_call(@direct_controller, 'Void', 3, 4, 5).nil?)
|
||||
when WS::Encoding::XmlRpcEncoding
|
||||
assert(do_method_call(@direct_controller, 'Void', 3, 4, 5) == true)
|
||||
end
|
||||
assert(@direct_controller.void_called == [])
|
||||
end
|
||||
|
||||
def test_direct_entrypoint
|
||||
assert(@direct_controller.respond_to?(:api))
|
||||
end
|
||||
|
||||
def test_direct_filtering
|
||||
assert_equal(false, @direct_controller.before_filter_called)
|
||||
assert_equal(false, @direct_controller.before_filter_target_called)
|
||||
do_method_call(@direct_controller, 'BeforeFiltered')
|
||||
assert_equal(true, @direct_controller.before_filter_called)
|
||||
assert_equal(false, @direct_controller.before_filter_target_called)
|
||||
assert_equal(false, @direct_controller.after_filter_called)
|
||||
assert_equal(false, @direct_controller.after_filter_target_called)
|
||||
assert_equal([5, 6, 7], do_method_call(@direct_controller, 'AfterFiltered'))
|
||||
assert_equal(true, @direct_controller.after_filter_called)
|
||||
assert_equal(true, @direct_controller.after_filter_target_called)
|
||||
end
|
||||
|
||||
def test_delegated_dispatching
|
||||
assert_equal(130, do_method_call(@delegated_controller, 'Add', 50, 80))
|
||||
service = @delegated_controller.web_service_object(:test_service)
|
||||
assert_equal(130, service.added)
|
||||
@delegated_controller.web_service_exception_reporting = true
|
||||
assert(service.intercepted.nil?)
|
||||
result = do_method_call(@delegated_controller, 'Interceptee')
|
||||
assert(service.intercepted.nil?)
|
||||
assert(is_exception?(result))
|
||||
assert_match(/permission denied/, exception_message(result))
|
||||
result = do_method_call(@delegated_controller, 'NonExistentMethod')
|
||||
assert(is_exception?(result))
|
||||
assert_match(/NonExistentMethod/, exception_message(result))
|
||||
assert(service.void_called == false)
|
||||
case @encoder
|
||||
when WS::Encoding::SoapRpcEncoding
|
||||
assert(do_method_call(@delegated_controller, 'Void', 3, 4, 5).nil?)
|
||||
when WS::Encoding::XmlRpcEncoding
|
||||
assert(do_method_call(@delegated_controller, 'Void', 3, 4, 5) == true)
|
||||
end
|
||||
assert(service.void_called == [])
|
||||
end
|
||||
|
||||
def test_garbage_request
|
||||
[@direct_controller, @delegated_controller].each do |controller|
|
||||
controller.class.web_service_exception_reporting = true
|
||||
send_garbage_request = lambda do
|
||||
request = create_ap_request(controller, 'invalid request body', 'xxx')
|
||||
response = ActionController::TestResponse.new
|
||||
controller.process(request, response)
|
||||
# puts response.body
|
||||
assert(response.headers['Status'] =~ /^500/)
|
||||
end
|
||||
send_garbage_request.call
|
||||
controller.class.web_service_exception_reporting = false
|
||||
send_garbage_request.call
|
||||
end
|
||||
end
|
||||
|
||||
def test_exception_marshaling
|
||||
@direct_controller.web_service_exception_reporting = true
|
||||
result = do_method_call(@direct_controller, 'Thrower')
|
||||
assert(is_exception?(result))
|
||||
assert_equal("Hi, I'm an exception", exception_message(result))
|
||||
@direct_controller.web_service_exception_reporting = false
|
||||
result = do_method_call(@direct_controller, 'Thrower')
|
||||
assert(exception_message(result) != "Hi, I'm an exception")
|
||||
end
|
||||
|
||||
def test_ar_struct_return
|
||||
[@direct_controller, @delegated_controller].each do |controller|
|
||||
result = do_method_call(controller, 'StructReturn')
|
||||
case @encoder
|
||||
when WS::Encoding::SoapRpcEncoding
|
||||
assert(result[0].is_a?(DispatcherTest::Node))
|
||||
assert(result[1].is_a?(DispatcherTest::Node))
|
||||
assert_equal('node1', result[0].name)
|
||||
assert_equal('node2', result[1].name)
|
||||
when WS::Encoding::XmlRpcEncoding
|
||||
assert(result[0].is_a?(Hash))
|
||||
assert(result[1].is_a?(Hash))
|
||||
assert_equal('node1', result[0]['name'])
|
||||
assert_equal('node2', result[1]['name'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def service_name(container)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def exception_message(obj)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def is_exception?(obj)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def do_method_call(container, public_method_name, *params)
|
||||
mode = container.web_service_dispatching_mode
|
||||
case mode
|
||||
when :direct
|
||||
api = container.class.web_service_api
|
||||
when :delegated
|
||||
api = container.web_service_object(service_name(container)).class.web_service_api
|
||||
end
|
||||
method_name = api.api_method_name(public_method_name)
|
||||
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, i, type_binding)
|
||||
params[i] = @marshaler.marshal(WS::Param.new(params[i], info))
|
||||
end
|
||||
body = @encoder.encode_rpc_call(public_method_name, params)
|
||||
# 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)
|
||||
@marshaler.unmarshal(return_value).value
|
||||
end
|
||||
end
|
|
@ -1,58 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
require 'soap/rpc/element'
|
||||
|
||||
class SoapTestError < StandardError
|
||||
end
|
||||
|
||||
class AbstractSoapTest < Test::Unit::TestCase
|
||||
def default_test
|
||||
end
|
||||
|
||||
protected
|
||||
def service_name
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def do_soap_call(public_method_name, *args)
|
||||
mapper = @container.class.soap_mapper
|
||||
param_def = []
|
||||
i = 1
|
||||
args.each do |arg|
|
||||
mapping = mapper.lookup(arg.class)
|
||||
param_def << ["in", "param#{i}", mapping.registry_mapping]
|
||||
i += 1
|
||||
end
|
||||
qname = XSD::QName.new('urn:ActionWebService', public_method_name)
|
||||
request = SOAP::RPC::SOAPMethodRequest.new(qname, param_def)
|
||||
soap_args = []
|
||||
i = 1
|
||||
args.each do |arg|
|
||||
soap_args << ["param#{i}", SOAP::Mapping.obj2soap(arg)]
|
||||
i += 1
|
||||
end
|
||||
request.set_param(soap_args)
|
||||
header = SOAP::SOAPHeader.new
|
||||
body = SOAP::SOAPBody.new(request)
|
||||
envelope = SOAP::SOAPEnvelope.new(header, body)
|
||||
raw_request = SOAP::Processor.marshal(envelope)
|
||||
test_request = ActionController::TestRequest.new
|
||||
test_request.request_parameters['action'] = service_name
|
||||
test_request.env['REQUEST_METHOD'] = "POST"
|
||||
test_request.env['HTTP_CONTENTTYPE'] = 'text/xml'
|
||||
test_request.env['HTTP_SOAPACTION'] = "/soap/#{service_name}/#{public_method_name}"
|
||||
test_request.env['RAW_POST_DATA'] = raw_request
|
||||
test_response = ActionController::TestResponse.new
|
||||
response = yield test_request, test_response
|
||||
raw_body = response.respond_to?(:body) ? response.body : response.raw_body
|
||||
envelope = SOAP::Processor.unmarshal(raw_body)
|
||||
if envelope
|
||||
if envelope.body.response
|
||||
SOAP::Mapping.soap2obj(envelope.body.response)
|
||||
else
|
||||
nil
|
||||
end
|
||||
else
|
||||
raise(SoapTestError, "empty/invalid body from server")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,4 +1,5 @@
|
|||
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
||||
$:.unshift(File.dirname(__FILE__) + '/../lib/action_web_service/vendor')
|
||||
|
||||
require 'test/unit'
|
||||
require 'action_web_service'
|
||||
|
|
|
@ -41,7 +41,7 @@ class TC_API < Test::Unit::TestCase
|
|||
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, Bignum, String], :returns=>nil}, API.api_methods[:class_types])
|
||||
assert_equal({:expects=>[TrueClass, Integer, String], :returns=>nil}, API.api_methods[:class_types])
|
||||
end
|
||||
|
||||
def test_not_instantiable
|
||||
|
@ -49,4 +49,17 @@ class TC_API < Test::Unit::TestCase
|
|||
API.new
|
||||
end
|
||||
end
|
||||
|
||||
def test_api_errors
|
||||
assert_raises(ActionWebService::ActionWebServiceError) do
|
||||
klass = Class.new(ActionWebService::API::Base) do
|
||||
api_method :test, :expects => [ActiveRecord::Base]
|
||||
end
|
||||
end
|
||||
assert_raises(ActionWebService::ActionWebServiceError) do
|
||||
klass = Class.new(ActionWebService::API::Base) do
|
||||
api_method :test, :invalid => [:int]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
3
actionwebservice/test/apis/auto_load_api.rb
Normal file
3
actionwebservice/test/apis/auto_load_api.rb
Normal file
|
@ -0,0 +1,3 @@
|
|||
class AutoLoadAPI < ActionWebService::API::Base
|
||||
api_method :void
|
||||
end
|
2
actionwebservice/test/apis/broken_auto_load_api.rb
Normal file
2
actionwebservice/test/apis/broken_auto_load_api.rb
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
|
|
@ -12,10 +12,10 @@ module ClientSoapTest
|
|||
test_request.env['HTTP_CONTENTTYPE'] = 'text/xml'
|
||||
test_request.env['HTTP_SOAPACTION'] = req.header['soapaction'][0]
|
||||
test_request.env['RAW_POST_DATA'] = req.body
|
||||
protocol_request = @controller.protocol_request(test_request)
|
||||
response = @controller.dispatch_request(protocol_request)
|
||||
response = ActionController::TestResponse.new
|
||||
@controller.process(test_request, response)
|
||||
res.header['content-type'] = 'text/xml'
|
||||
res.body = response.raw_body
|
||||
res.body = response.body
|
||||
rescue Exception => e
|
||||
$stderr.puts e.message
|
||||
$stderr.puts e.backtrace.join("\n")
|
||||
|
@ -24,10 +24,15 @@ module ClientSoapTest
|
|||
|
||||
class ClientContainer < ActionController::Base
|
||||
web_client_api :client, :soap, "http://localhost:#{PORT}/client/api", :api => ClientTest::API
|
||||
web_client_api :invalid, :null, "", :api => true
|
||||
|
||||
def get_client
|
||||
client
|
||||
end
|
||||
|
||||
def get_invalid
|
||||
invalid
|
||||
end
|
||||
end
|
||||
|
||||
class SoapServer < ClientTest::AbstractServer
|
||||
|
@ -83,6 +88,7 @@ class TC_ClientSoap < Test::Unit::TestCase
|
|||
|
||||
def test_client_container
|
||||
assert_equal(50, ClientContainer.new.get_client.client_container)
|
||||
assert(ClientContainer.new.get_invalid.nil?)
|
||||
end
|
||||
|
||||
def test_named_parameters
|
||||
|
@ -90,4 +96,11 @@ class TC_ClientSoap < Test::Unit::TestCase
|
|||
assert(@client.named_parameters("key", 5).nil?)
|
||||
assert_equal(["key", 5], @container.value_named_parameters)
|
||||
end
|
||||
|
||||
def test_capitalized_method_name
|
||||
@container.value_normal = nil
|
||||
assert_equal(5, @client.Normal(5, 6))
|
||||
assert_equal([5, 6], @container.value_normal)
|
||||
@container.value_normal = nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,12 +9,12 @@ module ClientXmlRpcTest
|
|||
test_request = ActionController::TestRequest.new
|
||||
test_request.request_parameters['action'] = req.path.gsub(/^\//, '').split(/\//)[1]
|
||||
test_request.env['REQUEST_METHOD'] = "POST"
|
||||
test_request.env['HTTP_CONTENTTYPE'] = 'text/xml'
|
||||
test_request.env['HTTP_CONTENT_TYPE'] = 'text/xml'
|
||||
test_request.env['RAW_POST_DATA'] = req.body
|
||||
protocol_request = @controller.protocol_request(test_request)
|
||||
response = @controller.dispatch_request(protocol_request)
|
||||
response = ActionController::TestResponse.new
|
||||
@controller.process(test_request, response)
|
||||
res.header['content-type'] = 'text/xml'
|
||||
res.body = response.raw_body
|
||||
res.body = response.body
|
||||
rescue Exception => e
|
||||
$stderr.puts e.message
|
||||
$stderr.puts e.backtrace.join("\n")
|
||||
|
@ -89,4 +89,16 @@ class TC_ClientXmlRpc < Test::Unit::TestCase
|
|||
assert_equal(true, @client.named_parameters("xxx", 7))
|
||||
assert_equal(["xxx", 7], @container.value_named_parameters)
|
||||
end
|
||||
|
||||
def test_exception
|
||||
assert_raises(ActionWebService::Client::ClientError) do
|
||||
assert(@client.thrower)
|
||||
end
|
||||
end
|
||||
|
||||
def test_invalid_signature
|
||||
assert_raises(ActionWebService::Client::ClientError) do
|
||||
@client.normal
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
|
||||
module ContainerTest
|
||||
|
||||
$immediate_service = Object.new
|
||||
$deferred_service = Object.new
|
||||
|
||||
|
@ -22,22 +21,34 @@ module ContainerTest
|
|||
|
||||
class DirectContainer < ActionController::Base
|
||||
web_service_dispatching_mode :direct
|
||||
end
|
||||
end
|
||||
|
||||
class InvalidContainer
|
||||
include ActionWebService::Container::Direct
|
||||
end
|
||||
end
|
||||
|
||||
class TC_Container < Test::Unit::TestCase
|
||||
include ContainerTest
|
||||
|
||||
def setup
|
||||
@delegate_container = ContainerTest::DelegateContainer.new
|
||||
@direct_container = ContainerTest::DirectContainer.new
|
||||
@delegate_container = DelegateContainer.new
|
||||
@direct_container = DirectContainer.new
|
||||
end
|
||||
|
||||
def test_registration
|
||||
assert(ContainerTest::DelegateContainer.has_web_service?(:immediate_service))
|
||||
assert(ContainerTest::DelegateContainer.has_web_service?(:deferred_service))
|
||||
assert(!ContainerTest::DelegateContainer.has_web_service?(:fake_service))
|
||||
assert(DelegateContainer.has_web_service?(:immediate_service))
|
||||
assert(DelegateContainer.has_web_service?(:deferred_service))
|
||||
assert(!DelegateContainer.has_web_service?(:fake_service))
|
||||
assert_raises(ActionWebService::Container::Delegated::ContainerError) do
|
||||
DelegateContainer.web_service('invalid')
|
||||
end
|
||||
end
|
||||
|
||||
def test_service_object
|
||||
assert_raises(ActionWebService::Container::Delegated::ContainerError) do
|
||||
@delegate_container.web_service_object(:nonexistent)
|
||||
end
|
||||
assert(@delegate_container.flag == true)
|
||||
assert(@delegate_container.web_service_object(:immediate_service) == $immediate_service)
|
||||
assert(@delegate_container.previous_flag.nil?)
|
||||
|
@ -48,6 +59,15 @@ class TC_Container < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_direct_container
|
||||
assert(ContainerTest::DirectContainer.web_service_dispatching_mode == :direct)
|
||||
assert(DirectContainer.web_service_dispatching_mode == :direct)
|
||||
end
|
||||
|
||||
def test_validity
|
||||
assert_raises(ActionWebService::Container::Direct::ContainerError) do
|
||||
InvalidContainer.web_service_api :test
|
||||
end
|
||||
assert_raises(ActionWebService::Container::Direct::ContainerError) do
|
||||
InvalidContainer.web_service_api 50.0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
$:.unshift(File.dirname(__FILE__) + '/apis')
|
||||
require File.dirname(__FILE__) + '/abstract_dispatcher'
|
||||
require 'wsdl/parser'
|
||||
|
||||
class AutoLoadController < ActionController::Base; end
|
||||
class FailingAutoLoadController < ActionController::Base; end
|
||||
class BrokenAutoLoadController < ActionController::Base; end
|
||||
|
||||
class TC_DispatcherActionControllerSoap < Test::Unit::TestCase
|
||||
include DispatcherTest
|
||||
include DispatcherCommonTests
|
||||
|
||||
def setup
|
||||
@encoder = WS::Encoding::SoapRpcEncoding.new
|
||||
@marshaler = WS::Marshaling::SoapMarshaler.new
|
||||
@direct_controller = DirectController.new
|
||||
@delegated_controller = DelegatedController.new
|
||||
end
|
||||
|
||||
def test_wsdl_generation
|
||||
ensure_valid_wsdl_generation DelegatedController.new
|
||||
ensure_valid_wsdl_generation DirectController.new
|
||||
end
|
||||
|
||||
def test_wsdl_action
|
||||
ensure_valid_wsdl_action DelegatedController.new
|
||||
ensure_valid_wsdl_action DirectController.new
|
||||
end
|
||||
|
||||
def test_autoloading
|
||||
assert(!AutoLoadController.web_service_api.nil?)
|
||||
assert(AutoLoadController.web_service_api.has_public_api_method?('Void'))
|
||||
assert(FailingAutoLoadController.web_service_api.nil?)
|
||||
assert_raises(LoadError, NameError) do
|
||||
FailingAutoLoadController.require_web_service_api :blah
|
||||
end
|
||||
assert_raises(ArgumentError) do
|
||||
FailingAutoLoadController.require_web_service_api 50.0
|
||||
end
|
||||
assert(BrokenAutoLoadController.web_service_api.nil?)
|
||||
end
|
||||
|
||||
protected
|
||||
def exception_message(soap_fault_exception)
|
||||
soap_fault_exception.detail.cause.message
|
||||
end
|
||||
|
||||
def is_exception?(obj)
|
||||
obj.respond_to?(:detail) && obj.detail.respond_to?(:cause) && \
|
||||
obj.detail.cause.is_a?(Exception)
|
||||
end
|
||||
|
||||
def create_ap_request(container, body, public_method_name, *args)
|
||||
test_request = ActionController::TestRequest.new
|
||||
test_request.request_parameters['action'] = service_name(container)
|
||||
test_request.env['REQUEST_METHOD'] = "POST"
|
||||
test_request.env['HTTP_CONTENTTYPE'] = 'text/xml'
|
||||
test_request.env['HTTP_SOAPACTION'] = "/soap/#{service_name(container)}/#{public_method_name}"
|
||||
test_request.env['RAW_POST_DATA'] = body
|
||||
test_request
|
||||
end
|
||||
|
||||
def service_name(container)
|
||||
container.is_a?(DelegatedController) ? 'test_service' : 'api'
|
||||
end
|
||||
|
||||
def ensure_valid_wsdl_generation(controller)
|
||||
wsdl = controller.generate_wsdl
|
||||
ensure_valid_wsdl(wsdl)
|
||||
end
|
||||
|
||||
def ensure_valid_wsdl(wsdl)
|
||||
definitions = WSDL::Parser.new.parse(wsdl)
|
||||
assert(definitions.is_a?(WSDL::Definitions))
|
||||
definitions.bindings.each do |binding|
|
||||
assert(binding.name.name.index(':').nil?)
|
||||
end
|
||||
definitions.services.each do |service|
|
||||
service.ports.each do |port|
|
||||
assert(port.name.name.index(':').nil?)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_valid_wsdl_action(controller)
|
||||
test_request = ActionController::TestRequest.new({ 'action' => 'wsdl' })
|
||||
test_request.env['REQUEST_METHOD'] = 'GET'
|
||||
test_request.env['HTTP_HOST'] = 'localhost:3000'
|
||||
test_response = ActionController::TestResponse.new
|
||||
wsdl = controller.process(test_request, test_response).body
|
||||
ensure_valid_wsdl(wsdl)
|
||||
end
|
||||
end
|
|
@ -1,186 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_soap'
|
||||
require 'wsdl/parser'
|
||||
|
||||
module DispatcherActionControllerTest
|
||||
class API < ActionWebService::API::Base
|
||||
api_method :add, :expects => [:int, :int], :returns => [:int]
|
||||
end
|
||||
|
||||
class DirectAPI < ActionWebService::API::Base
|
||||
api_method :add, :expects => [{:a=>:int}, {:b=>:int}], :returns => [:int]
|
||||
api_method :before_filtered
|
||||
api_method :after_filtered, :returns => [:int]
|
||||
api_method :thrower
|
||||
end
|
||||
|
||||
class Service < ActionWebService::Base
|
||||
web_service_api API
|
||||
|
||||
attr :added
|
||||
|
||||
def add(a, b)
|
||||
@added = a + b
|
||||
end
|
||||
end
|
||||
|
||||
class AbstractController < ActionController::Base
|
||||
def generate_wsdl(container, uri, soap_action_base)
|
||||
to_wsdl(container, uri, soap_action_base)
|
||||
end
|
||||
end
|
||||
|
||||
class DelegatedController < AbstractController
|
||||
web_service_dispatching_mode :delegated
|
||||
|
||||
web_service(:test_service) { @service ||= Service.new; @service }
|
||||
end
|
||||
|
||||
class DirectController < AbstractController
|
||||
web_service_api DirectAPI
|
||||
web_service_dispatching_mode :direct
|
||||
|
||||
before_filter :alwaysfail, :only => [:before_filtered]
|
||||
after_filter :alwaysok, :only => [:after_filtered]
|
||||
|
||||
attr :added
|
||||
attr :before_filter_called
|
||||
attr :before_filter_target_called
|
||||
attr :after_filter_called
|
||||
attr :after_filter_target_called
|
||||
|
||||
def initialize
|
||||
@before_filter_called = false
|
||||
@before_filter_target_called = false
|
||||
@after_filter_called = false
|
||||
@after_filter_target_called = false
|
||||
end
|
||||
|
||||
def add
|
||||
@added = @params['a'] + @params['b']
|
||||
end
|
||||
|
||||
def before_filtered
|
||||
@before_filter_target_called = true
|
||||
end
|
||||
|
||||
def after_filtered
|
||||
@after_filter_target_called = true
|
||||
5
|
||||
end
|
||||
|
||||
def thrower
|
||||
raise "Hi, I'm a SOAP exception"
|
||||
end
|
||||
|
||||
protected
|
||||
def alwaysfail
|
||||
@before_filter_called = true
|
||||
false
|
||||
end
|
||||
|
||||
def alwaysok
|
||||
@after_filter_called = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TC_DispatcherActionController < AbstractSoapTest
|
||||
include DispatcherActionControllerTest
|
||||
|
||||
def test_direct_dispatching
|
||||
@container = DirectController.new
|
||||
assert(do_soap_call('Add', 20, 50) == 70)
|
||||
assert(@container.added == 70)
|
||||
end
|
||||
|
||||
def test_direct_entrypoint
|
||||
@container = DirectController.new
|
||||
assert(@container.respond_to?(:api))
|
||||
end
|
||||
|
||||
def test_direct_filtering
|
||||
@container = DirectController.new
|
||||
assert(@container.before_filter_called == false)
|
||||
assert(@container.before_filter_target_called == false)
|
||||
assert(do_soap_call('BeforeFiltered').nil?)
|
||||
assert(@container.before_filter_called == true)
|
||||
assert(@container.before_filter_target_called == false)
|
||||
assert(@container.after_filter_called == false)
|
||||
assert(@container.after_filter_target_called == false)
|
||||
assert(do_soap_call('AfterFiltered') == 5)
|
||||
assert(@container.after_filter_called == true)
|
||||
assert(@container.after_filter_target_called == true)
|
||||
end
|
||||
|
||||
def test_delegated_dispatching
|
||||
@container = DelegatedController.new
|
||||
assert(do_soap_call('Add', 50, 80) == 130)
|
||||
assert(service.added == 130)
|
||||
end
|
||||
|
||||
def test_exception_marshaling
|
||||
@container = DirectController.new
|
||||
result = do_soap_call('Thrower')
|
||||
exception = result.detail
|
||||
assert(exception.cause.is_a?(RuntimeError))
|
||||
assert_equal("Hi, I'm a SOAP exception", exception.cause.message)
|
||||
@container.web_service_exception_reporting = false
|
||||
assert_raises(SoapTestError) do
|
||||
do_soap_call('Thrower')
|
||||
end
|
||||
end
|
||||
|
||||
def test_wsdl_generation
|
||||
ensure_valid_wsdl_generation DelegatedController.new
|
||||
ensure_valid_wsdl_generation DirectController.new
|
||||
end
|
||||
|
||||
def
|
||||
|
||||
def test_wsdl_action
|
||||
ensure_valid_wsdl_action DelegatedController.new
|
||||
ensure_valid_wsdl_action DirectController.new
|
||||
end
|
||||
|
||||
protected
|
||||
def service_name
|
||||
@container.is_a?(DelegatedController) ? 'test_service' : 'api'
|
||||
end
|
||||
|
||||
def service
|
||||
@container.web_service_object(:test_service)
|
||||
end
|
||||
|
||||
def do_soap_call(public_method_name, *args)
|
||||
super(public_method_name, *args) do |test_request, test_response|
|
||||
response = @container.process(test_request, test_response)
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_valid_wsdl_generation(controller)
|
||||
wsdl = controller.generate_wsdl(controller, 'http://localhost:3000/test/', '/test')
|
||||
ensure_valid_wsdl(wsdl)
|
||||
end
|
||||
|
||||
def ensure_valid_wsdl(wsdl)
|
||||
definitions = WSDL::Parser.new.parse(wsdl)
|
||||
assert(definitions.is_a?(WSDL::Definitions))
|
||||
definitions.bindings.each do |binding|
|
||||
assert(binding.name.name.index(':').nil?)
|
||||
end
|
||||
definitions.services.each do |service|
|
||||
service.ports.each do |port|
|
||||
assert(port.name.name.index(':').nil?)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_valid_wsdl_action(controller)
|
||||
test_request = ActionController::TestRequest.new({ 'action' => 'wsdl' })
|
||||
test_request.env['REQUEST_METHOD'] = 'GET'
|
||||
test_request.env['HTTP_HOST'] = 'localhost:3000'
|
||||
test_response = ActionController::TestResponse.new
|
||||
wsdl = controller.process(test_request, test_response).body
|
||||
ensure_valid_wsdl(wsdl)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,35 @@
|
|||
require File.dirname(__FILE__) + '/abstract_dispatcher'
|
||||
|
||||
class TC_DispatcherActionControllerXmlRpc < Test::Unit::TestCase
|
||||
include DispatcherTest
|
||||
include DispatcherCommonTests
|
||||
|
||||
def setup
|
||||
@encoder = WS::Encoding::XmlRpcEncoding.new
|
||||
@marshaler = WS::Marshaling::XmlRpcMarshaler.new
|
||||
@direct_controller = DirectController.new
|
||||
@delegated_controller = DelegatedController.new
|
||||
end
|
||||
|
||||
protected
|
||||
def exception_message(xmlrpc_fault_exception)
|
||||
xmlrpc_fault_exception.faultString
|
||||
end
|
||||
|
||||
def is_exception?(obj)
|
||||
obj.is_a?(XMLRPC::FaultException)
|
||||
end
|
||||
|
||||
def create_ap_request(container, body, public_method_name, *args)
|
||||
test_request = ActionController::TestRequest.new
|
||||
test_request.request_parameters['action'] = service_name(container)
|
||||
test_request.env['REQUEST_METHOD'] = "POST"
|
||||
test_request.env['HTTP_CONTENTTYPE'] = 'text/xml'
|
||||
test_request.env['RAW_POST_DATA'] = body
|
||||
test_request
|
||||
end
|
||||
|
||||
def service_name(container)
|
||||
container.is_a?(DelegatedController) ? 'test_service' : 'api'
|
||||
end
|
||||
end
|
|
@ -1,3 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
rcov -x '.*_test\.rb,rubygems,abstract_,/run' ./run
|
||||
rcov -x '.*_test\.rb,rubygems,abstract_,/run,/apis' ./run
|
||||
|
|
|
@ -12,23 +12,46 @@ module InvocationTest
|
|||
api_method :only_two
|
||||
end
|
||||
|
||||
class Interceptor
|
||||
attr :args
|
||||
|
||||
def initialize
|
||||
@args = nil
|
||||
end
|
||||
|
||||
def intercept(*args)
|
||||
@args = args
|
||||
end
|
||||
end
|
||||
|
||||
InterceptorClass = Interceptor.new
|
||||
|
||||
class Service < ActionWebService::Base
|
||||
web_service_api API
|
||||
|
||||
before_invocation :intercept_before, :except => [:no_before]
|
||||
after_invocation :intercept_after, :except => [:no_after]
|
||||
before_invocation :intercept_only, :only => [:only_one, :only_two]
|
||||
prepend_after_invocation :intercept_after_first, :except => [:no_after]
|
||||
prepend_before_invocation :intercept_only, :only => [:only_one, :only_two]
|
||||
after_invocation(:only => [:only_one]) do |*args|
|
||||
args[0].instance_variable_set('@block_invoked', args[1])
|
||||
end
|
||||
after_invocation InterceptorClass, :only => [:only_one]
|
||||
|
||||
attr_accessor :before_invoked
|
||||
attr_accessor :after_invoked
|
||||
attr_accessor :after_first_invoked
|
||||
attr_accessor :only_invoked
|
||||
attr_accessor :block_invoked
|
||||
attr_accessor :invocation_result
|
||||
|
||||
def initialize
|
||||
@before_invoked = nil
|
||||
@after_invoked = nil
|
||||
@after_first_invoked = nil
|
||||
@only_invoked = nil
|
||||
@invocation_result = nil
|
||||
@block_invoked = nil
|
||||
end
|
||||
|
||||
def add(a, b)
|
||||
|
@ -69,6 +92,10 @@ module InvocationTest
|
|||
@after_invoked = name
|
||||
@invocation_result = result
|
||||
end
|
||||
|
||||
def intercept_after_first(name, args, result)
|
||||
@after_first_invoked = name
|
||||
end
|
||||
|
||||
def intercept_only(name, args)
|
||||
raise "Interception error" unless name == :only_one || name == :only_two
|
||||
|
@ -94,11 +121,17 @@ class TC_Invocation < Test::Unit::TestCase
|
|||
|
||||
def test_interceptor_registration
|
||||
assert(InvocationTest::Service.before_invocation_interceptors.length == 2)
|
||||
assert(InvocationTest::Service.after_invocation_interceptors.length == 1)
|
||||
assert(InvocationTest::Service.after_invocation_interceptors.length == 4)
|
||||
assert_equal(:intercept_only, InvocationTest::Service.before_invocation_interceptors[0])
|
||||
assert_equal(:intercept_after_first, InvocationTest::Service.after_invocation_interceptors[0])
|
||||
end
|
||||
|
||||
def test_interception
|
||||
assert(@service.before_invoked.nil? && @service.after_invoked.nil? && @service.only_invoked.nil? && @service.invocation_result.nil?)
|
||||
assert(@service.before_invoked.nil?)
|
||||
assert(@service.after_invoked.nil?)
|
||||
assert(@service.only_invoked.nil?)
|
||||
assert(@service.block_invoked.nil?)
|
||||
assert(@service.invocation_result.nil?)
|
||||
perform_invocation(:add, 20, 50)
|
||||
assert(@service.before_invoked == :add)
|
||||
assert(@service.after_invoked == :add)
|
||||
|
@ -124,6 +157,7 @@ class TC_Invocation < Test::Unit::TestCase
|
|||
def test_interception_except_conditions
|
||||
perform_invocation(:no_before)
|
||||
assert(@service.before_invoked.nil?)
|
||||
assert(@service.after_first_invoked == :no_before)
|
||||
assert(@service.after_invoked == :no_before)
|
||||
assert(@service.invocation_result == 5)
|
||||
@service.before_invoked = @service.after_invoked = @service.invocation_result = nil
|
||||
|
@ -137,6 +171,8 @@ class TC_Invocation < Test::Unit::TestCase
|
|||
assert(@service.only_invoked.nil?)
|
||||
perform_invocation(:only_one)
|
||||
assert(@service.only_invoked == :only_one)
|
||||
assert(@service.block_invoked == :only_one)
|
||||
assert(InvocationTest::InterceptorClass.args[1] == :only_one)
|
||||
@service.only_invoked = nil
|
||||
perform_invocation(:only_two)
|
||||
assert(@service.only_invoked == :only_two)
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
|
||||
|
||||
module Foo
|
||||
include ActionWebService::Protocol
|
||||
|
||||
def self.append_features(base)
|
||||
super
|
||||
base.register_protocol(BodyOnly, FooMinimalProtocol)
|
||||
base.register_protocol(HeaderAndBody, FooMinimalProtocolTwo)
|
||||
base.register_protocol(HeaderAndBody, FooMinimalProtocolTwo)
|
||||
base.register_protocol(HeaderAndBody, FooFullProtocol)
|
||||
end
|
||||
|
||||
class FooFullProtocol < AbstractProtocol
|
||||
def self.create_protocol_request(klass, request)
|
||||
protocol = FooFullProtocol.new klass
|
||||
ActionWebService::Protocol::ProtocolRequest.new(protocol, '', '', '', '')
|
||||
end
|
||||
end
|
||||
|
||||
class FooMinimalProtocol < AbstractProtocol
|
||||
def self.create_protocol_request(klass, request)
|
||||
protocol = FooMinimalProtocol.new klass
|
||||
ActionWebService::Protocol::ProtocolRequest.new(protocol, '', '', '', '')
|
||||
end
|
||||
end
|
||||
|
||||
class FooMinimalProtocolTwo < AbstractProtocol
|
||||
end
|
||||
end
|
||||
|
||||
class ProtocolRegistry
|
||||
include ActionWebService::Protocol::Registry
|
||||
include Foo
|
||||
|
||||
def all_protocols
|
||||
header_and_body_protocols + body_only_protocols
|
||||
end
|
||||
|
||||
def protocol_request
|
||||
probe_request_protocol(nil)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class TC_ProtocolRegistry < Test::Unit::TestCase
|
||||
def test_registration
|
||||
registry = ProtocolRegistry.new
|
||||
assert(registry.all_protocols.length == 4)
|
||||
assert(registry.protocol_request.protocol.is_a?(Foo::FooFullProtocol))
|
||||
end
|
||||
end
|
|
@ -1,252 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_soap'
|
||||
|
||||
module ProtocolSoapTest
|
||||
class Person < ActionWebService::Struct
|
||||
member :id, Integer
|
||||
member :names, [String]
|
||||
member :lastname, String
|
||||
member :deleted, TrueClass
|
||||
|
||||
def ==(other)
|
||||
id == other.id && names == other.names && lastname == other.lastname && deleted == other.deleted
|
||||
end
|
||||
end
|
||||
|
||||
class EmptyAPI < ActionWebService::API::Base
|
||||
end
|
||||
|
||||
class EmptyService < ActionWebService::Base
|
||||
web_service_api EmptyAPI
|
||||
end
|
||||
|
||||
class API < ActionWebService::API::Base
|
||||
api_method :argument_passing, :expects => [{:int=>:int}, {:string=>:string}, {:array=>[:int]}], :returns => [:bool]
|
||||
api_method :array_returner, :returns => [[:int]]
|
||||
api_method :nil_returner
|
||||
api_method :struct_array_returner, :returns => [[Person]]
|
||||
api_method :exception_thrower
|
||||
|
||||
default_api_method :default
|
||||
end
|
||||
|
||||
class Service < ActionWebService::Base
|
||||
web_service_api API
|
||||
|
||||
attr :int
|
||||
attr :string
|
||||
attr :array
|
||||
attr :values
|
||||
attr :person
|
||||
attr :default_args
|
||||
|
||||
def initialize
|
||||
@int = 20
|
||||
@string = "wrong string value"
|
||||
@default_args = nil
|
||||
end
|
||||
|
||||
def argument_passing(int, string, array)
|
||||
@int = int
|
||||
@string = string
|
||||
@array = array
|
||||
true
|
||||
end
|
||||
|
||||
def array_returner
|
||||
@values = [1, 2, 3]
|
||||
end
|
||||
|
||||
def nil_returner
|
||||
nil
|
||||
end
|
||||
|
||||
def struct_array_returner
|
||||
@person = Person.new
|
||||
@person.id = 5
|
||||
@person.names = ["one", "two"]
|
||||
@person.lastname = "test"
|
||||
@person.deleted = false
|
||||
[@person]
|
||||
end
|
||||
|
||||
def exception_thrower
|
||||
raise "Hi, I'm a SOAP error"
|
||||
end
|
||||
|
||||
def default(*args)
|
||||
@default_args = args
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
class AbstractContainer < ActionController::Base
|
||||
wsdl_service_name 'Test'
|
||||
|
||||
def dispatch_request(request)
|
||||
protocol_request = probe_request_protocol(request)
|
||||
dispatch_protocol_request(protocol_request)
|
||||
end
|
||||
end
|
||||
|
||||
class DelegatedContainer < AbstractContainer
|
||||
web_service_dispatching_mode :delegated
|
||||
web_service :protocol_soap_service, Service.new
|
||||
web_service :empty_service, EmptyService.new
|
||||
end
|
||||
|
||||
class DirectContainer < AbstractContainer
|
||||
web_service_api API
|
||||
web_service_dispatching_mode :direct
|
||||
|
||||
attr :int
|
||||
attr :string
|
||||
attr :array
|
||||
attr :values
|
||||
attr :person
|
||||
attr :default_args
|
||||
|
||||
def initialize
|
||||
@int = 20
|
||||
@string = "wrong string value"
|
||||
@default_args = nil
|
||||
end
|
||||
|
||||
def argument_passing
|
||||
@int = @params['int']
|
||||
@string = @params['string']
|
||||
@array = @params['array']
|
||||
true
|
||||
end
|
||||
|
||||
def array_returner
|
||||
@values = [1, 2, 3]
|
||||
end
|
||||
|
||||
def nil_returner
|
||||
nil
|
||||
end
|
||||
|
||||
def struct_array_returner
|
||||
@person = Person.new
|
||||
@person.id = 5
|
||||
@person.names = ["one", "two"]
|
||||
@person.lastname = "test"
|
||||
@person.deleted = false
|
||||
[@person]
|
||||
end
|
||||
|
||||
def exception_thrower
|
||||
raise "Hi, I'm a SOAP error"
|
||||
end
|
||||
|
||||
def default
|
||||
@default_args = @method_params
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
class EmptyContainer < AbstractContainer
|
||||
web_service_dispatching_mode :delegated
|
||||
web_service :empty_service, EmptyService.new
|
||||
end
|
||||
end
|
||||
|
||||
class TC_ProtocolSoap < AbstractSoapTest
|
||||
def setup
|
||||
@delegated_container = ProtocolSoapTest::DelegatedContainer.new
|
||||
@direct_container = ProtocolSoapTest::DirectContainer.new
|
||||
@empty_container = ProtocolSoapTest::EmptyContainer.new
|
||||
end
|
||||
|
||||
def test_argument_passing
|
||||
in_all_containers do
|
||||
assert(do_soap_call('ArgumentPassing', 5, "test string", [true, false]) == true)
|
||||
assert(service.int == 5)
|
||||
assert(service.string == "test string")
|
||||
assert(service.array == [true, false])
|
||||
end
|
||||
end
|
||||
|
||||
def test_array_returner
|
||||
in_all_containers do
|
||||
assert(do_soap_call('ArrayReturner') == [1, 2, 3])
|
||||
assert(service.values == [1, 2, 3])
|
||||
end
|
||||
end
|
||||
|
||||
def test_nil_returner
|
||||
in_all_containers do
|
||||
assert(do_soap_call('NilReturner') == nil)
|
||||
end
|
||||
end
|
||||
|
||||
def test_struct_array_returner
|
||||
in_all_containers do
|
||||
assert(do_soap_call('StructArrayReturner') == [service.person])
|
||||
end
|
||||
end
|
||||
|
||||
def test_nonexistent_method
|
||||
@container = @empty_container
|
||||
assert_raises(ActionWebService::Dispatcher::DispatcherError) do
|
||||
do_soap_call('NonexistentMethod')
|
||||
end
|
||||
end
|
||||
|
||||
def test_exception_thrower
|
||||
in_all_containers do
|
||||
assert_raises(RuntimeError) do
|
||||
do_soap_call('ExceptionThrower')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_default_api_method
|
||||
in_all_containers do
|
||||
assert(do_soap_call('NonExistentMethodName', 50, false).nil?)
|
||||
assert(service.default_args == [50, false])
|
||||
end
|
||||
end
|
||||
|
||||
def test_service_name_setting
|
||||
in_all_containers do
|
||||
assert(ProtocolSoapTest::DelegatedContainer.soap_mapper.custom_namespace == 'urn:Test')
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def service_name
|
||||
case
|
||||
when @container == @direct_container
|
||||
'api'
|
||||
when @container == @delegated_container
|
||||
'protocol_soap_service'
|
||||
when @container == @empty_container
|
||||
'empty_service'
|
||||
end
|
||||
end
|
||||
|
||||
def service
|
||||
case
|
||||
when @container == @direct_container
|
||||
@container
|
||||
when @container == @delegated_container
|
||||
@container.web_service_object(:protocol_soap_service)
|
||||
when @container == @empty_container
|
||||
@container.web_service_object(:empty_service)
|
||||
end
|
||||
end
|
||||
|
||||
def in_all_containers(&block)
|
||||
[@direct_container, @delegated_container].each do |container|
|
||||
@container = container
|
||||
block.call
|
||||
end
|
||||
end
|
||||
|
||||
def do_soap_call(public_method_name, *args)
|
||||
super(public_method_name, *args) do |test_request, test_response|
|
||||
@container.dispatch_request(test_request)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,147 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
require 'xmlrpc/parser'
|
||||
require 'xmlrpc/create'
|
||||
require 'xmlrpc/config'
|
||||
|
||||
module XMLRPC
|
||||
class XmlRpcTestHelper
|
||||
include ParserWriterChooseMixin
|
||||
|
||||
def create_request(methodName, *args)
|
||||
create().methodCall(methodName, *args)
|
||||
end
|
||||
|
||||
def parse_response(response)
|
||||
parser().parseMethodResponse(response)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ProtocolXmlRpcTest
|
||||
class Person < ActionWebService::Struct
|
||||
member :firstname, String
|
||||
member :lastname, String
|
||||
member :active, TrueClass
|
||||
end
|
||||
|
||||
class API < ActionWebService::API::Base
|
||||
api_method :add, :expects => [Integer, Integer], :returns => [Integer]
|
||||
api_method :hash_returner, :returns => [Hash]
|
||||
api_method :array_returner, :returns => [[Integer]]
|
||||
api_method :something_hash, :expects => [Hash]
|
||||
api_method :struct_array_returner, :returns => [[Person]]
|
||||
|
||||
default_api_method :default
|
||||
end
|
||||
|
||||
class Service < ActionWebService::Base
|
||||
web_service_api API
|
||||
|
||||
attr :result
|
||||
attr :hashvalue
|
||||
attr :default_args
|
||||
|
||||
def initialize
|
||||
@result = nil
|
||||
@hashvalue = nil
|
||||
@default_args = nil
|
||||
end
|
||||
|
||||
def add(a, b)
|
||||
@result = a + b
|
||||
end
|
||||
|
||||
def something_hash(hash)
|
||||
@hashvalue = hash
|
||||
end
|
||||
|
||||
def array_returner
|
||||
[1, 2, 3]
|
||||
end
|
||||
|
||||
def hash_returner
|
||||
{'name' => 1, 'value' => 2}
|
||||
end
|
||||
|
||||
def struct_array_returner
|
||||
person = Person.new
|
||||
person.firstname = "John"
|
||||
person.lastname = "Doe"
|
||||
person.active = true
|
||||
[person]
|
||||
end
|
||||
|
||||
def default(*args)
|
||||
@default_args = args
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
$service = Service.new
|
||||
|
||||
class Container < ActionController::Base
|
||||
def protocol_request(request)
|
||||
probe_request_protocol(request)
|
||||
end
|
||||
|
||||
def dispatch_request(protocol_request)
|
||||
dispatch_protocol_request(protocol_request)
|
||||
end
|
||||
|
||||
web_service :xmlrpc, $service
|
||||
web_service_dispatching_mode :delegated
|
||||
end
|
||||
end
|
||||
|
||||
class TC_ProtocolXmlRpc < Test::Unit::TestCase
|
||||
def setup
|
||||
@helper = XMLRPC::XmlRpcTestHelper.new
|
||||
@container = ProtocolXmlRpcTest::Container.new
|
||||
end
|
||||
|
||||
def test_xmlrpc_request_dispatching
|
||||
retval = do_xmlrpc_call('Add', 50, 30)
|
||||
assert(retval == [true, 80])
|
||||
end
|
||||
|
||||
def test_array_returning
|
||||
retval = do_xmlrpc_call('ArrayReturner')
|
||||
assert(retval == [true, [1, 2, 3]])
|
||||
end
|
||||
|
||||
def test_hash_returning
|
||||
retval = do_xmlrpc_call('HashReturner')
|
||||
assert(retval == [true, {'name' => 1, 'value' => 2}])
|
||||
end
|
||||
|
||||
def test_struct_array_returning
|
||||
retval = do_xmlrpc_call('StructArrayReturner')
|
||||
assert(retval == [true, [{"firstname"=>"John", "lastname"=>"Doe", "active"=>true}]])
|
||||
end
|
||||
|
||||
def test_hash_parameter
|
||||
retval = do_xmlrpc_call('SomethingHash', {'name' => 1, 'value' => 2})
|
||||
assert(retval == [true, true])
|
||||
assert($service.hashvalue == {'name' => 1, 'value' => 2})
|
||||
end
|
||||
|
||||
def test_default_api_method
|
||||
retval = do_xmlrpc_call('SomeNonexistentMethod', 'test', [1, 2], {'name'=>'value'})
|
||||
assert(retval == [true, true])
|
||||
assert($service.default_args == ['test', [1, 2], {'name'=>'value'}])
|
||||
end
|
||||
|
||||
private
|
||||
def do_xmlrpc_call(public_method_name, *args)
|
||||
service_name = 'xmlrpc'
|
||||
raw_request = @helper.create_request(public_method_name, *args)
|
||||
test_request = ActionController::TestRequest.new
|
||||
test_request.request_parameters['action'] = service_name
|
||||
test_request.env['REQUEST_METHOD'] = "POST"
|
||||
test_request.env['HTTP_CONTENTTYPE'] = 'text/xml'
|
||||
test_request.env['RAW_POST_DATA'] = raw_request
|
||||
protocol_request = @container.protocol_request(test_request)
|
||||
response = @container.dispatch_request(protocol_request)
|
||||
@helper.parse_response(response.raw_body)
|
||||
end
|
||||
end
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
Dir[File.join(File.dirname(__FILE__), '*_test.rb')].each do |f|
|
||||
require f
|
||||
end
|
||||
require 'test/unit'
|
||||
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
|
||||
|
|
68
actionwebservice/test/ws/abstract_encoding.rb
Normal file
68
actionwebservice/test/ws/abstract_encoding.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
|
||||
module Nested
|
||||
class StructClass
|
||||
attr_accessor :name
|
||||
attr_accessor :version
|
||||
|
||||
def initialize
|
||||
@name = 5
|
||||
@version = "1.0"
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
@name == other.name && @version == other.version
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module EncodingTest
|
||||
def setup
|
||||
@call_signature = [:int, :bool, :string, :float, [:time], Nested::StructClass]
|
||||
@call_params = [1, true, "string", 5.0, [Time.now], Nested::StructClass.new]
|
||||
@response_signature = [:string]
|
||||
@response_param = "hello world"
|
||||
test_setup
|
||||
end
|
||||
|
||||
def test_abstract
|
||||
obj = WS::Encoding::AbstractEncoding.new
|
||||
assert_raises(NotImplementedError) do
|
||||
obj.encode_rpc_call(nil, nil)
|
||||
end
|
||||
assert_raises(NotImplementedError) do
|
||||
obj.decode_rpc_call(nil)
|
||||
end
|
||||
assert_raises(NotImplementedError) do
|
||||
obj.encode_rpc_response(nil, nil)
|
||||
end
|
||||
assert_raises(NotImplementedError) do
|
||||
obj.decode_rpc_response(nil)
|
||||
end
|
||||
end
|
||||
|
||||
def encode_rpc_call(method_name, signature, params)
|
||||
params = params.dup
|
||||
(0..(signature.length-1)).each do |i|
|
||||
type_binding = @marshaler.register_type(signature[i])
|
||||
info = WS::ParamInfo.create(signature[i], i, type_binding)
|
||||
params[i] = @marshaler.marshal(WS::Param.new(params[i], info))
|
||||
end
|
||||
@encoder.encode_rpc_call(method_name, params)
|
||||
end
|
||||
|
||||
def decode_rpc_call(obj)
|
||||
@encoder.decode_rpc_call(obj)
|
||||
end
|
||||
|
||||
def encode_rpc_response(method_name, signature, param)
|
||||
type_binding = @marshaler.register_type(signature[0])
|
||||
info = WS::ParamInfo.create(signature[0], 0, type_binding)
|
||||
param = @marshaler.marshal(WS::Param.new(param, info))
|
||||
@encoder.encode_rpc_response(method_name, param)
|
||||
end
|
||||
|
||||
def decode_rpc_response(obj)
|
||||
@encoder.decode_rpc_response(obj)
|
||||
end
|
||||
end
|
14
actionwebservice/test/ws/abstract_unit.rb
Normal file
14
actionwebservice/test/ws/abstract_unit.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
$:.unshift(File.dirname(File.dirname(__FILE__)) + '/../lib')
|
||||
$:.unshift(File.dirname(File.dirname(__FILE__)) + '/../lib/action_web_service/vendor')
|
||||
puts $:.inspect
|
||||
require 'test/unit'
|
||||
require 'ws'
|
||||
begin
|
||||
require 'active_record'
|
||||
rescue LoadError
|
||||
begin
|
||||
require 'rubygems'
|
||||
require_gem 'activerecord', '>= 1.6.0'
|
||||
rescue LoadError
|
||||
end
|
||||
end
|
3
actionwebservice/test/ws/gencov
Executable file
3
actionwebservice/test/ws/gencov
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
rcov -x '.*_test\.rb,rubygems,abstract_,/run' ./run
|
5
actionwebservice/test/ws/run
Executable file
5
actionwebservice/test/ws/run
Executable file
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
Dir[File.join(File.dirname(__FILE__), '*_test.rb')].each do |f|
|
||||
require f
|
||||
end
|
91
actionwebservice/test/ws/soap_marshaling_test.rb
Normal file
91
actionwebservice/test/ws/soap_marshaling_test.rb
Normal file
|
@ -0,0 +1,91 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
|
||||
module Nested
|
||||
class MyClass
|
||||
attr_accessor :id
|
||||
attr_accessor :name
|
||||
|
||||
def initialize(id, name)
|
||||
@id = id
|
||||
@name = name
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
@id == other.id && @name == other.name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class SoapMarshalingTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@marshaler = WS::Marshaling::SoapMarshaler.new
|
||||
end
|
||||
|
||||
def test_abstract
|
||||
marshaler = WS::Marshaling::AbstractMarshaler.new
|
||||
assert_raises(NotImplementedError) do
|
||||
marshaler.marshal(nil)
|
||||
end
|
||||
assert_raises(NotImplementedError) do
|
||||
marshaler.unmarshal(nil)
|
||||
end
|
||||
assert_equal(nil, marshaler.register_type(nil))
|
||||
end
|
||||
|
||||
def test_marshaling
|
||||
info = WS::ParamInfo.create(Nested::MyClass)
|
||||
param = WS::Param.new(Nested::MyClass.new(2, "name"), info)
|
||||
new_param = @marshaler.unmarshal(@marshaler.marshal(param))
|
||||
assert(param == new_param)
|
||||
end
|
||||
|
||||
def test_exception_marshaling
|
||||
info = WS::ParamInfo.create(RuntimeError)
|
||||
param = WS::Param.new(RuntimeError.new("hello, world"), info)
|
||||
new_param = @marshaler.unmarshal(@marshaler.marshal(param))
|
||||
assert_equal("hello, world", new_param.value.detail.cause.message)
|
||||
end
|
||||
|
||||
def test_registration
|
||||
type_binding1 = @marshaler.register_type(:int)
|
||||
type_binding2 = @marshaler.register_type(:int)
|
||||
assert(type_binding1.equal?(type_binding2))
|
||||
end
|
||||
|
||||
def test_active_record
|
||||
if Object.const_defined?('ActiveRecord')
|
||||
node_class = Class.new(ActiveRecord::Base) do
|
||||
def initialize(*args)
|
||||
super(*args)
|
||||
@new_record = false
|
||||
end
|
||||
|
||||
class << self
|
||||
def name
|
||||
"Node"
|
||||
end
|
||||
|
||||
def columns(*args)
|
||||
[
|
||||
ActiveRecord::ConnectionAdapters::Column.new('id', 0, 'int'),
|
||||
ActiveRecord::ConnectionAdapters::Column.new('name', nil, 'string'),
|
||||
ActiveRecord::ConnectionAdapters::Column.new('email', nil, 'string'),
|
||||
]
|
||||
end
|
||||
|
||||
def connection
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
||||
info = WS::ParamInfo.create(node_class, 0, @marshaler.register_type(node_class))
|
||||
ar_obj = node_class.new('name' => 'hello', 'email' => 'test@test.com')
|
||||
param = WS::Param.new(ar_obj, info)
|
||||
obj = @marshaler.marshal(param)
|
||||
param = @marshaler.unmarshal(obj)
|
||||
new_ar_obj = param.value
|
||||
assert_equal(ar_obj, new_ar_obj)
|
||||
assert(!ar_obj.equal?(new_ar_obj))
|
||||
end
|
||||
end
|
||||
end
|
47
actionwebservice/test/ws/soap_rpc_encoding_test.rb
Normal file
47
actionwebservice/test/ws/soap_rpc_encoding_test.rb
Normal file
|
@ -0,0 +1,47 @@
|
|||
require File.dirname(__FILE__) + '/abstract_encoding'
|
||||
require 'time'
|
||||
|
||||
class SoapRpcEncodingTest < Test::Unit::TestCase
|
||||
include EncodingTest
|
||||
|
||||
def test_setup
|
||||
@encoder = WS::Encoding::SoapRpcEncoding.new
|
||||
@marshaler = WS::Marshaling::SoapMarshaler.new
|
||||
end
|
||||
|
||||
def test_call_encoding_and_decoding
|
||||
obj = encode_rpc_call('DecodeMe', @call_signature, @call_params)
|
||||
method_name, decoded_params = decode_rpc_call(obj)
|
||||
params = decoded_params.map{|x| @marshaler.unmarshal(x).value}
|
||||
assert_equal(method_name, 'DecodeMe')
|
||||
assert_equal(@call_params[0..3], params[0..3])
|
||||
# XXX: DateTime not marshaled correctly yet
|
||||
assert_equal(@call_params[5..-1], params[5..-1])
|
||||
end
|
||||
|
||||
def test_response_encoding_and_decoding_simple
|
||||
obj = encode_rpc_response('DecodeMe', @response_signature, @response_param)
|
||||
method_name, return_value = decode_rpc_response(obj)
|
||||
return_value = @marshaler.unmarshal(return_value).value
|
||||
assert_equal('DecodeMe', method_name)
|
||||
assert_equal(@response_param, return_value)
|
||||
end
|
||||
|
||||
def test_response_encoding_and_decoding_struct
|
||||
struct = Nested::StructClass.new
|
||||
obj = encode_rpc_response('DecodeMe', [Nested::StructClass], struct)
|
||||
method_name, return_value = decode_rpc_response(obj)
|
||||
return_value = @marshaler.unmarshal(return_value).value
|
||||
assert_equal('DecodeMe', method_name)
|
||||
assert_equal(struct, return_value)
|
||||
end
|
||||
|
||||
def test_response_encoding_and_decoding_array
|
||||
struct = Nested::StructClass.new
|
||||
obj = encode_rpc_response('DecodeMe', [[Nested::StructClass]], [struct])
|
||||
method_name, return_value = decode_rpc_response(obj)
|
||||
return_value = @marshaler.unmarshal(return_value).value
|
||||
assert_equal('DecodeMe', method_name)
|
||||
assert_equal([struct], return_value)
|
||||
end
|
||||
end
|
41
actionwebservice/test/ws/types_test.rb
Normal file
41
actionwebservice/test/ws/types_test.rb
Normal file
|
@ -0,0 +1,41 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
|
||||
class TypesTest < Test::Unit::TestCase
|
||||
include WS
|
||||
|
||||
def setup
|
||||
@caster = BaseTypeCaster.new
|
||||
end
|
||||
|
||||
def test_base_types
|
||||
assert_equal(:int, BaseTypes.canonical_type_name(:integer))
|
||||
assert_equal(:int, BaseTypes.canonical_type_name(:fixnum))
|
||||
assert_equal(Integer, BaseTypes.type_name_to_class(:bignum))
|
||||
assert_equal(Date, BaseTypes.type_name_to_class(:date))
|
||||
assert_equal(Time, BaseTypes.type_name_to_class(:timestamp))
|
||||
assert_equal(TrueClass, BaseTypes.type_name_to_class(:bool))
|
||||
assert_equal(:int, BaseTypes.class_to_type_name(Bignum))
|
||||
assert_equal(:bool, BaseTypes.class_to_type_name(FalseClass))
|
||||
assert_equal(Integer, BaseTypes.canonical_type_class(Fixnum))
|
||||
assert_raises(TypeError) do
|
||||
BaseTypes.canonical_type_name(:fake)
|
||||
end
|
||||
end
|
||||
|
||||
def test_casting
|
||||
assert_equal(5, @caster.cast("5", Fixnum))
|
||||
assert_equal('50.0', @caster.cast(50.0, String))
|
||||
assert_equal(true, @caster.cast('true', FalseClass))
|
||||
assert_equal(false, @caster.cast('false', TrueClass))
|
||||
assert_raises(TypeError) do
|
||||
@caster.cast('yes', FalseClass)
|
||||
end
|
||||
assert_equal(3.14159, @caster.cast('3.14159', Float))
|
||||
now1 = Time.new
|
||||
now2 = @caster.cast("#{now1}", Time)
|
||||
assert_equal(now1.tv_sec, now2.tv_sec)
|
||||
date1 = Date.parse('2004-01-01')
|
||||
date2 = @caster.cast("#{date1}", Date)
|
||||
assert_equal(date1, date2)
|
||||
end
|
||||
end
|
34
actionwebservice/test/ws/xmlrpc_encoding_test.rb
Normal file
34
actionwebservice/test/ws/xmlrpc_encoding_test.rb
Normal file
|
@ -0,0 +1,34 @@
|
|||
require File.dirname(__FILE__) + '/abstract_encoding'
|
||||
require 'time'
|
||||
|
||||
class XmlRpcEncodingTest < Test::Unit::TestCase
|
||||
include EncodingTest
|
||||
|
||||
def test_setup
|
||||
@encoder = WS::Encoding::XmlRpcEncoding.new
|
||||
@marshaler = WS::Marshaling::XmlRpcMarshaler.new
|
||||
end
|
||||
|
||||
def test_typed_call_encoding_and_decoding
|
||||
obj = encode_rpc_call('DecodeMe', @call_signature, @call_params)
|
||||
method_name, params = decode_rpc_call(obj)
|
||||
(0..(@call_signature.length-1)).each do |i|
|
||||
params[i] = @marshaler.typed_unmarshal(params[i], @call_signature[i]).value
|
||||
end
|
||||
assert_equal(method_name, 'DecodeMe')
|
||||
assert_equal(@call_params[0..3], params[0..3])
|
||||
assert_equal(@call_params[5..-1], params[5..-1])
|
||||
end
|
||||
|
||||
def test_untyped_call_encoding_and_decoding
|
||||
obj = encode_rpc_call('DecodeMe', @call_signature, @call_params)
|
||||
method_name, params = decode_rpc_call(obj)
|
||||
(0..(@call_signature.length-1)).each do |i|
|
||||
params[i] = @marshaler.unmarshal(params[i]).value
|
||||
end
|
||||
assert_equal(method_name, 'DecodeMe')
|
||||
assert_equal(@call_params[0..3], params[0..3])
|
||||
assert_equal(@call_params[5].name, params[5]['name'])
|
||||
assert_equal(@call_params[5].version, params[5]['version'])
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue