X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FSub%2FDefer.pm;h=236b4a97c0a2c0ae649d8cc2d0b7fdc5ed64e4af;hb=efdff87e4e45cee9e0b2bc5ac2d7659e8870c249;hp=11430fa56e3ac1d7e2a9a800946100c44758eaef;hpb=eae82931c70bed45915ee6785db2a11cb4b16f0e;p=gitmo%2FMoo.git diff --git a/lib/Sub/Defer.pm b/lib/Sub/Defer.pm index 11430fa..236b4a9 100644 --- a/lib/Sub/Defer.pm +++ b/lib/Sub/Defer.pm @@ -2,34 +2,112 @@ package Sub::Defer; use strictures 1; use base qw(Exporter); +use Moo::_Utils; -our @EXPORT = qw(defer undefer); +our $VERSION = '1.003000'; +$VERSION = eval $VERSION; -our %DEFERRED; +our @EXPORT = qw(defer_sub undefer_sub); -sub _getglob { no strict 'refs'; \*{$_[0]} } +our %DEFERRED; -sub undefer { +sub undefer_sub { my ($deferred) = @_; my ($target, $maker, $undeferred_ref) = @{ $DEFERRED{$deferred}||return $deferred }; + return ${$undeferred_ref} + if ${$undeferred_ref}; ${$undeferred_ref} = my $made = $maker->(); - { no warnings 'redefine'; *{_getglob($target)} = $made } + + # make sure the method slot has not changed since deferral time + if (defined($target) && $deferred eq *{_getglob($target)}{CODE}||'') { + no warnings 'redefine'; + + # I believe $maker already evals with the right package/name, so that + # _install_coderef calls are not necessary --ribasushi + *{_getglob($target)} = $made; + } + $DEFERRED{$made} = $DEFERRED{$deferred}; + return $made; } -sub defer { +sub defer_info { + my ($deferred) = @_; + $DEFERRED{$deferred||''}; +} + +sub defer_sub { my ($target, $maker) = @_; my $undeferred; - my $deferred_string; - my $deferred = bless(sub { - goto &{$undeferred ||= undefer($deferred_string)}; - }, 'Sub::Defer::Deferred'); - $deferred_string = "$deferred"; - $DEFERRED{$deferred} = [ $target, $maker, \$undeferred ]; - *{_getglob $target} = $deferred; + my $deferred; + $deferred = sub { + $undeferred ||= undefer_sub($deferred); + goto &$undeferred; + }; + $DEFERRED{$deferred} = [ $target, $maker, \$undeferred, $deferred ]; + _install_coderef($target => $deferred) if defined $target; return $deferred; } +sub CLONE { + %DEFERRED = map { $_->[3] => $_ } values %DEFERRED; +} + 1; + +=head1 NAME + +Sub::Defer - defer generation of subroutines until they are first called + +=head1 SYNOPSIS + + use Sub::Defer; + + my $deferred = defer_sub 'Logger::time_since_first_log' => sub { + my $t = time; + sub { time - $t }; + }; + + Logger->time_since_first_log; # returns 0 and replaces itself + Logger->time_since_first_log; # returns time - $t + +=head1 DESCRIPTION + +These subroutines provide the user with a convenient way to defer creation of +subroutines and methods until they are first called. + +=head1 SUBROUTINES + +=head2 defer_sub + + my $coderef = defer_sub $name => sub { ... }; + +This subroutine returns a coderef that encapsulates the provided sub - when +it is first called, the provided sub is called and is -itself- expected to +return a subroutine which will be goto'ed to on subsequent calls. + +If a name is provided, this also installs the sub as that name - and when +the subroutine is undeferred will re-install the final version for speed. + +=head2 undefer_sub + + my $coderef = undefer_sub \&Foo::name; + +If the passed coderef has been L this will "undefer" it. +If the passed coderef has not been deferred, this will just return it. + +If this is confusing, take a look at the example in the L. + +=head1 SUPPORT + +See L for support and contact information. + +=head1 AUTHORS + +See L for authors. + +=head1 COPYRIGHT AND LICENSE + +See L for the copyright and license.