File Coverage

blib/lib/Tie/FS.pm
Criterion Covered Total %
statement 39 108 36.1
branch 5 58 8.6
condition 5 28 17.8
subroutine 7 14 50.0
pod n/a
total 56 208 26.9


line stmt bran cond sub pod time code
1             package Tie::FS;
2              
3 2     2   42402 use 5.006;
  2         8  
  2         199  
4 2     2   15 use strict;
  2         3  
  2         169  
5 2     2   30 use warnings;
  2         9  
  2         287  
6              
7             =head1 NAME
8              
9             Tie::FS - Read and write files using a tied hash
10              
11             =head1 VERSION
12              
13             Version 0.1.0
14              
15             =cut
16              
17             our $VERSION = '0.1.0';
18              
19             =head1 SYNOPSIS
20              
21             use Tie::FS; # Initialize Tie::FS module.
22              
23             tie %hash, Tie::FS, $flag, $dir; # Ties %hash to files in $dir.
24              
25             @files = keys %hash; # Retrieves directory list of $dir.
26              
27             $data = $hash{$file1}; # Reads "$dir/$file1" into $data.
28             $hash{$file1} = $data; # Writes $data into "$dir/$file1".
29             $hash{$file2} = $hash{$file1}; # Copies $file1 to $file2.
30              
31             if (exists $hash{$path}) {...} # Checks if $path exists at all.
32              
33             if (defined $hash{$path}) {...} # Checks if $path is a file.
34              
35             $data = delete $hash{$file}; # Deletes $file, returns contents.
36              
37             undef %hash; # Deletes ALL regular files in $dir.
38              
39             =head1 DESCRIPTION
40              
41             This module ties a hash to a directory in the filesystem. If no directory
42             is specified in the $dir parameter, then "." (the current directory) is
43             assumed. The $flag parameter defaults to the "Create" flag.
44              
45             The following (case-insensitive) access flags are available:
46              
47             ReadOnly Access is strictly read-only.
48             Create Files may be created but not overwritten or deleted.
49             Overwrite Files may be created, overwritten or deleted.
50             ClearDir Also allow files to be cleared (all deleted at once).
51              
52             The pathname specified as a key to the hash may either be a relative path
53             or an absolute path. For relative paths, the default directory specified
54             to tie() will be prepended to the path.
55              
56             The exists() function will be true if the specified path exists, including
57             directories and non-regular files (such as symbolic links). Directories and
58             non-regular files will return undef; only regular files will return a defined
59             values. Empty regular files return "", not undef. Attempting to store undef
60             in the tied will have no effect.
61              
62             The keys() function will scan the directory (without sorting it), eliminate
63             "." and ".." entries and append "/" for directories. (values() and each()
64             will follow the same rules for selecting entries.) The following code will
65             retrieve a sorted list of subdirectories:
66              
67             @subdirs = sort grep {s/\/$//} keys %hash;
68              
69             =head1 CAVEATS
70              
71             Unless an absolute path was specified to tie(), a later chdir() will affect
72             the paths accessed by the tied hash with relative paths.
73              
74             A symbolic link is considered a non-regular file; it will not be followed
75             unless a "/" follows the link name and the link points to a directory.
76              
77             To perform a defined() test, the entire file must be read into memory, even
78             if it will be discarded immediately after the test. The exists() function
79             does not need to read the contents of a file.
80              
81             =head1 AUTHOR
82              
83             Deven T. Corzine
84              
85             =head1 BUGS
86              
87             Please report any bugs or feature requests to C, or through
88             the web interface at L.
89              
90             =head1 SUPPORT
91              
92             You can find documentation for this module with the perldoc command.
93              
94             perldoc Tie::FS
95              
96             You can also look for information at:
97              
98             =over 4
99              
100             =item * RT: CPAN's request tracker (report bugs here)
101              
102             L
103              
104             =item * AnnoCPAN: Annotated CPAN documentation
105              
106             L
107              
108             =item * CPAN Ratings
109              
110             L
111              
112             =item * Search CPAN
113              
114             L
115              
116             =back
117              
118             =head1 ACKNOWLEDGEMENTS
119              
120             File::Slurp was the inspiration for this module, which is intended to
121             provide similar functionality in a more "Perlish" way.
122              
123             =head1 LICENSE AND COPYRIGHT
124              
125             Copyright 2011 Deven T. Corzine.
126              
127             This program is free software; you can redistribute it and/or modify it
128             under the terms of either: the GNU General Public License as published
129             by the Free Software Foundation; or the Artistic License.
130              
131             See http://dev.perl.org/licenses/ for more information.
132              
133             =cut
134              
135 2     2   36 use Carp;
  2         5  
  2         593  
136 2     2   4379 use Symbol;
  2         3830  
  2         5974  
137              
138             sub TIEHASH {
139 1     1   41 my $class = shift;
140 1   50     37 my $flag = lc shift || "create";
141 1   50     6 my $dir = shift || '.';
142              
143 1 50       6 croak "$class: Usage: \"tie %hash, $class, $dir, $flag;\"" if @_;
144              
145 1 0 33     10 croak "$class: Invalid flag \"$flag\"" unless $flag eq "readonly" or
      33        
      33        
146             $flag eq "create" or $flag eq "overwrite" or $flag eq "cleardir";
147              
148 1         6 my $file_handle = gensym;
149 1         23 my $dir_handle = gensym;
150 1 50       61 opendir $dir_handle, $dir or croak "$class: opendir \"$dir\": $!";
151              
152 1         8 my $obj = {
153             class => $class,
154             dir => $dir,
155             flag => $flag,
156             file_handle => $file_handle,
157             dir_handle => $dir_handle,
158             };
159              
160 1         7 return bless $obj, $class;
161             }
162              
163             sub FETCH {
164 1     1   98 my $self = shift;
165 1         4 my $file = shift;
166              
167 1         6 my $class = $self->{class};
168 1         4 my $handle = $self->{file_handle};
169              
170 1 50       8 $file = "$self->{dir}/$file" unless substr($file, 0, 1) eq "/";
171              
172 1         19 lstat $file;
173 1 50       7 return undef unless -f _;
174              
175 1         4 local $/;
176 1         3 undef $/;
177              
178 1 50       33 open $handle, "<$file" or croak "$class: reading \"$file\": $!";
179 1         22 my $contents = <$handle>;
180 1         8 close $handle;
181              
182 1         2 $contents .= "";
183              
184 1         6 return $contents;
185             }
186              
187             sub STORE {
188 0     0     my $self = shift;
189 0           my $file = shift;
190 0           my $contents = shift;
191              
192 0           my $class = $self->{class};
193 0           my $flag = $self->{flag};
194 0           my $handle = $self->{file_handle};
195              
196 0 0         return undef unless defined $contents;
197              
198 0 0         $file = "$self->{dir}/$file" unless substr($file, 0, 1) eq "/";
199              
200 0           lstat $file;
201              
202 0 0 0       if ($flag eq "readonly") {
    0          
    0          
203 0 0         croak "$class: won't overwrite \"$file\", flag is \"readonly\""
204             if -e _;
205 0           croak "$class: won't create \"$file\", flag is \"readonly\"";
206             } elsif ($flag eq "create") {
207 0 0         croak "$class: won't overwrite \"$file\", flag is \"create\""
208             if -e _;
209             } elsif ($flag eq "overwrite" or $flag eq "cleardir") {
210 0 0 0       croak "$class: can't overwrite non-file \"$file\"" if -e _ and not -f _;
211             } else {
212 0           die;
213             }
214              
215 0 0         open $handle, ">$file" or croak "$class: writing \"$file\": $!";
216 0           print $handle $contents;
217 0           close $handle;
218              
219 0           return $contents;
220             }
221              
222             sub DELETE {
223 0     0     my $self = shift;
224 0           my $file = shift;
225              
226 0           my $class = $self->{class};
227 0           my $contents = $self->FETCH($file);
228              
229 0 0         $file = "$self->{dir}/$file" unless substr($file, 0, 1) eq "/";
230              
231 0           lstat $file;
232              
233 0 0         return undef unless -e _;
234              
235 0           my $flag = $self->{flag};
236              
237 0 0 0       if ($flag eq "readonly" or $flag eq "create") {
    0 0        
238 0           croak "$class: won't delete \"$file\", flag is \"$flag\"";
239             } elsif ($flag eq "overwrite" or $flag eq "cleardir") {
240 0 0         croak "$class: won't delete non-file \"$file\"" unless -f _;
241             } else {
242 0           die;
243             }
244              
245 0 0         croak "$class: deleting \"$file\": $!" unless unlink $file;
246              
247 0           return $contents;
248             }
249              
250             sub CLEAR {
251 0     0     my $self = shift;
252              
253 0           my $class = $self->{class};
254 0           my $dir = $self->{dir};
255 0           my $flag = $self->{flag};
256 0           my $handle = $self->{file_handle};
257              
258 0 0         croak "$class: won't clear directory \"$dir\", flag is \"$flag\""
259             unless $flag eq "cleardir";
260              
261 0 0         opendir $handle, $dir or croak "$class: opendir \"$dir\": $!";
262 0 0         my @files = grep {lstat $_ and -f _} map {"$dir/$_"} readdir $handle;
  0            
  0            
263 0           close $handle;
264              
265 0           my $file;
266 0           foreach $file (@files) {
267 0 0         croak "$class: deleting \"$file\": $!" unless unlink $file;
268             }
269             }
270              
271             sub EXISTS {
272 0     0     my $self = shift;
273 0           my $file = shift;
274              
275 0 0         $file = "$self->{dir}/$file" unless substr($file, 0, 1) eq "/";
276              
277 0           lstat $file;
278              
279 0           return -e _;
280             }
281              
282             sub FIRSTKEY {
283 0     0     my $self = shift;
284              
285 0           my $handle = $self->{dir_handle};
286              
287 0           rewinddir $handle;
288 0           return $self->NEXTKEY();
289             }
290              
291             sub NEXTKEY {
292 0     0     my $self = shift;
293              
294 0           my $dir = $self->{dir};
295 0           my $handle = $self->{dir_handle};
296 0           my $file;
297              
298             {
299 0           $file = readdir $handle;
  0            
300 0 0         last unless defined $file;
301 0 0 0       redo if $file eq "." || $file eq "..";
302 0           lstat "$dir/$file";
303 0 0         $file .= "/" if -d _;
304             }
305 0           return $file;
306             }
307              
308             sub DESTROY {
309 0     0     my $self = shift;
310              
311 0           closedir $self->{dir_handle};
312             }
313              
314             1; # End of Tie::FS