Class::Sniff's killer feature: it walks the symbol table

Ovid on 2009-02-15T16:26:25

This code:

#!/usr/bin/env perl

use strict;
use warnings;

use Class::Sniff;
use HTML::TokeParser::Simple;
my @sniffs = Class::Sniff->new_from_namespace({
    namespace => 'HTML::TokeParser::Simple',
    universal => 1,
});
my $graph    = $sniffs[0]->combine_graphs( @sniffs[ 1 .. $#sniffs ] );
my $graphviz = $graph->as_graphviz();
open my $DOT, '|dot -Tpng -o graph.png' or die("Cannot open pipe to dot: $!");
print $DOT $graphviz;

... produces this graph.

It's a bit hackish, but now, as long as your code is loaded in memory when you run this, you can easily generate full inheritance graphs for your application.

sub new_from_namespace {
    my ( $class, $arg_for ) = @_;
    my $namespace = delete $arg_for->{namespace}
      or Carp::croak("new_from_namespace requires a 'namespace' argument");
    my @sniffs;
    my %seen;
    my $new_sniff = sub {
        my $symbol_name = shift;
        no warnings 'numeric';
        return if $seen{$symbol_name}++;    # prevent infinite loops
        if ( $symbol_name =~ /^$namespace/ ) {
            $symbol_name =~ s/::$//;
            $arg_for->{class} = $symbol_name;
            push @sniffs => Class::Sniff->new($arg_for);
        }
        return 1;
    };
    B::walksymtable( \%::, 'NAME', $new_sniff );
    return @sniffs;
}

Just for giggles, I ran it with the namespace of '.'. Since Perl cannot distinguish between OO and procedural code you see every namespace loaded.

Here's a closer look at the %B:: inheritance hierarchy.


Awesome

frew on 2009-02-16T05:08:28

You're like a perl/programming superhero...

Blimey

singingfish on 2009-02-16T08:45:04

Bloody hell, you're my new hero! You've just got me half way towards obsoleting a bloody awful piece of propietary software for qualitative data analysis. But that's my project for the second half of this year ...

Re:Blimey

Ovid on 2009-02-16T09:35:42

Wow. I'm practically blushing over here :)

If you can think of anything else I could add which can be determined via runtime analysis (though a combination of this and Perl::Critic might be good), let me know. I still have lots of bugs and loose ends to work out, but I'm happy to know it's helping folks.