| Cache-Historical documentation | Contained in the Cache-Historical distribution. |
Cache::Historical - Cache historical values
use Cache::Historical;
my $cache = Cache::Historical->new();
# Set a key's value on a specific date
$cache->set( $dt, $key, $value );
# Get a key's value on a specific date
my $value = $cache->get( $dt, $key );
# Same as 'get', but if we don't have a value at $dt, but we
# do have values for dates < $dt, return the previous
# historic value.
$cache->get_interpolated( $dt, $key );
Cache::Historical caches historical values by key and date. If you have something like historical stock quotes, for example
2008-01-02 msft 35.22
2008-01-03 msft 35.37
2008-01-04 msft 34.38
2008-01-07 msft 34.61
then you can store them in Cache::Historical like
my $cache = Cache::Historical->new();
my $fmt = DateTime::Format::Strptime->new(
pattern => "%Y-%m-%d");
$cache->set( $fmt->parse_datetime("2008-01-02"), "msft", 35.22 );
$cache->set( $fmt->parse_datetime("2008-01-03"), "msft", 35.37 );
$cache->set( $fmt->parse_datetime("2008-01-04"), "msft", 34.38 );
$cache->set( $fmt->parse_datetime("2008-01-07"), "msft", 34.61 );
and retrieve them later by date:
my $dt = $fmt->parse_datetime("2008-01-03");
# Returns 35.37
my $value = $cache->get( $dt, "msft" );
Even if there's no value available for a given date, but there are historical
values that predate the requested date, get_interpolated() will return
the next best historical value:
my $dt = $fmt->parse_datetime("2008-01-06");
# Returns undef, no value available for 2008-01-06
my $value = $cache->get( $dt, "msft" );
# Returns 34.48, the value for 2008-01-04, instead.
$value = $cache->get_interpolated( $dt, "msft" );
Creates the object. Takes the SQLite file to put the date into as an additional parameter:
my $cache = Cache::Historical->new(
sqlite_file => "/tmp/mydata.dat",
);
The SQLite file defaults to
$HOME/.cache-historical/cache-historical.dat
so if you have multiple caches, you need to use different SQLite files.
# List the time range for which we have values for $key
my($from, $to) = $cache->time_range( $key );
# List all keys
my @keys = $cache->keys();
# List all the values we have for $key, sorted by date
# ([$dt, $value], [$dt, $value], ...)
my @results = $cache->values( $key );
# Remove all values for a specific key
$cache->clear( $key );
# Clear the entire cache
$cache->clear();
# Return a DateTime object of the last update of a given key
my $when = $cache->last_update( $key );
# Return a DateTime::Duration object since the time of the last
# update of a given key.
my $since = $cache->since_last_update( $key );
Copyright 2007-2011 by Mike Schilli, all rights reserved. This program is free software, you can redistribute it and/or modify it under the same terms as Perl itself.
2007, Mike Schilli <cpan@perlmeister.com>
| Cache-Historical documentation | Contained in the Cache-Historical distribution. |
########################################### package Cache::Historical; ########################################### use strict; use warnings; use Rose::DB::Object::Loader; use File::Basename; use File::Path; use Log::Log4perl qw(:easy); use DBI; use DateTime::Format::Strptime; our $VERSION = "0.05"; ########################################### sub new { ########################################### my($class, %options) = @_; my($home) = glob "~"; my $default_cache_dir = "$home/.cache-historical"; my $self = { sqlite_file => "$default_cache_dir/cache-historical.dat", %options, }; my $cache_dir = dirname( $self->{sqlite_file} ); if(! -d $cache_dir ) { mkpath [ $cache_dir ] or die "Cannot mktree $cache_dir ($!)"; } bless $self, $class; $self->{dsn} = "dbi:SQLite:dbname=$self->{sqlite_file}"; if(! -f $self->{sqlite_file}) { $self->db_init(); } my $loader = Rose::DB::Object::Loader->new( db_dsn => $self->{dsn}, db_options => { AutoCommit => 1, RaiseError => 1 }, class_prefix => 'Cache::Historical', with_managers => 1, ); $loader->make_classes(); $self->{loader} = $loader; return $self; } ########################################### sub make_modules { ########################################### my($self, @options) = @_; DEBUG "Making modules in @options"; $self->{loader}->make_modules( @options ); } ########################################### sub dbh { ########################################### my($self) = @_; if(! $self->{dbh} ) { $self->{dbh} = DBI->connect($self->{dsn}, "", ""); } return $self->{dbh}; } ########################################### sub db_init { ########################################### my($self) = @_; my $dbh = $self->dbh(); DEBUG "Creating new SQLite db $self->{sqlite_file}"; $dbh->do(<<'EOT'); CREATE TABLE vals ( id INTEGER PRIMARY KEY, date DATETIME, upd_time DATETIME, key TEXT, value TEXT, UNIQUE(date, key) ); EOT $dbh->do(<<'EOT'); CREATE INDEX vals_date_idx ON vals(date); EOT $dbh->do(<<'EOT'); CREATE INDEX vals_key_idx ON vals(key); EOT return 1; } ########################################### sub set { ########################################### my($self, $dt, $key, $value) = @_; DEBUG "Setting $dt $key => $value"; my $r = Cache::Historical::Val->new(); $r->key( $key ); $r->date( $dt ); $r->upd_time( DateTime->now() ); $r->load( speculative => 1 ); $r->value( $value ); $r->save(); } ########################################### sub get { ########################################### my($self, $dt, $key, $interpolate) = @_; my @date_query = (date => $dt); @date_query = (date => {le => $dt}) if $interpolate; my $values = Cache::Historical::Val::Manager->get_vals( query => [ @date_query, key => $key, ], sort_by => "date DESC", limit => 1, ); if(@$values) { my $value = $values->[0]->value(); DEBUG "Getting $dt $key => $value"; return $value; } return undef; } ########################################### sub keys { ########################################### my($self) = @_; my @keys; my $keys = Cache::Historical::Val::Manager->get_vals( distinct => 1, select => [ 'key' ], ); for(@$keys) { push @keys, $_->key(); } return @keys; } ########################################### sub values { ########################################### my($self, $key) = @_; my @values = (); my @key = (); @key = (key => $key) if defined $key; my $values = Cache::Historical::Val::Manager->get_vals( query => [ @key ], sort_by => ['date'], ); for(@$values) { push @values, [$_->date(), $_->value()]; } return @values; } ########################################### sub last_update { ########################################### my($self, $key) = @_; my @key = (); @key = (key => $key) if defined $key; my $values = Cache::Historical::Val::Manager->get_vals( query => [ @key ], sort_by => ['upd_time DESC'], limit => 1, ); if(@$values) { my $date = $values->[0]->upd_time(); return $date; } return undef; } ########################################### sub since_last_update { ########################################### my($self, $key) = @_; my $date = $self->last_update($key); if(defined $date) { return DateTime->now() - $date; } return undef; } ########################################### sub get_interpolated { ########################################### my($self, $dtp, $key) = @_; return $self->get($dtp, $key, 1); } my $date_fmt = DateTime::Format::Strptime->new( pattern => "%Y-%m-%d %H:%M:%S"); ########################################### sub time_range { ########################################### my($self, $key) = @_; my $dbh = $self->dbh(); my($from, $to) = $dbh->selectrow_array( "SELECT MIN(date), MAX(date) FROM vals WHERE key = " . $dbh->quote( $key )); $from = $date_fmt->parse_datetime( $from ); $to = $date_fmt->parse_datetime( $to ); return($from, $to); } ########################################### sub clear { ########################################### my($self, $key) = @_; my @params = (all => 1); if(defined $key) { @params = ("where" => [ key => $key ]); } my $values = Cache::Historical::Val::Manager->delete_vals( @params ); } 1; __END__