# We will store the additional blocks here, and we will dig deeper into them recursively.
my$has_additional_blocks= 0;
# NOTE: Checking for consecutive try-catch block statements
# 2020-09-13: PPI will return 2 or more consecutive try-catch block as 1 statement
# It does not tell them apart, so we need to post process the result to effectively search within for possible for other try-catch blocks and update the @$ref array consequently
# Array to contain the new version of the $ref array.
my$alt_ref= [];
$self->_message( 3, "[blocks check level ${level}] Checking for consecutive try-catch blocks in ", scalar( @$ref), " results found by PPI") if( $self->{debug} >= 3 );
foreachmy$this( @$ref)
{
$self->_message( 3, "[blocks check level ${level}] Getting children from object '", overload::StrVal( $this), "'") if( $self->{debug} >= 3 );
$self->_message( 3, "[blocks check level ${level}] Checking if following code has children") if( $self->{debug} >= 3 );
$self->_messagef( 3, "[blocks check level ${level}] Found %d try-catch block(s) in initial PPI result.", scalar( @$tmp_ref) ) if( $self->{debug} >= 3 );
# If we did find consecutive try-catch blocks, we add each of them after the nominal one and remove the nominal one after. The nominal one should be empty by then
if( scalar( @$tmp_ref) > 1 )
{
my$last_obj= $this;
my$spaces= [];
foreachmy$arr( @$tmp_ref)
{
$self->_message( 3, "[blocks check level ${level}] Adding statement block with ", scalar( @$arr), " children after one at line ", $last_obj->line_number, ": '", substr( $last_obj, 0, 255 ), "'") if( $self->{debug} >= 3 );
$err= sprintf( 'Object to be added after last try-block statement must be a PPI::Element. Class provided is \"%s\".', $st->class );
}
elsif( !$rc)
{
my$requires;
if( $last_obj->isa( 'PPI::Structure') ||
$last_obj->isa( 'PPI::Token') )
{
$requires= 'PPI::Structure or PPI::Token';
}
elsif( $last_obj->isa( 'PPI::Statement') )
{
$requires= 'PPI::Statement or PPI::Token';
}
$err= sprintf( "Object of class \"%s\" could not be added after object with address '%s' and of class '%s' with parent '%s' with class '%s': '$last_obj'. The object of class '%s' must be a ${requires} object.", $st->class, Scalar::Util::refaddr( $last_obj), $last_obj->class, Scalar::Util::refaddr( $last_obj->parent ), $last_obj->parent->class, $st->class );
}
else
{
$last_obj= $st;
if( scalar( @$insignificants) )
{
$self->_messagef( 4, "[blocks check level ${level}] Adding %d trailing insignificant objects after last element of class '%s'", scalar( @$insignificants), $last_obj->class ) if( $self->{debug} >= 4 );
foreachmy$o( @$insignificants)
{
$self->_messagef( 4, "[blocks check level ${level}] Adding trailing insignificant object of class '%s' after last element of class '%s'", $o->class, $last_obj->class ) if( $self->{debug} >= 4 );
# printf( STDERR "Inserting object '%s' (%s) of type '%s' after object '%s' (%s) of type %s who has parent '%s' of type '%s'\n", overload::StrVal( $o ), Scalar::Util::refaddr( $o ), ref( $o ), overload::StrVal( $last_obj), Scalar::Util::refaddr( $last_obj ), ref( $last_obj ), overload::StrVal( $last_obj->parent ), ref( $last_obj->parent ) );
CORE::eval
{
$rc= $last_obj->insert_after( $o) ||
do
{
warn( "Failed to insert object of class '", $o->class, "' before last object of class '", $st->class, "'\n") if( $self->{debug} );
};
};
if( $@ )
{
if( ref( $o) )
{
warn( "Failed to insert object of class '", $o->class, "' before last object of class '", $st->class, "': $@\n") if( $self->{debug} );
}
else
{
warn( "Was expecting an object to insert before last object of class '", $st->class, "', but instead got '$o': $@\n") if( $self->{debug} );
}
}
elsif( !defined( $rc) )
{
warn( sprintf( 'Object to be added after last try-block statement must be a PPI::Element. Class provided is \"%s\".', $o->class ) ) if( $self->{debug} );
}
elsif( !$rc)
{
warn( sprintf( "Object of class \"%s\" could not be added after object of class '%s': '$last_obj'.", $o->class, $last_obj->class ) ) if( $self->{debug} );
}
# printf( STDERR "Object inserted '%s' (%s) of class '%s' now has parent '%s' (%s) of class '%s'\n", overload::StrVal( $o ), Scalar::Util::refaddr( $o ), ref( $o ), overload::StrVal( $o->parent ), Scalar::Util::refaddr( $o->parent ), ref( $o->parent ) );
my$begin_block= PPI::Token->new( $begin_block_code) || die( "Unable to create token");
$self->_message( 5, "Inserting BEGIN element object '", overload::StrVal( $begin_block), "', before '", overload::StrVal( $this), "'");
my$rv= $this->__insert_before( $begin_block);
if( !defined( $rv) )
{
warn( "BEGIN block object (", overload::StrVal( $begin_block), ") to be inserted before the DATA token is not a valid object.") if( warnings::enabled() );
last;
}
elsif( !$rv)
{
warn( "Somehow, the BEGIN block object (", overload::StrVal( $begin_block), ") could not be inserted before the DATA token.") if( warnings::enabled() );
$self->_message( 5, "Checking parent element of class ", $parent->class, " and value $parent") if( $self->{debug} >= 5 );
if( $parent->class eq 'PPI::Structure::Block')
{
my$sub_name;
my$prev= $parent->sprevious_sibling;
while( $prev)
{
if( $prev->content eq 'sub')
{
return({ element=> $parent, name=> $sub_name});
}
if( $prev->class eq 'PPI::Token::Word')
{
if( CORE::defined( $sub_name) )
{
warn( "Found some redefinition of a subroutine's name at line ", $prev->line_number, " for subroutine '${sub_name}'\n") if( warnings::enabled() );
}
$sub_name= $prev->content;
}
$prev= $prev->sprevious_sibling;
}
}
$parent= $parent->parent;
}
return;
};
my$def= $find_closest_sub->( $elem);
if( $def)
{
my$block= $def->{element};
$self->_message( 5, "Found a sub block at line ", $block->line_number, " of class ", $block->class, " with name '", ( $def->{name} // 'anonymous' ), "'" ) if( $self->{debug} >= 5 );
catch( My::Exception $e where { $_->code == 500 })
{
print( "Oopsie\n" );
}
catch( My::Exception $e where { $_->code == 401 })
{
print( "Get away!\n" );
}
catch( My::Exception $e )
{
print( "Got an exception: $e\n" );
}
catch( $default )
{
print( "Something weird has happened: $default\n" );
}
finally
{
$dbh->disconnect;
}
See more on this in L</"EXCEPTION CLASS">
=item * C<$@> is always available too
=item * You can return a value from try-catch blocks, even with embedded try-catch blocks
=item * It recognises C<@_> inside try-catch blocks, so you can do something like:
print( &gotme( 'Jacques' ), "\n" );
sub gotme
{
try
{
print( "I am trying my best $_[0]!\n" );
die( "But I failed\n" );
}
catch( $some_reason )
{
return( "Failed: $some_reason" );
}
}
Would produce:
I am trying my best Jacques!
Failed: But I failed
=item * C<try> or C<catch> blocks can contain flow control keywords such as C<next>, C<last> and C<redo>
while( defined( my $product = $items->[++$i] ) )
{
try
{
# Do something
last if( !$product->active );
}
catch( $oops )
{
$log->( "Error: $oops" );
last;
}
}
continue
{
try
{
if( $product->region eq 'Asia' )
{
push( @asia, $product );
}
else
{
next;
}
}
catch( $e )
{
$log->( "An unexpected error has occurred. Is $product an object? $e" );
last;
}
}
=item * Can be used with or without a C<catch> block
=item * Supports a C<finally> block called in void context for cleanup for example. The C<finally> block will always be called, if present.
#!/usr/local/bin/perl
use v5.36;
use strict;
use warnings;
use Nice::Try;
try
{
die( "Oops" );
}
catch( $e )
{
say "Caught an error: $e";
die( "Oops again" );
}
finally
{
# Some code here that will be executed after the catch block dies
say "Got here in finally with \$\@ -> $@";
}
The above would yield something like:
Caught error: Oops at ./test.pl line 9.
Oops again at ./test.pl line 14.
Got here in finally with $@ -> Oops again at ./test.pl line 14.
=item * L<Nice::Try> is rich context aware, which means it can provide you with a super granular context on how to return data back to the caller based on the caller's expectation, by using a module like L<Want>.
=item * Call to L<perlfunc/caller> will return the correct entry in call stack
#!/usr/bin/perl
BEGIN
{
use strict;
use warnings;
use Nice::Try;
};
{
&callme();
}
sub callme
{
try
{
my @info = caller(1); # or my @info = caller;
print( "Called from package $info[0] in file $info[1] at line $info[2]\n" );
}
catch( $e )
{
print( "Got an error: $e\n" );
}
}
Will yield: C<Called from package main in file ./test.pl at line 10>
=back
=head1 WHY USE IT?
There are quite a few implementations of try-catch blocks in perl, and they can be grouped in 4 categories:
It also imports the subroutines C<try> and C<catch> in your namespace.
And you cannot do exception variable assignment like C<catch( $err )>
In group 2, L<Try::Harder> does a very nice work, but relies on perl regular expression with L<Text::Balanced> and that makes it susceptible to failure if the try-catch block is not written as it expects it to be. For example if you put comments between try and catch, it would not work anymore. This is because parsing perl is famously difficult. Also, it does not do exception variable assignment, or catch filtered based on exception class like:
try
{
# Something
die( Exception->new( "Failed!" ) );
}
catch( Exception $e )
{
# Do something if exception is an Exception class
}
See L<perlfunc/"die"> for more information on dying with an object.
Also L<Try::Harder> will die if you use only C<try> with no catch, such as:
use Try::Harder;
try
{
die( "Oops\n" );
}
# Will never reach this
print( "Got here with $@\n" );
In this example, the print line will never get executed. With L<Nice::Try> you can use C<try> alone as an equivalent of L<perlfunc/"eval"> and the C<$@> will be available too. So:
use Nice::Try;
try
{
die( "Oops\n" );
}
print( "Got here with $@\n" );
will produces:
Got here with Oops
In group 3, L<TryCatch> was working wonderfully, but was relying on L<Devel::Declare> which was doing some esoteric stuff and eventually the version 0.006020 broke L<TryCatch> and there seems to be no intention of correcting this breaking change. Besides, L<Devel::Declare> is now marked as deprecated and its use is officially discouraged.
L<TryCatch> does not support any C<finally> block.
In group 4, there is L<Syntax::Keyword::Try>, which is a great alternative if you do not care about exception class filter (it supports class exception since 2020-07-21 with version 0.15 and variable assignment since 2020-08-01 with version 0.18).
Although, the following script would not work under L<Syntax::Keyword::Try> :
BEGIN
{
use strict;
use warnings;
use Syntax::Keyword::Try;
};
{
&callme();
}
sub callme
{
try {
print( "Hello there\n" );
}
catch ($e) {
print( "Got an error: $e\n" );
}
}
This will trigger the following error:
syntax error at ./test.pl line 18, near ") {"
syntax error at ./test.pl line 21, near "}"
Execution of ./test.pl aborted due to compilation errors.
That is because L<Syntax::Keyword::Try> expects to be C<used> outside of a BEGIN block like this:
use strict;
use warnings;
use Syntax::Keyword::Try;
# Rest of the script, same as above
Of course, with L<Nice::Try>, there is no such constraint. You can L<perlfunc/use> L<Nice::Try> inside or outside of a C<BEGIN> block indistinctively.
use feature 'try'; # will emit a warning this is experimental
This new feature supports try-catch block and variable assignment, but no exception class, nor support for C<finally> block until version L<perl 5.36 released on 2022-05-28|https://perldoc.perl.org/5.36.0/perldelta> of perl, so you can do:
try
{
# Oh no!
die( "Argh...\n" );
}
catch( $oh_well )
{
return( $self->error( "Something went awry: $oh_well" ) );
}
But B<you cannot do>:
try
{
# Oh no!
die( MyException->new( "Argh..." ) );
}
catch( MyException $oh_well )
{
return( $self->error( "Something went awry with MyException: $oh_well" ) );
}
# Support for 'finally' has been implemented in perl 5.36 released on 2022-05-28
Also, the C<use feature 'try'> expression must be in the relevant block where you use C<try-catch>. You cannot just put it in your C<BEGIN> block at the beginning of your script. If you have 3 subroutines using C<try-catch>, you need to put C<use feature 'try'> in each of them. See L<perl documentation on lexical effect|https://perldoc.perl.org/feature#Lexical-effect> for more explanation on this.
It is probably a matter of time until this is fully implemented in perl as a regular non-experimental feature.
So, L<Nice::Try> is quite unique and fills the missing features, and since it uses XS modules for a one-time filtering, it is quite fast.
=head1 FINALLY
Like with other language such as Java or JavaScript, the C<finally> block will be executed even if the C<try> or C<catch> block contains a return statement.
This is useful to do some clean-up. For example:
try
{
# Something worth dying
}
catch( $e )
{
return( "I failed: $e" );
}
finally
{
# Do some mop up
# This would be reached even if catch already returned
# Putting return statement here does not actually return anything.
# This is only for clean-up
}
However, because this is designed for clean-up, it is called in void context, so any C<return> statement there will not actually return anything back to the caller.
=head1 CATCHING OR NOT CATCHING?
L<Nice::Try> can be used with a single C<try> block which will, in effect, behaves like an eval and the special variable C<$@> will be available as always.
try
{
die( "Oh no, something went wrong!\n" );
}
print( "Got here with $@\n" );
or even:
try
{
die( "Oh no, something went wrong!\n" );
}
catch( $e ); # Not very meaningful, but it will work
print( "Got here with $@\n" );
However, if you decide to catch class exceptions, make sure to add a default C<catch( $e )>. For example:
try
{
die( MyException->new( "Oh no" ) );
}
print( "Got here with $@\n" );
will work and C<print> will display "Got here with Oh no". However:
try
{
die( MyException->new( "Oh no" ) );
}
catch( Some::Exception $e )
{
# won't reach here
}
will make your process die because of the exception not being caught, thus you might want to do instead:
try
{
die( MyException->new( "Oh no" ) );
}
catch( Some::Exception $e )
{
# won't reach here
}
catch( $default )
{
print( "Got you! Error was: $default\n" );
}
And the last catch will catch the exception.
Since, try-catch block can be nested, the following would work too:
try
{
try
{
die( MyException->new( "Oh no" ) );
}
catch( Some::Exception $e )
{
# won't reach here
}
}
catch( MyException $e )
{
print( "Got you! MyException was: $e\n" );
}
# to play it safe
catch( $e )
{
# do something about it
}
=head1 EXCEPTION CLASS
As mentioned above, you can use class when raising exceptions and you can filter them in a variety of ways when you catch them.
Here are your options (replace C<Exception::Class> with your favorite exception class):
In the condition block C<$_> will always be made available and will correspond to the exception object thrown, just like C<$oopsie> in this example. C<$@> is also available with the exception object as its value.
=item 3. catch( $e isa Exception::Class ) { }
This is a variant of the C<catch( Exception::Class $e ) {}> form
=item 4. catch( $e isa('Exception::Class') ) { }
A variant of the one above if you want to use single quotes.
=item 5. catch( $e isa("Exception::Class") ) { }
A variant of the one above if you want to use double quotes.
=item 6. catch( $e isa Exception::Class where { $condition } ) { }
This is not a class exception catching, but worth mentioning. For example:
try
{
die( "Something bad happened.\n" );
}
catch( $e where { /something bad/i })
{
# Do something about it
}
catch( $e )
{
# Default here
}
=back
=head1 LOOPS
Since version v0.2.0 L<Nice::Try> supports the use of flow control keywords such as C<next>, C<last> and C<redo> inside try-catch blocks. For example:
my @names = qw( John Jack Peter Paul Mark );
for( $i..$#names )
{
try
{
next if( $i == 2 );
# some more code...
}
catch( $e )
{
print( "Got exception: $e\n" );
}
}
It also works inside the catch block or inside the C<continue> block:
while( defined( my $product = $items->[++$i] ) )
{
# Do something
}
continue
{
try
{
if( $product->region eq 'Asia' )
{
push( @asia, $product );
}
else
{
next;
}
}
catch( $e )
{
$log->( "An unexpected error has occurred. Is $product an object? $e" );
last;
}
}
Control flow with labels also work
ELEM: foreach my $n ( @names )
{
try
{
$n->moveAfter( $this );
next ELEM if( $n->value == 1234567 );
}
catch( $oops )
{
last ELEM;
}
}
However, if you enclose a try-catch block inside another block, use of C<next>, C<last> or C<redo> will silently not work. This is due to perl control flow. See L<perlsyn> for more information on this. For example, the following would not yield the desired outcome:
ELEM: foreach my $n ( @names )
{
{ # <--- Here is the culprit
try
{
$n->moveAfter( $this );
# This next statement will not do anything.
next ELEM if( $n->value == 1234567 );
}
catch( $oops )
{
# Neither would this one.
last ELEM;
}
}
}
=head1 CONTEXT AWARENESS
L<Nice::Try> provides a high level of granularity about the context in which your subroutine was called.
Normally, you would write something like this, and it works as always:
sub info
{
try
{
# do something here
if( wantarray() )
{
return( @list_of_values );
}
# caller just want a scalar
elsif( defined( wantarray() ) )
{
return( $name );
}
# otherwise if undefined, it means we are called in void context, like:
# $o->info; with no expectation of return value
}
catch( $e )
{
print( "Caught an error: $e\n" );
}
}
The above is nice, but how do you differentiate cases were your caller wants a simple returned value and the one where the caller wants an object for chaining purpose, or if the caller wants an hash or array reference in return?
For example:
my $val = $o->info->[2]; # wants an array reference
my $val = $o->info->{name} # wants an hash reference
# etc...
Now, you can do the following:
use Want; # an awesome module which extends wantarray
sub info
{
my $self = shift( @_ );
try
{
# Do something
#
# same as wantarray() == 1
if( want('LIST') )
{
return( @some_data );
}
# same as: if( defined( wantarray() ) && !wantarray() )
elsif( want('SCALAR' ) )
{
return( $name ); # regular string
}
# same as if( !defined( wantarray() ) )
elsif( want('VOID') )
{
return;
}
# For the other contexts below, wantarray is of no help
if( want('OBJECT') )
{
return( $obj ); # useful for chaining
}
elsif( want('CODE') )
{
# dummy code ref for example
return( sub{ return( $name ); } );
}
elsif( want('ARRAY') )
{
return( \@some_data );
}
elsif( want('HASH') )
{
return({ name => $name, location => $city });
}
}
catch( $e )
{
$Logger->( "Caught exception: $e" );
}
}
Thus this is particularly useful if, for example, you want to differentiate if the caller just wants a return string, or an object for chaining.
L<perlfunc/wantarray> would not know the difference, and other try-catch implementation would not let you benefit from using L<Want>.
For example:
my $val = $o->info; # simple regular scalar context; but...
# here, we are called in object context and wantarray is of no help to tell the difference
my $val = $o->info->another_method;
Other cases are:
# regular string context
my $name = $o->info;
# list context like wantarray
my @data = $o->info;
# code context
my $name = $o->info->();
# hash context
my $name = $o->info->{name};
# array context
my $name = $o->info->[2];
# object context
my $name = $o->info->another_method;
See L<Want> for more information on how you can benefit from it.
Currently lvalues are not implemented and will be in future releases. Also note that L<Want> does not work within tie-handlers. It would trigger a segmentation fault. L<Nice::Try> detects this and disable automatically support for L<Want> if used inside a tie-handler, reverting to regular L<perlfunc/wantarray> context.
Also, for this rich context awareness to be used, obviously try-catch would need to be inside a subroutine, otherwise there is no rich context other than the one the regular L<perlfunc/wantarray> provides.
This is particularly true when running within an Apache modperl handler which has no caller. If you use L<Nice::Try> in such handler, it will kill Apache process, so you need to disable the use of L<Want>, by calling:
use Nice::Try dont_want => 1;
When there is an update to correct this bug from L<Want>, I will issue a new version.
The use of L<Want> is also automatically disabled when running under a package that use overloading.
=head1 __DATA__ and __END__ sections
Due to a limitation to the way source filter works with L<Filter::Util::Call>, normally, it is not possible to make the data available after the C<__DATA__> or C<__END__> token accessible with the special glob C<DATA>, but with C<Nice::Try>, it is possible. Thus, the following would work as you would expect:
#!/usr/bin/env perl
use strict;
use warnings;
use Nice::Try;
try
{
print "Poem by Pierre de Ronsard (1545)\n";
print while( <DATA> );
}
catch($e)
{
print( "Oh no: $e\n" );
}
__END__
Mignonne, allons voir si la rose
Qui ce matin avoit desclose
Sa robe de pourpre au Soleil,
A point perdu ceste vesprée
Les plis de sa robe pourprée,
Et son teint au vostre pareil.
The same would work if the C<__DATA__> were used instead of C<__END__>
And, if you mix POD, it will ignore it to only make available in the C<DATA> glob the non-POD data. For example:
#!/usr/bin/env perl
use strict;
use warnings;
use Nice::Try;
try
{
print "Poem by Pierre de Ronsard (1545)\n";
print while( <DATA> );
}
catch($e)
{
print( "Oh no: $e\n" );
}
__END__
Mignonne, allons voir si la rose
Qui ce matin avoit desclose
Sa robe de pourpre au Soleil,
A point perdu ceste vesprée
Les plis de sa robe pourprée,
Et son teint au vostre pareil.
=encoding utf-8
=head1 NAME
French::Poetry - Pierre de Ronsard, Ode à Cassandre
=head1 DESCRIPTION
This famous poem was made by Pierre de Ronsard after he met Cassandre Salviati, daughter of an Italian banker, at the court in 1545.
This poem is the epitome of Epicureanism (Carpe diem).
=cut
Las ! voyez comme en peu d'espace,
Mignonne, elle a dessus la place,
Las ! las ! ses beautés laissé choir !
Ô vraiment marâtre Nature,
Puisqu'une telle fleur ne dure
Que du matin jusques au soir !
Donc, si vous me croyez, mignonne,
Tandis que votre âge fleuronne
En sa plus verte nouveauté,
Cueillez, cueillez votre jeunesse :
Comme à cette fleur, la vieillesse
Fera ternir votre beauté.
This would yield the entire poem of 3 paragraph, while skipping the POD in-between. Of course, the same would work with C<__DATA__>
The C<DATA> is actually an C<IO::File> object generated with C<Symbol::geniosym()>
See also L<perldata/"Special-Literals"> for more information.
=head1 LIMITATIONS
Before version C<v1.3.5>, there was a limitation on using signature on a subroutine, but since version C<v1.3.5>, it has been fixed and there is no more any limitation. Thus the following works nicely too.
use strict;
use warnings;
use experimental 'signatures';
use Nice::Try;
sub test { 1 }
sub foo ($f = test()) { 1 }
try {
my $k = sub ($f = foo()) {}; # <-- this sub routine attribute inside try-catch block used to disrupt Nice::Try and make it fail.
print( "worked\n" );
}
catch($e) {
warn "caught: $e";
}
__END__
=head1 PERFORMANCE
C<Nice::Try> is quite fast, but as with any class implementing a C<try-catch> block, it is of course a bit slower than the natural C<eval> block.
Because C<Nice::Try> relies on L<PPI> for parsing the perl code, if your code is very long, there will be an execution time penalty.
If you use framework such as L<mod_perl2>, then it will only affect the first time the code is run, since afterward, the code will be loaded in memory.
Still, if you use perl version C<v5.34> or higher, and have simple need of C<try-catch>, then simply use instead perl experimental implementation, such as:
use v5.34;
use strict;
use warnings;
use feature 'try';
no warnings 'experimental';
try
{
# do something
}
catch( $e )
{
# catch fatal error here
}
=head1 DEBUGGING
And to have L<Nice::Try> save the filtered code to a file, pass it the C<debug_file> parameter like this:
use Nice::Try debug_file => './updated_script.pl';
You can also call your script using L<Filter::ExtractSource> like this:
In the updated script produced, you can add the line calling L<Nice::Try> to:
use Nice::Try no_filter => 1;
to avoid L<Nice::Try> from filtering your script
If you want L<Nice::Try> to produce human readable code, pass it the C<debug_code> parameter like this:
use Nice::Try debug_code => 1;
=head1 CLASS FUNCTIONS
The following class functions can be used.
=head2 implement
my $new_code = Nice::Try->implement( $perl_code );
eval( $new_code );
Provided with a perl code having one or more try-catch blocks and this will return a perl code converted to support try-catch blocks.
This is designed to be used for perl code you store, such as subroutines dynamically loaded or eval'ed.
For example:
my $code = Nice::Try->implement( <<EOT );
sub $method
{
my \$self = shift( \@_ );
try
{
# doing something that may die here
}
catch( \$e )
{
return( \$self->error( "Oops: \$e ) );
}
}
EOT
You can also pass an optional hash or hash reference of options to L</implement> and it will be used to instantiate a new L<Nice::Try> method. The options accepted are the same ones that can be passed when using C<use Nice::Try>
=head1 CREDITS
Credits to Stephen R. Scaffidi for his implementation of L<Try::Harder> from which I initially borrowed some code.