Tuesday, December 7, 2010

Day 7: Alternatives to Data::Dumper

Data::Dumper is a handy module. But I prefer Data::Dump: Its output is compacter and already sorted (hash keys). Data::Dumper also has a setting to sort the keys ($Data::Dumper::Sortkeys), but with Data::Dump "it just works". :)

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;
use Data::Dump;

my $small = {a => 1, b => 2, c => 3, d => 4, e => 5};
my $large = {
    aaa => 'data set 1',
    bbb => 'data set 2',
    ccc => 'data set 3',
    ddd => 'data set 4',
    xxx => $small,
};

print "Data::Dumper:\n";
print Dumper($small);
print Dumper($large);

print "\nData::Dump:\n";
dd($small);
dd($large);

The following output shows the default settings of each module:
Data::Dumper:
$VAR1 = {
          'e' => 5,
          'c' => 3,
          'a' => 1,
          'b' => 2,
          'd' => 4
        };
$VAR1 = {
          'bbb' => 'data set 2',
          'xxx' => {
                     'e' => 5,
                     'c' => 3,
                     'a' => 1,
                     'b' => 2,
                     'd' => 4
                   },
          'aaa' => 'data set 1',
          'ccc' => 'data set 3',
          'ddd' => 'data set 4'
        };

Data::Dump:
{ a => 1, b => 2, c => 3, d => 4, e => 5 }
{
  aaa => "data set 1",
  bbb => "data set 2",
  ccc => "data set 3",
  ddd => "data set 4",
  xxx => { a => 1, b => 2, c => 3, d => 4, e => 5 },
}

As I said, Data::Dump is compacter. :)
If a structure fits into one line, it will do so.

Another neat module is Data::Dumper::Concise. It is an "optimal" configuration of Data::Dumper. But I don't care, I use Data::Dump for that. But I like its Devel::Dwarn module. It exports Dwarn, which allows you this:
sub ... {
    my $ua = ...
    ...
    return Dwarn $ua->get(...);
}
Normally you would have to rewrite your code and assign the result of the method call to a temporary variable and dump it. With Dwarn the changes are minimal. Also exported: DwarnS for scalar context and DwarnL for list context.

Have a look at Data::Dump::Streamer for another alternative to Data::Dumper.

Links:

Monday, December 6, 2010

Day 6: Who has the longest ...

... sub routine? :)

Today I want to give you an example of PPI. PPI stands for "Parse Perl Isolated" and is a mammoth project of Adam Kennedy. PPI builds an abstract syntax tree out of your Perl code. Similar to DOM this tree is called PDOM (Perl Document Object Model). Have a look at the documentation for an overview.

Let's use PPI to count the number of lines in a sub routine:
#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;
use File::Find;
use PPI;

my @controller = ();
File::Find::find(
    {wanted => \&wanted, no_chdir => 1},
    qw(apps lib), ### change me ###
);

sub wanted {
    ### change me ###
    if (m!/Controller(/.+)?\.pm$!) {
        push @controller, $_;
    }
}

my @subs = ();
foreach my $file (@controller) {
    my $doc = PPI::Document->new($file);

    my $subs = $doc->find('Statement::Sub');
    next unless $subs;

    foreach my $sub (@$subs) {
        next unless $sub->name;
        ### change me ###
        next unless $sub->name =~/^op_/;

        my @lines = split /\n/, $sub->content;
        push @subs, {
            controller => $file,
            op_name    => $sub->name,
            lines      => scalar @lines,
        };
    }
}

@subs = sort { $b->{lines} <=> $a->{lines} } @subs;

print Dumper [@subs[0..9]];

The first part of the script uses File::Find to fill the @controller array with the file names of the wanted Perl modules. (I used this example at work to count the length of sub routines in our web framework.)

The second part uses PPI to build the PDOM tree ($doc) and query for sub routine nodes ('Statement::Sub'). The name of the sub has to start with 'op_' - you may want to change this. The number of lines are stored (together with the module and sub routine name) and finally the top 10 is printed.

So, Ovid: Show us some numbers of your fairly long methods ... :)

Links:

Sunday, December 5, 2010

Day 5: Check your Google PageRank with WWW::Google::PageRank

The Google PageRank is one factor in the placement of search results for Google. With WWW::Google::PageRank you can query the PageRank of any URL:
use WWW::Google::PageRank;
my $pr = WWW::Google::PageRank->new;
print scalar($pr->get('http://www.perl-uwe.de')), "\n";

Using the little advice from day 3 you can write it in one line:
perl -Maliased=WWW::Google::PageRank,PR -e 'print PR->new->get("http://is.gd")."\n"'

Ok, it does not quite fit into one line on this blog. :)
But I want to show you some options to '-M' here. Lets say you want use List::Util's 'max' function in your one liner. This is done by the '=' separator:
perl -MList::Util=max ...

All further imports have to be separated by ',':
perl -MList::Util=max,min ...

In the above example of aliased I use the two parameter form to give the alias an even shorter name ('PR'). The default would have been 'PageRank'.

Links:

Saturday, December 4, 2010

Day 4: inheritable DATA sections with Data::Section

Damn, RJBS beat me on Sub::Exporter / Sub::Import! I wanted to write about them too. But he was faster. Maybe this time, I am faster. :)

Today I want to show you Data::Section, which is (like Sub::Exporter and Sub::Import) from Ricardo Signes. It has two use cases (and I will show an example for both):
  • inheritable DATA sections
  • multiple 'files' (hunks) in the DATA section
Consider the following modules:
# file: Parent.pm
package Parent;
use Data::Section -setup => {default_name => 'default'};
1;
__DATA__
__[file1]__
Master version of file 1.
__[file2]__
Master version of file 2.

# file: Child.pm
package Child;
use base 'Parent';
1;
__DATA__
Default name content.
__[file1]__
Custom version of file 1.

Now you can use Data::Section's methods for retrieval on these modules:
use Child;
use Data::Dump;
print ${Child->section_data('file1')};
print dd(Child->merged_section_data);

Each data section is returned as scalar reference. The output is the following:
Custom version of file 1.
{
  default => \"Default name content.\n",
  file1   => \"Custom version of file 1.\n",
  file2   => \"Master version of file 2.\n",
}

I used Data::Section in my experimental web framework UVWeb for inheritable templates in CRUD actions. I stopped working on UVWeb and switched all my projects to Catalyst. But this was a feature I especially liked.

Have a look at Mojo::Command (and Mojolicious::Command::Generate::App) for another approach and an example use case.

Links:

Friday, December 3, 2010

Day 3: shorter package names with aliased

Day 1 saved us some characters (for FindBin), but this was just once per script. With aliased you ease typing of long class names:
use aliased 'Rose::HTML::Form::Field::DateTime' => 'DateField';
my $dt = DateField->new(...);

When you leave out the second argument, your alias is 'DateTime' (which would be a bad idea, because this clashes with the CPAN module DateTime).

If you have a complex class structure under a common name space, have a look at aliased::factory.

Links:

Thursday, December 2, 2010

Day 2: a better grep - ack

If you are a Unix/Linux user you propably know grep. It lets you search in the contents of files.

Back when I was using SVN as version control system, there was one annoying thing: SVN stored all versioned files twice. There always was a copy in the .svn directory. So, when I was using
grep -r 'use strict'
I also got these copies as results. By typing
grep -r 'use strict' | grep -v .svn
I only got the desired results.

It was at this time that I came across ack - and was immediately sold. Ack ignores also other version control directories and common backup files (e. g. ending with '~').

Also, it lets you specify the file types:
ack --perl 'use strict'

There are more options, have a look at the documentation.

Ack is available through CPAN - just install App::Ack. But there is also an standalone version which includes every module into one big script:
curl http://betterthangrep.com/ack-standalone > ~/bin/ack && chmod 0755 !#:3

This standalone version is also used in the ack-in-project and AckMate TextMate bundle. (TextMate is a popular text editor for MacOS.) I use TextMate at work - the AckMate bundle works very well.

Ack runs also on Windows (its pure perl). Under Debian and Ubuntu the package is called ack-grep. (Ack is a Kanji code converter there.)

Links:

Wednesday, December 1, 2010

Day 1: add relative paths to @INC with rlib

Welcome to day 1 of the Perl-Uwe Advent calendar. We start with a very small CPAN module, which is one of my favorites. It's quite old (its last release was 1998), but it still does its job. :)

When my Perl projects are larger than a single script file, I usually create folders like this:
bin/
lib/
t/

You know what goes into what folder. And especially in the scripts which use your own modules you want to include something like this:
use lib '../lib';

This works fine when you call this script from your project root directory, but nowhere else. :(

rlib to the rescue!

use rlib '../lib';

This is equivalent to:
use FindBin;
use lib "$FindBin::Bin/../lib";

Like "use lib" you can add more than one directory to @INC. If you specify no path at all, rlib uses "../lib" and "lib" as defaults. So, in our example we could have written "use rlib;" alone.

Links: