NAME

Moan - yet another "assert" module

SYNOPSIS

In lib/MyApp/Database.pm:

package MyApp::Database;
use strict;
use Moan;
sub new {
  my ($class, $dbh) = @_;
  assert($dbh->isa('DBI::db'), 'Passed a database.');
  assert($dbh->ping, 'Database is alive.');
  bless [$dbh], $class;
}
sub dbh {
  $_[0][0];
}
1;

In bin/myapp.pl:

#!/usr/bin/perl
use MyApp::Database;
use Moan;
assert(defined $ENV{MYAPP_DB}, 'MYAPP_DB set');
my $db = MyApp::Database->new(DBI->connect($ENV{MYAPP_DB}));
assert($db->isa('MyApp::Database'), 'Class OK');
# do something

At the command-line:

perl -Moan::more=MyApp::Database bin/myapp.pl

DESCRIPTION

Assertions are somewhere between inlined unit tests, checks against malicious input and executable comments. Assertions should be written to have no side-effects, and to always pass under normal circumstances.

This is not the only Perl assertion module, and probably not the best, but it does what I need. This module requires Perl 5.10.

Assertion Functions

assert($expression, $message, $tag)

Checks whether $expression evaluates to true. If not, the assertion fails (see "Assertion Consequences").

The second parameter $message is optional, and provides an error message to associate with the assertion. If omitted or undef, a default non-descriptive message is used.

The third parameter $tag is a category for the assertion. This allows you to classify assertions and disable them by tag. If ommitted or undef, the default tag is the package name where the assertion occurred. The following two assertions are equivalent:

package Foo::Bar;
assert(1);

package main;
assert(1, undef, 'Foo::Bar');

Tags starting with a single colon are appended to the package name. So the following two assertions are equivalent:

package Foo::Bar;
assert(1, undef, ':quux');

package main;
assert(1, undef, 'Foo::Bar:quux');

This means you have very little excuse for not namespacing your tags. For small packages, just leave the tag undefined and you'll get your assertion tagged with your package. For larger packages, use tags matching the expression /^:(\w+)$/.

Tags are in fact arbitrary strings, but you can save yourself headaches if you stick to the conventions above.

affirm {CODE_BLOCK} $message, $tag

Checks that the return value of the code is true. Good for more complex tests. (This is also more efficient than assert in cases where you've disabled assertion reporting, as affirm can avoid running the code block entirely.

diag $message, $tag

Strictly speaking, this does not actually make an assertion, but generates a diagnostic message (which is not usually shown).

The "Assertion Consequences" section explains how to enable diagnostic messages on a per-tag basis.

no Moan

Disables moaning within a lexical block.

Carp::Assert

These functions are provided for rough compatibility with Carp::Assert. You should be able to more or less drop in Moan as a replacement:

use Moan ':carpish';
should($given, $expected, $message, $tag)

Equivalent to:

assert($given ~~ $expected, $message, $tag);

This is slightly different from Carp::Assert::should which uses eq instead of the smart match.

shouldnt($given, $expected, $message, $tag)

Equivalent to:

assert(!($given ~~ $expected), $message, $tag);
DEBUG

Right now this just returns 1.

Test::More

These functions are provided for rough compatibility with Test::More. (Though note that Moan is not designed for writing module tests. It doesn't use TAP.) They are not exported by default.

use Moan ':testmore';
ok($expression, $message, $tag)

Largely the same as assert, provided for people addicted to Test::More.

is($given, $expected, $message, $tag)

Equivalent to:

ok($given eq $expected, $message, $tag);

or

ok($given == $expected, $message, $tag);

... depending on whether $expected looks like a number.

isnt($given, $expected, $message, $tag)

Equivalent to:

ok($given ne $expected, $message, $tag);

or

ok($given != $expected, $message, $tag);

... depending on whether $expected looks like a number.

like($given, $expected, $message, $tag)

Equivalent to:

ok($given =~ $expected, $message, $tag);
unlike($given, $expected, $message, $tag)

Equivalent to:

ok($given !~ $expected, $message, $tag);
isa_ok($package_name, $class, $message, $tag)
isa_ok($object, $class, $message, $tag)

Roughly equivalent to:

ok($package_name->isa($class), $message, $tag);
ok($object->isa($class), $message, $tag);

... but without failing horribly if $object is, say, an unblessed reference.

can_ok($package_name, $method, $message, $tag)
can_ok($object, $method, $message, $tag)

Roughly equivalent to:

ok($package_name->can($method), $message, $tag);
ok($object->can($method), $message, $tag);

... but without failing horribly if $object is, say, an unblessed reference.

Also Useful

Here are some other functions that are occasionally useful.

use Moan qw/:standard :testmore does_ok defined_ok blessed_ok/;
does_ok($package_name, $role, $message, $tag)
does_ok($object, $role, $message, $tag)

Roughly equivalent to:

ok($package_name->DOES($role), $message, $tag);
ok($object->DOES($role), $message, $tag);

... but without failing horribly if $object is, say, an unblessed reference.

defined_ok($value, $message, $tag)

Pretty much identical to:

ok(defined $value, $message, $tag);
blessed_ok($object, $message, $tag)
ok(Scalar::Util::blessed($object), $message, $tag);

Export

%EXPORT_TAGS = (
    testmore => [qw/ok is isnt like unlike can_ok isa_ok/],
    carpish  => [qw/assert affirm should shouldnt DEBUG/],
    standard => [qw/assert affirm diag/],
    );
@EXPORT = @{ $EXPORT_TAGS{standard} };
@EXPORT_OK = (
    qw/blessed_ok defined_ok does_ok TRUE FALSE/,
    @{ $EXPORT_TAGS{standard} },
    @{ $EXPORT_TAGS{testmore} },
    @{ $EXPORT_TAGS{carpish} },
    );

Yeah, you can export the constants TRUE and FALSE, just because they're so often handy to have.

Assertion Consequences

The normal consequence of an assertion is that a warning will be emitted, if the assertion does not hold (i.e. is false). Diagnostic messages (diag) will not be printed.

Moan can be kicked into a more verbose mode, where diagnostic messages are printed, and assertions that don't hold are accompanied by stacktracers. Just call Moan->loudly

Moan->loudly(qw/MyApp::Database YourApp::Greaterface/);

Each argument is a tag which you want to make "louder". The /^:\w+$/ convention for tags that are local to your package will work, but to specify tags in other packages, you need to fully-qualify the names.

For example, the tag ":connection" in package MyApp::Database could be made louder using:

Moan->loudly('MyApp::Database:connection');  # sic

Tags can be preceded with a minus to switch off the loudness feature.

Regular expressions also work:

Moan->loudly(qr/^MyApp::Database:(\w+)$/);

There's currently no way to remove regular expressions from the list though, so use with caution.

A special tag "ALL" makes all moaning louder. "-ALL" counteracts that.

But what if you want to decrease Moan's verbosity? As you might have guessed, Moan->quietly fulfils this role. It takes the same parameters as Moan->loudly.

If a tag has been quietened, then no warnings will be emitted for it whether the assertion holds or not. In fact, in many cases Moan can even avoid completely evaluating the assertion.

Curiously, a tag can be made both loud and quiet, as the two features are quite separate. If a tag is both, then it will act quiet, except that warnings for diagnostic messages will be emitted. This should be considered an "at risk feature" (i.e. an arguable bug).

There is also a Moan->fatally feature. This again takes the same parameters as Moan->loudly. It makes failed assertions throw an exception (i.e. die).

When Moan needs to throw an exception for an assertion, it checks to see if the tag is loud, and if not, appends all previous diagnostics to the exception.

At some point, the exceptions thrown will probably be switched from strings to blessed objects in the next release.

(Moan::loudly, Moan::quietly and Moan::fatally also work.)

The following functions are provided to check the behaviour of a tag:

Moan::is_loud($tag)
Moan::is_quiet($tag)
Moan::is_fatal($tag)

Command-Line Options

perl -Moan::quietly=tag1,tag2,tag3 - quietly
perl -Moan::loudly=tag1,tag2,tag3 - loudly
perl -d:oh=tag1,tag2,tag3 - loudly and fatally

BUGS

Please report any bugs to http://rt.cpan.org/Dist/Display.html?Queue=Moan.

SEE ALSO

Carp::Assert, Test::More.

AUTHOR

Toby Inkster <tobyink@cpan.org>.

COPYRIGHT AND LICENCE

This software is copyright (c) 2011 by Toby Inkster.

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

DISCLAIMER OF WARRANTIES

THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.