File Coverage

blib/lib/Tie/File/FixedRecLen/Store.pm
Criterion Covered Total %
statement 71 72 98.6
branch 14 22 63.6
condition 7 10 70.0
subroutine 12 13 92.3
pod n/a
total 104 117 88.8


line stmt bran cond sub pod time code
1             package Tie::File::FixedRecLen::Store;
2             {
3             $Tie::File::FixedRecLen::Store::VERSION = '2.112531';
4             }
5              
6 1     1   25952 use strict;
  1         2  
  1         75  
7 1     1   5 use warnings FATAL => 'all';
  1         2  
  1         35  
8              
9 1     1   22 use 5.004;
  1         3  
  1         43  
10 1     1   4 use Carp;
  1         2  
  1         122  
11 1     1   689 use Symbol;
  1         834  
  1         84  
12 1     1   7 use Fcntl qw(:flock);
  1         1  
  1         895  
13              
14             # ===========================================================================
15              
16             sub TIEARRAY {
17 3     3   1406 my $class = shift;
18 3         5 my $file = shift;
19 3         8 my %args = @_;
20              
21 3 100 100     34 if (! defined $file or ! defined $args{record_length}
      66        
22             or $args{record_length} =~ m/\D/) {
23 2         425 croak "usage: tie \@ARRAY, '" . __PACKAGE__
24             . "', record_length => \$reclen";
25             }
26              
27 1   50     9 my $pad_char = $args{pad_char} || ' ';
28 1   50     7 my $recsep = $args{recsep} || "\n";
29 1         3 my $reclen = $args{record_length};
30 1         3 my $elemlen = ($reclen + length $recsep);
31              
32             # open file for appending
33 1 50       239 open (my $fh, '>>', $file)
34             or croak "can't open filename '$file': $!\n";
35 1 50       13 flock ($fh, LOCK_EX)
36             or croak "can't lock file '$file': $!\n";
37              
38             # re-seek in case somebody wrote before we got the lock
39             # and set other things up like buffering
40 1         10 select ((select ($fh), $| = 1)[0]);
41 1         8 seek ($fh, 0, 2);
42 1         4 my $filesize = tell $fh;
43              
44             # check this looks like a FixedRecLen file
45 1 50       6 croak "file size ($filesize) does not match element length ($elemlen)\n"
46             if (($filesize % $elemlen) != 0);
47 1         3 my $num_records = ($filesize / $elemlen);
48              
49 1         18 return bless {
50             filename => $file,
51             fh => $fh,
52             pad_char => $pad_char,
53             reclen => $reclen,
54             recsep => $recsep,
55             recseplen => length $recsep,
56             elemlen => $elemlen,
57             records => $num_records, # will change
58             filesize => $filesize, # will change
59             }, $class;
60             }
61              
62             sub PUSH {
63 4     4   5 my $self = shift;
64 4         11 my @list = @_;
65 4         6 my $fh = $self->{fh};
66              
67 22         181 croak "length of value is greater than record length\n"
68 4 100       6 if grep {length $_ > $self->{reclen}} @list;
69              
70 21         65 croak "value contains record separator\n"
71 3 50       6 if grep {m/$self->{recsep}/} @list;
72              
73             # pad out (note: could run out of RAM doing this)
74 21         58 @list = map {
75 3         5 ($self->{pad_char} x ($self->{reclen} - length $_)) . $_
76             } @list;
77              
78 3         12 my $value = join $self->{recsep}, @list;
79              
80 3         126 print $fh $value, $self->{recsep};
81 3         7 $self->{records} += scalar @list;
82 3         9 $self->{filesize} = $self->{records} * $self->{elemlen};
83              
84 3         21 return $self->{records};
85             }
86              
87             sub STORE {
88 2     2   540 my $self = shift;
89 2         4 my ($index, $value) = @_;
90 2         3 my $fh = $self->{fh};
91              
92 2 100       81 croak "length of value is greater than record length\n"
93             if (length $value > $self->{reclen});
94              
95 1 50       13 croak "value contains record separator\n"
96             if ($value =~ m/$self->{recsep}/);
97              
98             # random stores are not allowed, but PUSHes beyond file end are
99 1         2 my $blanks = $index - $self->{records};
100 1 50       3 croak "can only append to array, please see Tie::File::FixedRecLen\n"
101             if $blanks < 0;
102              
103 1         2 $self->PUSH( (map {''} (1 .. $blanks)), $value );
  10         15  
104              
105 1         4 return undef; # just what should STORE return?
106             }
107              
108             sub STORESIZE {
109 1     1   2 my $self = shift;
110 1         1 my ($count) = @_;
111              
112 1 50       4 return undef if $count == $self->{records};
113              
114 1 50       3 croak "cannot shorten, please see Tie::File::FixedRecLen\n"
115             if $count < $self->{records};
116              
117 1         4 $self->PUSH( map {''} (1 .. ($count - $self->{records})) );
  9         14  
118              
119 1         4 return undef; # just what should STORESIZE return?
120             }
121              
122             sub FETCHSIZE {
123 6     6   2048 return $_[0]->{records};
124             }
125              
126             sub UNTIE {
127 1     1   2 my $self = shift;
128 1         3 my $fh = $self->{fh};
129              
130 1         10 flock ($fh, LOCK_UN);
131 1         27 close $fh;
132             }
133              
134             foreach my $meth (qw/SPLICE FETCH POP SHIFT UNSHIFT CLEAR DELETE EXISTS EXTEND/) {
135 0     0     *{Symbol::qualify_to_ref($meth)} = sub {croak "unsupported method: '$meth'"};
136             }
137              
138             1;