The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Sub::Exception;
use strict;
use warnings;

our $VERSION = '0.01';

sub import {
    my ($class, %args) = @_;

    my $caller = caller;

    while (my ($fn, $handler) = each %args) {
        $class->export(
            name          => $fn,
            error_handler => $handler,
            package       => $caller,
        );
    }
}

sub export {
    my ($class, %args) = @_;

    my $name          = $args{name};
    my $error_handler = $args{error_handler},
    my $pkg           = $args{package};

    no strict 'refs';
    *{"$pkg\::$name"} = sub (&) {
        my ($sub) = @_;

        eval {
            $sub->();
        };
        if ($@) {
            $error_handler->($_ = $@);
        }
    };
}

1;

__END__

=for stopwords Str

=head1 NAME

Sub::Exception - Code block with exception handler.

=head1 SYNOPSIS

Usually, this module acts in use phase:

    use Redis;
    use Sub::Exception redis_cmds => sub { MyException->throw($_) };

    # when some exception occurred in this block, exception MyException threw.
    redis_cmds {
        my $redis = Redis->new;
        
        $redis->multi;
        ... # redis commands
        $redis->exec;
    };

Optionally have class methods exporting code block to specified package.

    use Sub::Exception;
    
    Sub::Exception->export(
        name          => 'redis_cmds',
        error_handler => sub { MyException->throw($_) },
        package       => 'Target::Package',
    );

=head1 DESCRIPTION

Sub::Exception is code block generator that have own exception handler.

=head1 IMPORT FUNCTION

    use Sub::Exception %name_handler_pairs;

This is main usage of this module. You can set multiple sub name and error handler pairs at once.

    use Sub::Exception
        database_cmds => sub { die sprintf 'DB Error: %s', $_ },
        redis_cmds    => sub { die sprintf 'Redis Error: %s', $_ };

Above code is export two subs: C<database_cmds> and C<redis_cmds> into current package.
And these subs has own error handlers.

    database_cmd {
        # some database functions
    };

Exceptions in this code block is caught by its error handler:

    sub { die sprintf 'DB Error: %s', $_ }

So all exceptions wraps 'DB Error: ' prefix string and re-throw it.

=head1 CLASS METHODS

=head2 export( name => 'Str', error_handler => 'CodeRef', package => 'Str' )

    Sub::Exception->export(
        name          => 'redis_cmds',
        error_handler => sub { MyException->throw($_) },
        package       => 'Target::Package',
    );

Another way to export code blocks. 

    use Sub::Exception func => sub { ... };

is equivalent to:

    use Sub::Exception;
    Sub::Exception->export(
        name          => 'func',
        error_handler => sub { ... },
        package       => __PACKAGE__,
    );

This method is a bit verbosity but it's possible to export functions to any packages.

=head1 AUTHOR

Daisuke Murase <typester@cpan.org>

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2012 KAYAC Inc. All rights reserved.

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

The full text of the license can be found in the
LICENSE file included with this module.

=cut