|
Packit Service |
3b7cb7 |
# NAME
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
Exception::Class - A module that allows you to declare real exception classes in Perl
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
# VERSION
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
version 1.44
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
# SYNOPSIS
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
use Exception::Class (
|
|
Packit Service |
3b7cb7 |
'MyException',
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
'AnotherException' => { isa => 'MyException' },
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
'YetAnotherException' => {
|
|
Packit Service |
3b7cb7 |
isa => 'AnotherException',
|
|
Packit Service |
3b7cb7 |
description => 'These exceptions are related to IPC'
|
|
Packit Service |
3b7cb7 |
},
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
'ExceptionWithFields' => {
|
|
Packit Service |
3b7cb7 |
isa => 'YetAnotherException',
|
|
Packit Service |
3b7cb7 |
fields => [ 'grandiosity', 'quixotic' ],
|
|
Packit Service |
3b7cb7 |
alias => 'throw_fields',
|
|
Packit Service |
3b7cb7 |
},
|
|
Packit Service |
3b7cb7 |
);
|
|
Packit Service |
3b7cb7 |
use Scalar::Util qw( blessed );
|
|
Packit Service |
3b7cb7 |
use Try::Tiny;
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
try {
|
|
Packit Service |
3b7cb7 |
MyException->throw( error => 'I feel funny.' );
|
|
Packit Service |
3b7cb7 |
}
|
|
Packit Service |
3b7cb7 |
catch {
|
|
Packit Service |
3b7cb7 |
die $_ unless blessed $_ && $_->can('rethrow');
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
if ( $_->isa('Exception::Class') ) {
|
|
Packit Service |
3b7cb7 |
warn $_->error, "\n", $_->trace->as_string, "\n";
|
|
Packit Service |
3b7cb7 |
warn join ' ', $_->euid, $_->egid, $_->uid, $_->gid, $_->pid, $_->time;
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
exit;
|
|
Packit Service |
3b7cb7 |
}
|
|
Packit Service |
3b7cb7 |
elsif ( $_->isa('ExceptionWithFields') ) {
|
|
Packit Service |
3b7cb7 |
if ( $_->quixotic ) {
|
|
Packit Service |
3b7cb7 |
handle_quixotic_exception();
|
|
Packit Service |
3b7cb7 |
}
|
|
Packit Service |
3b7cb7 |
else {
|
|
Packit Service |
3b7cb7 |
handle_non_quixotic_exception();
|
|
Packit Service |
3b7cb7 |
}
|
|
Packit Service |
3b7cb7 |
}
|
|
Packit Service |
3b7cb7 |
else {
|
|
Packit Service |
3b7cb7 |
$_->rethrow;
|
|
Packit Service |
3b7cb7 |
}
|
|
Packit Service |
3b7cb7 |
};
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
# without Try::Tiny
|
|
Packit Service |
3b7cb7 |
eval { ... };
|
|
Packit Service |
3b7cb7 |
if ( my $e = Exception::Class->caught ) { ... }
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
# use an alias - without parens subroutine name is checked at
|
|
Packit Service |
3b7cb7 |
# compile time
|
|
Packit Service |
3b7cb7 |
throw_fields error => "No strawberry", grandiosity => "quite a bit";
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
# DESCRIPTION
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
**RECOMMENDATION 1**: If you are writing modern Perl code with [Moose](https://metacpan.org/pod/Moose) or
|
|
Packit Service |
3b7cb7 |
[Moo](https://metacpan.org/pod/Moo) I highly recommend using [Throwable](https://metacpan.org/pod/Throwable) instead of this module.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
**RECOMMENDATION 2**: Whether or not you use [Throwable](https://metacpan.org/pod/Throwable), you should use
|
|
Packit Service |
3b7cb7 |
[Try::Tiny](https://metacpan.org/pod/Try::Tiny).
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
Exception::Class allows you to declare exception hierarchies in your modules
|
|
Packit Service |
3b7cb7 |
in a "Java-esque" manner.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
It features a simple interface allowing programmers to 'declare' exception
|
|
Packit Service |
3b7cb7 |
classes at compile time. It also has a base exception class,
|
|
Packit Service |
3b7cb7 |
[Exception::Class::Base](https://metacpan.org/pod/Exception::Class::Base), that can be easily extended.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
It is designed to make structured exception handling simpler and better by
|
|
Packit Service |
3b7cb7 |
encouraging people to use hierarchies of exceptions in their applications, as
|
|
Packit Service |
3b7cb7 |
opposed to a single catch-all exception class.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
This module does not implement any try/catch syntax. Please see the "OTHER
|
|
Packit Service |
3b7cb7 |
EXCEPTION MODULES (try/catch syntax)" section for more information on how to
|
|
Packit Service |
3b7cb7 |
get this syntax.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
You will also want to look at the documentation for [Exception::Class::Base](https://metacpan.org/pod/Exception::Class::Base),
|
|
Packit Service |
3b7cb7 |
which is the default base class for all exception objects created by this
|
|
Packit Service |
3b7cb7 |
module.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
# DECLARING EXCEPTION CLASSES
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
Importing `Exception::Class` allows you to automagically create
|
|
Packit Service |
3b7cb7 |
[Exception::Class::Base](https://metacpan.org/pod/Exception::Class::Base) subclasses. You can also create subclasses via the
|
|
Packit Service |
3b7cb7 |
traditional means of defining your own subclass with `@ISA`. These two
|
|
Packit Service |
3b7cb7 |
methods may be easily combined, so that you could subclass an exception class
|
|
Packit Service |
3b7cb7 |
defined via the automagic import, if you desired this.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
The syntax for the magic declarations is as follows:
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
'MANDATORY CLASS NAME' => \%optional_hashref
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
The hashref may contain the following options:
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
- isa
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
This is the class's parent class. If this isn't provided then the class name
|
|
Packit Service |
3b7cb7 |
in `$Exception::Class::BASE_EXC_CLASS` is assumed to be the parent (see
|
|
Packit Service |
3b7cb7 |
below).
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
This parameter lets you create arbitrarily deep class hierarchies. This can
|
|
Packit Service |
3b7cb7 |
be any other [Exception::Class::Base](https://metacpan.org/pod/Exception::Class::Base) subclass in your declaration _or_ a
|
|
Packit Service |
3b7cb7 |
subclass loaded from a module.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
To change the default exception class you will need to change the value of
|
|
Packit Service |
3b7cb7 |
`$Exception::Class::BASE_EXC_CLASS` _before_ calling `import`. To do this
|
|
Packit Service |
3b7cb7 |
simply do something like this:
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
BEGIN { $Exception::Class::BASE_EXC_CLASS = 'SomeExceptionClass'; }
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
If anyone can come up with a more elegant way to do this please let me know.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
CAVEAT: If you want to automagically subclass an [Exception::Class::Base](https://metacpan.org/pod/Exception::Class::Base)
|
|
Packit Service |
3b7cb7 |
subclass loaded from a file, then you _must_ compile the class (via use or
|
|
Packit Service |
3b7cb7 |
require or some other magic) _before_ you import `Exception::Class` or
|
|
Packit Service |
3b7cb7 |
you'll get a compile time error.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
- fields
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
This allows you to define additional attributes for your exception class. Any
|
|
Packit Service |
3b7cb7 |
field you define can be passed to the `throw` or `new` methods as additional
|
|
Packit Service |
3b7cb7 |
parameters for the constructor. In addition, your exception object will have
|
|
Packit Service |
3b7cb7 |
an accessor method for the fields you define.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
This parameter can be either a scalar (for a single field) or an array
|
|
Packit Service |
3b7cb7 |
reference if you need to define multiple fields.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
Fields will be inherited by subclasses.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
- alias
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
Specifying an alias causes this class to create a subroutine of the specified
|
|
Packit Service |
3b7cb7 |
name in the _caller's_ namespace. Calling this subroutine is equivalent to
|
|
Packit Service |
3b7cb7 |
calling `<class>->throw(@_)` for the given exception class.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
Besides convenience, using aliases also allows for additional compile time
|
|
Packit Service |
3b7cb7 |
checking. If the alias is called _without parentheses_, as in `throw_fields
|
|
Packit Service |
3b7cb7 |
"an error occurred"`, then Perl checks for the existence of the
|
|
Packit Service |
3b7cb7 |
`throw_fields` subroutine at compile time. If instead you do `ExceptionWithFields->throw(...)`, then Perl checks the class name at
|
|
Packit Service |
3b7cb7 |
runtime, meaning that typos may sneak through.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
- description
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
Each exception class has a description method that returns a fixed
|
|
Packit Service |
3b7cb7 |
string. This should describe the exception _class_ (as opposed to any
|
|
Packit Service |
3b7cb7 |
particular exception object). This may be useful for debugging if you start
|
|
Packit Service |
3b7cb7 |
catching exceptions you weren't expecting (particularly if someone forgot to
|
|
Packit Service |
3b7cb7 |
document them) and you don't understand the error messages.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
The `Exception::Class` magic attempts to detect circular class hierarchies
|
|
Packit Service |
3b7cb7 |
and will die if it finds one. It also detects missing links in a chain, for
|
|
Packit Service |
3b7cb7 |
example if you declare Bar to be a subclass of Foo and never declare Foo.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
# [Try::Tiny](https://metacpan.org/pod/Try::Tiny)
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
If you are interested in adding try/catch/finally syntactic sugar to your code
|
|
Packit Service |
3b7cb7 |
then I recommend you check out [Try::Tiny](https://metacpan.org/pod/Try::Tiny). This is a great module that helps
|
|
Packit Service |
3b7cb7 |
you ignore some of the weirdness with `eval` and `$@`. Here's an example of
|
|
Packit Service |
3b7cb7 |
how the two modules work together:
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
use Exception::Class ( 'My::Exception' );
|
|
Packit Service |
3b7cb7 |
use Scalar::Util qw( blessed );
|
|
Packit Service |
3b7cb7 |
use Try::Tiny;
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
try {
|
|
Packit Service |
3b7cb7 |
might_throw();
|
|
Packit Service |
3b7cb7 |
}
|
|
Packit Service |
3b7cb7 |
catch {
|
|
Packit Service |
3b7cb7 |
if ( blessed $_ && $_->isa('My::Exception') ) {
|
|
Packit Service |
3b7cb7 |
handle_it();
|
|
Packit Service |
3b7cb7 |
}
|
|
Packit Service |
3b7cb7 |
else {
|
|
Packit Service |
3b7cb7 |
die $_;
|
|
Packit Service |
3b7cb7 |
}
|
|
Packit Service |
3b7cb7 |
};
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
Note that you **cannot** use `Exception::Class->caught` with [Try::Tiny](https://metacpan.org/pod/Try::Tiny).
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
# Catching Exceptions Without [Try::Tiny](https://metacpan.org/pod/Try::Tiny)
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
`Exception::Class` provides some syntactic sugar for catching exceptions in a
|
|
Packit Service |
3b7cb7 |
safe manner:
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
eval {...};
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
if ( my $e = Exception::Class->caught('My::Error') ) {
|
|
Packit Service |
3b7cb7 |
cleanup();
|
|
Packit Service |
3b7cb7 |
do_something_with_exception($e);
|
|
Packit Service |
3b7cb7 |
}
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
The `caught` method takes a class name and returns an exception object if the
|
|
Packit Service |
3b7cb7 |
last thrown exception is of the given class, or a subclass of that class. If
|
|
Packit Service |
3b7cb7 |
it is not given any arguments, it simply returns `$@`.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
You should **always** make a copy of the exception object, rather than using
|
|
Packit Service |
3b7cb7 |
`$@` directly. This is necessary because if your `cleanup` function uses
|
|
Packit Service |
3b7cb7 |
`eval`, or calls something which uses it, then `$@` is overwritten. Copying
|
|
Packit Service |
3b7cb7 |
the exception preserves it for the call to `do_something_with_exception`.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
Exception objects also provide a caught method so you can write:
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
if ( my $e = My::Error->caught ) {
|
|
Packit Service |
3b7cb7 |
cleanup();
|
|
Packit Service |
3b7cb7 |
do_something_with_exception($e);
|
|
Packit Service |
3b7cb7 |
}
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
## Uncatchable Exceptions
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
Internally, the `caught` method will call `isa` on the exception object. You
|
|
Packit Service |
3b7cb7 |
could make an exception "uncatchable" by overriding `isa` in that class like
|
|
Packit Service |
3b7cb7 |
this:
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
package Exception::Uncatchable;
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
sub isa { shift->rethrow }
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
Of course, this only works if you always call `Exception::Class->caught`
|
|
Packit Service |
3b7cb7 |
after an `eval`.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
# USAGE RECOMMENDATION
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
If you're creating a complex system that throws lots of different types of
|
|
Packit Service |
3b7cb7 |
exceptions, consider putting all the exception declarations in one place. For
|
|
Packit Service |
3b7cb7 |
an app called Foo you might make a `Foo::Exceptions` module and use that in
|
|
Packit Service |
3b7cb7 |
all your code. This module could just contain the code to make
|
|
Packit Service |
3b7cb7 |
`Exception::Class` do its automagic class creation. Doing this allows you to
|
|
Packit Service |
3b7cb7 |
more easily see what exceptions you have, and makes it easier to keep track of
|
|
Packit Service |
3b7cb7 |
them.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
This might look something like this:
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
package Foo::Bar::Exceptions;
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
use Exception::Class (
|
|
Packit Service |
3b7cb7 |
Foo::Bar::Exception::Senses =>
|
|
Packit Service |
3b7cb7 |
{ description => 'sense-related exception' },
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
Foo::Bar::Exception::Smell => {
|
|
Packit Service |
3b7cb7 |
isa => 'Foo::Bar::Exception::Senses',
|
|
Packit Service |
3b7cb7 |
fields => 'odor',
|
|
Packit Service |
3b7cb7 |
description => 'stinky!'
|
|
Packit Service |
3b7cb7 |
},
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
Foo::Bar::Exception::Taste => {
|
|
Packit Service |
3b7cb7 |
isa => 'Foo::Bar::Exception::Senses',
|
|
Packit Service |
3b7cb7 |
fields => [ 'taste', 'bitterness' ],
|
|
Packit Service |
3b7cb7 |
description => 'like, gag me with a spoon!'
|
|
Packit Service |
3b7cb7 |
},
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
...
|
|
Packit Service |
3b7cb7 |
);
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
You may want to create a real module to subclass [Exception::Class::Base](https://metacpan.org/pod/Exception::Class::Base) as
|
|
Packit Service |
3b7cb7 |
well, particularly if you want your exceptions to have more methods.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
## Subclassing Exception::Class::Base
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
As part of your usage of `Exception::Class`, you may want to create your own
|
|
Packit Service |
3b7cb7 |
base exception class which subclasses [Exception::Class::Base](https://metacpan.org/pod/Exception::Class::Base). You should
|
|
Packit Service |
3b7cb7 |
feel free to subclass any of the methods documented above. For example, you
|
|
Packit Service |
3b7cb7 |
may want to subclass `new` to add additional information to your exception
|
|
Packit Service |
3b7cb7 |
objects.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
# Exception::Class FUNCTIONS
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
The `Exception::Class` method offers one function, `Classes`, which is not
|
|
Packit Service |
3b7cb7 |
exported. This method returns a list of the classes that have been created by
|
|
Packit Service |
3b7cb7 |
calling the `Exception::Class` `import` method. Note that this is _all_
|
|
Packit Service |
3b7cb7 |
the subclasses that have been created, so it may include subclasses created by
|
|
Packit Service |
3b7cb7 |
things like CPAN modules, etc. Also note that if you simply define a subclass
|
|
Packit Service |
3b7cb7 |
via the normal Perl method of setting `@ISA` or `use base`, then your
|
|
Packit Service |
3b7cb7 |
subclass will not be included.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
# SUPPORT
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
Bugs may be submitted at [https://github.com/houseabsolute/Exception-Class/issues](https://github.com/houseabsolute/Exception-Class/issues).
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
I am also usually active on IRC as 'autarch' on `irc://irc.perl.org`.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
# SOURCE
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
The source code repository for Exception-Class can be found at [https://github.com/houseabsolute/Exception-Class](https://github.com/houseabsolute/Exception-Class).
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
# DONATIONS
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
If you'd like to thank me for the work I've done on this module, please
|
|
Packit Service |
3b7cb7 |
consider making a "donation" to me via PayPal. I spend a lot of free time
|
|
Packit Service |
3b7cb7 |
creating free software, and would appreciate any support you'd care to offer.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
Please note that **I am not suggesting that you must do this** in order for me
|
|
Packit Service |
3b7cb7 |
to continue working on this particular software. I will continue to do so,
|
|
Packit Service |
3b7cb7 |
inasmuch as I have in the past, for as long as it interests me.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
Similarly, a donation made in this way will probably not make me work on this
|
|
Packit Service |
3b7cb7 |
software much more, unless I get so many donations that I can consider working
|
|
Packit Service |
3b7cb7 |
on free software full time (let's all have a chuckle at that together).
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
To donate, log into PayPal and send money to autarch@urth.org, or use the
|
|
Packit Service |
3b7cb7 |
button at [http://www.urth.org/~autarch/fs-donation.html](http://www.urth.org/~autarch/fs-donation.html).
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
# AUTHOR
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
Dave Rolsky <autarch@urth.org>
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
# CONTRIBUTORS
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
- Alexander Batyrshin <0x62ash@gmail.com>
|
|
Packit Service |
3b7cb7 |
- Leon Timmermans <fawaka@gmail.com>
|
|
Packit Service |
3b7cb7 |
- Ricardo Signes <rjbs@cpan.org>
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
# COPYRIGHT AND LICENSE
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
This software is copyright (c) 2017 by Dave Rolsky.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
This is free software; you can redistribute it and/or modify it under
|
|
Packit Service |
3b7cb7 |
the same terms as the Perl 5 programming language system itself.
|
|
Packit Service |
3b7cb7 |
|
|
Packit Service |
3b7cb7 |
The full text of the license can be found in the
|
|
Packit Service |
3b7cb7 |
`LICENSE` file included with this distribution.
|