Perhaps the best feature of Ruby is blocks. Blocks as first class objects with syntactic support for easy use. A block is a closure- it maintains the envrionment it was created in even though it is called from another function. Perl has closures too, and we can use them to do powerful things.
Block usage in Ruby and Perl.
Ruby
call_with_block() do |block_arg| print block_arg end
Or
call_with_block() {|block_arg| print block_arg}
Perl
call_with_block( sub { my($block_arg) = @_; print $block_arg; });
Applying blocks
Ruby
def call_with_block yield 'hello' end
Or
def call_with_block(&block) block.call 'hello' end
Perl
sub call_with_block { my($block) = @_;
$block->('hello');
}
Blocks are powerful, so lets use that power to abstract out something that really sucks in Perl- error handling. Error handling is so important that it should be built into the language. Ruby has done a good job with making exceptions easy to use to handle errors.
begin code_with_possible_error rescue print "exception thrown" else print "nothing wrong" ensure print "this is printed no matter what" end
It is easy to combine this with blocks to produce code that gracefully handles errors. Here we will extend the low level ruby MySQL driver.
class MysqlWithTransactions < Mysql def transaction query('BEGIN') yield # run the block rescue => e rollback raise e else commit end end
Now running transactional code is as easy as putting it in the block
connection.transaction do # execute multiple queries in a transaction end
We can write the same code in Perl, but we first need a similiar exception handling construct. Blocks to the rescue! In Perl, an error must be trapped inside an eval block, and the error will be placed in the $@ variable. Below we implement the equivalent to Ruby’s exception handling construct.
sub begin_rescue_else_ensure {
my($main, $rescue, $else, $ensure) = @_;
sub {
eval { $main->(); };
my $e = $@;
if($e){ $rescue->(); }
else { $else->(); }
$ensure->();
}
}
Now to create the transaction code. We will do this using Perl’s DBI library in a non-object oriented style where the connection is passed as a parameter instead of being implicit in the envrionment.
sub transaction { my($connection, $main) = @_;
begin_rescue_else_ensure(
$main,
sub { $connection->begin_work; },
sub { $connection->rollback; },
{ $connection->commit; },
sub {}
)->();
}
And now we have easy transactional queries.
transaction($connection, sub { # execute multiple queries in a transaction });