Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/kentnl/generic-assertions

A Generic Assertion checking class
https://github.com/kentnl/generic-assertions

perl

Last synced: about 2 months ago
JSON representation

A Generic Assertion checking class

Awesome Lists containing this project

README

        

# NAME

Generic::Assertions - A Generic Assertion checking class

# VERSION

version 0.001003

# ALPHA

This is pre-release code, and as such `API` is very much subject to change.

Best attempts at being consolidated is already made, but there's no guarantees at this time
things won't change and break `API` without warning.

# SYNOPSIS

use Generic::Assertions;
use Path::Tiny qw(path);

my $assert = Generic::Assertions->new(
exist => sub {
return (1, "Path $_[0] exists") if path($_[0])->exists;
return (0, "Path $_[0] does not exist");
},
);

...

sub foo {
my ( $path ) = @_;

# carp unless $path exists with "Path $path does not exist"
$assert->should( exist => $path );

# carp if $path exists with "Path $path exists"
$assert->should_not( exist => $path );

# croak unless $path exists with "Path $path does not exist"
$assert->must( exist => $path );

# Lower level way to use the assertion simply to return truth value
# without side effects.
if ( $assert->test( exist => $path ) ) {

}

# carp unconditionally showing the test result and its message
$assert->log( exist => $path );
}

# DESCRIPTION

`Generic::Assertions` allows you to create portable containers of classes of assertions, and allows keeping
severity of assertions from their implementation.

Basic implementation entails

- Defining a list of things to test for
- Returning a pair of ( OK / NOT\_OK , "reason" ) for the tests conclusion
- \[optional\] Defining a default handler for various classes of severity ( `should`, `must` etc. )
- \[optional\] Defining an input transform (eg: always converting the first argument to a path)
- Invoking the assertion at the callpoint as `$instance->severity_level( test_name => @args_for_test )`

# METHODS

## `new`

Constructs a Generic::Assertions object.

my $assertion = Generic::Assertions->new( ARGS );

The following forms of `ARGS` is supported:

->new( key => value );
->new({ key => value });

All `keys` without a `-` prefix are assumed to be test names, and are equivalent to:

->new( -tests => { key => value } );

### `-tests`

All tests must have a simple string key, and a `CodeRef` value.

An example test looks like:

sub {
my ( @slurpy ) = @_;
if ( -e $slurpy[0] ) {
return ( 1, "$slurpy[0] exists" );
}
return ( 0, "$slurpy[1] does not exist" );
}

That is, each test must return either a `true` value or a `false` value.
And each test must return a string describing the condition.

This is so it composes nicely:

$ass->should( exist => $foo ); # warns "$foo does not exist" if it doesn't
$ass->should_not( exist => $foo ); # warns "$foo exists" if it does.

Note the test itself can only see the arguments passed directly to it at the calling point.

### `-handlers`

Each of the various assertion types have a handler underlying them, which can be overridden
during construction.

->new( -handlers => { should => sub { ... } } );

This for instance will override the default handler for "should" and will be invoked
somewhere after the result from

$assertion->should( )

Is obtained.

An example handler approximating the default `should` handler.

sub {
my ( $status, $message, $name, @slurpy ) = @_;
# $status is the 0/1 returned by the test.
# $message is the message the test gave.
# $name is the name of the test invoked ( ie: ->should( foo => ... )
# @slurpy is the arguments passed from the user to the test.
carp $message if $status;
return $slurpy[0];
}

Its worth noting that handlers dictate in entirety:

- What calls will be invoked in response to the fail/pass returned by the test
- What will be returned to the caller who invoked the test

For instance, the `test` handler is simply:

sub {
my ( $status ) = @_;
return $status;
}

And you could perhaps change that to

sub {
my ( $status, $message ) = @_;
return $message;
}

And then invoking

->test( foo => @args );

Would return `foo`'s message instead of its return value.

Use this power with care.

#### Custom Handlers

You can of course define custom handlers outside the core functionality,
except of course they won't be accessible as convenient methods.

You can perhaps invoke them via

->_assert( $handler_name, $test_name, @slurpy_args )

But it would be probably nicer for you to sub-class `Generic::Assertions` and make
it available as a native method:

->$handler_name( $test_name, @slurpy_args )

### `-input_transformer`

You can specify a `CodeRef` through which all tests get passed as a primary step.

->new(
-input_transformer => sub {
# Gets both the name, and all the tests arguments
my ( $name, $path ) = @_;
# Returns a substitute argument list
return path( $path );
},
exist => sub {
return ( 0, "$_[0] does not exist" ) unless $_[0]->exists;
return ( 1, "$_[1] exists" );
},
);

...
# The following code will now check that foo.pm exist
# and if it exists, return a path() object for it as $rval.
# If foo.pm does not exist, it will warn.
#
# $rval will be a path object in both cases.
my $rval = $ass->should( exist => "./foo.pm" );

# Under default configuration, this is basically the same as:
sub should_exist {
my @args = @_;
my $path = path($args[0]);
if ( not $path->exists() ) {
warn "$path does not exist";
}
return $path;
}
my $rval = $thing->should_exist("./foo.pm");
# Except of course more composable.

## `test`

Default implementation simply returns the result of the given test.

if ( $assertion->test( test_name => @args ) ) {

}

## `log`

Default implementation 'carp's the message and status given by `test_name`, and returns `$args[0]`

$assertion->log( test_name => @args );

## `should`

Default implementation carps if `test_name` returns `false` with the message provided by `test_name`.
It then returns `$args[0]`

$assertion->should( test_name => @args );

## `should_not`

Default implementation carps if `test_name` returns `true` with the message provided by `test_name`.
It then returns `$args[0]`

$assertion->should_not( test_name => @args );

## `must`

Default implementation croaks if `test_name` returns `false` with the message provided by `test_name`.

$assertion->must( test_name => @args );

## `must_not`

Default implementation croaks if `test_name` returns `true` with the message provided by `test_name`.

$assertion->must_not( test_name => @args );

# THANKS

To David Golden/xdg for oversight on some of the design concerns on this module.

It would be for sure much uglier than it presently is without his help :)

# AUTHOR

Kent Fredric

# COPYRIGHT AND LICENSE

This software is copyright (c) 2017 by Kent Fredric .

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