★   Advanced
    Modulinos
       brian d foy
     The Perl Review
     YAPC::NA 2012
Files that work as
programs and modules
   at the same time
Unit testing

Code reüse

Distribution
# hello.pl
use v5.10;

say 'Hello World!';
% perl hello.pl
Hello World!
STDIN   $0   STDOUT
@ARGV
        %ENV         exit code
Starting
use v5.10;

run();

sub run {
 say 'Hello World!';
 }
% perl hello.pl
Hello World!
% perl -e 'require q(hello.pl)'
Hello World!
# Hello.pm
use v5.10;

run() unless caller;

sub run {
 say 'Hello World!';
 }

_ _PACKAGE_ _
% perl Hello.pm
Hello World!
% perl -MHello -e 1
%
package Hello;
use v5.10;

__PACKAGE__->run
   unless caller;

sub run {
 say 'Hello World!';
 }

_ _PACKAGE_ _
use Test::More;
use Test::Output;

use_ok( 'Hello' );

stdout_ok(
   sub { Hello->run() },
   "Hello World!n", ...
   );
package Hello;
use v5.10;

_ _PACKAGE_ _->run
    unless caller;

sub run {
 my( $self ) = @_;
 say { $self->fh }
   'Hello World!';
 }
package Hello;
use v5.10;
...;
sub fh { *STDOUT }
use Test::More;

use_ok( 'Hello' );

our $string;
{
open my $fh, '>', $string;
*Hello::fh = sub { $fh };
}

Hello->run;
is($string, "Hello World!n");
package Hello;
use v5.10;
...;
BEGIN {
my $fh = *STDOUT;
sub fh { $fh }
sub set_fh {
  $fh = ...;
}
}
use Test::More;

use_ok( 'Hello' );

open my $fh, '>', my $string;
Hello->set_fh( $fh );
Hello->run;
is($string, "Hello World!n");
% perl hello.pl
Hello World!
% perl hello.pl Chicago
Hello World!
% perl hello.pl -m Rahm
Hello World!
% perl hello.pl < aldermen
Hello World!
Connect the
   command line to new()

% hello.pl -s Houston
                             ?
    use Hello;
                                 new()
    my $app->new(
      input => $in_fh,
      output => $out_fh,
      message => $message,
      );

    $app->greet;
sub run {
  my( $class, @args ) = @_;
  my %args =
    $class->process_args(@args);

  my $self = $class->new(%args);
  say { $self->fh }
    $self->message;
  }
sub process_args {
  require Getopt::Std;
  local @ARGV = @_;
  getopts('oim:', my %opts);

  $opts{'o'} //= *STDOUT;
  $opts{'i'} //= *STDIN;
  $opts{'m'} //= 'Hello World!';
  # left over @_?
  my %args = map {
    $opts_map{$_} => $opts{$_}
    } keys %opts;
  }
$app->new(
  input_fh => $in,
  output_fh => $out,
  message   => 'Hello World!'
  );
sub new {
  my( $class, %args ) = @_;
  my $self = bless {}, $class;

  foreach ( keys %args ) {
    # maybe more complicated
    $self->set( $_, $args{$_} );
    }

  return $self;
  }
Stopping
#!perl
...;
...;
...;

exit(0);
sub run {
  my( $class, @args ) = @_;
  my $object = eval {
   ...;
   Result->new( code => 0 );
   } or $@;
  exit( $object->code );
  }
sub some_sub {
  ...;
   die
     Result->new( code => 15 );
  ...;
  }
sub run {
  my( $class, @args ) = @_;
  my $object = eval {
   ...;
   Result->new( code => 0 );
   };
  exit( $error_object->code );
  }
Testing
run() unless caller;
UNITCHECK {
  if($ENV{TEST_HARNESS}){
    __PACKAGE__->run_tests;
    }
  elsif( ! caller ) {
    __PACKAGE__->run;
    }
  }
Docs
UNITCHECK {
  if($ENV{TEST_HARNESS}){
    __PACKAGE__->run_tests;
    }
  elsif($ENV{PERLDOC}){
    __PACKAGE__->show_docs;
    }
  elsif( ! caller ) {
    __PACKAGE__->run;
    }
  }

Advanced modulinos