Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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
  • Loading branch information...
commit 6f5a7b200443baf209d2f33c428ed4a4059782f7 1 parent 10faf20
@bitserf bitserf authored
Showing with 2,180 additions and 2,079 deletions.
  1. +9 −0 actionwebservice/ChangeLog
  2. +7 −7 actionwebservice/Rakefile
  3. +2 −9 actionwebservice/TODO
  4. +10 −6 actionwebservice/lib/action_web_service.rb
  5. +1 −2  actionwebservice/lib/action_web_service/api.rb
  6. +7 −70 actionwebservice/lib/action_web_service/api/{abstract.rb → base.rb}
  7. +0 −3  actionwebservice/lib/action_web_service/base.rb
  8. +1 −12 actionwebservice/lib/action_web_service/client/base.rb
  9. +27 −14 actionwebservice/lib/action_web_service/client/soap_client.rb
  10. +20 −15 actionwebservice/lib/action_web_service/client/xmlrpc_client.rb
  11. +3 −85 actionwebservice/lib/action_web_service/container.rb
  12. +2 −2 ...ebservice/lib/action_web_service/{api/action_controller.rb → container/action_controller_container.rb}
  13. +87 −0 actionwebservice/lib/action_web_service/container/delegated_container.rb
  14. +70 −0 actionwebservice/lib/action_web_service/container/direct_container.rb
  15. +71 −104 actionwebservice/lib/action_web_service/dispatcher/abstract.rb
  16. +180 −135 actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb
  17. +1 −1  actionwebservice/lib/action_web_service/protocol.rb
  18. +14 −112 actionwebservice/lib/action_web_service/protocol/abstract.rb
  19. +37 −0 actionwebservice/lib/action_web_service/protocol/discovery.rb
  20. +0 −55 actionwebservice/lib/action_web_service/protocol/registry.rb
  21. +35 −461 actionwebservice/lib/action_web_service/protocol/soap_protocol.rb
  22. +32 −152 actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb
  23. +1 −3 actionwebservice/lib/action_web_service/struct.rb
  24. +0 −100 actionwebservice/lib/action_web_service/support/signature.rb
  25. +4 −0 actionwebservice/lib/action_web_service/vendor/ws.rb
  26. +8 −0 actionwebservice/lib/action_web_service/vendor/ws/common.rb
  27. +3 −0  actionwebservice/lib/action_web_service/vendor/ws/encoding.rb
  28. +26 −0 actionwebservice/lib/action_web_service/vendor/ws/encoding/abstract.rb
  29. +90 −0 actionwebservice/lib/action_web_service/vendor/ws/encoding/soap_rpc_encoding.rb
  30. +53 −0 actionwebservice/lib/action_web_service/vendor/ws/encoding/xmlrpc_encoding.rb
  31. +3 −0  actionwebservice/lib/action_web_service/vendor/ws/marshaling.rb
  32. +17 −0 actionwebservice/lib/action_web_service/vendor/ws/marshaling/abstract.rb
  33. +224 −0 actionwebservice/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb
  34. +116 −0 actionwebservice/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb
  35. +162 −0 actionwebservice/lib/action_web_service/vendor/ws/types.rb
  36. +8 −11 actionwebservice/test/abstract_client.rb
  37. +294 −0 actionwebservice/test/abstract_dispatcher.rb
  38. +0 −58 actionwebservice/test/abstract_soap.rb
  39. +1 −0  actionwebservice/test/abstract_unit.rb
  40. +14 −1 actionwebservice/test/api_test.rb
  41. +3 −0  actionwebservice/test/apis/auto_load_api.rb
  42. +2 −0  actionwebservice/test/apis/broken_auto_load_api.rb
  43. +16 −3 actionwebservice/test/client_soap_test.rb
  44. +16 −4 actionwebservice/test/client_xmlrpc_test.rb
  45. +28 −8 actionwebservice/test/container_test.rb
  46. +93 −0 actionwebservice/test/dispatcher_action_controller_soap_test.rb
  47. +0 −186 actionwebservice/test/dispatcher_action_controller_test.rb
  48. +35 −0 actionwebservice/test/dispatcher_action_controller_xmlrpc_test.rb
  49. +1 −1  actionwebservice/test/gencov
  50. +39 −3 actionwebservice/test/invocation_test.rb
  51. +0 −53 actionwebservice/test/protocol_registry_test.rb
  52. +0 −252 actionwebservice/test/protocol_soap_test.rb
  53. +0 −147 actionwebservice/test/protocol_xmlrpc_test.rb
  54. +4 −4 actionwebservice/test/run
  55. +68 −0 actionwebservice/test/ws/abstract_encoding.rb
  56. +14 −0 actionwebservice/test/ws/abstract_unit.rb
  57. +3 −0  actionwebservice/test/ws/gencov
  58. +5 −0 actionwebservice/test/ws/run
  59. +91 −0 actionwebservice/test/ws/soap_marshaling_test.rb
  60. +47 −0 actionwebservice/test/ws/soap_rpc_encoding_test.rb
  61. +41 −0 actionwebservice/test/ws/types_test.rb
  62. +34 −0 actionwebservice/test/ws/xmlrpc_encoding_test.rb
View
9 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
View
14 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:"
View
11 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
View
16 actionwebservice/lib/action_web_service.rb
@@ -32,7 +32,10 @@
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/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
View
3  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'
View
77 ...webservice/lib/action_web_service/api/abstract.rb → 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 @@ class Base
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 @@ def api_method(name, options={})
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 @@ def validate_options(valid_option_keys, supplied_option_keys)
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
View
3  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
View
13 actionwebservice/lib/action_web_service/client/base.rb
@@ -12,28 +12,17 @@ def initialize(api, endpoint_uri)
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
View
41 actionwebservice/lib/action_web_service/client/soap_client.rb
@@ -28,10 +28,10 @@ class Soap < Base
# 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 @@ def soap_action(method_name)
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 @@ def create_soap_rpc_driver(api, endpoint_uri)
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)
View
35 actionwebservice/lib/action_web_service/client/xmlrpc_client.rb
@@ -31,6 +31,7 @@ def initialize(api, endpoint_uri, options={})
@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 @@ def perform_invocation(method_name, args)
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 @@ def transform_outgoing_method_params(method_name, params)
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)
View
88 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'
View
4 ...e/lib/action_web_service/api/action_controller.rb → ..._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 @@ def web_client_api(name, protocol, endpoint_uri, options={})
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
View
87 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
View
70 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
View
175 actionwebservice/lib/action_web_service/dispatcher/abstract.rb
@@ -14,136 +14,103 @@ def self.append_features(base) # :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
View
315 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 @@ def self.append_features(base) # :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 @@ def wsdl
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 @@ def to_wsdl(container, uri, soap_action_base)
'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 @@ def to_wsdl(container, uri, soap_action_base)
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
View
2  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'
View
126 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
View
37 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
View
55 actionwebservice/lib/action_web_service/protocol/registry.rb
@@ -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
View
496 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 @@ def self.extract_soap_action(request)
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