📄 file.pm
字号:
package Adaptor::File;
use strict;
use Carp;
use Storable (); # Don't want to import any names
#---- Global variables
use vars qw(%global_adaptor_info $debugging %g_attr_names);
$debugging = 0;
sub new {
@_ == 3 || croak 'Usage: File_Adaptor->new(file, config_file)';
my ($pkg,$file, $config_file) = @_;
my $rh_adaptor_info;
foreach $rh_adaptor_info (values %global_adaptor_info) {
if ($rh_adaptor_info->{'file'} eq $file) {
croak "File \'$file\' has already been opened\n";
}
}
_load_config_file($config_file);
my $all_instances = load_all($file);
$all_instances = {} unless defined($all_instances);
my $this = \$all_instances;
bless $this, $pkg;
$global_adaptor_info{$this} = {'file' => $file};
$this;
}
my %mapping_loaded = ();
sub _load_config_file {
my ($file) = @_;
return if (exists $mapping_loaded{$file});
$mapping_loaded{$file}++;
require $file; # for now.
}
sub delete {
(@_ == 2) || (@_ == 3) ||
croak "Error: adaptor->delete (obj), or \n" .
' adaptor->delete (class, id)';
my ($this, $id);
$this = shift;
if (@_ == 1) {
my $obj = $_[0];
($id) = $obj->get_attributes('_id');
} else {
$id = $_[1];
}
my $all_instances = $$this;
delete $all_instances->{$id};
}
sub store { # adaptor->store($obj)
(@_ == 2) || croak 'Usage adaptor->store ($obj_to_store)';
my ($this, $obj_to_store) = @_; # $this is 'all_instances'
my ($id) = $obj_to_store->get_attributes('_id');
my $all_instances = $$this;
if (!defined ($id )) {
$id = $this->_get_next_id();
$obj_to_store->set_attributes('_id'=> $id);
}
$all_instances->{$id} = $obj_to_store;
$id;
}
sub get_attrs_for_class {
my ($pkg) = @_;
}
sub flush { # adaptor->flush();
# Complements load_all()
my $this = $_[0];
my $all_instances = $$this;
return if (!exists $global_adaptor_info{$this});
my $file = $global_adaptor_info{$this}->{'file'};
return unless defined $file;
open (F, ">$file") || die "Error opening $file: $!\n";
my ($id, $obj);
while (($id, $obj) = each %$all_instances) {
my $class = ref($obj);
my @attrs = $obj->get_attributes(@{$g_attr_names{$class}});
Storable::store_fd([$class, $id, @attrs], \*F);
}
close(F);
}
sub load_all { # $all_instances = load_all($file);
# complement of flush ()
my $file = shift;
return () if (! -e $file);
open(F, $file) || croak "Unable to load $file: $!";
# Global information first
my ($class, $id, $obj, $rh_attr_names, @attrs, $all_instances);
eval {
while (1) {
($class, $id, @attrs) = @{Storable::retrieve_fd(\*F)};
$obj = $all_instances->{$id};
$obj = $class->new() unless defined($obj);
$rh_attr_names = $g_attr_names{$class};
$obj->set_attributes("_id" => $id,
map {$rh_attr_names->[$_] => $attrs[$_]}
(0 .. $#attrs));
$all_instances->{$id} = $obj;
}
};
$all_instances;
}
sub begin_transaction {
my ($this) = @_;
$this->flush();
}
sub commit_transaction {
my ($this) = @_;
$this->flush();
}
sub rollback_transaction {
my ($this) = @_;
my ($file) = $global_adaptor_info{$this}->{'file'};
my $all_instances = load_all($file);
${$this} = $all_instances;
}
sub retrieve {
my ($this, $class, $id) = @_;
$$this->{$id};
}
sub retrieve_all {
my ($this) = @_;
values %$$this;
}
sub retrieve_where {
my ($this, $class, $query) = @_;
my $all_instances = $$this;
# blank queries get everything
return $this->retrieve_all() if ($query !~ /\S/);
my ($boolean_expression, @attrs) = parse_query($query);
# @attrs contains the attr. names used in the query
# ("a", "b") translates to
# my ($a, $b) = $obj->get_attributes("a", "b");
my $fetch_stmt = "my (" . join(",",map{'$' . $_} @attrs) . ") = " .
"\$obj->get_attributes(qw(@attrs))";
my (@retval);
my $eval_str = qq{
my \$dummy_key; my \$obj;
while ((\$dummy_key, \$obj) = each \%\$all_instances) {
next unless ref(\$obj) eq "$class";
$fetch_stmt;
push (\@retval, \$obj) if ($boolean_expression);
}
};
($debugging) && (print STDERR "EVAL:\n\t$eval_str\n");
eval ($eval_str);
if ($@) {
print STDERR "Ill-formed query:\n\t$query\n";
($debugging) && (print STDERR $@);
}
@retval;
}
#-----------------------------------------------------------------
# Query evaluator
my %string_op = ( # Map from any operator to corresponding string op
'==' => 'eq',
'<' => 'lt',
'<=' => 'le',
'>' => 'gt',
'>=' => 'ge',
'!=' => 'ne',
);
my $ANY_OP = '<=|>=|<|>|!=|=='; # Any comparison operator
sub parse_query {
my ($query) = @_;
# A query like (name = 'santa') && (num_helpers < 5) gets translated to
# a boolean expression:
# ($obj->{name} eq 'santa') && ($obj->{num_helpers} < 5);
# Instead of parsing the expression, we just do a set of simple
# transformations to convert it to an eval'able Perl expression.
# These are the rules;
# 1. A query term is <variable> <op> <value> (e.g. name == 'santa')
# 2. If query is blank, it should evaluate to 1
# 3. <variable> mapped to '$obj->{<variable>}'
# 4. If <value> is a *quoted* string, then <op> gets mapped to the
# appropriate string op.
# Rule 2.
return 1 if ($query =~ /^\s*$/);
# First squirrel away all instances of escaped quotes. We'll
# restore them later. This way it doesn't get in the way when
# we are processing rule 4.
$query =~ s/\\[']/\200/g;
$query =~ s/\\["]/\201/g;
# Replace all '=' by '==' because it messes up the data inside the
# object !
# TBD: should not do this for '=' inside strings
$query =~ s/([^!><=])=/$1 == /g;
# Rule 3.
my %attrs;
$query =~
s/(\w+)\s*($ANY_OP)/$attrs{$1}++, "\$$1 $2"/eg;
# Rule 4.
# Replace any comparison operator followed by a quoted string
# with the appropriate string operator. A quoted string is
# a quote followed by sequence of non-quotes followed by a quote
$query =~ s{
($ANY_OP) (?# Any comparison operator)
\s* (?# followed by zero or more spaces,)
['"]([^'"]*)['"] (?# then by a quoted string )
}{
$string_op{$1} . ' \'' . $2 . '\''
}goxse; # global, compile-once, extended, treat as single line, eval
# Restore all escaped quote characters
$query =~ s/\200/\\'/g;
$query =~ s/\201/\\"/g;
($query, keys %attrs);
}
my $counter = 0;
# Calculate seconds since Jan 1, 1997 when counter was reset
my $counter_reset_time = time();
sub _get_next_id { # adaptor->_get_next_id()
if (++$counter > 99999) {
# Assuming you can't create 99999 Perl objects in one second
$counter_reset_time = time();
$counter = 0;
}
sprintf("%09d%05d", $counter_reset_time, ++$counter);
}
1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -