NAME

Perl::Metrics::Simple - Count packages, subs, lines, etc. of many files.

SYNOPSIS

use Perl::Metrics::Simple;
$file_count    = $analysis->file_count;
$package_count = $analysis->package_count;
$sub_count     = $analysis->sub_count;
$lines         = $analysis->lines;
$main_stats    = $analysis->main_stats;
$file_stats    = $analysis->file_stats;

DESCRIPTION

USAGE

use Perl::Metrics::Simple;
my $analzyer = Perl::Metrics::Simple->new;
my $analysis = $analzyer->analyze_files(@ARGV);

EXAMPLE SCRIPT

use strict;
use warnings;
use Data::Dumper;
use Perl::Metrics::Simple;
use Pod::Usage;
use Statistics::Basic::StdDev;
use Statistics::Basic::Mean;
use Statistics::Basic::Median;

pod2usage( -verbose => 1 ) if ( !@ARGV );
my $analzyer = Perl::Metrics::Simple->new;

my $IMPROBABLY_LARGE_NUMBER = 999_999_999_999;

my $analysis = $analzyer->analyze_files(@ARGV);

my $file_count    = $analysis->file_count;
my $package_count = $analysis->package_count;
my $sub_count     = $analysis->sub_count;
my $lines         = $analysis->lines;
my $main_stats    = $analysis->main_stats;
my $file_stats    = $analysis->file_stats;

my %lines = ();
@lines{ 'min', 'max', 'counts' } =
  _get_min_max_values( $analysis->subs, 'lines' );
$lines{average} = sprintf '%.2f',
  Statistics::Basic::Mean->new( $lines{counts} )->query;

$lines{median} = sprintf '%.2f',
  Statistics::Basic::Median->new( $lines{counts} )->query;

my %complexity = ();
@complexity{ 'min', 'max', 'scores' } =
  _get_min_max_values( $analysis->subs, 'mccabe_complexity' );
$complexity{average} = sprintf '%.2f',
  Statistics::Basic::Mean->new( $complexity{scores} )->query;

$complexity{median} = sprintf '%.2f',
  Statistics::Basic::Median->new( $complexity{scores}, $sub_count )->query;
$complexity{standard_deviation} = sprintf '%.2f',
  Statistics::Basic::StdDev->new( $complexity{scores}, $sub_count )->query;

my %main_complexity = ();
$main_complexity{average} = sprintf '%.2f',
  $main_stats->{mccabe_complexity} / $file_count;
@main_complexity{ 'min', 'max', 'scores' } =
  _get_min_max_values( $analysis->subs, 'mccabe_complexity' );
$main_complexity{median} = sprintf '%.2f',
  Statistics::Basic::Median->new( $main_complexity{scores}, $file_count )->query;
$main_complexity{standard_deviation} = sprintf '%.2f',
  Statistics::Basic::StdDev->new( $main_complexity{scores}, $file_count )->query;

print <<"EOS";

Perl Files:      $file_count

Line Counts
-----------
lines:           $lines
packages:        $package_count
subs:            $sub_count
all main code:   $main_stats->{lines}

min. sub size:   $lines{min} lines
max. sub size:   $lines{max} lines
avg. sub size:   $lines{average} lines
median sub size: $lines{median}

McCabe Complexity
-----------------
min. main:    $main_complexity{min}
max. main:    $main_complexity{max}
median main:  $main_complexity{median}
average main: $main_complexity{average}

subs:
min:             $complexity{min}
max:             $complexity{max}
avg:             $complexity{average}
median:          $complexity{median}
std. deviation:  $complexity{standard_deviation}

EOS

my @sorted_subs = sort _by_complexity(), @{ $analysis->subs };
print join( "\t", 'complexity', 'sub', 'path', 'size' ), "\n";
foreach my $sub (@sorted_subs) {
    my %sub_hash = %{$sub};
    print join( "\t",
        @sub_hash{ 'mccabe_complexity', 'name', 'file_path', 'lines' } ),
      "\n";
}

exit;

sub _by_complexity {
    return $b->{mccabe_complexity} <=> $a->{mccabe_complexity};
}

sub _get_min_max_values {
    my $nodes    = shift;
    my $hash_key = shift;
    my @values   = ();
    my $min      = $IMPROBABLY_LARGE_NUMBER;
    my $max      = 0;
    foreach my $node ( @{$nodes} ) {
        my $value = $node->{$hash_key};
        $max = $value > $max ? $value : $max;
        $min = $value < $min ? $value : $min;
        push @values, $value;
    }

    return ( $min, $max, \@values );
}
__END__

PACKAGE PROPERTIES

Readonly values:

Used to measure mccabe_complexity, each occurance adds 1:

Readonly::Array our @LOGIC_OPERATORS =>
  qw( && || ||= &&= or and xor ? <<= >>= );
Readonly::Hash our %LOGIC_OPERATORS => hashify(@LOGIC_OPERATORS);

Readonly::Array our @LOGIC_KEYWORDS =>
  qw( for foreach if else elsif unless until while );
Readonly::Hash our %LOGIC_KEYWORDS => hashify(@LOGIC_KEYWORDS);

CLASS METHODS

new

Blah blah

OBJECT METHODS

analyze_files( @files_and_or_dirs )

Takes an array of files and or directory paths and returns a Perl::Metrics::Simple::Analysis object.

analyze_one_file

find_files

get_node_length

list_perl_files

measure_complexity($PPI_node)

Attempts to measure the cyclomatic complexity of a chunk of code.

Takes a PPI::Node and returns the total number of logic keywords and logic operators. plus 1. See the PACKAGE PROPERTIES section for a list.

See also: http://en.wikipedia.org/wiki/Cyclomatic_complexity

The code for this method was copied from Perl::Critic::Policy::Subroutines::ProhibitExcessComplexity

is_perl_file($path)

Takes a path to a file and returns true if the file appears to be a Perl file, otherwise returns false.

If the file name does not match any of @Perl::Metrics::Simple::PERL_FILE_SUFFIXES then the file is opened for reading and the first line examined for a a Perl 'shebang' line. An exception is thrown if the file cannot be opened in this case.

BUGS

None reported yet :-) See: http://rt.cpan.org/NoAuth/Bugs.html?Dist=Perl-Metrics-Simple

SUPPORT

Via CPAN:

Disussion Forum

http://www.cpanforum.com/dist/Perl-Metrics-Simple

Bug Reports

http://rt.cpan.org/NoAuth/Bugs.html?Dist=Perl-Metrics-Simple

AUTHOR

Matisse Enzer
CPAN ID: MATISSE
Eigenstate Consulting, LLC
matisse@eigenstate.net
http://www.eigenstate.net/

COPYRIGHT

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

The full text of the license can be found in the LICENSE file included with this module.

SEE ALSO

PPI
Perl::Critic
http://en.wikipedia.org/wiki/Cyclomatic_complexity