- 追加された行はこの色です。
- 削除された行はこの色です。
RIGHT:&tag(クラス,設計,フレームワーク,CGI);
RIGHT:&tag(クラス,設計,フレームワーク,CGI,更新中);
*目次 [#zf7e2b15]
#contents
----
#lsx(new=true);
*関連 [#se5e42f2]
#lsx(tag=FlyweightFactory,new=true,except=^FlyweightFactory(/.*)?$)
検索:FlyweightFactory
*FlyweightFactory周辺のタグ [#tc936eee]
#tag(0,FlyweightFactory)
*FlyweightFactoryとは [#g800cfe7]
RIGHT:[[:t/FlyweightFactory]]
[[CGI/FlyweightFactory]]
シングルトン。
ファイルを扱うクラス。DBMSの役目を持つ。
オブジェクトの永続化(ファイル化)と復帰を行うクラス。
**参考 [#y989716e]
#lsx(prefix=フレームワーク,new=true)
**インデックス [#we60b271]
***インデックス更新時 [#n3c3c3ad]
インデックス更新時、オブジェクトから属性値を得て、すべてのインデックスを更新。
キー(属性値)→{オブジェクトID}
オブジェクトIDの1つを削除したり、追加したり、
インデックスの更新=各インデックスから削除・追加すること
***インデックス [#ofb342e8]
インデックスに含めるもの、インデックスのキーにするものはインデックス作成側が決める。固定。
キー毎に複数作成。
オブジェクトID→永続化されたオブジェクト名(ファイル名)も含む。
インデックス更新時はインデックス自身の更新日時も更新。
キーは
-オブジェクトID
-永続化時のファイル名(ほとんどの場合、オブジェクトIDと同じ)
-クラス名
-ページ名(Unicode)
-他
***インデックス作成に必要なものは [#lb38e030]
-オブジェクト1つ以上
-インデックスに含める情報は何か?
これは固定。インデックス側が決める。
-オブジェクトID→ファイル名
オブジェクトIDをファイル名に変換するルールはあるが、ルールだけではオブジェクトID→ファイル名はできないので、データとしても残す。
(ファイル名が重複して末尾に連番を付けたときなどに必要)
***インデックスは1つのキーで複数のオブジェクトが得られるからこそ効率がいい [#cf5abaf5]
***インデックスの更新日時 [#l9d65773]
オブジェクトID→更新日時(1対1)
ファイル名→ファイルの更新日時(ファイル拡張子.idxmtime)でいい。
比較相手もファイルの更新日時なので。
**DB [#t19652ce]
Data::Dumperで。
----
***データベースの使われ方 [#sfbac316]
-クラス名 → オブジェクトのリスト または 空リスト (下位クラスのオブジェクト含む)
-(キー名(属性名),値)の集合 → オブジェクトのリスト または 空リスト (キー名には「オブジェクトID」を含む)
-オブジェクトのリスト → 戻り値なし(インデックス更新)
-全オブジェクトのインデックス更新(未更新分のみ)
-全オブジェクトのインデックス更新(本当にすべてのオブジェクト分)
**プロトタイプ [#e92021e3]
package FlyweightFactory;
use strict;
use warnings;
use POSIX;
use AnyDBM_File;
use Tie::DB_Lock;
use Storable qw/lock_nstore lock_Retrieve/;
use Storable qw/lock_nstore lock_retrieve/;
use Safe;
use Toolkit;
# my %Index;
# $Index{classname} = {};
# $Index{mtime} = {};
# $Index{file} = {};
# $Index{class} = {};
# $Index{id} = {};
# BEGIN {
# @AnyDBM_File::ISA = qw/DB_File GDBM_File NDBM_File/;
# }
# use AnyDBM_File;
my $safe = new Safe;
# because of opcodes used in "use strict":
$safe->permit(qw(:default require));
local $Storable::Deparse = 1;
local $Storable::Eval = sub { $safe->reval($_[0]) };
my $instance = undef;
sub _New
{
my $class = shift;
my $self = {};
bless $self, $class;
}
sub Singleton
{
my $class = shift;
if (not defined $instance){
$instance = _New();
$instance = $class->_New();
}
$instance;
}
# クラス名 → オブジェクトのリスト または 空リスト (下位クラスのオブジェクト含む)
# FetchObjects(ClassName) -> @Obj
sub FetchObjects_ByClassName
{
my $self = shift;
my($class) = @_;
tie %Index, 'Tie::DB_Lock', 'index.db', 'r' or die $!;
my $index = $self->Retrieve_Index('index.db');
my @ret = ();
foreach my $obj_id (@{$Index{classname}->{$class}}){
foreach my $obj_id (@{$index->{class}->{$class}}){
push @ret, $self->Retrieve($obj_id);
}
untie %Index;
@ret;
}
# (キー名(属性名),値)の集合 → オブジェクトのリスト または 空リスト (キー名には「オブジェクトID」を含む)
# FindObjects({key, value}) -> @Obj
sub FindObjects
{
my $self = shift;
my($keyvalue) = @_;
tie %Index, 'Tie::DB_Lock', 'index.db', 'r' or die $!;
my $index = $self->Retrieve_Index('index.db');
my @ret;
my($key, $value);
while (($key, $value) = each %{$keyvalue}){
my @ret_part = ();
foreach my $obj_id (@{$Index{$key}->{$value}}){
foreach my $obj_id (@{$index->{$key}->{$value}}){
my $obj = $self->Retrieve($obj_id);
if ($obj){
push @ret_part, $self->Retrieve($obj_id);
}
}
@ret = intersection(\@ret, \@ret_part);
}
untie %Index;
@ret;
}
# FetchObject('key=value,key=value') -> @Obj
sub FetchObject
{
my $self = shift;
my($keyvalue) = @_;
tie %Index, 'Tie::DB_Lock', 'index.db', 'r' or die $!;
my $index = $self->Retrieve_Index('index.db');
my @ret;
my @keyvalue_list = split /, */, $keyvalue;
my($key, $value);
while (($key, $value) = split /=/, shift(@keyvalue_list)){
### assert:$key and $value
my @ret_part = ();
foreach my $obj_id (@{$Index{$key}->{$value}}){
foreach my $obj_id (@{$index->{$key}->{$value}}){
my $obj = $self->Retrieve($obj_id);
if ($obj){
push @ret_part, $self->Retrieve($obj_id);
}
}
@ret = intersection(\@ret, \@ret_part);
}
untie %Index;
@ret;
}
sub InitializeIndex
{
my $self = shift;
my $index = {
mtime => {},
file => {},
class => {},
id => {},
};
$self->Store_Index($index, 'index.db');
}
# オブジェクトのリスト → 戻り値なし(インデックス更新)
# UpdateIndexes(@Obj)
sub UpdateIndexes
{
my $self = shift;
my(@object) = @_;
tie %Index, 'Tie::DB_Lock', 'index.db', 'rw' or die $!;
my $index = $self->Retrieve_Index('index.db');
foreach my $obj (@object){
### assert:exists $obj->{mtime}
### assert:exists $obj->{id}
if ($obj->{mtime} > $Index{mtime}->{$obj->{id}}){
if (exists $index->{mtime}->{$obj->{id}} and $obj->{mtime} > $index->{mtime}->{$obj->{id}}){
$self->UpdateIndexesForce($obj);
}
}
untie %Index;
undef;
}
# UpdateIndexesForce(@Obj)
sub UpdateIndexesForce
{
my $self = shift;
my(@object) = @_;
tie %Index, 'Tie::DB_Lock', 'index.db', 'rw' or die $!;
my $index = $self->Retrieve_Index('index.db');
foreach my $obj (@object){
### assert:exists $obj->{file}
### assert:exists $obj->{id}
### assert:exists $obj->{mtime}
### assert:exists $obj->{class}
{
my $idx = $Index{file};
my $key = $obj->{file};
my @id = grep {$_ ne $obj->{id}} split(/, */, $idx->{$key});
unshift @id, $obj->{id};
$idx->{$key} = join(',', $obj->{id});
my $idx = $index->{file};
my $key = $obj->{id};
my @id = exists $idx->{$key} ? grep {$_ ne $obj->{id}} split(/, */, $idx->{$key}) : ();
unshift @id, $obj->{file};
$idx->{$key} = join(',', @id);
}
{
my $idx = $Index{class};
my $key = $obj->{class};
my @id = grep {$_ ne $obj->{id}} split(/, */, $idx->{$key});
unshift @id, $obj->{id};
$idx->{$key} = join(',', $obj->{id});
my $idx = $index->{class};
my $key = $obj->{id};
my @id = exists $idx->{$key} ? grep {$_ ne $obj->{id}} split(/, */, $idx->{$key}) : ();
unshift @id, $obj->{class};
$idx->{$key} = join(',', @id);
}
}
$self->Store_Index($index, 'index.db');
untie %Index;
undef;
}
# 全オブジェクトのインデックス更新(未更新分のみ)
# UpdateAllIndex()
sub UpdateAllIndex
{
my $self = shift;
{
#全てのオブジェクトを得る
tie %Index, 'Tie::DB_Lock', 'index.db', 'rw' or die $!;
my $index = $self->Retrieve_Index('index.db');
my($obj_id);
while ((undef, $obj_id) = each %{$Index{id}}){
while ((undef, $obj_id) = each %{$index->{id}}){
my $obj = $self->Retrieve($obj_id);
#更新日時がインデックスのそれよりも新しいオブジェクトについてインデックス作成
if ($obj->{mtime} > $Index{mtime}->{$obj->{id}}){
if (exists $index->{mtime}->{$obj->{id}} and $obj->{mtime} > $index->{mtime}->{$obj->{id}}){
$self->UpdateIndexesForce($obj);
}
}
untie %Index;
}
undef;
}
# 全オブジェクトのインデックス更新(本当にすべてのオブジェクト分)
# UpdateAllIndexForce()
sub UpdateAllIndexForce
{
my $self = shift;
{
#全てのオブジェクトを得る
tie %Index, 'Tie::DB_Lock', 'index.db', 'rw' or die $!;
my $index = $self->Retrieve_Index('index.db');
my($obj_id);
while ((undef, $obj_id) = each %{$Index{id}}){
while ((undef, $obj_id) = each %{$index->{id}}){
#それぞれのインデックス作成
my $obj = $self->Retrieve($obj_id);
$self->UpdateIndexesForce($obj);
}
untie %Index;
}
undef;
}
sub Store
{
my $self = shift;
my($obj, $filename) = @_;
my($obj) = @_;
### assert:defined $obj
### assert:$filename
### assert:exists $obj->{id} && defined $obj->{id}
# lock_nstore $obj, $filename;
# lock_nstore $obj, $obj->{file};
{
use Data::Dumper qw/Dumper/;
$Data::Dumper::Indent = 3;
$Data::Dumper::Terse = 1;
open my $fh, '>', $filename or die $!;
if (not defined $obj->{file}){
$obj->{file} = $self->_Id2NewFilename($obj->{id});
}
open my $fh, '>', $obj->{file} or die $!;
flock $fh, LOCK_EX;
print $fh Dumper $obj;
close $fh;
}
$self->UpdateIndexes($obj);
$self->UpdateIndexesForce($obj);
undef;
}
sub Retrieve
{
my $self = shift;
my($id) = @_;
my($filename) = @_;
### assert:$filename
my $obj;
# lock_retrieve $filename;
{
open my $fh, '<', _Id2Filename($id) or die $!;
open my $fh, '<', $filename or die $!;
flock $fh, LOCK_SH;
my $file = join '', <$fh>;
my $fc = join '', <$fh>;
close $fh;
$obj = $safe->reval($file);
$obj = $safe->reval($fc);
}
$obj;
}
sub _Id2Filename
{
sub _Id2NewFilename
{
my $self = shift;
my($id) = @_;
my $filename = $id;
#TODO:ファイルが既存だったときの新名
$filename;
}
}
sub Store_Index
{
my $self = shift;
my($obj, $filename) = @_;
### assert:defined $obj
### assert:defined $filename
# lock_nstore $obj, $filename;
{
use Data::Dumper qw/Dumper/;
$Data::Dumper::Indent = 3;
$Data::Dumper::Terse = 1;
open my $fh, '>', $filename or die $!;
flock $fh, LOCK_EX;
print $fh Dumper $obj;
close $fh;
}
undef;
}
sub Retrieve_Index
{
my $self = shift;
my($filename) = @_;
### assert:$filename
my $obj;
# lock_retrieve $filename;
{
open my $fh, '<', $filename or die $!;
flock $fh, LOCK_SH;
my $fc = join '', <$fh>;
close $fh;
$obj = $safe->reval($fc);
}
$obj;
}
1;