=head1 VERSION
-Version 0.10
+Version 0.16
=cut
our $VERSION;
BEGIN {
- $VERSION = '0.10';
+ $VERSION = '0.16';
}
=head1 SYNOPSIS
- package X;
+L</reap>, L</localize>, L</localize_elem>, L</localize_delete> and L</WORDS> :
- use Scope::Upper qw/reap localize localize_elem localize_delete :words/;
+ package Scope;
- sub desc { shift->{desc} }
+ use Scope::Upper qw<reap localize localize_elem localize_delete :words>;
- sub set_tag {
- my ($desc) = @_;
+ sub new {
+ my ($class, $name) = @_;
- # First localize $x so that it gets destroyed last
- localize '$x' => bless({ desc => $desc }, __PACKAGE__) => UP; # one scope up
+ localize '$tag' => bless({ name => $name }, $class) => UP;
- reap sub {
- my $pkg = caller;
- my $x = do { no strict 'refs'; ${$pkg.'::x'} }; # Get the $x in the scope
- print $x->desc . ": done\n";
- } => SCOPE 1; # same as UP here
+ reap { print Scope->tag->name, ": end\n" } UP;
+ }
+
+ # Get the tag stored in the caller namespace
+ sub tag {
+ my $l = 0;
+ my $pkg = __PACKAGE__;
+ $pkg = caller $l++ while $pkg eq __PACKAGE__;
+
+ no strict 'refs';
+ ${$pkg . '::tag'};
+ }
+ sub name { shift->{name} }
+
+ # Locally capture warnings and reprint them with the name prefixed
+ sub catch {
localize_elem '%SIG', '__WARN__' => sub {
- my $pkg = caller;
- my $x = do { no strict 'refs'; ${$pkg.'::x'} }; # Get the $x in the scope
- CORE::warn($x->desc . ': ' . join('', @_));
- } => UP CALLER 0; # same as UP here
+ print Scope->tag->name, ': ', @_;
+ } => UP;
+ }
- # delete last @ARGV element
- localize_delete '@ARGV', -1 => UP SUB HERE; # same as UP here
+ # Locally clear @INC
+ sub private {
+ for (reverse 0 .. $#INC) {
+ # First UP is the for loop, second is the sub boundary
+ localize_delete '@INC', $_ => UP UP;
+ }
}
- package Y;
+ ...
+
+ package UserLand;
{
- X::set_tag('pie');
- # $x is now a X object, and @ARGV has one element less
- warn 'what'; # warns "pie: what at ..."
- ...
- } # "pie: done" is printed
+ Scope->new("top"); # initializes $UserLand::tag
+
+ {
+ Scope->catch;
+ my $one = 1 + undef; # prints "top: Use of uninitialized value..."
+
+ {
+ Scope->private;
+ eval { require Cwd };
+ print $@; # prints "Can't locate Cwd.pm in @INC (@INC contains:) at..."
+ }
+
+ require Cwd; # loads Cwd.pm
+ }
+
+ } # prints "top: done"
- package Z;
+L</unwind> and L</want_at> :
- use Scope::Upper qw/unwind want_at :words/;
+ package Try;
+
+ use Scope::Upper qw<unwind want_at :words>;
sub try (&) {
my @result = shift->();
- my $cx = SUB UP SUB;
+ my $cx = SUB UP; # Point to the sub above this one
unwind +(want_at($cx) ? @result : scalar @result) => $cx;
}
sub zap {
try {
+ my @things = qw<a b c>;
return @things; # returns to try() and then outside zap()
# not reached
- }
+ };
# not reached
}
- my @what = zap(); # @what contains @things
+ my @stuff = zap(); # @stuff contains qw<a b c>
+ my $stuff = zap(); # $stuff contains 3
+
+L</uplevel> :
+
+ package Uplevel;
+
+ use Scope::Upper qw<uplevel CALLER>;
+
+ sub target {
+ faker(@_);
+ }
+
+ sub faker {
+ uplevel {
+ my $sub = (caller 0)[3];
+ print "$_[0] from $sub()";
+ } @_ => CALLER(1);
+ }
+
+ target('hello'); # "hello from Uplevel::target()"
=head1 DESCRIPTION
=item *
-return values immediately to an upper level with L</unwind>, and know which context was in use then with L</want_at>.
+return values immediately to an upper level with L</unwind>, and know which context was in use then with L</want_at> ;
+
+=item *
+
+execute a subroutine in the setting of an upper subroutine stack frame with L</uplevel>.
=back
will set C<$x> to a reference to the string C<'foo'>.
Other sigils (C<'@'>, C<'%'>, C<'&'> and C<'*'>) require C<$value> to be a reference of the corresponding type.
-When the symbol is given by a string, it is resolved when the actual localization takes place and not when C<localize> is called.
-Thus, if the symbol name is not qualified, it will refer to the variable in the package where the localization actually takes place and not in the one where the C<localize> call was compiled.
+When the symbol is given by a string, it is resolved when the actual localization takes place and not when L</localize> is called.
+Thus, if the symbol name is not qualified, it will refer to the variable in the package where the localization actually takes place and not in the one where the L</localize> call was compiled.
For example,
{
=head2 C<localize_elem $what, $key, $value, $context>
Introduces a C<local $what[$key] = $value> or C<local $what{$key} = $value> delayed to the time of first return into the upper scope denoted by C<$context>.
-Just like for L</localize>, the type of localization is determined from which kind of reference C<$value> is when C<$what> is a glob, and from the sigil when it's a string.
+Unlike L</localize>, C<$what> must be a string and the type of localization is inferred from its sigil.
+The two only valid types are array and hash ; for anything besides those, L</localize_elem> will throw an exception.
C<$key> is either an array index or a hash key, depending of which kind of variable you localize.
If C<$what> is a string pointing to an undeclared variable, the variable will be vivified as soon as the localization occurs and emptied when it ends, although it will still exist in its glob.
will rightfully set C<$num> to C<26>.
+=head2 C<uplevel $code, @args, $context>
+
+Executes the code reference C<$code> with arguments C<@args> as if it were located at the subroutine stack frame pointed by C<$context>, effectively fooling C<caller> and C<die> into believing that the call actually happened higher in the stack.
+The code is executed in the context of the C<uplevel> call, and what it returns is returned as-is by C<uplevel>.
+
+ sub target {
+ faker(@_);
+ }
+
+ sub faker {
+ uplevel {
+ map { 1 / $_ } @_;
+ } @_ => CALLER(1);
+ }
+
+ my @inverses = target(1, 2, 4); # @inverses contains (0, 0.5, 0.25)
+ my $count = target(1, 2, 4); # $count is 3
+
+L<Sub::Uplevel> also implements a pure-Perl version of C<uplevel>.
+Both are identical, with the following caveats :
+
+=over 4
+
+=item *
+
+The L<Sub::Uplevel> implementation of C<uplevel> may execute a code reference in the context of B<any> upper stack frame.
+The L<Scope::Upper> version only allows to uplevel to a B<subroutine> stack frame, and will croak if you try to target an C<eval> or a format.
+
+=item *
+
+Exceptions thrown from the code called by this version of C<uplevel> will not be caught by C<eval> blocks between the target frame and the uplevel call, while they will for L<Sub::Uplevel>'s version.
+This means that :
+
+ eval {
+ sub {
+ local $@;
+ eval {
+ sub {
+ uplevel { die 'wut' } CALLER(2); # for Scope::Upper
+ # uplevel(3, sub { die 'wut' }) # for Sub::Uplevel
+ }->();
+ };
+ print "inner block: $@";
+ $@ and exit;
+ }->();
+ };
+ print "outer block: $@";
+
+will print "inner block: wut..." with L<Sub::Uplevel> and "outer block: wut..." with L<Scope::Upper>.
+
+=item *
+
+L<Sub::Uplevel> globally overrides the Perl keyword C<caller>, while L<Scope::Upper> does not.
+
+=back
+
+A simple wrapper lets you mimic the interface of L<Sub::Uplevel/uplevel> :
+
+ use Scope::Upper;
+
+ sub uplevel {
+ my $frame = shift;
+ my $code = shift;
+ my $cxt = Scope::Upper::CALLER($frame);
+ &Scope::Upper::uplevel($code => @_ => $cxt);
+ }
+
+Albeit the three exceptions listed above, it passes all the tests of L<Sub::Uplevel>.
+
=head1 CONSTANTS
=head2 C<SU_THREADSAFE>
# $cxt = SCOPE(4), UP SUB UP SUB, or UP SUB EVAL, or UP CALLER(2), or TOP
...
-Where L</unwind> and L</want_at> point to depending on the C<$cxt>:
+Where L</unwind>, L</want_at> and L</uplevel> point to depending on the C<$cxt>:
sub {
eval {
sub {
{
- unwind @things => $cxt;
+ unwind @things => $cxt; # or uplevel { ... } $cxt;
...
}
...
}->(); # $cxt = SCOPE(0 .. 1), or HERE, or UP, or SUB, or CALLER(0)
...
- }; # $cxt = SCOPE(2), or UP UP, or UP SUB, or EVAL, or CALLER(1)
+ }; # $cxt = SCOPE(2), or UP UP, or UP SUB, or EVAL, or CALLER(1) (*)
...
}->(); # $cxt = SCOPE(3), or SUB UP SUB, or SUB EVAL, or CALLER(2)
...
+ # (*) Note that uplevel() will croak if you pass that scope frame,
+ # because it cannot target eval scopes.
+
=head1 EXPORT
-The functions L</reap>, L</localize>, L</localize_elem>, L</localize_delete>, L</unwind> and L</want_at> are only exported on request, either individually or by the tags C<':funcs'> and C<':all'>.
+The functions L</reap>, L</localize>, L</localize_elem>, L</localize_delete>, L</unwind>, L</want_at> and L</uplevel> are only exported on request, either individually or by the tags C<':funcs'> and C<':all'>.
The constant L</SU_THREADSAFE> is also only exported on request, individually or by the tags C<':consts'> and C<':all'>.
=cut
-use base qw/Exporter/;
+use base qw<Exporter>;
our @EXPORT = ();
our %EXPORT_TAGS = (
- funcs => [ qw/reap localize localize_elem localize_delete unwind want_at/ ],
- words => [ qw/TOP HERE UP SUB EVAL SCOPE CALLER/ ],
- consts => [ qw/SU_THREADSAFE/ ],
+ funcs => [ qw<
+ reap
+ localize localize_elem localize_delete
+ unwind want_at
+ uplevel
+ > ],
+ words => [ qw<TOP HERE UP SUB EVAL SCOPE CALLER> ],
+ consts => [ qw<SU_THREADSAFE> ],
);
our @EXPORT_OK = map { @$_ } values %EXPORT_TAGS;
$EXPORT_TAGS{'all'} = [ @EXPORT_OK ];
=head1 SEE ALSO
+L<perlfunc/local>, L<perlsub/"Temporary Values via local()">.
+
L<Alias>, L<Hook::Scope>, L<Scope::Guard>, L<Guard>.
+L<Sub::Uplevel>.
+
L<Continuation::Escape> is a thin wrapper around L<Scope::Upper> that gives you a continuation passing style interface to L</unwind>.
It's easier to use, but it requires you to have control over the scope where you want to return.
+L<Scope::Escape>.
+
=head1 AUTHOR
Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
=head1 COPYRIGHT & LICENSE
-Copyright 2008,2009,2010 Vincent Pit, all rights reserved.
+Copyright 2008,2009,2010,2011 Vincent Pit, all rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.