1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

be explicit about the object to do #instance_eval in for delegated dispatching,

clean up iterations to use #zip (bitsweat), 


git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1090 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
Leon Breedt 2005-04-05 05:39:45 +00:00
parent d7a7d85dbd
commit 361dcad8fd
11 changed files with 226 additions and 230 deletions

View file

@ -25,7 +25,7 @@ task :default => [ :test ]
# Run the unit tests # Run the unit tests
Rake::TestTask.new { |t| Rake::TestTask.new { |t|
t.libs << "test" t.libs << "test"
t.test_files = Dir['test/*_test.rb'] + Dir['test/ws/*_test.rb'] t.test_files = Dir['test/*_test.rb']
t.verbose = true t.verbose = true
} }

View file

@ -1 +1,204 @@
require 'action_web_service/api/base' module ActionWebService # :nodoc:
module API # :nodoc:
# 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.
#
# It is not intended to be instantiated.
#
# It is attached to web service implementation classes like
# ActionWebService::Base and ActionController::Base derivatives by using
# ClassMethods#web_service_api.
class Base
# Whether to transform the public API method names into camel-cased names
class_inheritable_option :inflect_names, true
# Whether to allow ActiveRecord::Base models in <tt>:expects</tt>.
# The default is +false+, you should be aware of the security implications
# of allowing this, and ensure that you don't allow remote callers to
# easily overwrite data they should not have access to.
class_inheritable_option :allow_active_record_expects, false
# If present, the name of a method to call when the remote caller
# tried to call a nonexistent method. Semantically equivalent to
# +method_missing+.
class_inheritable_option :default_api_method
# Disallow instantiation
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.
#
# The signatures for the method input parameters and return value can
# by specified in +options+.
#
# A signature is an array of one or more parameter specifiers.
# A parameter specifier can be one of the following:
#
# * A symbol or string of representing one of the Action Web Service base types.
# See ActionWebService::Signature for a canonical list of the base types.
# * The Class object of the parameter type
# * A single-element Array containing one of the two preceding items. This
# will cause Action Web Service to treat the parameter at that position
# as an array containing only values of the given type.
# * A Hash containing as key the name of the parameter, and as value
# one of the three preceding items
#
# If no method input parameter or method return value signatures are given,
# the method is assumed to take no parameters and/or return no values of
# interest, and any values that are received by the server will be
# discarded and ignored.
#
# Valid options:
# [<tt>:expects</tt>] Signature for the method input parameters
# [<tt>:returns</tt>] Signature for the method return value
# [<tt>:expects_and_returns</tt>] Signature for both input parameters and return value
def api_method(name, options={})
validate_options([:expects, :returns, :expects_and_returns], options.keys)
if options[:expects_and_returns]
expects = options[:expects_and_returns]
returns = options[:expects_and_returns]
else
expects = options[:expects]
returns = options[:returns]
end
expects = canonical_signature(expects)
returns = canonical_signature(returns)
if 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
end
name = name.to_sym
public_name = public_api_method_name(name)
method = Method.new(name, public_name, expects, returns)
write_inheritable_hash("api_methods", name => method)
write_inheritable_hash("api_public_method_names", public_name => name)
end
# Whether the given method name is a service method on this API
def has_api_method?(name)
api_methods.has_key?(name)
end
# Whether the given public method name has a corresponding service method
# on this API
def has_public_api_method?(public_name)
api_public_method_names.has_key?(public_name)
end
# The corresponding public method name for the given service method name
def public_api_method_name(name)
if inflect_names
name.to_s.camelize
else
name.to_s
end
end
# The corresponding service method name for the given public method name
def api_method_name(public_name)
api_public_method_names[public_name]
end
# A Hash containing all service methods on this API, and their
# associated metadata.
def api_methods
read_inheritable_attribute("api_methods") || {}
end
# The Method instance for the given public API method name, if any
def public_api_method_instance(public_method_name)
api_method_instance(api_method_name(public_method_name))
end
# The Method instance for the given API method name, if any
def api_method_instance(method_name)
api_methods[method_name]
end
# The Method instance for the default API method, if any
def default_api_method_instance
return nil unless name = default_api_method
instance = read_inheritable_attribute("default_api_method_instance")
if instance && instance.name == name
return instance
end
instance = Method.new(name, public_api_method_name(name), nil, nil)
write_inheritable_attribute("default_api_method_instance", instance)
instance
end
private
def api_public_method_names
read_inheritable_attribute("api_public_method_names") || {}
end
def validate_options(valid_option_keys, supplied_option_keys)
unknown_option_keys = supplied_option_keys - valid_option_keys
unless unknown_option_keys.empty?
raise(ActionWebServiceError, "Unknown options: #{unknown_option_keys}")
end
end
end
end
# Represents an API method and its associated metadata, and provides functionality
# to assist in commonly performed API method tasks.
class Method
attr :name
attr :public_name
attr :expects
attr :returns
def initialize(name, public_name, expects, returns)
@name = name
@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
@expects.map{ |type| type.name }
end
# Casts a set of Ruby values into the expected Ruby values
def cast_expects(params)
@caster.cast_expects(params)
end
# Cast a Ruby return value into the expected Ruby value
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], false) + " ") : "void ")
fqn << "#{@public_name}("
fqn << @expects.map{ |p| friendly_param(p) }.join(", ") if @expects
fqn << ")"
fqn
end
private
def friendly_param(type, show_name=true)
name = type.name.to_s
type_type = type.array?? type.element_type.type.to_s : type.type.to_s
str = type.array?? (type_type + '[]') : type_type
show_name ? (str + " " + name) : str
end
end
end
end

View file

@ -1,204 +0,0 @@
module ActionWebService # :nodoc:
module API # :nodoc:
# 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.
#
# It is not intended to be instantiated.
#
# It is attached to web service implementation classes like
# ActionWebService::Base and ActionController::Base derivatives by using
# ClassMethods#web_service_api.
class Base
# Whether to transform the public API method names into camel-cased names
class_inheritable_option :inflect_names, true
# Whether to allow ActiveRecord::Base models in <tt>:expects</tt>.
# The default is +false+, you should be aware of the security implications
# of allowing this, and ensure that you don't allow remote callers to
# easily overwrite data they should not have access to.
class_inheritable_option :allow_active_record_expects, false
# If present, the name of a method to call when the remote caller
# tried to call a nonexistent method. Semantically equivalent to
# +method_missing+.
class_inheritable_option :default_api_method
# Disallow instantiation
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.
#
# The signatures for the method input parameters and return value can
# by specified in +options+.
#
# A signature is an array of one or more parameter specifiers.
# A parameter specifier can be one of the following:
#
# * A symbol or string of representing one of the Action Web Service base types.
# See ActionWebService::Signature for a canonical list of the base types.
# * The Class object of the parameter type
# * A single-element Array containing one of the two preceding items. This
# will cause Action Web Service to treat the parameter at that position
# as an array containing only values of the given type.
# * A Hash containing as key the name of the parameter, and as value
# one of the three preceding items
#
# If no method input parameter or method return value signatures are given,
# the method is assumed to take no parameters and/or return no values of
# interest, and any values that are received by the server will be
# discarded and ignored.
#
# Valid options:
# [<tt>:expects</tt>] Signature for the method input parameters
# [<tt>:returns</tt>] Signature for the method return value
# [<tt>:expects_and_returns</tt>] Signature for both input parameters and return value
def api_method(name, options={})
validate_options([:expects, :returns, :expects_and_returns], options.keys)
if options[:expects_and_returns]
expects = options[:expects_and_returns]
returns = options[:expects_and_returns]
else
expects = options[:expects]
returns = options[:returns]
end
expects = canonical_signature(expects)
returns = canonical_signature(returns)
if 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
end
name = name.to_sym
public_name = public_api_method_name(name)
method = Method.new(name, public_name, expects, returns)
write_inheritable_hash("api_methods", name => method)
write_inheritable_hash("api_public_method_names", public_name => name)
end
# Whether the given method name is a service method on this API
def has_api_method?(name)
api_methods.has_key?(name)
end
# Whether the given public method name has a corresponding service method
# on this API
def has_public_api_method?(public_name)
api_public_method_names.has_key?(public_name)
end
# The corresponding public method name for the given service method name
def public_api_method_name(name)
if inflect_names
name.to_s.camelize
else
name.to_s
end
end
# The corresponding service method name for the given public method name
def api_method_name(public_name)
api_public_method_names[public_name]
end
# A Hash containing all service methods on this API, and their
# associated metadata.
def api_methods
read_inheritable_attribute("api_methods") || {}
end
# The Method instance for the given public API method name, if any
def public_api_method_instance(public_method_name)
api_method_instance(api_method_name(public_method_name))
end
# The Method instance for the given API method name, if any
def api_method_instance(method_name)
api_methods[method_name]
end
# The Method instance for the default API method, if any
def default_api_method_instance
return nil unless name = default_api_method
instance = read_inheritable_attribute("default_api_method_instance")
if instance && instance.name == name
return instance
end
instance = Method.new(name, public_api_method_name(name), nil, nil)
write_inheritable_attribute("default_api_method_instance", instance)
instance
end
private
def api_public_method_names
read_inheritable_attribute("api_public_method_names") || {}
end
def validate_options(valid_option_keys, supplied_option_keys)
unknown_option_keys = supplied_option_keys - valid_option_keys
unless unknown_option_keys.empty?
raise(ActionWebServiceError, "Unknown options: #{unknown_option_keys}")
end
end
end
end
# Represents an API method and its associated metadata, and provides functionality
# to assist in commonly performed API method tasks.
class Method
attr :name
attr :public_name
attr :expects
attr :returns
def initialize(name, public_name, expects, returns)
@name = name
@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
@expects.map{ |type| type.name }
end
# Casts a set of Ruby values into the expected Ruby values
def cast_expects(params)
@caster.cast_expects(params)
end
# Cast a Ruby return value into the expected Ruby value
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], false) + " ") : "void ")
fqn << "#{@public_name}("
fqn << @expects.map{ |p| friendly_param(p) }.join(", ") if @expects
fqn << ")"
fqn
end
private
def friendly_param(type, show_name=true)
name = type.name.to_s
type_type = type.array?? type.element_type.type.to_s : type.type.to_s
str = type.array?? (type_type + '[]') : type_type
show_name ? (str + " " + name) : str
end
end
end
end

View file

@ -29,8 +29,7 @@ module ActionWebService # :nodoc:
def cast_expects(api_method, params) # :nodoc: def cast_expects(api_method, params) # :nodoc:
return [] if api_method.expects.nil? return [] if api_method.expects.nil?
i = -1 api_method.expects.zip(params).map{ |type, param| cast(param, type) }
api_method.expects.map{ |type| cast(params[i+=1], type) }
end end
def cast_returns(api_method, return_value) # :nodoc: def cast_returns(api_method, return_value) # :nodoc:

View file

@ -78,12 +78,10 @@ module ActionWebService # :nodoc:
expects = method.expects expects = method.expects
returns = method.returns returns = method.returns
param_def = [] param_def = []
i = 0
if expects if expects
expects.each do |type| expects.each do |type|
type_binding = @protocol.marshaler.lookup_type(type) type_binding = @protocol.marshaler.lookup_type(type)
param_def << ['in', type.name, type_binding.mapping] param_def << ['in', type.name, type_binding.mapping]
i += 1
end end
end end
if returns if returns

View file

@ -79,7 +79,7 @@ module ActionWebService # :nodoc:
raise(ContainerError, "no such web service '#{web_service_name}'") raise(ContainerError, "no such web service '#{web_service_name}'")
end end
service = info[:block] service = info[:block]
service ? instance_eval(&service) : info[:object] service ? self.instance_eval(&service) : info[:object]
end end
end end
end end

View file

@ -121,8 +121,7 @@ module ActionWebService # :nodoc:
api_method = request.api_method api_method = request.api_method
params = request.method_params params = request.method_params
if api_method && api_method.expects if api_method && api_method.expects
i = 0 params = api_method.expects.zip(params).map{ |type, param| "#{type.name}=>#{param.inspect}" }
params = api_method.expects.map{ |type| param = "#{type.name}=>#{params[i].inspect}"; i+= 1; param }
else else
params = params.map{ |param| param.inspect } params = params.map{ |param| param.inspect }
end end
@ -255,11 +254,9 @@ module ActionWebService # :nodoc:
end end
else else
expects = method.expects expects = method.expects
i = 1
expects.each do |type| expects.each do |type|
binding = marshaler.register_type(type) binding = marshaler.register_type(type)
xm.part('name' => type.name, 'type' => binding.qualified_type_name('typens')) xm.part('name' => type.name, 'type' => binding.qualified_type_name('typens'))
i += 1
end if expects end if expects
end end
end end

View file

@ -40,13 +40,10 @@ module ActionWebService # :nodoc:
param_types.each{ |type| marshaler.register_type(type) } if param_types param_types.each{ |type| marshaler.register_type(type) } if param_types
qname = XSD::QName.new(marshaler.type_namespace, method_name) qname = XSD::QName.new(marshaler.type_namespace, method_name)
param_def = [] param_def = []
i = 0
if param_types if param_types
params = params.map do |param| params = param_types.zip(params).map do |type, param|
param_type = param_types[i] param_def << ['in', type.name, marshaler.lookup_type(type).mapping]
param_def << ['in', param_type.name, marshaler.lookup_type(param_type).mapping] [type.name, marshaler.ruby_to_soap(param)]
i += 1
[param_type.name, marshaler.ruby_to_soap(param)]
end end
else else
params = [] params = []

View file

@ -9,12 +9,9 @@ module ActionWebService # :nodoc:
def canonical_signature_entry(spec, i) def canonical_signature_entry(spec, i)
name = "param#{i}" name = "param#{i}"
if spec.is_a?(Hash) if spec.is_a?(Hash)
name = spec.keys.first name, spec = spec.keys.first, spec.values.first
spec = spec.values.first
type = spec
else
type = spec
end end
type = spec
if spec.is_a?(Array) if spec.is_a?(Array)
ArrayType.new(canonical_signature_entry(spec[0], 0), name) ArrayType.new(canonical_signature_entry(spec[0], 0), name)
else else
@ -173,7 +170,7 @@ module ActionWebService # :nodoc:
yield name, type yield name, type
end end
elsif @type_class.respond_to?(:columns) elsif @type_class.respond_to?(:columns)
i = 0 i = -1
@type_class.columns.each do |column| @type_class.columns.each do |column|
yield column.name, canonical_signature_entry(column.klass, i += 1) yield column.name, canonical_signature_entry(column.klass, i += 1)
end end

View file

@ -10,13 +10,11 @@
</p> </p>
<strong>Method Parameters:</strong><br /> <strong>Method Parameters:</strong><br />
<% i = 0 %>
<% @scaffold_method.expects.each do |type| %> <% @scaffold_method.expects.each do |type| %>
<p> <p>
<label for="method_params[]"><%= type.name.to_s.camelize %></label><br /> <label for="method_params[]"><%= type.name.to_s.camelize %></label><br />
<%= method_parameter_input_fields(@scaffold_method, type) %> <%= method_parameter_input_fields(@scaffold_method, type) %>
</p> </p>
<% i += 1 %>
<% end %> <% end %>
<%= submit_tag "Invoke" %> <%= submit_tag "Invoke" %>

View file

@ -83,7 +83,18 @@ module Test # :nodoc:
end end
def protocol def protocol
@protocol ||= ActionWebService::Protocol::Soap::SoapProtocol.new if @protocol.nil?
@protocol ||= ActionWebService::Protocol::Soap::SoapProtocol.new
else
case @protocol
when :xmlrpc
@protocol = ActionWebService::Protocol::XmlRpc::XmlRpcProtocol.new
when :soap
@protocol = ActionWebService::Protocol::Soap::SoapProtocol.new
else
@protocol
end
end
end end
def is_exception?(obj) def is_exception?(obj)