Assert-Refute

 view release on metacpan or  search on metacpan

lib/Assert/Refute/Build.pm  view on Meta::CPAN

use warnings;
our $VERSION = '0.1701';

=head1 NAME

Assert::Refute::Build - tool for extending Assert::Refute suite

=head1 DESCRIPTION

Although arbitrary checks may be created using just the C<refute> function,
they may be cumbersome to use and especially share.

This module takes care of some boilerplate as well as maintains parity
between functional and object-oriented interfaces of L<Assert::Refute>.

=head1 SYNOPSIS

Extending the test suite goes as follows:

    package My::Package;
    use Assert::Refute::Build;
    use parent qw(Exporter);

    build_refute is_everything => sub {
        return if $_[0] == 42;
        return "$_[0] is not answer to life, universe, and everything";
    }, export => 1, args => 1;

    1;

This can be later used inside production code to check a condition:

    use Assert::Refute qw(:all);
    use My::Package;
    my $fun_check = contract {
        is_everything( shift );
    };
    my $oo_check = contract {
        $_[0]->is_everything( $_[1] );
    }, need_object => 1;
    # ditto

    # apply $fun_check or $oo_check to a variable, get result

    my $log = $oo_check->apply(137);
    $log->is_passing; # nope
    $log->get_tap;    # get details

This call will create a prototyped function is_everything(...) in the calling
package, with C<args> positional parameters and an optional human-readable
message. (Think C<ok 1>, C<ok 1 'test passed'>).

=head1 FUNCTIONS

All functions are exportable.

=cut

use Carp;
use Data::Dumper;
use Scalar::Util qw(weaken blessed set_prototype looks_like_number refaddr);
use parent qw(Exporter);
our @EXPORT = qw(build_refute current_contract to_scalar);

# NOTE HACK
# If we're being loaded after Test::More, we're *likely* inside a test script
# This has to be re-done properly
# Cannot instantiate *here* because cyclic dependencies
#    so wait until current_contract() is called
our $MORE_DETECTED = Test::Builder->can("new") ? 1 : 0;

=head2 build_refute name => \&CODE, %options

This function

=over

=item * accepts a subroutine reference that returns a false value on success
and a brief description of the discrepancy on failure
(e.g. C<"$got != $expected">);

Note that this function does not need to know anything about the testing
environment it is in, it just cares about its arguments
(think I<pure function>).

=item * builds an exportable wrapper around it that would talk to
the most recent L<Assert::Refute::Report> instance;

=item * adds a method with the same name to L<Assert::Refute::Report>
so that object-oriented and functional interfaces
are as close to each other as possible.

=back

As a side effect, Assert::Refute's internals are added to the caller's
C<@CARP_NOT> array so that carp/croak points to where the built function
is actually used.

B<NOTE> One needs to use Exporter explicitly if either C<export>
or C<export_ok> option is in use. This MAY change in the future.

Options may include:

=over

=item * C<export> => 1    - add function to @EXPORT
(Exporter still has to be used by target module explicitly).

=item * C<export_ok> => 1 - add function to @EXPORT_OK (don't export by default).

=item * C<no_create> => 1 - don't generate a function at all, just add to
L<Assert::Refute>'s methods.

=item * C<manual> => 1 - don't generate any code.
Instead, assume that user has already done that and just add a method
to L<Assert::Refute::Report> and a prototyped exportable wrapper.

This may be useful to create refutations based on subcontract or such.

B<[EXPERIMENTAL]>.



( run in 1.448 second using v1.01-cache-2.11-cpan-39bf76dae61 )