aboutsummaryrefslogtreecommitdiffstats
path: root/actionwebservice/lib
diff options
context:
space:
mode:
authorLeon Breedt <bitserf@gmail.com>2005-04-02 21:03:36 +0000
committerLeon Breedt <bitserf@gmail.com>2005-04-02 21:03:36 +0000
commitaaea48fe9826b9e5d2d5b92795a297b8f238c58d (patch)
treee7c01c7f95d467f837c1f96d58dac74c3c902610 /actionwebservice/lib
parentaa09c770e9b5400683be11952673017295246de7 (diff)
downloadrails-aaea48fe9826b9e5d2d5b92795a297b8f238c58d.tar.gz
rails-aaea48fe9826b9e5d2d5b92795a297b8f238c58d.tar.bz2
rails-aaea48fe9826b9e5d2d5b92795a297b8f238c58d.zip
* collapse 'ws' back into protocols, it just added complexity and indirection, and was hard to extend.
* extract casting into seperate support file * ensure casting always does the right thing for return values, should fix interoperability issues with Ecto and possibly other XML-RPC clients * add functional unit tests for scaffolding * represent signature items with classes instead of symbols/Class objects, much more flexible * tweak logging to always show casted versions of parameters and return values, if possible. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1072 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'actionwebservice/lib')
-rw-r--r--actionwebservice/lib/action_web_service.rb4
-rw-r--r--actionwebservice/lib/action_web_service/api/base.rb146
-rw-r--r--actionwebservice/lib/action_web_service/casting.rb105
-rw-r--r--actionwebservice/lib/action_web_service/client/soap_client.rb20
-rw-r--r--actionwebservice/lib/action_web_service/client/xmlrpc_client.rb10
-rw-r--r--actionwebservice/lib/action_web_service/dispatcher/abstract.rb40
-rw-r--r--actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb63
-rw-r--r--actionwebservice/lib/action_web_service/protocol/abstract.rb93
-rw-r--r--actionwebservice/lib/action_web_service/protocol/discovery.rb4
-rw-r--r--actionwebservice/lib/action_web_service/protocol/soap_protocol.rb111
-rw-r--r--actionwebservice/lib/action_web_service/protocol/soap_protocol/marshaler.rb197
-rw-r--r--actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb55
-rw-r--r--actionwebservice/lib/action_web_service/scaffolding.rb43
-rw-r--r--actionwebservice/lib/action_web_service/struct.rb15
-rw-r--r--actionwebservice/lib/action_web_service/support/signature_types.rb191
-rw-r--r--actionwebservice/lib/action_web_service/templates/scaffolds/parameters.rhtml6
-rw-r--r--actionwebservice/lib/action_web_service/test_invoke.rb37
-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.rb44
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/marshaling.rb3
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/marshaling/abstract.rb33
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb283
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb143
-rw-r--r--actionwebservice/lib/action_web_service/vendor/ws/types.rb165
28 files changed, 838 insertions, 1104 deletions
diff --git a/actionwebservice/lib/action_web_service.rb b/actionwebservice/lib/action_web_service.rb
index d8dc132313..82be41a327 100644
--- a/actionwebservice/lib/action_web_service.rb
+++ b/actionwebservice/lib/action_web_service.rb
@@ -35,12 +35,12 @@ end
$:.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/support/signature_types'
require 'action_web_service/base'
require 'action_web_service/client'
require 'action_web_service/invocation'
require 'action_web_service/api'
+require 'action_web_service/casting'
require 'action_web_service/struct'
require 'action_web_service/container'
require 'action_web_service/protocol'
diff --git a/actionwebservice/lib/action_web_service/api/base.rb b/actionwebservice/lib/action_web_service/api/base.rb
index 03e406cfc3..c9fb9f967f 100644
--- a/actionwebservice/lib/action_web_service/api/base.rb
+++ b/actionwebservice/lib/action_web_service/api/base.rb
@@ -1,8 +1,5 @@
module ActionWebService # :nodoc:
module API # :nodoc:
- class CastingError < ActionWebService::ActionWebServiceError
- end
-
# A web service API class specifies the methods that will be available for
# invocation for an API. It also contains metadata such as the method type
# signature hints.
@@ -31,6 +28,8 @@ module ActionWebService # :nodoc:
private_class_method :new, :allocate
class << self
+ include ActionWebService::SignatureTypes
+
# API methods have a +name+, which must be the Ruby method name to use when
# performing the invocation on the web service object.
#
@@ -70,10 +69,9 @@ module ActionWebService # :nodoc:
expects = canonical_signature(expects)
returns = canonical_signature(returns)
if expects
- expects.each do |param|
- klass = WS::BaseTypes.canonical_param_type_class(param)
- klass = klass[0] if klass.is_a?(Array)
- if klass.ancestors.include?(ActiveRecord::Base) && !allow_active_record_expects
+ expects.each do |type|
+ type = type.element_type if type.is_a?(ArrayType)
+ if type.type_class.ancestors.include?(ActiveRecord::Base) && !allow_active_record_expects
raise(ActionWebServiceError, "ActiveRecord model classes not allowed in :expects")
end
end
@@ -138,16 +136,6 @@ module ActionWebService # :nodoc:
instance
end
- # Creates a dummy API Method instance for the given public method name
- def dummy_public_api_method_instance(public_method_name)
- Method.new(public_method_name.underscore.to_sym, public_method_name, nil, nil)
- end
-
- # Creates a dummy API Method instance for the given method name
- def dummy_api_method_instance(method_name)
- Method.new(method_name, public_api_method_name(method_name), nil, nil)
- end
-
private
def api_public_method_names
read_inheritable_attribute("api_public_method_names") || {}
@@ -159,11 +147,6 @@ module ActionWebService # :nodoc:
raise(ActionWebServiceError, "Unknown options: #{unknown_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
@@ -180,134 +163,41 @@ module ActionWebService # :nodoc:
@public_name = public_name
@expects = expects
@returns = returns
+ @caster = ActionWebService::Casting::BaseCaster.new(self)
end
# The list of parameter names for this method
def param_names
return [] unless @expects
- i = 0
- @expects.map{ |spec| param_name(spec, i += 1) }
- end
-
- # The name for the given parameter
- def param_name(spec, i=1)
- spec.is_a?(Hash) ? spec.keys.first.to_s : "p#{i}"
- end
-
- # The type of the parameter declared in +spec+. Is either
- # the Class of the parameter, or its canonical name (if its a
- # base type). Typed array specifications will return the type of
- # their elements.
- def param_type(spec)
- spec = spec.values.first if spec.is_a?(Hash)
- param_type = spec.is_a?(Array) ? spec[0] : spec
- WS::BaseTypes::class_to_type_name(param_type) rescue param_type
- end
-
- # The Class of the parameter declared in +spec+.
- def param_class(spec)
- type = param_type(spec)
- type.is_a?(Symbol) ? WS::BaseTypes.type_name_to_class(type) : type
- end
-
- # Registers all types known to this method with the given marshaler
- def register_types(marshaler)
- @expects.each{ |x| marshaler.register_type(x) } if @expects
- @returns.each{ |x| marshaler.register_type(x) } if @returns
+ @expects.map{ |type| type.name }
end
- # Encodes an RPC call for this method. Casting is performed if
- # the <tt>:strict</tt> option is given.
- def encode_rpc_call(marshaler, encoder, params, options={})
- name = options[:method_name] || @public_name
- expects = @expects || []
- returns = @returns || []
- (expects + returns).each { |spec| marshaler.register_type spec }
- (0..(params.length-1)).each do |i|
- spec = expects[i] || params[i].class
- type_binding = marshaler.lookup_type(spec)
- param_info = WS::ParamInfo.create(spec, type_binding, i)
- if options[:strict]
- value = marshaler.cast_outbound_recursive(params[i], spec)
- else
- value = params[i]
- end
- param = WS::Param.new(value, param_info)
- params[i] = marshaler.marshal(param)
- end
- encoder.encode_rpc_call(name, params)
- end
-
- # Encodes an RPC response for this method. Casting is performed if
- # the <tt>:strict</tt> option is given.
- def encode_rpc_response(marshaler, encoder, return_value, options={})
- if !return_value.nil? && @returns
- return_type = @returns[0]
- type_binding = marshaler.register_type(return_type)
- param_info = WS::ParamInfo.create(return_type, type_binding, 0)
- if options[:strict]
- return_value = marshaler.cast_inbound_recursive(return_value, return_type)
- end
- return_value = marshaler.marshal(WS::Param.new(return_value, param_info))
- else
- return_value = nil
- end
- encoder.encode_rpc_response(response_name(encoder), return_value)
- end
-
- # Casts a set of WS::Param values into the appropriate
- # Ruby values
- def cast_expects_ws2ruby(marshaler, params)
- return [] if @expects.nil?
- i = 0
- @expects.map do |spec|
- value = marshaler.cast_inbound_recursive(params[i].value, spec)
- i += 1
- value
- end
- end
-
# Casts a set of Ruby values into the expected Ruby values
- def cast_expects(marshaler, params)
- return [] if @expects.nil?
- i = 0
- @expects.map do |spec|
- value = marshaler.cast_outbound_recursive(params[i], spec)
- i += 1
- value
- end
+ def cast_expects(params)
+ @caster.cast_expects(params)
end
# Cast a Ruby return value into the expected Ruby value
- def cast_returns(marshaler, return_value)
- return nil if @returns.nil?
- marshaler.cast_inbound_recursive(return_value, @returns[0])
+ def cast_returns(return_value)
+ @caster.cast_returns(return_value)
end
# String representation of this method
def to_s
fqn = ""
- fqn << (@returns ? (friendly_param(@returns[0], nil) + " ") : "void ")
+ fqn << (@returns ? (friendly_param(@returns[0], false) + " ") : "void ")
fqn << "#{@public_name}("
- if @expects
- i = 0
- fqn << @expects.map{ |p| friendly_param(p, i+= 1) }.join(", ")
- end
+ fqn << @expects.map{ |p| friendly_param(p) }.join(", ") if @expects
fqn << ")"
fqn
end
private
- def response_name(encoder)
- encoder.is_a?(WS::Encoding::SoapRpcEncoding) ? (@public_name + "Response") : @public_name
- end
-
- def friendly_param(spec, i)
- name = param_name(spec, i)
- type = param_type(spec)
- spec = spec.values.first if spec.is_a?(Hash)
- type = spec.is_a?(Array) ? (type.to_s + "[]") : type.to_s
- i ? (type + " " + name) : type
+ def friendly_param(type, show_name=true)
+ name = type.name.to_s
+ type_type = type.type.to_s
+ str = type.array?? (type_type + '[]') : type_type
+ show_name ? (str + " " + name) : str
end
end
end
diff --git a/actionwebservice/lib/action_web_service/casting.rb b/actionwebservice/lib/action_web_service/casting.rb
new file mode 100644
index 0000000000..ce90c463d8
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/casting.rb
@@ -0,0 +1,105 @@
+require 'time'
+require 'date'
+require 'generator'
+
+module ActionWebService # :nodoc:
+ module Casting # :nodoc:
+ class CastingError < ActionWebServiceError # :nodoc:
+ end
+
+ # Performs casting of arbitrary values into the correct types for the signature
+ class BaseCaster
+ def initialize(api_method)
+ @api_method = api_method
+ end
+
+ # Coerces the parameters in +params+ (an Enumerable) into the types
+ # this method expects
+ def cast_expects(params)
+ self.class.cast_expects(@api_method, params)
+ end
+
+ # Coerces the given +return_value+ into the the type returned by this
+ # method
+ def cast_returns(return_value)
+ self.class.cast_returns(@api_method, return_value)
+ end
+
+ class << self
+ include ActionWebService::SignatureTypes
+
+ def cast_expects(api_method, params) # :nodoc:
+ return [] if api_method.expects.nil?
+ SyncEnumerator.new(params, api_method.expects).map{ |r| cast(r[0], r[1]) }
+ end
+
+ def cast_returns(api_method, return_value) # :nodoc:
+ return nil if api_method.returns.nil?
+ cast(return_value, api_method.returns[0])
+ end
+
+ def cast(value, signature_type) # :nodoc:
+ return value if signature_type.nil? # signature.length != params.length
+ unless signature_type.array?
+ return value if canonical_type(value.class) == signature_type.type
+ end
+ if signature_type.array?
+ unless value.respond_to?(:entries) && !value.is_a?(String)
+ raise CastingError, "Don't know how to cast #{value.class} into #{signature_type.type.inspect}"
+ end
+ value.entries.map do |entry|
+ cast(entry, signature_type.element_type)
+ end
+ elsif signature_type.structured?
+ cast_to_structured_type(value, signature_type)
+ elsif !signature_type.custom?
+ cast_base_type(value, signature_type)
+ end
+ end
+
+ def cast_base_type(value, signature_type) # :nodoc:
+ case signature_type.type
+ when :int
+ Integer(value)
+ when :string
+ value.to_s
+ when :bool
+ return false if value.nil?
+ return value if value == true || value == false
+ case value.to_s.downcase
+ when '1', 'true', 'y', 'yes'
+ true
+ when '0', 'false', 'n', 'no'
+ false
+ else
+ raise CastingError, "Don't know how to cast #{value.class} into Boolean"
+ end
+ when :float
+ Float(value)
+ when :time
+ Time.parse(value.to_s)
+ when :date
+ Date.parse(value.to_s)
+ when :datetime
+ DateTime.parse(value.to_s)
+ end
+ end
+
+ def cast_to_structured_type(value, signature_type) # :nodoc:
+ obj = signature_type.type_class.new
+ if value.respond_to?(:each_pair)
+ klass = signature_type.type_class
+ value.each_pair do |name, val|
+ type = klass.respond_to?(:member_type) ? klass.member_type(name) : nil
+ val = cast(val, type) if type
+ obj.send("#{name}=", val)
+ end
+ else
+ raise CastingError, "Don't know how to cast #{value.class} to #{signature_type.type_class}"
+ end
+ obj
+ end
+ 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 b9eb0d11ad..c906a71331 100644
--- a/actionwebservice/lib/action_web_service/client/soap_client.rb
+++ b/actionwebservice/lib/action_web_service/client/soap_client.rb
@@ -46,8 +46,7 @@ module ActionWebService # :nodoc:
@type_namespace = options[:type_namespace] || 'urn:ActionWebService'
@method_namespace = options[:method_namespace] || 'urn:ActionWebService'
@driver_options = options[:driver_options] || {}
- @marshaler = WS::Marshaling::SoapMarshaler.new @type_namespace
- @encoder = WS::Encoding::SoapRpcEncoding.new @method_namespace
+ @protocol = ActionWebService::Protocol::Soap::SoapProtocol.new
@soap_action_base = options[:soap_action_base]
@soap_action_base ||= URI.parse(endpoint_uri).path
@driver = create_soap_rpc_driver(api, endpoint_uri)
@@ -59,9 +58,9 @@ module ActionWebService # :nodoc:
protected
def perform_invocation(method_name, args)
method = @api.api_methods[method_name.to_sym]
- args = method.cast_expects(@marshaler, args)
+ args = method.cast_expects(args.dup) rescue args
return_value = @driver.send(method_name, *args)
- method.cast_returns(@marshaler, return_value)
+ method.cast_returns(return_value.dup) rescue return_value
end
def soap_action(method_name)
@@ -70,9 +69,9 @@ module ActionWebService # :nodoc:
private
def create_soap_rpc_driver(api, endpoint_uri)
- api.api_methods.each{ |name, method| method.register_types(@marshaler) }
+ @protocol.register_api(api)
driver = SoapDriver.new(endpoint_uri, nil)
- driver.mapping_registry = @marshaler.registry
+ driver.mapping_registry = @protocol.marshaler.registry
api.api_methods.each do |name, method|
qname = XSD::QName.new(@method_namespace, method.public_name)
action = soap_action(method.public_name)
@@ -81,15 +80,14 @@ module ActionWebService # :nodoc:
param_def = []
i = 0
if expects
- expects.each do |spec|
- param_name = method.param_name(spec, i)
- type_binding = @marshaler.lookup_type(spec)
- param_def << ['in', param_name, type_binding.mapping]
+ expects.each do |type|
+ type_binding = @protocol.marshaler.lookup_type(type)
+ param_def << ['in', type.name, type_binding.mapping]
i += 1
end
end
if returns
- type_binding = @marshaler.lookup_type(returns[0])
+ type_binding = @protocol.marshaler.lookup_type(returns[0])
param_def << ['retval', 'return', type_binding.mapping]
end
driver.add_method(qname, action, method.name.to_s, 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 e0b7efc864..42b5c5d4f9 100644
--- a/actionwebservice/lib/action_web_service/client/xmlrpc_client.rb
+++ b/actionwebservice/lib/action_web_service/client/xmlrpc_client.rb
@@ -30,20 +30,22 @@ module ActionWebService # :nodoc:
def initialize(api, endpoint_uri, options={})
@api = api
@handler_name = options[:handler_name]
+ @protocol = ActionWebService::Protocol::XmlRpc::XmlRpcProtocol.new
@client = XMLRPC::Client.new2(endpoint_uri, options[:proxy], options[:timeout])
- @marshaler = WS::Marshaling::XmlRpcMarshaler.new
end
protected
def perform_invocation(method_name, args)
method = @api.api_methods[method_name.to_sym]
- method.register_types(@marshaler)
if method.expects && method.expects.length != args.length
raise(ArgumentError, "#{method.public_name}: wrong number of arguments (#{args.length} for #{method.expects.length})")
end
- args = method.cast_expects(@marshaler, args)
+ args = method.cast_expects(args.dup) rescue args
+ if method.expects
+ method.expects.each_with_index{ |type, i| args[i] = @protocol.value_to_xmlrpc_wire_format(args[i], type) }
+ end
ok, return_value = @client.call2(public_name(method_name), *args)
- return method.cast_returns(@marshaler, return_value) if ok
+ return (method.cast_returns(return_value.dup) rescue return_value) if ok
raise(ClientError, "#{return_value.faultCode}: #{return_value.faultString}")
end
diff --git a/actionwebservice/lib/action_web_service/dispatcher/abstract.rb b/actionwebservice/lib/action_web_service/dispatcher/abstract.rb
index 69c3b9de3b..975120212f 100644
--- a/actionwebservice/lib/action_web_service/dispatcher/abstract.rb
+++ b/actionwebservice/lib/action_web_service/dispatcher/abstract.rb
@@ -12,14 +12,6 @@ module ActionWebService # :nodoc:
base.send(:include, ActionWebService::Dispatcher::InstanceMethods)
end
- def self.layered_service_name(public_method_name) # :nodoc:
- if public_method_name =~ /^([^\.]+)\.(.*)$/
- $1
- else
- nil
- end
- end
-
module InstanceMethods # :nodoc:
private
def invoke_web_service_request(protocol_request)
@@ -40,13 +32,7 @@ module ActionWebService # :nodoc:
else
return_value = self.__send__(invocation.api_method.name)
end
- if invocation.api.has_api_method?(invocation.api_method.name)
- api_method = invocation.api_method
- else
- api_method = invocation.api_method.dup
- api_method.instance_eval{ @returns = [ return_value.class ] }
- end
- invocation.protocol.marshal_response(api_method, return_value)
+ web_service_create_response(invocation.protocol, invocation.api, invocation.api_method, return_value)
end
def web_service_delegated_invoke(invocation)
@@ -57,7 +43,7 @@ module ActionWebService # :nodoc:
if cancellation_reason
raise(DispatcherError, "request canceled: #{cancellation_reason}")
end
- invocation.protocol.marshal_response(invocation.api_method, return_value)
+ web_service_create_response(invocation.protocol, invocation.api, invocation.api_method, return_value)
end
def web_service_invocation(request)
@@ -79,6 +65,7 @@ module ActionWebService # :nodoc:
invocation.service = web_service_object(invocation.service_name)
invocation.api = invocation.service.class.web_service_api
end
+ invocation.protocol.register_api(invocation.api)
request.api = invocation.api
if invocation.api.has_public_api_method?(public_method_name)
invocation.api_method = invocation.api.public_api_method_instance(public_method_name)
@@ -89,15 +76,20 @@ module ActionWebService # :nodoc:
invocation.api_method = invocation.api.default_api_method_instance
end
end
+ if invocation.service.nil?
+ raise(DispatcherError, "no service available for service name #{invocation.service_name}")
+ end
unless invocation.service.respond_to?(invocation.api_method.name)
- raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api} (#{invocation.api_method.name})")
+ raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api} (#{invocation.api_method.name})")
end
request.api_method = invocation.api_method
begin
- invocation.method_ordered_params = invocation.api_method.cast_expects_ws2ruby(request.protocol.marshaler, request.method_params)
+ invocation.method_ordered_params = invocation.api_method.cast_expects(request.method_params.dup)
rescue
- invocation.method_ordered_params = request.method_params.map{ |x| x.value }
+ logger.warn "Casting of method parameters failed" unless logger.nil?
+ invocation.method_ordered_params = request.method_params
end
+ request.method_params = invocation.method_ordered_params
invocation.method_named_params = {}
invocation.api_method.param_names.inject(0) do |m, n|
invocation.method_named_params[n] = invocation.method_ordered_params[m]
@@ -106,6 +98,16 @@ module ActionWebService # :nodoc:
invocation
end
+ def web_service_create_response(protocol, api, api_method, return_value)
+ if api.has_api_method?(api_method.name)
+ return_type = api_method.returns ? api_method.returns[0] : nil
+ return_value = api_method.cast_returns(return_value)
+ else
+ return_type = ActionWebService::SignatureTypes.canonical_signature_entry(return_value.class, 0)
+ end
+ protocol.encode_response(api_method.public_name + 'Response', return_value, return_type)
+ end
+
class Invocation # :nodoc:
attr_accessor :protocol
attr_accessor :service_name
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 0140039c49..a4659e5183 100644
--- a/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb
+++ b/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb
@@ -45,7 +45,6 @@ module ActionWebService # :nodoc:
exception = e
end
if request
- log_request(request, @request.raw_post)
response = nil
exception = nil
bm = Benchmark.measure do
@@ -55,6 +54,7 @@ module ActionWebService # :nodoc:
exception = e
end
end
+ log_request(request, @request.raw_post)
if exception
log_error(exception) unless logger.nil?
send_web_service_error_response(request, exception)
@@ -82,10 +82,10 @@ module ActionWebService # :nodoc:
unless self.class.web_service_exception_reporting
exception = DispatcherError.new("Internal server error (exception raised)")
end
- api_method = request.api_method ? request.api_method.dup : nil
- api_method ||= request.api.dummy_api_method_instance(request.method_name)
- api_method.instance_eval{ @returns = [ exception.class ] }
- response = request.protocol.marshal_response(api_method, exception)
+ api_method = request.api_method
+ public_method_name = api_method ? api_method.public_name : request.method_name
+ return_type = ActionWebService::SignatureTypes.canonical_signature_entry(Exception, 0)
+ response = request.protocol.encode_response(public_method_name + 'Response', exception, return_type)
send_web_service_response(response)
else
if self.class.web_service_exception_reporting
@@ -118,7 +118,14 @@ module ActionWebService # :nodoc:
def log_request(request, body)
unless logger.nil?
name = request.method_name
- params = request.method_params.map{|x| "#{x.info.name}=>#{x.value.inspect}"}
+ api_method = request.api_method
+ params = request.method_params
+ if api_method && api_method.expects
+ i = 0
+ params = api_method.expects.map{ |type| param = "#{type.name}=>#{params[i].inspect}"; i+= 1; param }
+ else
+ params = params.map{ |param| param.inspect }
+ end
service = request.service_name
logger.debug("\nWeb Service Request: #{name}(#{params.join(", ")}) Entrypoint: #{service}")
logger.debug(indent(body))
@@ -127,7 +134,8 @@ module ActionWebService # :nodoc:
def log_response(response, elapsed=nil)
unless logger.nil?
- logger.debug("\nWeb Service Response" + (elapsed ? " (%f):" % elapsed : ":"))
+ elapsed = (elapsed ? " (%f):" % elapsed : ":")
+ logger.debug("\nWeb Service Response" + elapsed + " => #{response.return_value.inspect}")
logger.debug(indent(response.body))
end
end
@@ -171,7 +179,7 @@ module ActionWebService # :nodoc:
namespace = 'urn:ActionWebService'
soap_action_base = "/#{controller_name}"
- marshaler = WS::Marshaling::SoapMarshaler.new(namespace)
+ marshaler = ActionWebService::Protocol::Soap::SoapMarshaler.new(namespace)
apis = {}
case dispatching_mode
when :direct
@@ -208,7 +216,7 @@ module ActionWebService # :nodoc:
xm.xsd(:schema, 'xmlns' => XsdNs, 'targetNamespace' => namespace) do
custom_types.each do |binding|
case
- when binding.is_typed_array?
+ when binding.type.array?
xm.xsd(:complexType, 'name' => binding.type_name) do
xm.xsd(:complexContent) do
xm.xsd(:restriction, 'base' => 'soapenc:Array') do
@@ -217,11 +225,11 @@ module ActionWebService # :nodoc:
end
end
end
- when binding.is_typed_struct?
+ when binding.type.structured?
xm.xsd(:complexType, 'name' => binding.type_name) do
xm.xsd(:all) do
- binding.each_member do |name, spec|
- b = marshaler.register_type(spec)
+ binding.type.each_member do |name, type|
+ b = marshaler.register_type(type)
xm.xsd(:element, 'name' => name, 'type' => b.qualified_type_name('typens'))
end
end
@@ -249,14 +257,8 @@ module ActionWebService # :nodoc:
expects = method.expects
i = 1
expects.each do |type|
- if type.is_a?(Hash)
- param_name = type.keys.shift
- type = type.values.shift
- else
- param_name = "param#{i}"
- end
binding = marshaler.register_type(type)
- xm.part('name' => param_name, 'type' => binding.qualified_type_name('typens'))
+ xm.part('name' => type.name, 'type' => binding.qualified_type_name('typens'))
i += 1
end if expects
end
@@ -340,7 +342,9 @@ module ActionWebService # :nodoc:
def register_api(api, marshaler)
bindings = {}
traverse_custom_types(api, marshaler) do |binding|
- bindings[binding] = nil unless bindings.has_key?(binding.type_class)
+ bindings[binding] = nil unless bindings.has_key?(binding)
+ element_binding = binding.element_binding
+ bindings[binding.element_binding] = nil if element_binding && !bindings.has_key?(element_binding)
end
bindings.keys
end
@@ -348,21 +352,18 @@ module ActionWebService # :nodoc:
def traverse_custom_types(api, marshaler, &block)
api.api_methods.each do |name, method|
expects, returns = method.expects, method.returns
- expects.each{|x| traverse_custom_type_spec(marshaler, x, &block)} if expects
- returns.each{|x| traverse_custom_type_spec(marshaler, x, &block)} if returns
+ expects.each{ |type| traverse_type(marshaler, type, &block) if type.custom? } if expects
+ returns.each{ |type| traverse_type(marshaler, type, &block) if type.custom? } if returns
end
end
- def traverse_custom_type_spec(marshaler, spec, &block)
- binding = marshaler.register_type(spec)
- if binding.is_typed_struct?
- binding.each_member do |name, member_spec|
- traverse_custom_type_spec(marshaler, member_spec, &block)
- end
- elsif binding.is_typed_array?
- traverse_custom_type_spec(marshaler, binding.element_binding.type_class, &block)
+ def traverse_type(marshaler, type, &block)
+ yield marshaler.register_type(type)
+ if type.array?
+ yield marshaler.register_type(type.element_type)
+ type = type.element_type
end
- yield binding
+ type.each_member{ |name, type| traverse_type(marshaler, type, &block) } if type.structured?
end
end
end
diff --git a/actionwebservice/lib/action_web_service/protocol/abstract.rb b/actionwebservice/lib/action_web_service/protocol/abstract.rb
index 0ff4feef84..70b922ce73 100644
--- a/actionwebservice/lib/action_web_service/protocol/abstract.rb
+++ b/actionwebservice/lib/action_web_service/protocol/abstract.rb
@@ -3,22 +3,11 @@ module ActionWebService # :nodoc:
class ProtocolError < ActionWebServiceError # :nodoc:
end
- class AbstractProtocol
- attr :marshaler
- attr :encoder
-
- def unmarshal_request(ap_request)
- end
-
- def marshal_response(method, return_value)
- body = method.encode_rpc_response(marshaler, encoder, return_value)
- Response.new(body, 'text/xml')
+ class AbstractProtocol # :nodoc:
+ def decode_action_pack_request(action_pack_request)
end
- def protocol_client(api, protocol_name, endpoint_uri, options)
- end
-
- def create_action_pack_request(service_name, public_method_name, raw_body, options={})
+ def encode_action_pack_request(service_name, public_method_name, raw_body, options={})
klass = options[:request_class] || SimpleActionPackRequest
request = klass.new
request.request_parameters['action'] = service_name.to_s
@@ -27,50 +16,30 @@ module ActionWebService # :nodoc:
request.env['HTTP_CONTENT_TYPE'] = 'text/xml'
request
end
- end
- class SimpleActionPackRequest < ActionController::AbstractRequest
- def initialize
- @env = {}
- @qparams = {}
- @rparams = {}
- @cookies = {}
- reset_session
+ def decode_request(raw_request, service_name)
end
- def query_parameters
- @qparams
+ def encode_request(method_name, params, param_types)
end
- def request_parameters
- @rparams
+ def decode_response(raw_response)
end
- def env
- @env
- end
-
- def host
- ''
- end
-
- def cookies
- @cookies
+ def encode_response(method_name, return_value, return_type)
end
- def session
- @session
+ def protocol_client(api, protocol_name, endpoint_uri, options)
end
- def reset_session
- @session = {}
+ def register_api(api)
end
end
class Request # :nodoc:
attr :protocol
attr :method_name
- attr :method_params
+ attr_accessor :method_params
attr :service_name
attr_accessor :api
attr_accessor :api_method
@@ -88,10 +57,50 @@ module ActionWebService # :nodoc:
class Response # :nodoc:
attr :body
attr :content_type
+ attr :return_value
- def initialize(body, content_type)
+ def initialize(body, content_type, return_value)
@body = body
@content_type = content_type
+ @return_value = return_value
+ end
+ end
+
+ class SimpleActionPackRequest < ActionController::AbstractRequest # :nodoc:
+ def initialize
+ @env = {}
+ @qparams = {}
+ @rparams = {}
+ @cookies = {}
+ reset_session
+ end
+
+ def query_parameters
+ @qparams
+ end
+
+ def request_parameters
+ @rparams
+ end
+
+ def env
+ @env
+ end
+
+ def host
+ ''
+ end
+
+ def cookies
+ @cookies
+ end
+
+ def session
+ @session
+ end
+
+ def reset_session
+ @session = {}
end
end
end
diff --git a/actionwebservice/lib/action_web_service/protocol/discovery.rb b/actionwebservice/lib/action_web_service/protocol/discovery.rb
index 40875975bf..a911c7d017 100644
--- a/actionwebservice/lib/action_web_service/protocol/discovery.rb
+++ b/actionwebservice/lib/action_web_service/protocol/discovery.rb
@@ -14,10 +14,10 @@ module ActionWebService # :nodoc:
module InstanceMethods # :nodoc:
private
- def discover_web_service_request(ap_request)
+ def discover_web_service_request(action_pack_request)
(self.class.read_inheritable_attribute("web_service_protocols") || []).each do |protocol|
protocol = protocol.new
- request = protocol.unmarshal_request(ap_request)
+ request = protocol.decode_action_pack_request(action_pack_request)
return request unless request.nil?
end
nil
diff --git a/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb b/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb
index 5e56748ae3..cc3e90a4cc 100644
--- a/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb
+++ b/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb
@@ -1,3 +1,5 @@
+require 'action_web_service/protocol/soap_protocol/marshaler'
+
module ActionWebService # :nodoc:
module Protocol # :nodoc:
module Soap # :nodoc:
@@ -7,28 +9,105 @@ module ActionWebService # :nodoc:
end
class SoapProtocol < AbstractProtocol # :nodoc:
- def initialize
- @encoder = WS::Encoding::SoapRpcEncoding.new 'urn:ActionWebService'
- @marshaler = WS::Marshaling::SoapMarshaler.new 'urn:ActionWebService'
+ def marshaler
+ @marshaler ||= SoapMarshaler.new
+ end
+
+ def decode_action_pack_request(action_pack_request)
+ return nil unless has_valid_soap_action?(action_pack_request)
+ service_name = action_pack_request.parameters['action']
+ decode_request(action_pack_request.raw_post, service_name)
+ end
+
+ def encode_action_pack_request(service_name, public_method_name, raw_body, options={})
+ request = super
+ request.env['HTTP_SOAPACTION'] = '/soap/%s/%s' % [service_name, public_method_name]
+ request
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']
+ def decode_request(raw_request, service_name)
+ envelope = SOAP::Processor.unmarshal(raw_request)
+ unless envelope
+ raise ProtocolError, "Failed to parse SOAP request message"
+ end
+ request = envelope.body.request
+ method_name = request.elename.name
+ params = request.collect{ |k, v| marshaler.soap_to_ruby(request[k]) }
Request.new(self, method_name, params, service_name)
end
+ def encode_request(method_name, params, param_types)
+ param_types.each{ |type| marshaler.register_type(type) } if param_types
+ qname = XSD::QName.new(marshaler.type_namespace, method_name)
+ param_def = []
+ i = 0
+ if param_types
+ params = params.map do |param|
+ param_type = param_types[i]
+ param_def << ['in', param_type.name, marshaler.lookup_type(param_type).mapping]
+ i += 1
+ [param_type.name, marshaler.ruby_to_soap(param)]
+ end
+ else
+ params = []
+ 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_response(raw_response)
+ envelope = SOAP::Processor.unmarshal(raw_response)
+ unless envelope
+ raise ProtocolError, "Failed to parse SOAP request message"
+ end
+ method_name = envelope.body.request.elename.name
+ return_value = envelope.body.response
+ return_value = marshaler.soap_to_ruby(return_value) unless return_value.nil?
+ [method_name, return_value]
+ end
+
+ def encode_response(method_name, return_value, return_type)
+ if return_type
+ return_binding = marshaler.register_type(return_type)
+ marshaler.annotate_arrays(return_binding, return_value)
+ end
+ qname = XSD::QName.new(marshaler.type_namespace, method_name)
+ if return_value.nil?
+ response = SOAP::RPC::SOAPMethodResponse.new(qname, nil)
+ else
+ if return_value.is_a?(Exception)
+ detail = SOAP::Mapping::SOAPException.new(return_value)
+ response = SOAP::SOAPFault.new(
+ SOAP::SOAPQName.new('%s:%s' % [SOAP::SOAPNamespaceTag, 'Server']),
+ SOAP::SOAPString.new(return_value.to_s),
+ SOAP::SOAPString.new(self.class.name),
+ marshaler.ruby_to_soap(detail))
+ else
+ if return_type
+ param_def = [['retval', 'return', marshaler.lookup_type(return_type).mapping]]
+ response = SOAP::RPC::SOAPMethodResponse.new(qname, param_def)
+ response.retval = marshaler.ruby_to_soap(return_value)
+ else
+ response = SOAP::RPC::SOAPMethodResponse.new(qname, nil)
+ end
+ end
+ end
+ envelope = create_soap_envelope(response)
+ Response.new(SOAP::Processor.marshal(envelope), 'text/xml', return_value)
+ end
+
def protocol_client(api, protocol_name, endpoint_uri, options={})
return nil unless protocol_name == :soap
ActionWebService::Client::Soap.new(api, endpoint_uri, options)
end
- def create_action_pack_request(service_name, public_method_name, raw_body, options={})
- request = super
- request.env['HTTP_SOAPACTION'] = '/soap/%s/%s' % [service_name, public_method_name]
- request
+ def register_api(api)
+ api.api_methods.each do |name, method|
+ method.expects.each{ |type| marshaler.register_type(type) } if method.expects
+ method.returns.each{ |type| marshaler.register_type(type) } if method.returns
+ end
end
private
@@ -43,7 +122,13 @@ module ActionWebService # :nodoc:
return nil if soap_action.empty?
soap_action
end
- end
+
+ def create_soap_envelope(body)
+ header = SOAP::SOAPHeader.new
+ body = SOAP::SOAPBody.new(body)
+ SOAP::SOAPEnvelope.new(header, body)
+ end
+ end
end
end
end
diff --git a/actionwebservice/lib/action_web_service/protocol/soap_protocol/marshaler.rb b/actionwebservice/lib/action_web_service/protocol/soap_protocol/marshaler.rb
new file mode 100644
index 0000000000..5d9a80b007
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/protocol/soap_protocol/marshaler.rb
@@ -0,0 +1,197 @@
+require 'soap/mapping'
+
+module ActionWebService
+ module Protocol
+ module Soap
+ class SoapMarshaler
+ attr :type_namespace
+ attr :registry
+
+ def initialize(type_namespace=nil)
+ @type_namespace = type_namespace || 'urn:ActionWebService'
+ @registry = SOAP::Mapping::Registry.new
+ @type2binding = {}
+ end
+
+ def soap_to_ruby(obj)
+ SOAP::Mapping.soap2obj(obj, @registry)
+ end
+
+ def ruby_to_soap(obj)
+ SOAP::Mapping.obj2soap(obj, @registry)
+ end
+
+ def register_type(type)
+ return @type2binding[type] if @type2binding.has_key?(type)
+
+ type_class = type.array?? type.element_type.type_class : type.type_class
+ type_type = type.array?? type.element_type : type
+ 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(self, qname, type_type, 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(self, qname, type_type, mapping)
+ end
+
+ array_binding = nil
+ if type.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.element_type.type_class.name) + 'Array')
+ array_binding = SoapBinding.new(self, qname, type, array_mapping, type_binding)
+ end
+
+ @type2binding[type] = array_binding ? array_binding : type_binding
+ @type2binding[type]
+ end
+ alias :lookup_type :register_type
+
+ def annotate_arrays(binding, value)
+ if binding.type.array?
+ mark_typed_array(value, binding.element_binding.qname)
+ if binding.element_binding.type.custom?
+ value.each do |element|
+ annotate_arrays(binding.element_binding, element)
+ end
+ end
+ elsif binding.type.structured?
+ binding.type.each_member do |name, type|
+ member_binding = register_type(type)
+ member_value = value.respond_to?('[]') ? value[name] : value.send(name)
+ annotate_arrays(member_binding, member_value) if type.custom?
+ end
+ end
+ end
+
+ private
+ def typed_struct_factory(type_class)
+ if Object.const_defined?('ActiveRecord')
+ if type_class.ancestors.include?(ActiveRecord::Base)
+ 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 mark_typed_array(array, qname)
+ (class << array; self; end).class_eval do
+ define_method(:arytype) do
+ qname
+ end
+ end
+ 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
+
+ def soap_type_name(type_name)
+ type_name.gsub(/::/, '..')
+ end
+ end
+
+ class SoapBinding
+ attr :qname
+ attr :type
+ attr :mapping
+ attr :element_binding
+
+ def initialize(marshaler, qname, type, mapping, element_binding=nil)
+ @marshaler = marshaler
+ @qname = qname
+ @type = type
+ @mapping = mapping
+ @element_binding = element_binding
+ end
+
+ def type_name
+ @type.custom? ? @qname.name : nil
+ end
+
+ def qualified_type_name(ns=nil)
+ if @type.custom?
+ "#{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
+
+ def eql?(other)
+ @qname == other.qname
+ end
+ alias :== :eql?
+
+ def hash
+ @qname.hash
+ 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.class.columns.each do |column|
+ key = column.name.to_s
+ value = obj.send(key)
+ 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
+end
diff --git a/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb b/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb
index f8ff12cfa3..de6c8c8a30 100644
--- a/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb
+++ b/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb
@@ -1,3 +1,5 @@
+require 'xmlrpc/marshal'
+
module ActionWebService # :nodoc:
module Protocol # :nodoc:
module XmlRpc # :nodoc:
@@ -6,22 +8,61 @@ module ActionWebService # :nodoc:
end
class XmlRpcProtocol < AbstractProtocol # :nodoc:
- def initialize
- @encoder = WS::Encoding::XmlRpcEncoding.new
- @marshaler = WS::Marshaling::XmlRpcMarshaler.new
+ def decode_action_pack_request(action_pack_request)
+ service_name = action_pack_request.parameters['action']
+ decode_request(action_pack_request.raw_post, service_name)
end
- def unmarshal_request(ap_request)
- method_name, params = @encoder.decode_rpc_call(ap_request.raw_post)
- params = params.map{|x| @marshaler.unmarshal(x)}
- service_name = ap_request.parameters['action']
+ def decode_request(raw_request, service_name)
+ method_name, params = XMLRPC::Marshal.load_call(raw_request)
Request.new(self, method_name, params, service_name)
end
+ def encode_request(method_name, params, param_types)
+ if param_types
+ params = params.dup
+ param_types.each_with_index{ |type, i| params[i] = value_to_xmlrpc_wire_format(params[i], type) }
+ end
+ XMLRPC::Marshal.dump_call(method_name, *params)
+ end
+
+ def decode_response(raw_response)
+ [nil, XMLRPC::Marshal.load_response(raw_response)]
+ end
+
+ def encode_response(method_name, return_value, return_type)
+ return_value = true if return_value.nil?
+ if return_type
+ return_value = value_to_xmlrpc_wire_format(return_value, return_type)
+ end
+ raw_response = XMLRPC::Marshal.dump_response(return_value)
+ Response.new(raw_response, 'text/xml', return_value)
+ end
+
def protocol_client(api, protocol_name, endpoint_uri, options={})
return nil unless protocol_name == :xmlrpc
ActionWebService::Client::XmlRpc.new(api, endpoint_uri, options)
end
+
+ def value_to_xmlrpc_wire_format(value, value_type)
+ if value_type.array?
+ value.map{ |val| value_to_xmlrpc_wire_format(val, value_type.element_type) }
+ else
+ if value.is_a?(ActionWebService::Struct)
+ struct = {}
+ value.class.members.each do |name, type|
+ struct[name.to_s] = value_to_xmlrpc_wire_format(value[name], type)
+ end
+ struct
+ elsif value.is_a?(ActiveRecord::Base)
+ value.attributes.dup
+ elsif value.is_a?(Exception) && !value.is_a?(XMLRPC::FaultException)
+ XMLRPC::FaultException.new(2, value.message)
+ else
+ value
+ end
+ end
+ end
end
end
end
diff --git a/actionwebservice/lib/action_web_service/scaffolding.rb b/actionwebservice/lib/action_web_service/scaffolding.rb
index e311515fba..0fab01eced 100644
--- a/actionwebservice/lib/action_web_service/scaffolding.rb
+++ b/actionwebservice/lib/action_web_service/scaffolding.rb
@@ -1,9 +1,13 @@
require 'ostruct'
require 'uri'
require 'benchmark'
+require 'pathname'
module ActionWebService
module Scaffolding # :nodoc:
+ class ScaffoldingError < ActionWebServiceError # :nodoc:
+ end
+
def self.append_features(base)
super
base.extend(ClassMethods)
@@ -63,17 +67,32 @@ module ActionWebService
when :xmlrpc
protocol = Protocol::XmlRpc::XmlRpcProtocol.new
end
- cgi = @request.cgi
+ cgi = @request.respond_to?(:cgi) ? @request.cgi : nil
bm = Benchmark.measure do
- @method_request_xml = @scaffold_method.encode_rpc_call(protocol.marshaler, protocol.encoder, @params['method_params'].dup)
- @request = protocol.create_action_pack_request(@scaffold_service.name, @scaffold_method.public_name, @method_request_xml)
+ protocol.register_api(@scaffold_service.api)
+ params = @params['method_params'] ? @params['method_params'].dup : nil
+ params = @scaffold_method.cast_expects(params)
+ @method_request_xml = protocol.encode_request(@scaffold_method.public_name, params, @scaffold_method.expects)
+ @request = protocol.encode_action_pack_request(@scaffold_service.name, @scaffold_method.public_name, @method_request_xml)
dispatch_web_service_request
@method_response_xml = @response.body
- @method_return_value = protocol.marshaler.unmarshal(protocol.encoder.decode_rpc_response(@method_response_xml)[1]).value
+ method_name, obj = protocol.decode_response(@method_response_xml)
+ if obj.respond_to?(:detail) && obj.detail.respond_to?(:cause) && obj.detail.cause.is_a?(Exception)
+ raise obj.detail.cause
+ elsif obj.is_a?(XMLRPC::FaultException)
+ raise obj
+ end
+ @method_return_value = @scaffold_method.cast_returns(obj)
end
@method_elapsed = bm.real
add_instance_variables_to_assigns
- @response = ::ActionController::CgiResponse.new(cgi)
+ template = @response.template
+ if cgi
+ @response = ::ActionController::CgiResponse.new(cgi)
+ else
+ @response = ::ActionController::TestResponse.new
+ end
+ @response.template = template
@performed_render = false
render_#{action_name}_scaffold 'result'
end
@@ -99,20 +118,19 @@ module ActionWebService
end
def scaffold_path(template_name)
- File.dirname(__FILE__) + "/templates/scaffolds/" + template_name + ".rhtml"
+ Pathname.new(File.dirname(__FILE__) + "/templates/scaffolds/" + template_name + ".rhtml").realpath.to_s
end
END
end
end
module Helpers # :nodoc:
- def method_parameter_input_fields(method, param_spec, i)
- klass = method.param_class(param_spec)
- unless WS::BaseTypes.base_type?(klass)
- name = method.param_name(param_spec, i)
+ def method_parameter_input_fields(method, type)
+ name = type.name.to_s
+ type_name = type.type
+ unless type_name.is_a?(Symbol)
raise "Parameter #{name}: Structured/array types not supported in scaffolding input fields yet"
end
- type_name = method.param_type(param_spec)
field_name = "method_params[]"
case type_name
when :int
@@ -168,6 +186,9 @@ module ActionWebService
@name = name.to_s
@object = real_service
@api = @object.class.web_service_api
+ if @api.nil?
+ raise ScaffoldingError, "No web service API attached to #{object.class}"
+ end
@api_methods = {}
@api_methods_full = []
@api.api_methods.each do |name, method|
diff --git a/actionwebservice/lib/action_web_service/struct.rb b/actionwebservice/lib/action_web_service/struct.rb
index d4e2ba9ce6..c5e6346bfa 100644
--- a/actionwebservice/lib/action_web_service/struct.rb
+++ b/actionwebservice/lib/action_web_service/struct.rb
@@ -33,11 +33,20 @@ module ActionWebService
send(name.to_s)
end
+ # Iterates through each member
+ def each_pair(&block)
+ self.class.members.each do |name, type|
+ yield name, type
+ end
+ end
+
class << self
# 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 => WS::BaseTypes.canonical_param_type_class(type))
+ name = name.to_sym
+ type = ActionWebService::SignatureTypes.canonical_signature_entry({ name => type }, 0)
+ write_inheritable_hash("struct_members", name => type)
class_eval <<-END
def #{name}; @#{name}; end
def #{name}=(value); @#{name} = value; end
@@ -47,6 +56,10 @@ module ActionWebService
def members # :nodoc:
read_inheritable_attribute("struct_members") || {}
end
+
+ def member_type(name) # :nodoc:
+ members[name.to_sym]
+ end
end
end
end
diff --git a/actionwebservice/lib/action_web_service/support/signature_types.rb b/actionwebservice/lib/action_web_service/support/signature_types.rb
new file mode 100644
index 0000000000..5c57254bc3
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/support/signature_types.rb
@@ -0,0 +1,191 @@
+module ActionWebService # :nodoc:
+ module SignatureTypes # :nodoc:
+ def canonical_signature(signature)
+ return nil if signature.nil?
+ i = -1
+ signature.map{ |spec| canonical_signature_entry(spec, i += 1) }
+ end
+
+ def canonical_signature_entry(spec, i)
+ name = "param#{i}"
+ if spec.is_a?(Hash)
+ name = spec.keys.first
+ spec = spec.values.first
+ type = spec
+ else
+ type = spec
+ end
+ if spec.is_a?(Array)
+ ArrayType.new(canonical_signature_entry(spec[0], 0), name)
+ else
+ type = canonical_type(type)
+ if type.is_a?(Symbol)
+ BaseType.new(type, name)
+ else
+ StructuredType.new(type, name)
+ end
+ end
+ end
+
+ def canonical_type(type)
+ type_name = symbol_name(type) || class_to_type_name(type)
+ type = type_name || type
+ return canonical_type_name(type) if type.is_a?(Symbol)
+ type
+ 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, :timestamp
+ :time
+ when :datetime
+ :datetime
+ when :date
+ :date
+ else
+ raise(TypeError, "#{name} is not a valid base type")
+ end
+ end
+
+ def canonical_type_class(type)
+ type = canonical_type(type)
+ type.is_a?(Symbol) ? type_name_to_class(type) : type
+ end
+
+ def symbol_name(name)
+ return name.to_sym if name.is_a?(Symbol) || name.is_a?(String)
+ nil
+ end
+
+ def class_to_type_name(klass)
+ klass = klass.class unless klass.is_a?(Class)
+ if derived_from?(Integer, klass) || derived_from?(Fixnum, klass) || derived_from?(Bignum, klass)
+ :int
+ elsif klass == String
+ :string
+ elsif klass == TrueClass || klass == FalseClass
+ :bool
+ elsif derived_from?(Float, klass) || derived_from?(Precision, klass) || derived_from?(Numeric, klass)
+ :float
+ elsif klass == Time
+ :time
+ elsif klass == DateTime
+ :datetime
+ elsif klass == Date
+ :date
+ else
+ nil
+ end
+ end
+
+ 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
+ when :datetime
+ DateTime
+ else
+ nil
+ end
+ end
+
+ def derived_from?(ancestor, child)
+ child.ancestors.include?(ancestor)
+ end
+
+ module_function :type_name_to_class
+ module_function :class_to_type_name
+ module_function :symbol_name
+ module_function :canonical_type_class
+ module_function :canonical_type_name
+ module_function :canonical_type
+ module_function :canonical_signature_entry
+ module_function :canonical_signature
+ module_function :derived_from?
+ end
+
+ class BaseType # :nodoc:
+ include SignatureTypes
+
+ attr :type
+ attr :type_class
+ attr :name
+
+ def initialize(type, name)
+ @type = canonical_type(type)
+ @type_class = canonical_type_class(@type)
+ @name = name
+ end
+
+ def custom?
+ false
+ end
+
+ def array?
+ false
+ end
+
+ def structured?
+ false
+ end
+ end
+
+ class ArrayType < BaseType # :nodoc:
+ attr :element_type
+
+ def initialize(element_type, name)
+ super(Array, name)
+ @element_type = element_type
+ end
+
+ def custom?
+ true
+ end
+
+ def array?
+ true
+ end
+ end
+
+ class StructuredType < BaseType # :nodoc:
+ def each_member
+ if @type_class.respond_to?(:members)
+ @type_class.members.each do |name, type|
+ yield name, type
+ end
+ elsif @type_class.respond_to?(:columns)
+ i = 0
+ @type_class.columns.each do |column|
+ yield column.name, canonical_signature_entry(column.klass, i += 1)
+ end
+ end
+ end
+
+ def custom?
+ true
+ end
+
+ def structured?
+ true
+ end
+ end
+end
diff --git a/actionwebservice/lib/action_web_service/templates/scaffolds/parameters.rhtml b/actionwebservice/lib/action_web_service/templates/scaffolds/parameters.rhtml
index 0516738da6..e62d234c1a 100644
--- a/actionwebservice/lib/action_web_service/templates/scaffolds/parameters.rhtml
+++ b/actionwebservice/lib/action_web_service/templates/scaffolds/parameters.rhtml
@@ -5,10 +5,10 @@
<%= hidden_field_tag "method", @scaffold_method.public_name %>
<% i = 0 %>
-<% @scaffold_method.expects.each do |spec| %>
+<% @scaffold_method.expects.each do |type| %>
<p>
- <label for="method_params[]"><%= @scaffold_method.param_name(spec, i).camelize %></label><br />
- <%= method_parameter_input_fields(@scaffold_method, spec, i) %>
+ <label for="method_params[]"><%= type.name.to_s.camelize %></label><br />
+ <%= method_parameter_input_fields(@scaffold_method, type) %>
</p>
<% i += 1 %>
<% end %>
diff --git a/actionwebservice/lib/action_web_service/test_invoke.rb b/actionwebservice/lib/action_web_service/test_invoke.rb
index 5d364e4225..c22ca618dc 100644
--- a/actionwebservice/lib/action_web_service/test_invoke.rb
+++ b/actionwebservice/lib/action_web_service/test_invoke.rb
@@ -21,7 +21,7 @@ module Test # :nodoc:
# invoke the specified layered API method on the correct service
def invoke_layered(service_name, method_name, *args)
- if protocol == :soap
+ if protocol.is_a?(ActionWebService::Protocol::Soap::SoapProtocol)
raise "SOAP protocol support for :layered dispatching mode is not available"
end
prepare_request('api', service_name, method_name, *args)
@@ -37,10 +37,10 @@ module Test # :nodoc:
@request.env['HTTP_CONTENT_TYPE'] = 'text/xml'
@request.env['RAW_POST_DATA'] = encode_rpc_call(service_name, api_method_name, *args)
case protocol
- when :soap
+ when ActionWebService::Protocol::Soap::SoapProtocol
soap_action = "/#{@controller.controller_name}/#{service_name}/#{public_method_name(service_name, api_method_name)}"
@request.env['HTTP_SOAPACTION'] = soap_action
- when :xmlrpc
+ when ActionWebService::Protocol::XmlRpc::XmlRpcProtocol
@request.env.delete('HTTP_SOAPACTION')
end
end
@@ -52,19 +52,18 @@ module Test # :nodoc:
when :delegated, :layered
api = @controller.web_service_object(service_name.to_sym).class.web_service_api
end
+ protocol.register_api(api)
method = api.api_methods[api_method_name.to_sym]
- method.register_types(marshaler)
- method.encode_rpc_call(marshaler, encoder, args.dup, :method_name => public_method_name(service_name, api_method_name))
+ protocol.encode_request(public_method_name(service_name, api_method_name), args.dup, method.expects)
end
def decode_rpc_response
- public_method_name, return_value = encoder.decode_rpc_response(@response.body)
- result = marshaler.unmarshal(return_value).value
+ public_method_name, return_value = protocol.decode_response(@response.body)
unless @return_exceptions
- exception = is_exception?(result)
+ exception = is_exception?(return_value)
raise exception if exception
end
- result
+ return_value
end
def public_method_name(service_name, api_method_name)
@@ -86,25 +85,7 @@ module Test # :nodoc:
end
def protocol
- @protocol ||= :soap
- end
-
- def marshaler
- case protocol
- when :soap
- @soap_marshaler ||= WS::Marshaling::SoapMarshaler.new 'urn:ActionWebService'
- when :xmlrpc
- @xmlrpc_marshaler ||= WS::Marshaling::XmlRpcMarshaler.new
- end
- end
-
- def encoder
- case protocol
- when :soap
- @soap_encoder ||= WS::Encoding::SoapRpcEncoding.new 'urn:ActionWebService'
- when :xmlrpc
- @xmlrpc_encoder ||= WS::Encoding::XmlRpcEncoding.new
- end
+ @protocol ||= ActionWebService::Protocol::Soap::SoapProtocol.new
end
def is_exception?(obj)
diff --git a/actionwebservice/lib/action_web_service/vendor/ws.rb b/actionwebservice/lib/action_web_service/vendor/ws.rb
deleted file mode 100644
index 18a32a555e..0000000000
--- a/actionwebservice/lib/action_web_service/vendor/ws.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-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
deleted file mode 100644
index 4266a7141d..0000000000
--- a/actionwebservice/lib/action_web_service/vendor/ws/common.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-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
deleted file mode 100644
index 790317639b..0000000000
--- a/actionwebservice/lib/action_web_service/vendor/ws/encoding.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-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
deleted file mode 100644
index 257c7d0993..0000000000
--- a/actionwebservice/lib/action_web_service/vendor/ws/encoding/abstract.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-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
deleted file mode 100644
index f4d2f5a7d6..0000000000
--- a/actionwebservice/lib/action_web_service/vendor/ws/encoding/soap_rpc_encoding.rb
+++ /dev/null
@@ -1,90 +0,0 @@
-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
deleted file mode 100644
index 4876c08705..0000000000
--- a/actionwebservice/lib/action_web_service/vendor/ws/encoding/xmlrpc_encoding.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-require 'xmlrpc/marshal'
-
-module WS
- module Encoding
- 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)
- 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)
- [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
deleted file mode 100644
index 3a0a2e8cc1..0000000000
--- a/actionwebservice/lib/action_web_service/vendor/ws/marshaling.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-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
deleted file mode 100644
index e897f62297..0000000000
--- a/actionwebservice/lib/action_web_service/vendor/ws/marshaling/abstract.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-module WS
- module Marshaling
- class AbstractMarshaler
- def initialize
- @base_type_caster = BaseTypeCaster.new
- end
-
- def marshal(param)
- raise NotImplementedError
- end
-
- def unmarshal(param)
- raise NotImplementedError
- end
-
- def register_type(type)
- nil
- end
- alias :lookup_type :register_type
-
- def cast_inbound_recursive(value, spec)
- raise NotImplementedError
- end
-
- def cast_outbound_recursive(value, spec)
- raise NotImplementedError
- end
-
- attr :base_type_caster
- protected :base_type_caster
- 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
deleted file mode 100644
index 14c8d8401d..0000000000
--- a/actionwebservice/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb
+++ /dev/null
@@ -1,283 +0,0 @@
-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='')
- super()
- @type_namespace = type_namespace
- @registry = SOAP::Mapping::Registry.new
- @spec2binding = {}
- end
-
- def marshal(param)
- annotate_arrays(param.info.data, param.value)
- if param.value.is_a?(Exception)
- detail = SOAP::Mapping::SOAPException.new(param.value)
- soap_obj = SOAP::SOAPFault.new(
- SOAP::SOAPQName.new('%s:%s' % [SOAP::SOAPNamespaceTag, '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(self, soap_object.arytype, Array, mapping)
- else
- param.info.data = SoapBinding.new(self, soap_type, value.class, 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(self, qname, type_class, 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(self, qname, type_class, 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(self, qname, Array, array_mapping, type_binding)
- end
-
- @spec2binding[spec] = array_binding ? array_binding : type_binding
- @spec2binding[spec]
- end
- alias :lookup_type :register_type
-
- def cast_inbound_recursive(value, spec)
- binding = lookup_type(spec)
- if binding.is_custom_type?
- value
- else
- base_type_caster.cast(value, binding.type_class)
- end
- end
-
- def cast_outbound_recursive(value, spec)
- binding = lookup_type(spec)
- if binding.is_custom_type?
- value
- else
- base_type_caster.cast(value, binding.type_class)
- end
- end
-
- protected
- def annotate_arrays(binding, value)
- if binding.is_typed_array?
- mark_typed_array(value, binding.element_binding.qname)
- if binding.element_binding.is_custom_type?
- value.each do |element|
- annotate_arrays(register_type(element.class), element)
- end
- end
- elsif binding.is_typed_struct?
- if binding.type_class.respond_to?(:members)
- binding.type_class.members.each do |name, spec|
- member_binding = register_type(spec)
- member_value = value.respond_to?('[]') ? value[name] : value.send(name)
- if member_binding.is_custom_type?
- annotate_arrays(member_binding, member_value)
- end
- end
- end
- end
- end
-
- def mark_typed_array(array, qname)
- (class << array; self; end).class_eval do
- define_method(:arytype) do
- qname
- end
- end
- end
-
- 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 :type_class
- attr :mapping
- attr :element_binding
-
- def initialize(marshaler, qname, type_class, mapping, element_binding=nil)
- @marshaler = marshaler
- @qname = qname
- @type_class = type_class
- @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)
- if is_typed_struct?
- if @mapping[1] == SOAP::Mapping::Registry::TypedStructFactory
- if @type_class.respond_to?(:members)
- @type_class.members.each do |name, spec|
- yield name, spec
- end
- end
- elsif @mapping[1].is_a?(WS::Marshaling::SoapActiveRecordStructFactory)
- @type_class.columns.each do |column|
- yield column.name, column.klass
- end
- end
- 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.class.columns.each do |column|
- key = column.name.to_s
- value = obj.send(key)
- 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
deleted file mode 100644
index 56cc7597fb..0000000000
--- a/actionwebservice/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb
+++ /dev/null
@@ -1,143 +0,0 @@
-module WS
- module Marshaling
- class XmlRpcError < WSError
- end
-
- class XmlRpcMarshaler < AbstractMarshaler
- def initialize
- super()
- @spec2binding = {}
- end
-
- def marshal(param)
- value = param.value
- cast_outbound_recursive(param.value, spec_for(param)) rescue value
- end
-
- def unmarshal(obj)
- obj.param
- end
-
- def typed_unmarshal(obj, spec)
- obj.param.info.data = lookup_type(spec)
- value = obj.param.value
- obj.param.value = cast_inbound_recursive(value, spec) rescue value
- obj.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
- alias :lookup_type :register_type
-
- def cast_inbound_recursive(value, spec)
- binding = lookup_type(spec)
- case binding
- when XmlRpcArrayBinding
- value.map{ |x| cast_inbound(x, binding.element_klass) }
- when XmlRpcBinding
- cast_inbound(value, binding.klass)
- end
- end
-
- def cast_outbound_recursive(value, spec)
- binding = lookup_type(spec)
- case binding
- when XmlRpcArrayBinding
- value.map{ |x| cast_outbound(x, binding.element_klass) }
- when XmlRpcBinding
- cast_outbound(value, binding.klass)
- end
- end
-
- private
- def spec_for(param)
- binding = param.info.data
- binding.is_a?(XmlRpcArrayBinding) ? [binding.element_klass] : binding.klass
- end
-
- def cast_inbound(value, klass)
- if BaseTypes.base_type?(klass)
- value = value.to_time if value.is_a?(XMLRPC::DateTime)
- base_type_caster.cast(value, klass)
- elsif value.is_a?(XMLRPC::FaultException)
- value
- elsif klass.ancestors.include?(ActionWebService::Struct)
- obj = klass.new
- klass.members.each do |name, klass|
- name = name.to_s
- obj.send('%s=' % name, cast_inbound_recursive(value[name], klass))
- end
- obj
- else
- obj = klass.new
- if obj.respond_to?(:update)
- obj.update(value)
- else
- value.each do |name, val|
- obj.send('%s=' % name.to_s, val)
- end
- end
- obj
- end
- end
-
- def cast_outbound(value, klass)
- if BaseTypes.base_type?(klass)
- base_type_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
- elsif value.is_a?(ActionWebService::Struct)
- struct = {}
- value.class.members.each do |name, klass|
- name = name.to_s
- struct[name] = cast_outbound_recursive(value[name], klass)
- end
- struct
- else
- struct = {}
- if value.respond_to?(:each_pair)
- value.each_pair{ |key, value| struct[key] = value }
- else
- value.instance_variables.each do |name|
- key = name.sub(/^@/, '')
- struct[key] = value.instance_variable_get(name)
- end
- end
- struct
- 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
deleted file mode 100644
index 88098b5bce..0000000000
--- a/actionwebservice/lib/action_web_service/vendor/ws/types.rb
+++ /dev/null
@@ -1,165 +0,0 @@
-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, data, index=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?
- int_value = Integer(value) rescue nil
- return true if int_value == 1
- return false if int_value == 0
- 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