talkingCode

Archive for the perl category

JSON “RPC” from Perl to Ruby / Webrick

posted by codders in code, perl, ruby

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.

Perl and JSON: heaven =~ m/made/ ?

posted by codders in ajax, code, perl

If you read the above as ‘Heaven Made’, I’m afraid you lose code Dingbats. Try harder.

Perl is a nice language and JSON is a good way to structure data if you’re about to send over your favourite IPC mechanism. And there are, it seems, many similarities between the way JSON structures data and the way Perl structures data. Unfortunately, I don’t have an awful lot to write on the topic, so as well as briefly introducing the basics of creating JSON in Perl I will, with the irrefutable logic of a professor of philosophy and all the religious sensitivity of a Dan Brown novel, be examining the evidence for Larry Wall being God.

We’re going to start with a motivating example. I want to represent the following JSON object in Perl:

{
  keywords:['cat', 'fish', 'pig'],
  somekey: ’somevalue’,
  someobject: {
    nested: ‘value’,
    another: ‘nestedvalue’,
    a_number: 4
  }
  simple:true
}

I don’t know if you’ve come across JSON before, but it’s basically the way Javascript (ECMAScript) represents objects. Your modelling tools there are strings, numbers and booleans, as you might expect, and some collecting constructs. Specifically, JSON has a way to associate string keys with values - within the ‘{}’, thing on the left of the ‘:’ is the key and the thing on the right is the value, with commas seperating key-value pairs - and a way to create lists of values - inside the ‘[]‘, again seperated by commas.

Now, if you’re familiar with Hashes and Lists (or Arrays), you’ll see that JSON’s collection structures are exactly those. How do we represent those in Perl? Well, we use ‘{}’ for a hash and ‘[]‘ for a list. What’s more, we use commas to separate are values. I know what you’re thinking - “It’s mere coincidence”. The kind of coincidence that sees the Earth just the right temperature to support life, as a function of its being in just the right place in our solar system? Yes. _That_ kind of mere coincidence.

So how, naively, would I go about representing the above in Perl?

my $object = {
  keywords => ['cat', 'fish', 'pig'],
  somekey => ’somevalue’,
  someobject => {
    nested => ‘value’,
    another => ‘nestedvalue’,
    a_number => 4
  }
  simple => true
};

But all I’ve done so far is replace the ‘:’ with ‘=>’. It can’t be that simple, can it? That would be too easy. Almost as though, at the start of the universe, someone had determined that JSON and Perl would have basically the same syntax. Could it be that this synergy is part of God’s great plan? Well, not quite. Turns out you can’t just whack ‘true’ in there for your boolean value. I’ve defined a little function here to return ‘true’:

sub json_true()
{
  return bless ( {value => "true"}, "JSON::NotString" );
}

That’s ugly as sin, I know. (AS SIN! And it’s BLESSED! This conspiracy writes itself.) Anyway. Why would a God, in designing this synergy, allow such evil? Here I distract you by shouting “Look out! Earthquake!”, say “Natural Evil” three times, and we sweep that under the carpet.

The only piece missing from this puzzle are the final bits of fairy dust that’ll actually let you convert JSON to a format suitable for sending over the wire. May I introduce:

use JSON;

my $obj = { test => 'object' };
my $json_string = objToJson($obj);
my $rebuilt_object = jsonToObj($json_string);

Simple as. But… is Larry Wall God? Could anyone but the supreme being have designed this happy coincidence into the starting conditions of the universe? That a humble pre-WWW scripting language would turn out to have approximately similar syntax to one of the high-level interchange protocols powering the modern web? Well, if Larry Wall were God I’d certainly be disappointed, and that’s to say nothing of the fuss that world religions would kick up. And, in fairness, this isn’t a coincidence that has to have been orchestrated from the dawn of the universe. You’d really only need to start thinking about it in… say… 1962.

Maybe Larry isn’t God. Maybe he’s just Al Gore.

Recent Posts
Recent Comments
About Us
Franta: and Step 7: Become frustrated again...
Dave: hey, just wondering if there is a working demo somewhere. The above demo does not se...
Flemming Frandsen: Hi, I'd just like to thank you for posting this, it was an imeasureable help to me, s...
qbJim: Doing it with C++ iostreams would have saved remembering the parameter list to read a...
C-rat: I better put the Prelude on my reading list too. I might use init as a good example o...

This is the personal blog of a professional software engineer. This site and the views expressed on it are in no way endorsed by the RIAA.