File Coverage

blib/lib/Catalyst/Model/File.pm
Criterion Covered Total %
statement 104 116 89.6
branch 33 46 71.7
condition 14 20 70.0
subroutine 27 31 87.1
pod 9 11 81.8
total 187 224 83.4


line stmt bran cond sub pod time code
1             package Catalyst::Model::File;
2              
3 5     5   2344657 use Moose;
  5         1938836  
  5         36  
4 5     5   41155 use namespace::autoclean;
  5         33872  
  5         28  
5              
6             extends 'Catalyst::Model';
7             with 'Catalyst::Component::InstancePerContext';
8              
9              
10 5     5   394 use MRO::Compat;
  5         10  
  5         160  
11 5     5   31 use Carp;
  5         12  
  5         323  
12 5     5   2656 use MooseX::Types::Path::Class qw/ Dir /;
  5         2044675  
  5         54  
13 5     5   6922 use MooseX::Types::Moose qw/ Str /;
  5         11  
  5         40  
14 5     5   25080 use IO::Dir;
  5         14  
  5         411  
15 5     5   36 use Path::Class ();
  5         14  
  5         90  
16 5     5   46 use IO::File;
  5         12  
  5         7938  
17              
18             our $VERSION = '0.10';
19              
20             =head1 NAME
21              
22             Catalyst::Model::File - File based storage model for Catalyst.
23              
24             =head1 SYNOPSIS
25              
26             # use the helper to create a model
27             myapp_create.pl model File File
28              
29             # configure in lib/MyApp.pm
30              
31             MyApp->config(
32             name => 'MyApp',
33             root => MyApp->path_to('root'),
34             'Model::File' => {
35             root_dir => MyApp->path_to('file_store')
36             },
37             );
38              
39             Simple file based storage model for Catalyst.
40              
41             @file_names = $c->model('File')->list;
42              
43             =head1 METHODS
44              
45             =cut
46              
47             has root_dir => (
48             isa => Dir,
49             is => 'ro',
50             coerce => 1,
51             required => 1,
52             );
53              
54             has _dir => (
55             isa => Dir,
56             is => 'rw',
57             lazy => 1,
58             default => sub { shift()->root_dir },
59             clearer => '_clear_dir',
60             );
61              
62             has dir_create_mask => (
63             isa => Str,
64             is => 'ro',
65             default => '0775',
66             );
67              
68             has _directory => (
69             isa => Dir,
70             is => 'rw',
71             default => sub { Path::Class::dir('/') },
72             );
73              
74             sub BUILD {
75 4     4 0 10141 my $self = shift;
76 4         161 mkdir($self->root_dir, oct($self->dir_create_mask));
77             }
78              
79             sub build_per_context_instance {
80 5     5 0 305183 my ($self, $c) = @_;
81              
82 5         36 $self->cd('/');
83              
84 5         34 return $self;
85             }
86              
87             =head2 list
88              
89             Returns a list of files (and/or directories) found under the current working
90             dir. Default will return files (including those found under sub-directories)
91             but not directories.
92              
93             To change this behaviour specify a C<mode> param of C<files> (default),
94             C<dirs> or C<both>:
95              
96             $mdl->list(mode => 'both')
97              
98             To only get files/dirs directly under the current dir specify a C<recurse>
99             option of 0.
100              
101             Please note: the exact order in which files and directories are listed will
102             change from OS to OS.
103              
104             =cut
105              
106             sub list {
107 7     7 1 2980 my ($self, %opt) = @_;
108 7         13 my @files;
109 7   100     40 $opt{mode} ||= 'files';
110 7 100       29 $opt{recurse} = 1 unless exists $opt{recurse};
111              
112 7 100       41 $opt{dir} = 1 if $opt{mode} =~ /^both|dirs$/;
113 7 100       61 $opt{file} = 1 if $opt{mode} =~ /^both|files$/;
114              
115 7 100       21 if ($opt{recurse}) {
116             $self->_dir->recurse(callback => sub {
117 11     11   3098 my ($entry) = @_;
118             push @files, $entry
119             if !$entry->is_dir && $opt{file}
120 11 100 66     53 || $entry->is_dir && $opt{dir};
      33        
      66        
121 4         138 });
122 4         74 return map { $self->_rebless($_) } @files;
  5         33  
123             }
124              
125 3         99 @files = map { $self->_rebless($_) } $self->_dir->children;
  12         2539  
126              
127 3 100 100     62 return @files if $opt{dir} && $opt{file};
128              
129             return $opt{dir} ?
130 4         21 grep { $_->is_dir } @files :
131 2 100       9 grep { !$_->is_dir } @files;
  4         19  
132              
133             }
134              
135             sub _rebless {
136 17     17   37 my ($self, $entity) = @_;
137              
138 17         569 $entity = $entity->absolute($self->root_dir);
139 17 100       698 if ($entity->is_dir) {
140 3         19 bless $entity, 'Catalyst::Model::File::Dir';
141             }
142             else {
143 14         82 bless $entity, 'Catalyst::Model::File::File';
144             }
145              
146 17         485 $entity->{stringify_as} = $entity->relative($self->_dir)->as_foreign('Unix')->stringify;
147 17         259 return $entity;
148             }
149              
150             =head2 change_dir
151              
152             =head2 cd
153              
154             Set current working directory (relative to current) and return $self.
155              
156             =cut
157              
158 9     9 1 2276 sub cd { shift->change_dir(@_) }
159              
160              
161             sub change_dir {
162 9     9 1 21 my $self = shift;
163              
164 9         30 my $dir = shift;
165              
166 9 50       35 return $self unless defined $dir;
167              
168 9 50       55 $dir = Path::Class::dir($dir, @_) unless ref $dir;
169              
170 9         701 my @dir_list = ();
171 9         28 $self->_directory(Path::Class::dir(''));
172              
173 9 100       46 if ($dir->is_absolute) {
174 6         491 $self->_clear_dir;
175 6         44 @dir_list = $dir->dir_list(1);
176             } else {
177 3         493 $dir = $self->_dir->subdir($dir);
178 3         245 $self->_clear_dir;
179 3 50       83 return $self unless ($self->root_dir->subsumes($dir) );
180              
181 3         947 @dir_list = $dir->relative($self->{root_dir})->dir_list;
182             }
183              
184             # $self->_directory($self->_directory->subdir(@dir_list));
185 9         550 foreach my $subdir (@dir_list) {
186 7 100       204 $self->_dir($self->_dir->subdir($subdir)) unless $subdir eq '..';
187 7 100       50 $self->_dir($self->_dir->parent) if $subdir eq '..';
188             }
189              
190 9         300 $self->_directory($self->_dir->relative($self->root_dir)->absolute('/'));
191              
192 9         59 return $self;
193             }
194              
195             =head2 directory
196              
197             =head2 pwd
198              
199             Get the current working directory, from which all relative paths are based.
200              
201             =cut
202              
203 7     7 1 227 sub pwd { shift->directory(@_) }
204              
205             sub directory {
206 7     7 1 209 return shift->_directory->as_foreign('Unix');
207             }
208              
209             =head2 parent
210              
211             Move up to the parent of the working directory. Returns $self.
212              
213             =cut
214              
215             sub parent {
216 3     3 1 2448 my ($self) = @_;
217              
218 3         111 $self->_dir($self->_dir->parent);
219              
220 3 100       88 unless ($self->root_dir->subsumes($self->_dir)) {
221 1         315 $self->_clear_dir;
222 1         5 return $self;
223             }
224              
225 2         631 $self->_directory($self->_dir->relative($self->root_dir)->absolute('/'));
226              
227 2         14 return $self;
228             }
229              
230             =head2 $self->file($file)
231              
232             Returns an L<Path::Class::File> object of $file (which can be a string or a
233             Class::Path::File object,) or undef if the file is an invalid path - i.e.
234             outside the directory structure specified in the config.
235              
236             =cut
237              
238             sub file {
239 7     7 1 264 my ($self, $file) = @_;
240              
241 7 50       21 return unless $file;
242              
243 7 50       35 $file = (ref $file ? $file : Path::Class::file($file) )->absolute($self->_dir);
244              
245 7 50       1472 return undef unless $self->root_dir->subsumes($file);
246              
247             # Make sure the dir tree exists
248 7         2191 $file->dir->mkpath(0, oct($self->dir_create_mask));
249 7         1013 return $file;
250              
251             }
252              
253             =head2 $self->slurp($file)
254              
255             Shortcut to $self->file($file)->slurp.
256              
257             In a scalar context, returns the contents of $file in a string. In a list
258             context, returns the lines of $file (according to how $/ is set) as a list. If
259             the file can't be read, this method will throw an exception.
260              
261             If you want "chomp()" run on each line of the file, pass a true value for the
262             "chomp" or "chomped" parameters:
263              
264             my @lines = $self->slurp($file, chomp => 1);
265              
266              
267             =cut
268              
269             sub slurp {
270 3 0   3 1 2029 my $file = shift->file(shift) or return wantarray ? () : undef;
    50          
271              
272 3 0       29 return $file->stat ? $file->slurp(@_) : wantarray ? () : undef;
    50          
273             }
274              
275             =head2 $self->splat($file, PRINT_ARGS)
276              
277             Does a print to C<$file> with the specified C<PRINT_ARGS>. Does the same as
278             C<$self->file->openw->print(@_)>
279              
280             =cut
281              
282             sub splat {
283 3 50   3 1 1995 my $file = shift->file(shift) or return;
284              
285 3         36 $file->openw->print(@_);
286             }
287              
288             __PACKAGE__->meta->make_immutable;
289              
290             package #
291             Catalyst::Model::File::File;
292 5     5   47 use base 'Path::Class::File';
  5         11  
  5         2479  
293             sub stringify {
294 52   66 52   6971 return $_[0]->{stringify_as} || $_[0]->abs_stringify;
295             }
296              
297             sub abs_stringify {
298 29     29   92 Path::Class::File::stringify(shift)
299             }
300              
301             # All these would probably be better done with Moose or something, but i'm lazy
302             sub open {
303 0     0   0 my $s = shift;
304 0         0 local $s->{stringify_as};
305 0         0 return $s->SUPER::open(@_);
306             }
307              
308             sub touch {
309 0     0   0 my $s = shift;
310 0         0 local $s->{stringify_as};
311 0         0 return $s->SUPER::touch(@_);
312             }
313              
314             sub remove {
315 0     0   0 my $s = shift;
316 0         0 local $s->{stringify_as};
317 0         0 return $s->SUPER::touch(@_);
318             }
319              
320             sub stat {
321 1     1   9 my $s = shift;
322 1         4 local $s->{stringify_as};
323 1         6 return $s->SUPER::stat(@_);
324             }
325             sub lstat {
326 0     0   0 my $s = shift;
327 0         0 local $s->{stringify_as};
328 0         0 return $s->SUPER::lstat(@_);
329             }
330              
331             @Catalyst::Model::File::Dir::ISA = 'Path::Class::Dir';
332             sub Catalyst::Model::File::Dir::stringify {
333             return $_[0]->{stringify_as}
334 11   66 11   1362 || Path::Class::Dir::stringify($_[0]);
335             }
336              
337             =head1 AUTHOR
338              
339             Ash Berlin, C<ash@cpan.org>
340              
341             =head1 LICENSE
342              
343             This library is free software, you can redistribute it and/or modify
344             it under the same terms as Perl itself.
345              
346             =cut
347              
348             1;
349