Acme-Lexical-Thief
view release on metacpan or search on metacpan
lib/Acme/Lexical/Thief.pm view on Meta::CPAN
BEGIN {
$Acme::Lexical::Thief::AUTHORITY = 'cpan:TOBYINK';
$Acme::Lexical::Thief::VERSION = '0.002';
}
use Carp;
use Keyword::Simple ();
use PadWalker ();
use Text::Balanced ();
my $KEYWORD = 'steal';
my $CLASS = __PACKAGE__;
sub import
{
Keyword::Simple::define $KEYWORD, sub
{
my $ref = shift;
$$ref =~ s/^\s+//;
my $depth = 0;
if ($$ref =~ /^((?: 0x[0-9A-F]+ | 0b[0-1]+ | 0[0-7]* | [1-9][0-9]* )\s*)/ixs)
{
$depth = eval $1;
substr($$ref, 0, length $1) = '';
}
my $extracted;
if ($$ref =~ /^\(/)
{
$extracted = Text::Balanced::extract_bracketed($$ref)
or croak "usage: $KEYWORD (VARIABLES);";
$extracted =~ s/(^\(|\)$)//gs;
}
else
{
($extracted, $$ref) = ($$ref =~ /^([^;]+)(;.*)$/s)
or croak "usage: $KEYWORD VARIABLES;";
}
(my $globs = $extracted) =~ s/[\$\%\@]/*/gs;
$$ref = "our($extracted); local($globs) = $CLASS\::_callback(q($extracted), $depth);$$ref";
}
}
sub unimport
{
Keyword::Simple::undefine $KEYWORD;
}
sub _callback
{
my $vars = shift;
my $depth = shift // 0;
$vars =~ s/(^\s*|\s*$)//g;
my @vars = split /\s*,\s*/, $vars;
my $MY = PadWalker::peek_my($depth + 2);
my $OUR = PadWalker::peek_our($depth + 2);
return map {
exists $MY->{$_} ? $MY->{$_} :
exists $OUR->{$_} ? $OUR->{$_} :
croak "$KEYWORD($_) failed; caller has no $_ defined";
} @vars;
}
1;
__END__
=head1 NAME
Acme::Lexical::Thief - steal lexical variables from your caller
=head1 SYNOPSIS
use 5.012;
use strict;
use warnings;
use Acme::Lexical::Thief;
sub greet {
my $name = shift;
greet_verbally();
}
sub greet_verbally {
steal $name; # caller variable
say "Hello $name";
}
=head1 DESCRIPTION
This package allows you access to your caller's lexical variables, without
them knowing! Full read/write access. This is generally a pretty bad idea,
hence the Acme namespace.
You can steal scalars, arrays and hashes:
steal $car, @treasures, %stash;
Parentheses can surround the list of variables to steal:
steal ($car, @treasures, %stash);
Generally everything should "just work" as you expect it to. Except when it
does not.
Technically speaking, your stolen C<< $car >> is a package-scoped (C<our>)
variable which is lexically aliased (C<< local *car >>) to the caller's
variable of the same name. Because C<steal> is parsed at compile-time,
you don't need to (and indeed should not!) pre-declare your stolen
variables.
sub greet_verbally {
my $name; # don't do this!
steal $name;
say "Hello $name";
}
( run in 1.530 second using v1.01-cache-2.11-cpan-13bb782fe5a )