Declare::Constraints::Simple::Library::Base - Library Base Class


Declare-Constraints-Simple documentation Contained in the Declare-Constraints-Simple distribution.

Index


Code Index:

NAME

Top

Declare::Constraints::Simple::Library::Base - Library Base Class

SYNOPSIS

Top

  package My::Constraint::Library;
  use warnings;
  use strict;

  # this installs the base class and helper functions
  use Declare::Constraints::Simple-Library;

  # we can also automagically provide other libraries
  # to the importer
  use base 'Declare::Constraints::Simple::Library::Numericals';

  # with this we define a constraint to check a value
  # against a serial number regular expression
  constraint 'SomeSerial',
    sub {
      return sub {
        return _true if $_[0] =~ /\d{3}-\d{3}-\d{4}/;
        return _false('Not in SomeSerial format');
      };
    };

  1;

DESCRIPTION

Top

This base class contains the common library functionalities. This includes helper functions and install mechanisms.

METHODS

Top

install_into($target)

Installs the base classes and helper functions into the $target namespace. The %CONSTRAINT_GENERATORS package variable of that class will be used as storage for it's constraints.

fetch_constraint_declarations()

Class method. Returns all constraints registered to the class.

fetch_constraint_generator($name)

Class method. Returns the constraint generator code reference registered under $name. The call will raise a croak if the generator could not be found.

prepare_generator($constraint_name, $generator)

Class method. This wraps the $generator in a closure that provides stack and failure-collapsing decisions.

add_constraint_generator($name, $code)

Class method. The actual registration method, used by constraint.

HELPER FUNCTIONS

Top

Note that some of the helper functions are prefixed with _. Although this means they are internal functions, it is ok to call them, as they have a fixed API. They are not distribution internal, but library internal, and only intended to be used from inside constraints.

constraint($name, $code)

  constraint 'Foo', sub { ... };

This registers a new constraint in the calling library. Note that constraints have to return result objects. To do this, you can use the helper functions _result($bool, $msg, _true() and _false($msg).

_result($bool, $msg)

Returns a new result object. It's validity flag will depend on the $bool argument. The $msg argument is the error message to use on failure.

_false($msg)

Returns a non-valid result object, with it's message set to $msg.

_true()

Returns a valid result object.

_info($info)

Sets the current failure info to use in the stack info part.

_apply_checks($value, \@constraints, [$info])

This applies all constraints in the \@constraints array reference to the passed $value. You can optionally specify an $info string to be used in the stack of the newly created non-valid results.

_listify($value)

Puts $value into an array reference and returns it, if it isn't already one.

_with_message($msg, $closure, @args)

This is the internal version of the general Message constraint. It sets the current overriden message to $msg and executes the $closure with @args as arguments.

_with_scope($scope_name, $constraint, @args)

Applies the $constraint to @args in a newly created scope named by $scope_name.

_set_result($scope, $name, $result)

Stores the given $result unter the name $name in $scope.

_get_result($scope, $name)

Returns the result named $name from $scope.

_has_result($scope, $name)

Returns true only if such a result was registered already.

SEE ALSO

Top

Declare::Constraints::Simple, Declare::Constraints::Simple::Library

AUTHOR

Top

Robert 'phaylon' Sedlacek <phaylon@dunkelheit.at>

LICENSE AND COPYRIGHT

Top


Declare-Constraints-Simple documentation Contained in the Declare-Constraints-Simple distribution.
package Declare::Constraints::Simple::Library::Base;
use warnings;
use strict;

use aliased 'Declare::Constraints::Simple::Result';

use Carp::Clan qw(^Declare::Constraints::Simple);

our $FAIL_MESSAGE_DEFAULT = 'Validation Error';
our $FAIL_MESSAGE = '';
our $FAIL_INFO;
our %SCOPES;

use base 'Declare::Constraints::Simple::Library::Exportable';

sub install_into {
    my ($class, $target) = @_;

    {   no strict 'refs';
        unshift @{$target . '::ISA'}, $class;

        *{$target . '::' . $_} = $class->can($_)
            for qw/ 
                    constraint
                    _apply_checks
                    _listify
                    _result
                    _false
                    _true
                    _info
                    _with_message
                    _with_scope
                    _set_result
                    _get_result
                    _has_result
                /;
    }

    1;
}

sub fetch_constraint_declarations {
    my ($class) = @_;
    
    {   no strict 'refs';
        no warnings;
        return keys %{$class . '::CONSTRAINT_GENERATORS'};
    }
}

sub fetch_constraint_generator {
    my ($class, $name) = @_;

    my $generator = do {
        no strict 'refs';
        ${$class . '::CONSTRAINT_GENERATORS'}{$name};
    };
    croak "Unknown Constraint Generators: $name"
        unless $generator;

    return $class->prepare_generator($name, $generator);
}

sub prepare_generator {
    my ($class, $constraint, $generator) = @_;
    return sub {
        my (@g_args) = @_;
        my $closure = $generator->(@g_args);

        return sub {
            my (@c_args) = @_;

            local $FAIL_INFO;
            my $result = $closure->(@c_args);
            my $info = '';
            if ($FAIL_INFO) {
                $info = $FAIL_INFO;
                $info =~ s/([\[\]])/\\$1/gsm;
                $info = "[$info]";
            }
            $result->add_to_stack($constraint . $info) unless $result;

            return $result;
        };
    };
}

sub add_constraint_generator {
    my ($class, $name, $code) = @_;

    {   no strict 'refs';
        ${$class . '::CONSTRAINT_GENERATORS'}{$name} = $code;
    }

    1;
}

sub constraint {
    my ($name, $code) = @_;
    my $target = scalar(caller);
    $target->add_constraint_generator($name => $code);

    1;
}

sub _result {
    my ($result, $msg) = @_;
    my $result_obj = Result->new;
    $result_obj->set_valid($result);
    $result_obj->set_message(
        $FAIL_MESSAGE || $msg || $FAIL_MESSAGE_DEFAULT)
        unless $result_obj->is_valid;
    return $result_obj;
}

sub _false { _result(0, @_) }
sub _true  { _result(1, @_) }

sub _info  { $FAIL_INFO = shift }

sub _apply_checks {
    my ($value, $checks, $info) = @_;
    $checks ||= [];
    $FAIL_INFO = $info if $info;
    for (@$checks) {
        my $result = $_->($value);
        return $result unless $result->is_valid;
    }
    return _true;
}

sub _listify {
    my ($value) = @_;
    return (ref($value) eq 'ARRAY' ? $value : [$value]);
}

sub _with_message {
    my ($msg, $closure, @args) = @_;
    local $FAIL_MESSAGE = $msg;
    return $closure->(@args);
}

sub _with_scope {
    my ($scope_name, $closure, @args) = @_;
    local %SCOPES = ($scope_name => {})
        unless exists $SCOPES{$scope_name};
    return $closure->(@args);
}

sub _set_result {
    my ($scope, $name, $result) = @_;
    $SCOPES{$scope}{result}{$name} = $result;
    1;
}

sub _get_result {
    my ($scope, $name) = @_;
    return $SCOPES{$scope}{result}{$name};
}

sub _has_result {
    my ($scope, $name) = @_;
    return exists $SCOPES{$scope}{result}{$name};
}

1;