aboutsummaryrefslogblamecommitdiffstats
path: root/actionwebservice/lib/action_web_service/client/xmlrpc_client.rb
blob: 27fe537404076baec54739b4b9801ac7d07b4332 (plain) (tree)
1
2
3
4
5
6
7
8
9
10


                       
                                 





                                       
                                                      



                                                                
                                                                                                             




                                                                    
                                                                    










                                                                                                                 
                                                        











                                                                                      





                                                                                                  
             




                                                           
                                                                

                                                                            






                                                             

                                                             
                                                                  

                                                                         








                                                                         
require 'uri'
require 'xmlrpc/client'

module ActionWebService # :nodoc:
  module Client # :nodoc:

    # Implements XML-RPC client support
    #
    # ==== Example Usage
    #
    #   class BloggerAPI < ActionWebService::API::Base
    #     inflect_names false
    #     api_method :getRecentPosts, :returns => [[Blog::Post]]
    #   end
    #
    #   blog = ActionWebService::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 ActionWebService::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])
        @marshaler = WS::Marshaling::XmlRpcMarshaler.new
      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]
          expects = info[:expects]
          expects_length = expects.nil?? 0 : expects.length
          if expects_length != params.length
            raise(ClientError, "API declares #{public_name(method_name)} to accept " +
                               "#{expects_length} parameters, but #{params.length} parameters " + 
                               "were supplied")
          end
          params = params.dup
          if expects_length > 0
            i = 0
            expects.each do |spec|
              type_binding = @marshaler.register_type(spec)
              info = WS::ParamInfo.create(spec, type_binding, i)
              params[i] = @marshaler.marshal(WS::Param.new(params[i], info))
              i += 1
            end
          end
          params
        end

        def transform_return_value(method_name, return_value)
          info = @api.api_methods[method_name.to_sym]
          return true unless returns = info[:returns]
          type_binding = @marshaler.register_type(returns[0])
          info = WS::ParamInfo.create(returns[0], type_binding, 0)
          info.name = 'return'
          @marshaler.transform_inbound(WS::Param.new(return_value, info))
        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