aboutsummaryrefslogtreecommitdiffstats
path: root/actionservice/lib/action_service/client
diff options
context:
space:
mode:
Diffstat (limited to 'actionservice/lib/action_service/client')
-rw-r--r--actionservice/lib/action_service/client/base.rb35
-rw-r--r--actionservice/lib/action_service/client/soap.rb87
-rw-r--r--actionservice/lib/action_service/client/xmlrpc.rb76
3 files changed, 198 insertions, 0 deletions
diff --git a/actionservice/lib/action_service/client/base.rb b/actionservice/lib/action_service/client/base.rb
new file mode 100644
index 0000000000..955887a4d8
--- /dev/null
+++ b/actionservice/lib/action_service/client/base.rb
@@ -0,0 +1,35 @@
+module ActionService # :nodoc:
+ module Client # :nodoc:
+ class ClientError < StandardError # :nodoc:
+ end
+
+ class Base # :nodoc:
+ def initialize(api, endpoint_uri)
+ @api = api
+ @endpoint_uri = endpoint_uri
+ end
+
+ def method_missing(name, *args) # :nodoc:
+ call_name = method_name(name)
+ return super(name, *args) if call_name.nil?
+ perform_invocation(call_name, args)
+ end
+
+ protected
+ def perform_invocation(method_name, args) # :nodoc:
+ raise NotImplementedError, "use a protocol-specific client"
+ end
+
+ private
+ def method_name(name)
+ if @api.has_api_method?(name.to_sym)
+ name.to_s
+ elsif @api.has_public_api_method?(name.to_s)
+ @api.api_method_name(name.to_s).to_s
+ else
+ nil
+ end
+ end
+ end
+ end
+end
diff --git a/actionservice/lib/action_service/client/soap.rb b/actionservice/lib/action_service/client/soap.rb
new file mode 100644
index 0000000000..c617f36589
--- /dev/null
+++ b/actionservice/lib/action_service/client/soap.rb
@@ -0,0 +1,87 @@
+require 'soap/rpc/driver'
+require 'uri'
+
+module ActionService # :nodoc:
+ module Client # :nodoc:
+
+ # Implements SOAP client support (using RPC encoding for the messages).
+ #
+ # ==== Example Usage
+ #
+ # class PersonAPI < ActionService::API::Base
+ # api_method :find_all, :returns => [[Person]]
+ # end
+ #
+ # soap_client = ActionService::Client::Soap.new(PersonAPI, "http://...")
+ # persons = soap_client.find_all
+ #
+ class Soap < Base
+
+ # Creates a new web service client using the SOAP RPC protocol.
+ #
+ # +api+ must be an ActionService::API::Base derivative, and
+ # +endpoint_uri+ must point at the relevant URL to which protocol requests
+ # will be sent with HTTP POST.
+ #
+ # Valid options:
+ # [<tt>:service_name</tt>] If the remote server has used a custom +wsdl_service_name+
+ # option, you must specify it here
+ def initialize(api, endpoint_uri, options={})
+ super(api, endpoint_uri)
+ @service_name = options[:service_name] || 'ActionService'
+ @namespace = "urn:#{@service_name}"
+ @mapper = ActionService::Protocol::Soap::SoapMapper.new(@namespace)
+ @protocol = ActionService::Protocol::Soap::SoapProtocol.new(@mapper)
+ @soap_action_base = options[:soap_action_base]
+ @soap_action_base ||= URI.parse(endpoint_uri).path
+ @driver = create_soap_rpc_driver(api, endpoint_uri)
+ end
+
+ protected
+ def perform_invocation(method_name, args)
+ @driver.send(method_name, *args)
+ end
+
+ def soap_action(method_name)
+ "#{@soap_action_base}/#{method_name}"
+ end
+
+ private
+ def create_soap_rpc_driver(api, endpoint_uri)
+ @mapper.map_api(api)
+ driver = SoapDriver.new(endpoint_uri, nil)
+ driver.mapping_registry = @mapper.registry
+ api.api_methods.each do |name, info|
+ public_name = api.public_api_method_name(name)
+ qname = XSD::QName.new(@namespace, public_name)
+ action = soap_action(public_name)
+ expects = info[:expects]
+ returns = info[:returns]
+ param_def = []
+ i = 1
+ if expects
+ expects.each do |klass|
+ param_name = klass.is_a?(Hash) ? klass.keys[0] : "param#{i}"
+ mapping = @mapper.lookup(klass)
+ param_def << ['in', param_name, mapping.registry_mapping]
+ i += 1
+ end
+ end
+ if returns
+ mapping = @mapper.lookup(returns[0])
+ param_def << ['retval', 'return', mapping.registry_mapping]
+ end
+ driver.add_method(qname, action, name.to_s, param_def)
+ end
+ driver
+ end
+
+ class SoapDriver < SOAP::RPC::Driver # :nodoc:
+ def add_method(qname, soapaction, name, param_def)
+ @proxy.add_rpc_method(qname, soapaction, name, param_def)
+ add_rpc_method_interface(name, param_def)
+ end
+ end
+ end
+ end
+end
diff --git a/actionservice/lib/action_service/client/xmlrpc.rb b/actionservice/lib/action_service/client/xmlrpc.rb
new file mode 100644
index 0000000000..d0d007f871
--- /dev/null
+++ b/actionservice/lib/action_service/client/xmlrpc.rb
@@ -0,0 +1,76 @@
+require 'uri'
+require 'xmlrpc/client'
+
+module ActionService # :nodoc:
+ module Client # :nodoc:
+
+ # Implements XML-RPC client support
+ #
+ # ==== Example Usage
+ #
+ # class BloggerAPI < ActionService::API::Base
+ # inflect_names false
+ # api_method :getRecentPosts, :returns => [[Blog::Post]]
+ # end
+ #
+ # blog = ActionService::Client::XmlRpc.new(BloggerAPI, "http://.../RPC", :handler_name => "blogger")
+ # posts = blog.getRecentPosts
+ class XmlRpc < Base
+
+ # Creates a new web service client using the XML-RPC protocol.
+ #
+ # +api+ must be an ActionService::API::Base derivative, and
+ # +endpoint_uri+ must point at the relevant URL to which protocol requests
+ # will be sent with HTTP POST.
+ #
+ # Valid options:
+ # [<tt>:handler_name</tt>] If the remote server defines its services inside special
+ # handler (the Blogger API uses a <tt>"blogger"</tt> handler name for example),
+ # provide it here, or your method calls will fail
+ def initialize(api, endpoint_uri, options={})
+ @api = api
+ @handler_name = options[:handler_name]
+ @client = XMLRPC::Client.new2(endpoint_uri, options[:proxy], options[:timeout])
+ end
+
+ protected
+ def perform_invocation(method_name, args)
+ args = transform_outgoing_method_params(method_name, args)
+ ok, return_value = @client.call2(public_name(method_name), *args)
+ return transform_return_value(method_name, return_value) if ok
+ raise(ClientError, "#{return_value.faultCode}: #{return_value.faultString}")
+ end
+
+ def transform_outgoing_method_params(method_name, params)
+ info = @api.api_methods[method_name.to_sym]
+ signature = info[:expects]
+ signature_length = signature.nil?? 0 : signature.length
+ if signature_length != params.length
+ raise(ProtocolError, "API declares #{public_name(method_name)} to accept " +
+ "#{signature_length} parameters, but #{params.length} parameters " +
+ "were supplied")
+ end
+ if signature_length > 0
+ signature = Protocol::XmlRpc::XmlRpcProtocol.transform_array_types(signature)
+ (1..signature.size).each do |i|
+ i -= 1
+ params[i] = Protocol::XmlRpc::XmlRpcProtocol.ruby_to_xmlrpc(params[i], signature[i])
+ end
+ end
+ params
+ end
+
+ def transform_return_value(method_name, return_value)
+ info = @api.api_methods[method_name.to_sym]
+ return true unless signature = info[:returns]
+ signature = Protocol::XmlRpc::XmlRpcProtocol.transform_array_types(signature)
+ Protocol::XmlRpc::XmlRpcProtocol.xmlrpc_to_ruby(return_value, signature[0])
+ end
+
+ def public_name(method_name)
+ public_name = @api.public_api_method_name(method_name)
+ @handler_name ? "#{@handler_name}.#{public_name}" : public_name
+ end
+ end
+ end
+end