Martin Tithonium (tithonium) wrote,
Martin Tithonium

Perl vs Ruby

So, I have these tools, you see. Both written in perl.
One, given a database and login, generates perl modules to interface to that database and manipulate it. It generates this sort of thing:

package ContentionStalker::DB::UsersBase;
use base ContentionStalker::DB::TABLE;

our $table = 'users';
our $fields = {id => q[id], name => q[name], login => q[login], password => q[password], active => q[active]};
our $primary_key = {id => 1};
our $autofield = 'id';

sub new {
my $self = shift->SUPER::new(@_);
unless(scalar(grep { $self->{$_} } keys %$primary_key) == scalar(keys %$primary_key)) {
$self->{_new} = 1;
return $self;


This tool is useful when you've already designed the database.

The other, I wrote to help design the databases. Given a /description/ of a database, it will generate the SQL to create it, a DOT file (and from that a PNG) for graphviz that illustrates the relationships between the tables, and - as of today - a set of ruby classes for interfacing to and manipulating that database.

require 'TABLE'

class UsersBase < TABLE
attr_accessor :id, :name, :login, :password, :active
private :id=

def initialize(row, db=nil)
@db = db
row.each { |k,v| send("#{k}=", v) }


Now, the perl version above is much more mature and includes the ability to do things like /save/ your changes (hence things like $primary_key and $autofield)
Also, the initialize in the ruby version really should move to the TABLE class, as it has in perl (the SUPER::new call)

Here's the perl version of that init:

sub new {
my $class = shift;
my $self = ref($_[0]) ? {%{$_[0]}, _db => $_[1]} : {@_};
$self->{creation_time} ||= time();
return bless $self, $class;

It takes the arguments passed, and if the first is a reference, it assumes you called it new( {data}, db object ) [as would happen if you queried the database and inserted a row into an object], otherwise it assumes you're creating a new object and just sticks all your args into a hash. It's very flexible. I can pass any random crap I want in that hash, it gets blessed blind. My new() doesn't need to know anything about the fields a given class has, which can be a pain to deal with when most of your code lives in the parent object.
The accessors and setters are defined at package load time, based on the fields listed in the class, and the save function will only send the ones that actually exist to the database. Easy. New doesn't need to know anything or worry about anything.

So, the ruby version..It's not setting creation time, or the new flag, 'cause I don't have saving and so I don't need them yet. For now, I just need to populate the instance variables with the data from the row.
Most of the help I found searching the internet for this sort of thing led me to believe my only choice would be something like this:

def initialize(row, db=nil)
@db = db
@id = row["id"] if row["id"]
@name = row["name"] if row["name"]
@login = row["login"] if row["login"]
@password = row["password"] if row["password"]
@active = row["password"] if row["active"]

Which is.. inelegant at best. It offends the sensibilities. NOT an acceptable solution, even if it IS autogenerated. So, after much searching, I finally came across something that led to this:

def initialize(row, db=nil)
@db = db
row.each { |k,v| send("#{k}=", v) }

Which basically means, for every key/value, call key= on self and pass value as an argument.
Which works, mind you. But it means that the database and the code must be kept strongly synced. I can't go adding fields to the database because I'm planning to expand some functionality unless I'm also, at the same time, going to update all the code that's talking to the database to let it know about those fields. Basically, it means any database structure change requires an outage. Plus, the syntax of send(foo, bar) rather than is, for whatever reason, distasteful to me. BUT, it's one line, instead of one per value. so, it's what I'm using for the moment. I'm sure I could make it 'better' with a little introspection.

But, here's the point of this post.
In perl, I can say here is some data and then this data is an object. BEHOLD.. In ruby, not so much, it seems.
Thus far, ruby tastes more like here is some data, I shall take your data and insert it into this blank object, if I feel like it.
I'm not sure ruby will be worth it to me, just to gain some pretty syntactic sugar I've been wanting for a while, if it means I lose the flexibility I've grown accustomed to.
Tags: computers suck, perl, ruby
  • Post a new comment


    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

    Your IP address will be recorded