The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

###################################################################################
#
#   DBIx::Recordset - Copyright (c) 1997-2000 Gerald Richter / ECOS
#
#   You may distribute under the terms of either the GNU General Public
#   License or the Artistic License, as specified in the Perl README file.
#
#   THIS IS BETA SOFTWARE!
#
#   THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
#   IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
#   WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
#   $Id: FileSeq.pm,v 1.4 2000/06/26 05:16:18 richter Exp $
#
###################################################################################


package DBIx::Recordset::FileSeq ;

use strict 'vars' ;

use Cwd ;


## ----------------------------------------------------------------------------
##
## new
##
## creates a new DBIx::Recordset::FileSeq object. 
##
## $dir          = Directory which holds the sequences
##

sub new

    {
    my ($class, $dummy, $dir, $min, $max) = @_ ;
    

    mkdir $dir, 0755 or die "Cannot create $dir ($!)" if (!-e $dir) ; 
    
    die "$dir is not a directory" if (!-d $dir) ;

    

    my $self = {
                '*Debug'      => $DBIx::Recordset::Debug,
                '*Dir'        => Cwd::abs_path ($dir),
                '*DefaultMin' => $min || 1,
                '*DefaultMax' => $max || '',
               } ;

    bless ($self, $class) ;

    $self -> ReadCounter ;

    return $self ;
    }


## ----------------------------------------------------------------------------
##
## ReadCounter
##
## read current counters form filesystem
##
##


sub ReadCounter

    {
    my $self = shift ;

    my %counter ;
    my %max ;

    opendir DH, $self -> {'*Dir'} or die "Cannot open directory $self->{'*Dir'} ($!)" ;
    
    while ($_ = readdir DH)
        {
        if (/seq\.(.*?)\.(\d*?)\.(\d+)$/) 
            {
            $counter{$1}=$3 ;
            $max{$1}=$2 ;
            }
        }
    
    $self -> {'*Counter'} = \%counter ;
    $self -> {'*Max'} = \%max ;
    }


## ----------------------------------------------------------------------------
##
## NextVal
##
## get next value from counter
##
## in   $name = counter name
##


sub NextVal 

    {
    my ($self, $name) = @_ ;

    my $dir = $self -> {'*Dir'} ;
    my $lastcnt ;

    local $^W = 0 ;
 
    while (1)
        {
        my $cnt = $self -> {'*Counter'}{$name} ;
        my $max = $self -> {'*Max'}{$name} ;

        if (!defined ($cnt))
            {
            $cnt = $self->{'*DefaultMin'} ;
            $max = $self->{'*DefaultMax'} ;
            open FH, ">$dir/seq.$name.$max.$cnt" or die "Cannot create seq.$name..1 ($!)" ;
            close FH ;
            }

        my $cnt1 = $cnt + 1 ;

        die "Max count reached for Sequence $name" if ($max ne '' && $cnt1 > $max) ;

        if (rename ("$dir/seq.$name.$max.$cnt", "$dir/seq.$name.$max.$cnt1"))
            {
            $self -> {'*Counter'}{$name} = $cnt1 ;
            return $cnt ;
            }

	my $lastcnt = $cnt ;
        $self -> ReadCounter ;
	die "Problems updating Sequence $name (File $dir/seq.$name.$max.$cnt)" if ($lastcnt == $self -> {'*Counter'}{$name} ) ;
        }
    }

1;

__END__


=pod

=head1 NAME

DBIx::Recordset::FileSeq - Sequence generator in Filesystem

=head1 SYNOPSIS

 use DBIx::Recordset::FileSeq ;

 $self = DBIx::Recordset::FileSeq (undef, '/tmp/seq', $min, $max) ;
 
 $val1 = $self -> NextVal ('foo') ;
 $val2 = $self -> NextVal ('foo') ;
 $val3 = $self -> NextVal ('bar') ;
 

=head1 DESCRIPTION


DBIx::Recordset::FileSeq generates unique numbers. State is kept in the
filesystem. With the new constructor you sepcify the directory
where the state is kept. (First parameter is a dummy values, that will
receive the database handle from DBIx::Recordset, but you don't need it
when you use it without DBIx::Recordset). Optionaly you can give a min and
a max values, which will be used for new sequences.

With B<NextVal> you can get the next value
for the sequence of the given name.

The state if kept by haveing a file with the name

seq.<seqencename>.<max>.<count>

Each time the sequnce value increments the file is renamed. If the <max>
if a numeric value the new value is checked against <max> and NextVal
dies if the sequnce value increment above max.

=head1 AUTHOR

G.Richter (richter@dev.ecos.de)

=head1 SEE ALSO

=item DBIx::Recordset


=cut