package Class::MakeMethods::Utility::DiskCache; $VERSION = 1.008; @EXPORT_OK = qw( disk_cache ); sub import { require Exporter and goto &Exporter::import } # lazy Exporter use strict; use Carp; use File::Spec; use File::Path; ######################################################################## use vars qw( $DiskCacheDir ); my $IndexFile = "methods.ix"; # file also serves as timestamp my $FileEnding = ".mm"; sub import { my $package = shift; if ( scalar @_ ) { $DiskCacheDir = shift; } } ######################################################################## my %HaveCheckedFreshness; # $result = disk_cache( $package, $file, $sub, @args ); sub disk_cache { my ( $full_funct, $args_string, $function, @args ) = @_; unless ( $DiskCacheDir and -e $DiskCacheDir ) { return &$function( @args ); } my ($package, $func_name) = ( $full_funct =~ /^(.+)::(\w+)$/ ); my $pack_dir = File::Spec->catdir( $DiskCacheDir, split /::/, $package ); if ( ! -e $pack_dir and -w $DiskCacheDir ) { mkpath($pack_dir, 0, 07777); } unless ( defined $HaveCheckedFreshness{$package} ) { my $idx = File::Spec->catfile( $pack_dir, $IndexFile ); my $signature = dependency_signature($package); if ( -e $idx and read_file( $idx ) eq $signature ) { $HaveCheckedFreshness{$package} = 1; } else { if ( ! -w $pack_dir ) { # The index is out of date, but not writable -- abandon it $HaveCheckedFreshness{ $package } = 0; } else { rmtree($pack_dir, 0, 1); mkpath($pack_dir, 0, 07777); write_file( $idx, $signature ); $HaveCheckedFreshness{$package} = 1; } } } unless ( $HaveCheckedFreshness{$package} ) { return &$function( @args ); } my $func_dir = File::Spec->catdir( $pack_dir, $func_name ); if ( ! -e $func_dir and -w $pack_dir ) { mkpath($func_dir, 0, 07777); } my $file = File::Spec->catfile( $func_dir, $args_string . $FileEnding ); if ( -e $file ) { return read_file( $file ); } my $value = ( &$function( @args ) ); if ( -e $func_dir and -w $func_dir ) { write_file( $file, $value ); } else { warn "Can't cache: $file\n"; } return $value; } ######################################################################## sub dependency_signature { my @sources = shift; my @results; no strict 'refs'; while ( my $class = shift @sources ) { push @sources, @{"$class\::ISA"}; push @results, $class unless ( grep { $_ eq $class } @results ); } foreach ( @results ) { s!::!/!g; $_ .= '.pm'; } return join "\n", map { $_ . ' '. (stat($::INC{ $_ }))[9] } @results; } ######################################################################## sub read_file { my $file = shift; # warn "Reading file: $file\n"; local *FILE; open FILE, "$file" or die "Can't open $file: $!"; local $/ = undef; return ; } sub write_file { my $file = shift; # warn "Writing file: $file \n"; local *FILE; open FILE, ">$file" or die "Can't write to $file: $!"; print FILE shift(); } sub read_dir { my $dir = shift; local *DIR; opendir(DIR, $dir); readdir(DIR); } ######################################################################## 1; __END__ =head1 NAME Class::MakeMethods::Utility::DiskCache - Optional Template feature =head1 SYNOPSIS use Class::MakeMethods::Utility::DiskCache qw( /my/code/dir ); =head1 DESCRIPTION To enable disk caching of Class::MakeMethods::Template generated code, create an empty directory and pass it to the DiskCache package: use Class::MakeMethods::Utility::DiskCache qw( /my/code/dir ); This has a mixed effect on performance, but has the notable advantage of letting you view the subroutines that are being generated by your templates. =head1 SEE ALSO See L for more information. =cut