A piggy bank of commands, fixes, succinct reviews, some mini articles and technical opinions from a (mostly) Perl developer.

Example ssh config

Host [alias]
    HostName [remote hostname]
    User [remote username]
    IdentityFile [file]

[alias] is what you type to select the connection, e.g. ssh [alias]
[file] is your DSA or RSA private key file

Toggle comments in vi

Add a comment for one line only:
&

A unary-plus sign before a package name in Perl

ok +Some::Package::Foo->a_method_call($var), 'test succeeded';

This is to get around Perl's 'indirect object syntax'.
Otherwise
ok Mock::Basic->update(...);
looks like
Mock::Basic->ok->update(...);
which is probably not what is intended.
ok(Mock::Basic->update(...)); works as well.

It tells the parser "Hi. Since I have a leading (unary) plus sign, I'm now a scalar expression, so I qualify to match the first item in ok()'s prototype; please do not get confused and interpret me as a class name you should call an ok() method on."

Demonstration:
perl -MCGI -MTest::More -e'ok 1, "ok"; ok +CGI->new, +CGI->new; ok scalar CGI->new, "scalar"; ok( CGI->new, "brackets" ); ok CGI->new, "no plus";'
ok 1 - ok
ok 2 - CGI=HASH(0x8b68c0)
ok 3 - scalar
ok 4 - brackets
Undefined subroutine CGI::ok
 at -e line 1

See alsoand

What a solitary underscore in Perl means

It represents the previous 'stat' structure, saving a system call.
See: http://perldoc.perl.org/5.8.8/functions/-X.html
and http://perldoc.perl.org/5.8.8/functions/stat.html

Get a full backtrace upon Perl error

$SIG{__DIE__} = \&confess;

How to tell if a perl script is only being syntax checked

The special variable $^C will be true if the script is only being compiled with: perl -c script.pl

Different ways to set up a database test in Perl

  • Use a DBI wrapper subroutine to code up each SQL statement:
    • pros: all the data is kept inside each test, and you can put Perl comments next to the SQL statements
    • cons: you have to translate the SQL statements into Perl and back for testing/debugging
subtest 'a' {
    clear_db;
    db_wrapper( db => 'db1' sql =>'INSERT INTO table_a SET x = ?, y = ?', bind_values => [1, 2]);
    # do something
}
subtest 'b' {
    clear_db;
    db_wrapper( db => 'db1' sql =>'INSERT INTO table_a SET x = ?, y = ?', bind_values => [1, 3]);

    # do something
}
  • Put all the statements inline, in the DATA section, and use a special subroutine to read them all:
    • pros: the database statements stay in SQL format
    • cons: you have to use different ID numbers for each test, 
setup_db_once( data => \*DATA );
...
subtest 'a' { # do something with first row }
subtest 'b' { # do something with second row }
...
__DATA__

INSERT INTO table_a SET x = 1, y = 2
INSERT INTO table_a SET x = 5, y = 6
  • Use a DBI wrapper subroutine to read SQL statements from an inline heredoc:
    • all the pros and none of the cons
subtest 'a' {
    clear_db;
    setup_db_per_test( data => <<EOM
INSERT INTO table_a SET x = 1, y = 2
EOM;
    # do something
}


subtest 'b' {

    clear_db;
    setup_db_per_test( data => <<EOM
INSERT INTO table_a SET x = 1, y = 3

EOM;
    # do something
}

Mock modules & Rules of mocking for Perl tests

Mock modules

  • Test2::V0 - I really must start using this soon. See Test2::Mock and possibly Test2::Tools::Mock
  • Test::MockObject - Works. Has set_isa() to pass Moose constraints. Start with an empty object and add methods as needed. This one.
    • see also Test::MockObject::Extends 
  • Test::MockModule - "Override subroutines in a module for unit testing".
    • Does not fool Moose (modules appear as Test::MockModule instead of what they are).
    • Only overrides the methods you say.
    • Has strict mode (can't accidentally mock non-existent methods)
    • Has an 'original()' function for wrapping methods (source):
      • my $mock = Test::MockModule->new("MyModule");
        $mock->redefine("something", sub { my ($self, $args) = @_; push @record_for_testing_later, $args;
                return $mock->original(@_);
        });
  • DBD::Mock - don't use this, use Test::DBIx::Class (with DBIx::Class::Fixtures) or a real test database.
  • Test::Mock::Class - looks clunky
  • Mock::Quick - another one, syntax may be better. "less side effects", doesn't reload modules.
    • To call original un-mocked method, use (source):
    • my $mock = qtakeover 'Some::Module' => (
          id => sub {
              my ( $mock_self, @args ) = @_;
              $counter->{id}++ if scalar @args;
              return $mock_self->MQ_CONTROL->original('id')->( $mock_self, @args );
          },
      );

Reasons to use Mock::Quick

Monkey patching modules

  • Manually overriding subroutines AKA monkey patching - risky. What if you miss a method? Same as what Test::MockModule does.
    • example: { package X; *method_name = sub { return 'foo'; } }
  • Class::Monkey
  • Monkey::Patch
  • Mojo::Util::monkey_patch
  • Sub::Override
  • Mock::Sub - another one
  • Mock::MonkeyPatch
    • Can easily call original sub with Mock::MonkeyPatch::ORIGINAL

Rules of mocking

Don't monkey patch. Don't modify the symbol table directly like this:
*Acme::Foo::method = sub { return "mock you"; }

Instead use Test::MockModule or Sub::Override or similar. Reasons:
  • If you type the method name wrong, or if it gets moved, it becomes so obvious that it can't be missed
  • You won't continue to test code that doesn't exist anymore
The first rule of mocking is: Don't mock.

Instead use dependency injection. If you think you want to use a mock:
  • instead expose the object as an attribute,
  • and pass in one mock object once
  • this is easier to develop & maintain than mocking different things in every test
  • and less prone to leakage between tests
Or if you need a mock method to be in place when an app is instantiated, add logic in the attribute to load a custom object from config, or default to the real object if nothing found in config:
  • create a test class like Foo::Test that inherits from Foo
  • override the dangerous methods
  • add test code: my $test_foo = Foo::Test->(@test_args)
  • and then: $t = Test::Mojo->new("App", { foo => $test_foo });
  • instantiate like this: $class = $config->foo || Foo->new(@args)
  • if necessary, the main library and test library should share an abstract interface (implemented via a role)
Only if all the above fail, use MockModule. Note that scope can matter when using set_isa()

Patch

patch -p0 < patch.file

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::TraceDebug::LTraceDevel::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".



Open files from the command line in Ubuntu

Either:
evince [filename] (for document viewer, e.g. pdfs)
or:
gnome-open [filename] (for any file)

http://ubuntuforums.org/showthread.php?t=446766