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:
parent
d7a7d85dbd
commit
361dcad8fd
11 changed files with 226 additions and 230 deletions
|
@ -25,7 +25,7 @@ task :default => [ :test ]
|
|||
# Run the unit tests
|
||||
Rake::TestTask.new { |t|
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -29,8 +29,7 @@ module ActionWebService # :nodoc:
|
|||
|
||||
def cast_expects(api_method, params) # :nodoc:
|
||||
return [] if api_method.expects.nil?
|
||||
i = -1
|
||||
api_method.expects.map{ |type| cast(params[i+=1], type) }
|
||||
api_method.expects.zip(params).map{ |type, param| cast(param, type) }
|
||||
end
|
||||
|
||||
def cast_returns(api_method, return_value) # :nodoc:
|
||||
|
|
|
@ -78,12 +78,10 @@ module ActionWebService # :nodoc:
|
|||
expects = method.expects
|
||||
returns = method.returns
|
||||
param_def = []
|
||||
i = 0
|
||||
if expects
|
||||
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
|
||||
|
|
|
@ -79,7 +79,7 @@ module ActionWebService # :nodoc:
|
|||
raise(ContainerError, "no such web service '#{web_service_name}'")
|
||||
end
|
||||
service = info[:block]
|
||||
service ? instance_eval(&service) : info[:object]
|
||||
service ? self.instance_eval(&service) : info[:object]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -121,8 +121,7 @@ module ActionWebService # :nodoc:
|
|||
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 }
|
||||
params = api_method.expects.zip(params).map{ |type, param| "#{type.name}=>#{param.inspect}" }
|
||||
else
|
||||
params = params.map{ |param| param.inspect }
|
||||
end
|
||||
|
@ -255,11 +254,9 @@ module ActionWebService # :nodoc:
|
|||
end
|
||||
else
|
||||
expects = method.expects
|
||||
i = 1
|
||||
expects.each do |type|
|
||||
binding = marshaler.register_type(type)
|
||||
xm.part('name' => type.name, 'type' => binding.qualified_type_name('typens'))
|
||||
i += 1
|
||||
end if expects
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,13 +40,10 @@ module ActionWebService # :nodoc:
|
|||
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)]
|
||||
params = param_types.zip(params).map do |type, param|
|
||||
param_def << ['in', type.name, marshaler.lookup_type(type).mapping]
|
||||
[type.name, marshaler.ruby_to_soap(param)]
|
||||
end
|
||||
else
|
||||
params = []
|
||||
|
|
|
@ -9,12 +9,9 @@ module ActionWebService # :nodoc:
|
|||
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
|
||||
name, spec = spec.keys.first, spec.values.first
|
||||
end
|
||||
type = spec
|
||||
if spec.is_a?(Array)
|
||||
ArrayType.new(canonical_signature_entry(spec[0], 0), name)
|
||||
else
|
||||
|
@ -173,7 +170,7 @@ module ActionWebService # :nodoc:
|
|||
yield name, type
|
||||
end
|
||||
elsif @type_class.respond_to?(:columns)
|
||||
i = 0
|
||||
i = -1
|
||||
@type_class.columns.each do |column|
|
||||
yield column.name, canonical_signature_entry(column.klass, i += 1)
|
||||
end
|
||||
|
|
|
@ -10,13 +10,11 @@
|
|||
</p>
|
||||
|
||||
<strong>Method Parameters:</strong><br />
|
||||
<% i = 0 %>
|
||||
<% @scaffold_method.expects.each do |type| %>
|
||||
<p>
|
||||
<label for="method_params[]"><%= type.name.to_s.camelize %></label><br />
|
||||
<%= method_parameter_input_fields(@scaffold_method, type) %>
|
||||
</p>
|
||||
<% i += 1 %>
|
||||
<% end %>
|
||||
|
||||
<%= submit_tag "Invoke" %>
|
||||
|
|
|
@ -83,7 +83,18 @@ module Test # :nodoc:
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
def is_exception?(obj)
|
||||
|
|
Loading…
Reference in a new issue