JSON “RPC” from Perl to Ruby / Webrick
So I have a bunch of software I’d written in Ruby because it was “The Right Choice™” and I need to make it talk to a stack of software I’d written in Perl (because “I Was An Idiot™”). Specifically, I need the Perl to be able to call the Ruby. I had a quick dig around, and there’s a Perl module Inline::Ruby which, on the face of it, would do the job. Unfortunately, Inline::Ruby is version 0.0.2 software and “Doesn’t Work So Good™”. Not only that, but I’d really like some persistence in the Ruby code so that I don’t have to new up the state every time I make a call. What this calls for, then, is some IPC
Linux IPC comes in three varieties - Sockets, Files, and Shared Memory. Files is obviously a pretty poor idea in that you’ll either be polling a lot or writing nasty dnotify stuff. Shared Memory is okay but extremely unportable. Sockets are a pretty good idea, and if you choose an IP socket you get the advantage that you can run the communicating processes on different machines (assuming they don’t need to share other local resources).
So you’ve selected TCP/IP as a transport, but you’ve then got all sorts of irritating high-level protocol implementation to do. Unless…
#!/usr/bin/ruby
# A Simple Webserver for JSON RPC
require 'webrick'
require 'json'
require 'yaml'
include WEBrick
include YAML
server = HTTPServer.new(:Port => 8000)
server.mount_proc("/rhapsodise") do |request, response|
response['Content-Type'] = “application/json”
response.body = handle_json_request(request)
end
trap(”INT”) { server.shutdown }
def handle_json_request(request)
object = JSON.parse(request.body)
YAML.dump(object)
end
server.start
and then…
#!/usr/bin/perl -w
#
# A Simple Perl Client for JSON RPC
use strict;
use JSON;
use LWP;
my $actionurl = "http://localhost:8000/rhapsodise";
my $ua = LWP::UserAgent->new();
$ua->agent("JSONClient/0.1");
my $object = { test => "fish",
wibble => ["meep", "flange" ] };
my $json = objToJson($object);
print “$json\n”;
my $req = HTTP::Request->new(POST => $actionurl);
$req->content_type(’application/json’);
$req->content($json);
my $res = $ua->request($req);
if ($res->is_success)
{
print “Succeeded:\n” . $res->content;
my $result = jsonToObj($res->content);
}
else
{
print “Failed\n”;
}
… et voila. For less code than it might cost you to bind the socket in C(!) you’ve got a nice, portable way of making Ruby calls from your Perl through the Webrick webserver. Even better, you get to use Perl and JSON, as exhorted in my previous post. A dash more code for the server, and it’s very nearly useful:
def error_object(message)
return { :error => message }.to_json
end
def handle_json_request(request)
command = JSON.parse(request.body)
if command["method"] == nil
return error_object(”No method suppled”)
end
if command["method"] =~ /^rd_/
return self.send(command["method"], command)
end
return error_object(”No matching method”)
end
def method_missing(m, *args)
return error_object(”Invalid command: #{m}”)
end
Notice that I’m prefixing my RPC-able methods with ‘rd_’. This probably isn’t much more secure than not bothering, but is a useful kind of Hungarian Notation for the methods. I’m not worrying too much about security on this one since I write both ends and I trust the link across which the packets run - you’d need to take appropriate precautions if that wasn’t true.