XML-RPC under Ruby on Rails

On a current project, I needed to develop a series of web services for a custom single-signon (unified login) for a bunch of different websites to share. The project needed to be in Ruby on Rails, since that is what is available to the servers, and needed to use a protocol which PHP, Java and Ruby could all understand.

At first I tried to investigate using ActiveResource, but I found this to be excessively Rails-centric, and it only seemed to provide basic CRUD functionality. I needed these webservices to do a lot more work, with a lot more parameters. Since the Rails community (and a lot of web developers in general) seem to rave about RESTful services, my next direction was to write a custom series of REST-based webservices. As I proceeded, it became glaringly obvious that REST services have on major shortcoming – complex data.

Since the basic idea of REST is that all parameters become part of the URI itself (something, I might add, ActiveResource violates right away), you immediately have a problem when it comes to things like street addresses. Everyone’s solution is to use basic HTTP parameters or to URL-encode the data, but to me these solution tainted the point of REST, and made for some truly hideous looking URL’s, respectively.

I also didn’t like the lack of type control. This same lack also negated using JSON-RPC or even some custom YAML-based solution. All the experiences I had with SOAP left a bad taste in my mouth for that one, so what was I to do?

Thankfully, I discovered that Ruby had XML-RPC functionality built right in. But the problem arose that all examples I could find of using it (since Ruby’s documentation is complete and utter dog-sh*t) only showed the XML-RPC server running in stand-alone mode. I certainly couldn’t do this. So after much tinkering, I herewith present a controller class which you can extend to offer very simple XML-RPC calls from within a Rails environment:

#
# This class provides a framework for XML-RPC services on Rails
#
require 'xmlrpc/server'
class WebServiceController < ApplicationController

  # XML-RPC calls are not session-aware, so always turn this off
  session :off

  def initialize
    @server = XMLRPC::BasicServer.new
    # loop through all the methods, adding them as handlers
    self.class.instance_methods(false).each do |method|
      unless ['index'].member?(method)
        @server.add_handler(method) do |*args|
          self.send(method.to_sym, *args)
        end
      end
    end
  end

  def index
    result = @server.process(request.body)
    puts "\n\n----- BEGIN RESULT -----\n#{result}\n----- END RESULT -----\n"
    render :text => result, :content_type => 'text/xml'
  end

end

Here is a working example of using the above code:

class StringController < WebServiceController

  def upper_case(s)
    s.upcase
  end

  def down_case(s)
    s.downcase
  end

end

Invoking the remove method couldn’t be any simpler:

require 'xmlrpc/client'
require 'pp'


server = XMLRPC::Client.new2("http://localhost:3010/string")

result = server.call("upper_case", "This is my string")
pp result

result = server.call("down_case", "This is my string")
pp result

I certainly hope this simple bit of Ruby will help anyone else who may have suffered trying to figure this out as I have.

Enjoy!