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!