Attribute::Contract - Design by contract via Perl attributes
package Interface; use AttributeContract; sub do_smth :ContractRequires(VALUE, @ANY?) :ContractEnsures(VALUE) { ...; } package Implementation; use base 'Interface'; use AttributeContract; sub do_smth { my $self = shift; my ($foo, @rest) = @_; return 1; } Implementaion->do_smth('hi', 'there'); # works Implementaion->do_smth(); # croaks! Implementaion->do_smth(sub {}); # croaks!
Attribute::Contract by using Perl attributes allows you to specify contract (Design by Contract) for every method in your class. You can check incoming and outgoing values by specifying ContractRequires and ContractEnsures attributes.
ContractRequires
ContractEnsures
It's the most useful for interfaces or abstract classes when you want to control whether your implementation follows the same interface and respects the Liskov substitution principle.
This module does not check the actual types like Str, Int etc, but the Perl data types like scalars, arrays, hashes, references and so on. When the type does not match a Carp's confess function will be called with detailed information like:
Str
Int
confess
0 param(s) passed, at least 1 param(s) is required
Why attributes? They feel and look natural and are applied during compile time.
ANY
Any scalar value is accepted.
VALUE
Anything but not a reference.
REF
A non blessed reference to anything.
REF(SCALAR)
A reference to scalar.
REF(ARRAY)
A reference to array.
REF(HASH)
A reference to hash.
REF(Regexp)
A reference to regular expression.
OBJECT
A blessed reference.
OBJECT(ISA)
A blessed reference with specified isa.
Types that eat all the elements. Can be specified at the end of the elements list for manual unpacking. @ stands for arrays and % stands for hashes. All the scalar types can be used to specify the types of the elements.
@
%
@ARRAY
@VALUE
Which could mean something like:
$object->method(1, 2, 3, 4);
%HASH
%ANY
$object->method(foo => 'bar', 'baz' => \123);
It also checks that the number of elements is even.
Use , when specifying several arguments.
,
VALUE,ANY,REF(CODE),@VALUE
$object->method($foo, \@array, sub { ... }, 1, 2, 3);
Use | when specifying an alternative type.
|
VALUE|REF(VALUE)
$object->method($foo);
or
$object->method(\$foo);
Alternatives can be really deep, like this one:
@(REF(HASH|CODE)|VALUE)
Which is an array of references to hash or code or simple value.
Use ? when specifying an optional value.
?
VALUE,VALUE?
$object->method('foo');
$object->method('foo', 'bar');
By default all the contracts are inherited. Just don't forget to use Attribute::Contract in the derived class. But if no methods are override then even using this module is not needed.
use
using
During the compile time for every contract a Perl subroutine is built and evaled. If the methods share the same contract they use the same checking code reference. This speeds up the checking and saves some memory.
Errors are as specific as possible. On error you will get a meaningful message and a stack trace.
You can switch off contract checking by specifying an environment variable NO_ATTRIBUTE_CONTRACT.
NO_ATTRIBUTE_CONTRACT
http://github.com/vti/attribute-contract
Viacheslav Tykhanovskyi, vti@cpan.org.
vti@cpan.org
Copyright (C) 2012, Viacheslav Tykhanovskyi
This program is free software, you can redistribute it and/or modify it under the terms of the Artistic License version 2.0.
To install Attribute::Contract, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Attribute::Contract
CPAN shell
perl -MCPAN -e shell install Attribute::Contract
For more information on module installation, please visit the detailed CPAN module installation guide.