A piggy bank of commands, fixes, succinct reviews, some mini articles and technical opinions from a (mostly) Perl developer.
Jump to
Showing posts with label subroutines. Show all posts
Showing posts with label subroutines. Show all posts
Have Perl debugger break on a sub in any module
b Name::Of::Module::name_of_sub
Labels:
break,
debugging,
perl,
subroutines
Reasons to split up a very long subroutine into several more subroutines
- Repetition
- If the same or very similar code is being called several times, it should be put into a subroutine to avoid code duplication.
- This is the most obvious reason.
- Clarity
- You should be able to read the code like English
- Reading the code in a top level subroutine or script should give an overview of what it does, without going into detail
- Each subroutine should be able to be easily described in one or two sentences
- Comments are not sufficient for this, as you would still have to scan through many pages to gain an understanding.
- This reason is controversial; Some people say repetition is the only valid reason (see 'dangers' below)
- Testing
- You should be able to test each conceptual part of the system separately.
- It should be obvious to which part of the system a unit test applies.
Dangers of the second two reasons:
- Too much abstraction, difficult to understand and follow code flow
- Code is slow because of overhead of creating and managing subroutines
Labels:
idea,
subroutines,
testing
Perl: Fix DBI::db->disconnect invalidates 1 active statement handle
Add this subroutine to the module:
sub DESTROY {
my ($self) = @_;
# Finish off our statement handle, so we don't get warnings like:
# DBI::db->disconnect invalidates 1 active statement handle
my $sth = $self->{data_handle};
if (blessed($sth) && $sth->can('finish')) {
$sth->finish;
}
}
sub DESTROY {
my ($self) = @_;
# Finish off our statement handle, so we don't get warnings like:
# DBI::db->disconnect invalidates 1 active statement handle
my $sth = $self->{data_handle};
if (blessed($sth) && $sth->can('finish')) {
$sth->finish;
}
}
Labels:
databases,
mysql,
oo,
perl,
subroutines
Trace the execution of subroutines under mod_perl
1) First try one of these to trace every line (doesn't actually capture subroutine names):
Debug::Trace, Debug::LTrace, Devel::Trace, or others.
2) Then if you want to filter the list, try:
perl PERL5DB='sub DB::DB {my @c=caller;return if $c[1] =~ m|/opt/foo/bar| || $c[1] =~ m|/qux/| || $c[1] =~ m|Useless|; print STDERR qq|@c[1,2] ${"::_<$c[1]"}[$c[2]]|}' perl -d path/to/test/file.t
(thanks Brian)
3) Here's one I came up with using Moose:
use Moose;
use Scalar::Util;
use Data::Dumper;
for my $func qw(list all the subroutine names here) {
around $func => sub {
my $orig = shift;
my $self = shift;
warn "Running __PACKAGE__::${func} with:\n";
my $i = 0;
foreach my $param (@_) {
$i++;
# don't display class it$self
next if blessed $param and $param->isa(__PACKAGE__);
if (blessed $param) {
} else {
warn "\t$i = ".Dumper($param);
}
}
# call the original sub
$self->$orig(@_);
}
}
4) Simple way to log all parameters:
my $r=\@_; $logger->debug('entering ', sub { $logger->dump(args => $r) });
5) Simple way to log method name only, without potentially verbose parameters:
$logger->info("entering ".( caller(0) )[3]);
6) Log4perl configuration
Use %M to log the method name, then just log anything, e.g. "entering".
Debug::Trace, Debug::LTrace, Devel::Trace, or others.
2) Then if you want to filter the list, try:
perl PERL5DB='sub DB::DB {my @c=caller;return if $c[1] =~ m|/opt/foo/bar| || $c[1] =~ m|/qux/| || $c[1] =~ m|Useless|; print STDERR qq|@c[1,2] ${"::_<$c[1]"}[$c[2]]|}' perl -d path/to/test/file.t
(thanks Brian)
3) Here's one I came up with using Moose:
use Moose;
use Scalar::Util;
use Data::Dumper;
for my $func qw(list all the subroutine names here) {
around $func => sub {
my $orig = shift;
my $self = shift;
warn "Running __PACKAGE__::${func} with:\n";
my $i = 0;
foreach my $param (@_) {
$i++;
# don't display class it$self
next if blessed $param and $param->isa(__PACKAGE__);
if (blessed $param) {
# try not to dump out huge objects
warn "\t$i = $param\n";} else {
warn "\t$i = ".Dumper($param);
}
}
# call the original sub
$self->$orig(@_);
}
}
4) Simple way to log all parameters:
my $r=\@_; $logger->debug('entering ', sub { $logger->dump(args => $r) });
5) Simple way to log method name only, without potentially verbose parameters:
$logger->info("entering ".( caller(0) )[3]);
6) Log4perl configuration
Use %M to log the method name, then just log anything, e.g. "entering".
Labels:
debugging,
moose,
perl,
subroutines,
trace
Perl sub name in vi status line
" Thanks Ovid!
" http://blogs.perl.org/users/ovid/2011/01/show-perl-subname-in-vim-statusline.html
:set laststatus=2
if ! exists("g:did_perl_statusline")
setlocal statusline+=%(\ %{StatusLineIndexLine()}%)
setlocal statusline+=%=
setlocal statusline+=%f\
setlocal statusline+=%P
let g:did_perl_statusline = 1
endif
if has( 'perl' )
perl << EOP
use strict;
sub current_sub {
my $curwin = $main::curwin;
my $curbuf = $main::curbuf;
my @document = map { $curbuf->Get($_) } 0 .. $curbuf->Count;
my ( $line_number, $column ) = $curwin->Cursor;
my $sub_name;
# for modules, display the current sub name
for my $i ( reverse ( 1 .. $line_number -1 ) ) {
my $line = $document[$i];
if ( $line =~ /^\s*sub\s+(\w+)\b/ ) {
$sub_name = $1;
last;
}
}
# for templates, display the current block starting line
if (not $sub_name) {
for my $i ( reverse ( 1 .. $line_number -1 ) ) {
my $line = $document[$i];
# if ($input_state eq 'next_disc_at_station') {
if ( $line =~ /^}/ ) {
# we're below a block
last;
}
elsif ( $line =~ /^(\S.+{)\s*$/ ) {
$sub_name = $1;
$sub_name =~ s/'/''/g; #' # escape single quotes for vim function
last;
}
}
}
# TODO:
# * Reset sub name if we're out of the sub (check for closing bracket: })
# * Search upwards vertically in the same column for nested blocks
$sub_name ||= '..';
VIM::DoCommand "let subName='$line_number: $sub_name'";
}
EOP
function! StatusLineIndexLine()
perl current_sub()
return subName
endfunction
endif
" http://blogs.perl.org/users/ovid/2011/01/show-perl-subname-in-vim-statusline.html
:set laststatus=2
if ! exists("g:did_perl_statusline")
setlocal statusline+=%(\ %{StatusLineIndexLine()}%)
setlocal statusline+=%=
setlocal statusline+=%f\
setlocal statusline+=%P
let g:did_perl_statusline = 1
endif
if has( 'perl' )
perl << EOP
use strict;
sub current_sub {
my $curwin = $main::curwin;
my $curbuf = $main::curbuf;
my @document = map { $curbuf->Get($_) } 0 .. $curbuf->Count;
my ( $line_number, $column ) = $curwin->Cursor;
my $sub_name;
# for modules, display the current sub name
for my $i ( reverse ( 1 .. $line_number -1 ) ) {
my $line = $document[$i];
if ( $line =~ /^\s*sub\s+(\w+)\b/ ) {
$sub_name = $1;
last;
}
}
# for templates, display the current block starting line
if (not $sub_name) {
for my $i ( reverse ( 1 .. $line_number -1 ) ) {
my $line = $document[$i];
# if ($input_state eq 'next_disc_at_station') {
if ( $line =~ /^}/ ) {
# we're below a block
last;
}
elsif ( $line =~ /^(\S.+{)\s*$/ ) {
$sub_name = $1;
$sub_name =~ s/'/''/g; #' # escape single quotes for vim function
last;
}
}
}
# TODO:
# * Reset sub name if we're out of the sub (check for closing bracket: })
# * Search upwards vertically in the same column for nested blocks
$sub_name ||= '..';
VIM::DoCommand "let subName='$line_number: $sub_name'";
}
EOP
function! StatusLineIndexLine()
perl current_sub()
return subName
endfunction
endif
Labels:
files,
perl,
status,
subroutines,
text editor,
vi
Perl subroutine parameters
Call subs like this:
Define subs like this:
sub_name(
param1 => 'value1',
param2 => 'value2',
);Define subs like this:
sub sub_name {
my $p1 = {@_}->{param1};
my $p2 = {@_}->{param2};
# etc
my $p1 = {@_}->{param1};
my $p2 = {@_}->{param2};
# etc
}
Labels:
namespaces,
parameters,
perl,
subroutines
Subscribe to:
Posts (Atom)