File Coverage

blib/lib/CHI/Memoize.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package CHI::Memoize;
2             $CHI::Memoize::VERSION = '0.07';
3 1     1   12 use Carp;
  1         1  
  1         71  
4 1     1   1193 use CHI;
  0            
  0            
5             use CHI::Memoize::Info;
6             use CHI::Driver;
7             use Hash::MoreUtils qw(slice_grep);
8             use strict;
9             use warnings;
10             use base qw(Exporter);
11              
12             my $no_memoize = {};
13             sub NO_MEMOIZE { $no_memoize }
14              
15             our @EXPORT = qw(memoize);
16             our @EXPORT_OK = qw(memoize memoized unmemoize NO_MEMOIZE);
17             our %EXPORT_TAGS = ( all => \@EXPORT_OK );
18              
19             my %memoized;
20             my @get_set_options = qw( busy_lock expire_if expires_at expires_in expires_variance );
21             my %is_get_set_option = map { ( $_, 1 ) } @get_set_options;
22              
23             sub memoize {
24             my ( $func, %options ) = @_;
25              
26             my ( $func_name, $func_ref, $func_id ) = _parse_func_arg( $func, scalar(caller) );
27             croak "'$func_id' is already memoized" if exists( $memoized{$func_id} );
28              
29             my $passed_key = delete( $options{key} );
30             my $cache = delete( $options{cache} );
31             my %compute_options = slice_grep { $is_get_set_option{$_} } \%options;
32             my $prefix = "memoize::$func_id";
33              
34             if ( !$cache ) {
35             my %cache_options = slice_grep { !$is_get_set_option{$_} } \%options;
36             $cache_options{namespace} ||= $prefix;
37             if ( !$cache_options{driver} && !$cache_options{driver_class} ) {
38             $cache_options{driver} = "Memory";
39             }
40             if ( $cache_options{driver} eq 'Memory' || $cache_options{driver} eq 'RawMemory' ) {
41             $cache_options{global} = 1;
42             }
43             $cache = CHI->new(%cache_options);
44             }
45              
46             my $wrapper = sub {
47             my $wantarray = wantarray ? 'L' : 'S';
48             my @key_parts =
49             defined($passed_key)
50             ? ( ( ref($passed_key) eq 'CODE' ) ? $passed_key->(@_) : ($passed_key) )
51             : @_;
52             if ( @key_parts == 1 && ( $key_parts[0] || 0 ) eq NO_MEMOIZE ) {
53             return $func_ref->(@_);
54             }
55             else {
56             my $key = [ $prefix, $wantarray, @key_parts ];
57             my $args = \@_;
58             return $cache->compute( $key, {%compute_options}, sub { $func_ref->(@$args) } );
59             }
60             };
61             $memoized{$func_id} = CHI::Memoize::Info->new(
62             orig => $func_ref,
63             wrapper => $wrapper,
64             cache => $cache,
65             key_prefix => $prefix
66             );
67              
68             no strict 'refs';
69             no warnings 'redefine';
70             *{$func_name} = $wrapper if $func_name;
71              
72             return $wrapper;
73             }
74              
75             sub memoized {
76             my ( $func_name, $func_ref, $func_id ) = _parse_func_arg( $_[0], scalar(caller) );
77             return $memoized{$func_id};
78             }
79              
80             sub unmemoize {
81             my ( $func_name, $func_ref, $func_id ) = _parse_func_arg( $_[0], scalar(caller) );
82             my $info = $memoized{$func_id} or die "$func_id is not memoized";
83              
84             eval { $info->cache->clear() };
85             no strict 'refs';
86             no warnings 'redefine';
87             *{$func_name} = $info->orig if $func_name;
88             delete( $memoized{$func_id} );
89             return $info->orig;
90             }
91              
92             sub _parse_func_arg {
93             my ( $func, $caller ) = @_;
94             my ( $func_name, $func_ref, $func_id );
95             if ( ref($func) eq 'CODE' ) {
96             $func_ref = $func;
97             $func_id = "$func_ref";
98             }
99             else {
100             $func_name = $func;
101             $func_name = join( "::", $caller, $func_name ) if $func_name !~ /::/;
102             $func_id = $func_name;
103             no strict 'refs';
104             $func_ref = \&$func_name;
105             die "no such function '$func_name'" if ref($func_ref) ne 'CODE';
106             }
107             return ( $func_name, $func_ref, $func_id );
108             }
109              
110             1;
111              
112             __END__