aboutsummaryrefslogtreecommitdiffstats
path: root/actionwebservice
diff options
context:
space:
mode:
Diffstat (limited to 'actionwebservice')
-rw-r--r--actionwebservice/ChangeLog9
-rw-r--r--actionwebservice/Rakefile14
-rw-r--r--actionwebservice/TODO11
-rw-r--r--actionwebservice/lib/action_web_service.rb16
-rw-r--r--actionwebservice/lib/action_web_service/api.rb3
-rw-r--r--actionwebservice/lib/action_web_service/api/base.rb (renamed from actionwebservice/lib/action_web_service/api/abstract.rb)77
-rw-r--r--actionwebservice/lib/action_web_service/base.rb3
-rw-r--r--actionwebservice/lib/action_web_service/client/base.rb13
-rw-r--r--actionwebservice/lib/action_web_service/client/soap_client.rb41
-rw-r--r--actionwebservice/lib/action_web_service/client/xmlrpc_client.rb35
-rw-r--r--actionwebservice/lib/action_web_service/container.rb88
-rw-r--r--actionwebservice/lib/action_web_service/container/action_controller_container.rb (renamed from actionwebservice/lib/action_web_service/api/action_controller.rb)4
-rw-r--r--actionwebservice/lib/action_web_service/container/delegated_container.rb87
-rw-r--r--actionwebservice/lib/action_web_service/container/direct_container.rb70
-rw-r--r--actionwebservice/lib/action_web_service/dispatcher/abstract.rb175
-rw-r--r--actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb315
-rw-r--r--actionwebservice/lib/action_web_service/protocol.rb2
-rw-r--r--actionwebservice/lib/action_web_service/protocol/abstract.rb126
-rw-r--r--actionwebservice/lib/action_web_service/protocol/discovery.rb37
-rw-r--r--actionwebservice/lib/action_web_service/protocol/registry.rb55
-rw-r--r--actionwebservice/lib/action_web_service/protocol/soap_protocol.rb496
-rw-r--r--actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb184
-rw-r--r--actionwebservice/lib/action_web_service/struct.rb4
-rw-r--r--actionwebservice/lib/action_web_service/support/signature.rb100
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws.rb4
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/common.rb8
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/encoding.rb3
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/encoding/abstract.rb26
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/encoding/soap_rpc_encoding.rb90
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/encoding/xmlrpc_encoding.rb53
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/marshaling.rb3
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/marshaling/abstract.rb17
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb224
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb116
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/types.rb162
-rw-r--r--actionwebservice/test/abstract_client.rb19
-rw-r--r--actionwebservice/test/abstract_dispatcher.rb294
-rw-r--r--actionwebservice/test/abstract_soap.rb58
-rw-r--r--actionwebservice/test/abstract_unit.rb1
-rw-r--r--actionwebservice/test/api_test.rb15
-rw-r--r--actionwebservice/test/apis/auto_load_api.rb3
-rw-r--r--actionwebservice/test/apis/broken_auto_load_api.rb2
-rw-r--r--actionwebservice/test/client_soap_test.rb19
-rw-r--r--actionwebservice/test/client_xmlrpc_test.rb20
-rw-r--r--actionwebservice/test/container_test.rb36
-rw-r--r--actionwebservice/test/dispatcher_action_controller_soap_test.rb93
-rw-r--r--actionwebservice/test/dispatcher_action_controller_test.rb186
-rw-r--r--actionwebservice/test/dispatcher_action_controller_xmlrpc_test.rb35
-rwxr-xr-xactionwebservice/test/gencov2
-rw-r--r--actionwebservice/test/invocation_test.rb42
-rw-r--r--actionwebservice/test/protocol_registry_test.rb53
-rw-r--r--actionwebservice/test/protocol_soap_test.rb252
-rw-r--r--actionwebservice/test/protocol_xmlrpc_test.rb147
-rwxr-xr-xactionwebservice/test/run8
-rw-r--r--actionwebservice/test/ws/abstract_encoding.rb68
-rw-r--r--actionwebservice/test/ws/abstract_unit.rb14
-rwxr-xr-xactionwebservice/test/ws/gencov3
-rwxr-xr-xactionwebservice/test/ws/run5
-rw-r--r--actionwebservice/test/ws/soap_marshaling_test.rb91
-rw-r--r--actionwebservice/test/ws/soap_rpc_encoding_test.rb47
-rw-r--r--actionwebservice/test/ws/types_test.rb41
-rw-r--r--actionwebservice/test/ws/xmlrpc_encoding_test.rb34
62 files changed, 2180 insertions, 2079 deletions
diff --git a/actionwebservice/ChangeLog b/actionwebservice/ChangeLog
index d627625b41..fdb2cc89da 100644
--- a/actionwebservice/ChangeLog
+++ b/actionwebservice/ChangeLog
@@ -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
diff --git a/actionwebservice/Rakefile b/actionwebservice/Rakefile
index 76f0ad422e..fff80c3ff5 100644
--- a/actionwebservice/Rakefile
+++ b/actionwebservice/Rakefile
@@ -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:"
diff --git a/actionwebservice/TODO b/actionwebservice/TODO
index 461aa2bcda..b3b4ec2c00 100644
--- a/actionwebservice/TODO
+++ b/actionwebservice/TODO
@@ -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
diff --git a/actionwebservice/lib/action_web_service.rb b/actionwebservice/lib/action_web_service.rb
index 5cf988a0f8..2865dff633 100644
--- a/actionwebservice/lib/action_web_service.rb
+++ b/actionwebservice/lib/action_web_service.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/api.rb b/actionwebservice/lib/action_web_service/api.rb
index 0c71de5654..ab8b696ab4 100644
--- a/actionwebservice/lib/action_web_service/api.rb
+++ b/actionwebservice/lib/action_web_service/api.rb
@@ -1,2 +1 @@
-require 'action_web_service/api/abstract'
-require 'action_web_service/api/action_controller'
+require 'action_web_service/api/base'
diff --git a/actionwebservice/lib/action_web_service/api/abstract.rb b/actionwebservice/lib/action_web_service/api/base.rb
index 0ce68d10f7..952c6baa0d 100644
--- a/actionwebservice/lib/action_web_service/api/abstract.rb
+++ b/actionwebservice/lib/action_web_service/api/base.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/base.rb b/actionwebservice/lib/action_web_service/base.rb
index 42a514716a..16e4d87f41 100644
--- a/actionwebservice/lib/action_web_service/base.rb
+++ b/actionwebservice/lib/action_web_service/base.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/client/base.rb b/actionwebservice/lib/action_web_service/client/base.rb
index 431b78c748..9dada7bf98 100644
--- a/actionwebservice/lib/action_web_service/client/base.rb
+++ b/actionwebservice/lib/action_web_service/client/base.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/client/soap_client.rb b/actionwebservice/lib/action_web_service/client/soap_client.rb
index dfabfd86ee..b93f6475d9 100644
--- a/actionwebservice/lib/action_web_service/client/soap_client.rb
+++ b/actionwebservice/lib/action_web_service/client/soap_client.rb
@@ -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)
diff --git a/actionwebservice/lib/action_web_service/client/xmlrpc_client.rb b/actionwebservice/lib/action_web_service/client/xmlrpc_client.rb
index bb52c20453..dc7ad1517f 100644
--- a/actionwebservice/lib/action_web_service/client/xmlrpc_client.rb
+++ b/actionwebservice/lib/action_web_service/client/xmlrpc_client.rb
@@ -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)
diff --git a/actionwebservice/lib/action_web_service/container.rb b/actionwebservice/lib/action_web_service/container.rb
index f02717579e..13d9d8ab56 100644
--- a/actionwebservice/lib/action_web_service/container.rb
+++ b/actionwebservice/lib/action_web_service/container.rb
@@ -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'
diff --git a/actionwebservice/lib/action_web_service/api/action_controller.rb b/actionwebservice/lib/action_web_service/container/action_controller_container.rb
index 604cbfe704..4dda93ec11 100644
--- a/actionwebservice/lib/action_web_service/api/action_controller.rb
+++ b/actionwebservice/lib/action_web_service/container/action_controller_container.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/container/delegated_container.rb b/actionwebservice/lib/action_web_service/container/delegated_container.rb
new file mode 100644
index 0000000000..674141aab6
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/container/delegated_container.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/container/direct_container.rb b/actionwebservice/lib/action_web_service/container/direct_container.rb
new file mode 100644
index 0000000000..b53c542fc8
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/container/direct_container.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/dispatcher/abstract.rb b/actionwebservice/lib/action_web_service/dispatcher/abstract.rb
index 2262cd1cdd..b7560afc87 100644
--- a/actionwebservice/lib/action_web_service/dispatcher/abstract.rb
+++ b/actionwebservice/lib/action_web_service/dispatcher/abstract.rb
@@ -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)
- 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)
+
+ 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 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
diff --git a/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb b/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb
index 511d00ca44..a35029b40d 100644
--- a/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb
+++ b/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb
@@ -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")
+ 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
- logger.error("No response available") unless logger.nil?
- render_text("Internal protocol error", "500 Internal Server Error")
+ 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 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]
- 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
- @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)
+ def web_service_direct_invoke(invocation)
+ @params ||= {}
+ invocation.method_named_params.each do |name, value|
+ @params[name] = value
+ end
+ @session ||= {}
+ @assigns ||= {}
+ @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(global_service, service)
+ "#{global_service}#{service.to_s.camelize}Binding"
end
- def binding_name_for(wsdl_service_name, service_name)
- "#{wsdl_service_name}#{service_name.to_s.camelize}Binding"
+ 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
diff --git a/actionwebservice/lib/action_web_service/protocol.rb b/actionwebservice/lib/action_web_service/protocol.rb
index b15e850676..053e9cb4be 100644
--- a/actionwebservice/lib/action_web_service/protocol.rb
+++ b/actionwebservice/lib/action_web_service/protocol.rb
@@ -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'
diff --git a/actionwebservice/lib/action_web_service/protocol/abstract.rb b/actionwebservice/lib/action_web_service/protocol/abstract.rb
index 9199dfe33f..f628fc4aee 100644
--- a/actionwebservice/lib/action_web_service/protocol/abstract.rb
+++ b/actionwebservice/lib/action_web_service/protocol/abstract.rb
@@ -1,126 +1,28 @@
module ActionWebService # :nodoc:
module Protocol # :nodoc:
- CheckedMessage = :checked
- UncheckedMessage = :unchecked
-
- class ProtocolError < ActionWebService::ActionWebServiceError # :nodoc:
- 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
+ class ProtocolError < ActionWebService::ActionWebServiceError
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
-
- attr_accessor :content_type
+ class Response
+ attr :body
+ attr :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
diff --git a/actionwebservice/lib/action_web_service/protocol/discovery.rb b/actionwebservice/lib/action_web_service/protocol/discovery.rb
new file mode 100644
index 0000000000..ab51958ed9
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/protocol/discovery.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/protocol/registry.rb b/actionwebservice/lib/action_web_service/protocol/registry.rb
deleted file mode 100644
index 0173673556..0000000000
--- a/actionwebservice/lib/action_web_service/protocol/registry.rb
+++ /dev/null
@@ -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
diff --git a/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb b/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb
index 3c527fea93..f2e761f431 100644
--- a/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb
+++ b/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb
@@ -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:
- 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}"))
- end
-
- def soap_mapper # :nodoc:
- read_inheritable_attribute("soap_mapper")
- end
+module ActionWebService
+ module Protocol
+ module Soap
+ def self.included(base)
+ base.register_protocol(SoapProtocol)
+ base.class_inheritable_option(:wsdl_service_name)
end
-
- class SoapProtocol < AbstractProtocol # :nodoc:
- attr :mapper
-
- def initialize(mapper)
- @mapper = mapper
- 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)
- end
-
- def self.create_protocol_client(api, protocol_name, endpoint_uri, options)
- return nil unless protocol_name.to_s.downcase.to_sym == :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
+
+ class SoapProtocol
+ def initialize
+ @encoder = WS::Encoding::SoapRpcEncoding.new
+ @marshaler = WS::Marshaling::SoapMarshaler.new
+ end
+
+ 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 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
- if protocol_request.checked?
- []
- else
- unmarshal.call
- end
+ return_value = nil
end
+ body = @encoder.encode_rpc_response(method_name, return_value)
+ Response.new(body, 'text/xml')
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')
+ def register_signature_type(spec)
+ @marshaler.register_type(spec)
end
- def marshal_exception(exc)
- ProtocolResponse.new(self, create_exception_response(exc), 'text/xml')
+ def protocol_client(api, protocol_name, endpoint_uri, options)
+ return nil unless protocol_name == :soap
+ ActionWebService::Client::Soap.new(api, endpoint_uri, options)
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
diff --git a/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb b/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb
index 1addccba56..1533a2bdb9 100644
--- a/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb
+++ b/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb
@@ -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
- 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
- ActionWebService::Client::XmlRpc.new(api, endpoint_uri, options)
+ def initialize
+ @encoder = WS::Encoding::XmlRpcEncoding.new
+ @marshaler = WS::Marshaling::XmlRpcMarshaler.new
end
- def initialize(container_class)
- super(container_class)
+ 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 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)
+ 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
- # 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
+ return_value = nil
end
- ProtocolResponse.new(self, raw_response, 'text/xml')
+ body = @encoder.encode_rpc_response(method_name, return_value)
+ Response.new(body, '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 register_signature_type(spec)
+ nil
+ end
- def transform_array_types(signature)
- signature.map{|x| x.is_a?(Array) ? XmlRpcArray.new(x[0]) : x}
- 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
-
- 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
diff --git a/actionwebservice/lib/action_web_service/struct.rb b/actionwebservice/lib/action_web_service/struct.rb
index 5420f4cf49..77f4fbf4aa 100644
--- a/actionwebservice/lib/action_web_service/struct.rb
+++ b/actionwebservice/lib/action_web_service/struct.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/support/signature.rb b/actionwebservice/lib/action_web_service/support/signature.rb
deleted file mode 100644
index 00c62a2232..0000000000
--- a/actionwebservice/lib/action_web_service/support/signature.rb
+++ /dev/null
@@ -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
diff --git a/actionwebservice/lib/action_web_service/vendor/ws.rb b/actionwebservice/lib/action_web_service/vendor/ws.rb
new file mode 100644
index 0000000000..18a32a555e
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/vendor/ws.rb
@@ -0,0 +1,4 @@
+require 'ws/common'
+require 'ws/types'
+require 'ws/marshaling'
+require 'ws/encoding'
diff --git a/actionwebservice/lib/action_web_service/vendor/ws/common.rb b/actionwebservice/lib/action_web_service/vendor/ws/common.rb
new file mode 100644
index 0000000000..4266a7141d
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/vendor/ws/common.rb
@@ -0,0 +1,8 @@
+module WS
+ class WSError < StandardError
+ end
+
+ def self.derived_from?(ancestor, child)
+ child.ancestors.include?(ancestor)
+ end
+end
diff --git a/actionwebservice/lib/action_web_service/vendor/ws/encoding.rb b/actionwebservice/lib/action_web_service/vendor/ws/encoding.rb
new file mode 100644
index 0000000000..790317639b
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/vendor/ws/encoding.rb
@@ -0,0 +1,3 @@
+require 'ws/encoding/abstract'
+require 'ws/encoding/soap_rpc_encoding'
+require 'ws/encoding/xmlrpc_encoding'
diff --git a/actionwebservice/lib/action_web_service/vendor/ws/encoding/abstract.rb b/actionwebservice/lib/action_web_service/vendor/ws/encoding/abstract.rb
new file mode 100644
index 0000000000..257c7d0993
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/vendor/ws/encoding/abstract.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/vendor/ws/encoding/soap_rpc_encoding.rb b/actionwebservice/lib/action_web_service/vendor/ws/encoding/soap_rpc_encoding.rb
new file mode 100644
index 0000000000..f4d2f5a7d6
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/vendor/ws/encoding/soap_rpc_encoding.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/vendor/ws/encoding/xmlrpc_encoding.rb b/actionwebservice/lib/action_web_service/vendor/ws/encoding/xmlrpc_encoding.rb
new file mode 100644
index 0000000000..b38ae81abf
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/vendor/ws/encoding/xmlrpc_encoding.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/vendor/ws/marshaling.rb b/actionwebservice/lib/action_web_service/vendor/ws/marshaling.rb
new file mode 100644
index 0000000000..3a0a2e8cc1
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/vendor/ws/marshaling.rb
@@ -0,0 +1,3 @@
+require 'ws/marshaling/abstract'
+require 'ws/marshaling/soap_marshaling'
+require 'ws/marshaling/xmlrpc_marshaling'
diff --git a/actionwebservice/lib/action_web_service/vendor/ws/marshaling/abstract.rb b/actionwebservice/lib/action_web_service/vendor/ws/marshaling/abstract.rb
new file mode 100644
index 0000000000..53120e1447
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/vendor/ws/marshaling/abstract.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb b/actionwebservice/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb
new file mode 100644
index 0000000000..99e2a7ff28
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb b/actionwebservice/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb
new file mode 100644
index 0000000000..87154f87e1
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb
@@ -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
diff --git a/actionwebservice/lib/action_web_service/vendor/ws/types.rb b/actionwebservice/lib/action_web_service/vendor/ws/types.rb
new file mode 100644
index 0000000000..24b96dc327
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/vendor/ws/types.rb
@@ -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
diff --git a/actionwebservice/test/abstract_client.rb b/actionwebservice/test/abstract_client.rb
index 88f886f05f..0efa1d7ec1 100644
--- a/actionwebservice/test/abstract_client.rb
+++ b/actionwebservice/test/abstract_client.rb
@@ -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
diff --git a/actionwebservice/test/abstract_dispatcher.rb b/actionwebservice/test/abstract_dispatcher.rb
new file mode 100644
index 0000000000..b743afce4c
--- /dev/null
+++ b/actionwebservice/test/abstract_dispatcher.rb
@@ -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
diff --git a/actionwebservice/test/abstract_soap.rb b/actionwebservice/test/abstract_soap.rb
deleted file mode 100644
index 351a4f8479..0000000000
--- a/actionwebservice/test/abstract_soap.rb
+++ /dev/null
@@ -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
diff --git a/actionwebservice/test/abstract_unit.rb b/actionwebservice/test/abstract_unit.rb
index e33c5c9bbe..e8304e3790 100644
--- a/actionwebservice/test/abstract_unit.rb
+++ b/actionwebservice/test/abstract_unit.rb
@@ -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'
diff --git a/actionwebservice/test/api_test.rb b/actionwebservice/test/api_test.rb
index b61a4b57c5..a84726f0b5 100644
--- a/actionwebservice/test/api_test.rb
+++ b/actionwebservice/test/api_test.rb
@@ -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
diff --git a/actionwebservice/test/apis/auto_load_api.rb b/actionwebservice/test/apis/auto_load_api.rb
new file mode 100644
index 0000000000..a35bbe3ff7
--- /dev/null
+++ b/actionwebservice/test/apis/auto_load_api.rb
@@ -0,0 +1,3 @@
+class AutoLoadAPI < ActionWebService::API::Base
+ api_method :void
+end
diff --git a/actionwebservice/test/apis/broken_auto_load_api.rb b/actionwebservice/test/apis/broken_auto_load_api.rb
new file mode 100644
index 0000000000..139597f9cb
--- /dev/null
+++ b/actionwebservice/test/apis/broken_auto_load_api.rb
@@ -0,0 +1,2 @@
+
+
diff --git a/actionwebservice/test/client_soap_test.rb b/actionwebservice/test/client_soap_test.rb
index 5d62b05c82..94a4f24c26 100644
--- a/actionwebservice/test/client_soap_test.rb
+++ b/actionwebservice/test/client_soap_test.rb
@@ -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
diff --git a/actionwebservice/test/client_xmlrpc_test.rb b/actionwebservice/test/client_xmlrpc_test.rb
index cd393fbad6..53b6de51e1 100644
--- a/actionwebservice/test/client_xmlrpc_test.rb
+++ b/actionwebservice/test/client_xmlrpc_test.rb
@@ -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
diff --git a/actionwebservice/test/container_test.rb b/actionwebservice/test/container_test.rb
index 8c66651b64..325d420f24 100644
--- a/actionwebservice/test/container_test.rb
+++ b/actionwebservice/test/container_test.rb
@@ -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
diff --git a/actionwebservice/test/dispatcher_action_controller_soap_test.rb b/actionwebservice/test/dispatcher_action_controller_soap_test.rb
new file mode 100644
index 0000000000..9cb99be78d
--- /dev/null
+++ b/actionwebservice/test/dispatcher_action_controller_soap_test.rb
@@ -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
diff --git a/actionwebservice/test/dispatcher_action_controller_test.rb b/actionwebservice/test/dispatcher_action_controller_test.rb
deleted file mode 100644
index 11ab21c6a6..0000000000
--- a/actionwebservice/test/dispatcher_action_controller_test.rb
+++ /dev/null
@@ -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
diff --git a/actionwebservice/test/dispatcher_action_controller_xmlrpc_test.rb b/actionwebservice/test/dispatcher_action_controller_xmlrpc_test.rb
new file mode 100644
index 0000000000..13f193e2c5
--- /dev/null
+++ b/actionwebservice/test/dispatcher_action_controller_xmlrpc_test.rb
@@ -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
diff --git a/actionwebservice/test/gencov b/actionwebservice/test/gencov
index 144233107a..1faab34c07 100755
--- a/actionwebservice/test/gencov
+++ b/actionwebservice/test/gencov
@@ -1,3 +1,3 @@
#!/bin/sh
-rcov -x '.*_test\.rb,rubygems,abstract_,/run' ./run
+rcov -x '.*_test\.rb,rubygems,abstract_,/run,/apis' ./run
diff --git a/actionwebservice/test/invocation_test.rb b/actionwebservice/test/invocation_test.rb
index 0d519bf770..22752fefaf 100644
--- a/actionwebservice/test/invocation_test.rb
+++ b/actionwebservice/test/invocation_test.rb
@@ -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)
diff --git a/actionwebservice/test/protocol_registry_test.rb b/actionwebservice/test/protocol_registry_test.rb
deleted file mode 100644
index 261f6f400e..0000000000
--- a/actionwebservice/test/protocol_registry_test.rb
+++ /dev/null
@@ -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
diff --git a/actionwebservice/test/protocol_soap_test.rb b/actionwebservice/test/protocol_soap_test.rb
deleted file mode 100644
index c55b7f55af..0000000000
--- a/actionwebservice/test/protocol_soap_test.rb
+++ /dev/null
@@ -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
diff --git a/actionwebservice/test/protocol_xmlrpc_test.rb b/actionwebservice/test/protocol_xmlrpc_test.rb
deleted file mode 100644
index cda0bba6d3..0000000000
--- a/actionwebservice/test/protocol_xmlrpc_test.rb
+++ /dev/null
@@ -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
diff --git a/actionwebservice/test/run b/actionwebservice/test/run
index 5c6f8b2bc2..90ad85fff5 100755
--- a/actionwebservice/test/run
+++ b/actionwebservice/test/run
@@ -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
diff --git a/actionwebservice/test/ws/abstract_encoding.rb b/actionwebservice/test/ws/abstract_encoding.rb
new file mode 100644
index 0000000000..9a6aec44e0
--- /dev/null
+++ b/actionwebservice/test/ws/abstract_encoding.rb
@@ -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
diff --git a/actionwebservice/test/ws/abstract_unit.rb b/actionwebservice/test/ws/abstract_unit.rb
new file mode 100644
index 0000000000..f5015bea69
--- /dev/null
+++ b/actionwebservice/test/ws/abstract_unit.rb
@@ -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
diff --git a/actionwebservice/test/ws/gencov b/actionwebservice/test/ws/gencov
new file mode 100755
index 0000000000..144233107a
--- /dev/null
+++ b/actionwebservice/test/ws/gencov
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+rcov -x '.*_test\.rb,rubygems,abstract_,/run' ./run
diff --git a/actionwebservice/test/ws/run b/actionwebservice/test/ws/run
new file mode 100755
index 0000000000..5c6f8b2bc2
--- /dev/null
+++ b/actionwebservice/test/ws/run
@@ -0,0 +1,5 @@
+#!/usr/bin/env ruby
+
+Dir[File.join(File.dirname(__FILE__), '*_test.rb')].each do |f|
+ require f
+end
diff --git a/actionwebservice/test/ws/soap_marshaling_test.rb b/actionwebservice/test/ws/soap_marshaling_test.rb
new file mode 100644
index 0000000000..ee6413478d
--- /dev/null
+++ b/actionwebservice/test/ws/soap_marshaling_test.rb
@@ -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
diff --git a/actionwebservice/test/ws/soap_rpc_encoding_test.rb b/actionwebservice/test/ws/soap_rpc_encoding_test.rb
new file mode 100644
index 0000000000..e5dcbfeb1b
--- /dev/null
+++ b/actionwebservice/test/ws/soap_rpc_encoding_test.rb
@@ -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
diff --git a/actionwebservice/test/ws/types_test.rb b/actionwebservice/test/ws/types_test.rb
new file mode 100644
index 0000000000..649fdbad7c
--- /dev/null
+++ b/actionwebservice/test/ws/types_test.rb
@@ -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
diff --git a/actionwebservice/test/ws/xmlrpc_encoding_test.rb b/actionwebservice/test/ws/xmlrpc_encoding_test.rb
new file mode 100644
index 0000000000..45aaa901bd
--- /dev/null
+++ b/actionwebservice/test/ws/xmlrpc_encoding_test.rb
@@ -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