aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLeon Breedt <bitserf@gmail.com>2005-02-25 23:39:39 +0000
committerLeon Breedt <bitserf@gmail.com>2005-02-25 23:39:39 +0000
commit6f5a7b200443baf209d2f33c428ed4a4059782f7 (patch)
tree9c3942fe27be69c102873d9fdaa13f66dc12853d
parent10faf204b712763f05a2b3155a4fd9c5338f1fb2 (diff)
downloadrails-6f5a7b200443baf209d2f33c428ed4a4059782f7.tar.gz
rails-6f5a7b200443baf209d2f33c428ed4a4059782f7.tar.bz2
rails-6f5a7b200443baf209d2f33c428ed4a4059782f7.zip
merged the changes for the upcoming 0.6.0:
seperate out protocol marshaling into a small 'ws' library in vendor, so that AWS itself only does integration with ActionPack, and so we can keep protocol specific code in AWS proper to a minimum. refactor unit tests to get 95% code coverage (for a baseline). be far more relaxed about the types given to us by the remote side, don't do any poor man's type checking, just try to cast and marshal to the correct types if possible, and if not, return what they gave us anyway. this should make interoperating with fuzzy XML-RPC clients easier. if exception reporting is turned on, do best-effort error responses, so that we can avoid "Internal protocol error" with no details if there is a bug in AWS itself. also perform extensive cleanups on AWS proper. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@800 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
-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