Sunday, February 22, 2009

today's module: Scope::Guard

Updated: Thanks for Eddy to point out the errors. :)

I have been very happy with Scope::Guard recently.

mainly the module can reduce the size of the code if you use it correctly.

for example, we have some piece of code like follows to deal with Sphinx error.
sub while_error {
my ( $ret ) = @_;

my $use_db = { use_db_please => 1 };

# 1, connection to {localhost}:{3312} failed: Connection refused
if ($ret->{error} =~ /Connection refused/is) {
faked_try_to_reconnect();
print STDERR "$ret->{error}\n";
return $use_db;
}
# 2, received zero-sized searchd response
elsif ($ret->{error} =~ /zero-sized searchd/) {
faked_try_to_re_search();
print STDERR "$ret->{error}\n";
return $use_db;
}
# 3, unknown local index
elsif ( $ret->{error} =~ /unknown local index/ ) {
faked_try_to_re_index();
print STDERR "$ret->{error}\n";
return $use_db;
}
# 4, recv: Connection reset by peer
elsif ( $ret->{error} =~ /Connection reset by peer/ ) {
print STDERR "$ret->{error}\n";
return $use_db;
}
return $ret;
}
OK, it's not so ugly.
but with Scope::Guard, we can rewrite it much more clear and small-size.
sub while_error {
my ( $ret ) = @_;

my $use_db = { use_db_please => 1 };

my $sg = Scope::Guard->new( sub {
print STDERR "$ret->{error}\n";
} );

# 1, connection to {localhost}:{3312} failed: Connection refused
if ($ret->{error} =~ /Connection refused/is) {
faked_try_to_reconnect();
return $use_db;
}
# 2, received zero-sized searchd response
elsif ($ret->{error} =~ /zero-sized searchd/) {
faked_try_to_re_search();
return $use_db;
}
# 3, unknown local index
elsif ( $ret->{error} =~ /unknown local index/ ) {
faked_try_to_re_index();
return $use_db;
}
# 4, recv: Connection reset by peer
elsif ( $ret->{error} =~ /Connection reset by peer/ ) {
return $use_db;
}

$sg->dismiss();

return $ret;
}
I'm not sure if you're happy with it or not, but I like it. :)
I had used it in http://code.fayland.org/fayland/CPAN/day_day_up/lib/DayDayUp/Backup.pm. that's just another example.

Thanks and Enjoy!

2 Comments:

Anonymous Eddy said...

Hi Fay,

Thanks for the info about Scope::Guard - looks very interesting indeed.

However, trying your code (with a little modification below) it seems that whenever an exception is raised $sg never returns {use_db_please => 1} to the caller?


#!/usr/bin/perl

use strict;
use warnings;

use Scope::Guard;
use Data::Dumper;

my $e = while_error({ error => 'Connection refused' });
print 'return code: '. Dumper $e;

$e = while_error({ error => 'unknown local index' });
print 'return code: ' . Dumper $e;

$e = while_error({ error => 'no error!' });
print 'return code: ' . Dumper $e;


sub while_error {
my ( $ret ) = @_;

print '$ret : ' . Dumper $ret;

my $sg = Scope::Guard->new( sub {
print STDERR "$ret->{error} : THROW BY Scope::Guard\n";
return { use_db_please => 1 };
} );

# 1, connection to {localhost}:{3312} failed: Connection refused
if ($ret->{error} =~ /Connection refused/is) {
print "in 1st if\n";
return;
}
# 2, received zero-sized searchd response
elsif ($ret->{error} =~ /zero-sized searchd/) {
print "in 2nd if\n";
return;
}
# 3, unknown local index
elsif ( $ret->{error} =~ /unknown local index/ ) {
print "in 3rd if\n";
return;
}
# 4, recv: Connection reset by peer
elsif ( $ret->{error} =~ /Connection reset by peer/ ) {
print "in 4th if\n";
return;
}

print "Ahoy there... before calling dismiss()\n";

$sg->dismiss();

print "Ahoy there... end of the sub\n";

return $ret;
}

2/23/2009 9:41 AM  
Blogger Fayland said...

Sorry, you are correct. the return is captured by Scope::Guard->new( sub { } )'s "sub {}".

so what we need do is something like

my $sg = Scope::Guard->new( sub {
print STDERR "$ret->{error} : THROW BY Scope::Guard\n";
} );

# 1, connection to {localhost}:{3312} failed: Connection refused
if ($ret->{error} =~ /Connection refused/is) {
print "in 1st if\n";
return { use_db_please => 1 };
}

I'll fix the example. Thanks.

2/23/2009 9:49 AM  

Post a Comment

Links to this post:

Create a Link

<< Home