File Coverage

blib/lib/Cantella/Store/UUID/File.pm
Criterion Covered Total %
statement 18 64 28.1
branch 0 26 0.0
condition 0 11 0.0
subroutine 6 14 42.8
pod 5 5 100.0
total 29 120 24.1


line stmt bran cond sub pod time code
1             package Cantella::Store::UUID::File;
2              
3 1     1   9687 use Moose;
  1         687141  
  1         10  
4 1     1   8279 use JSON ();
  1         2  
  1         29  
5              
6 1     1   1244 use File::MimeInfo::Magic ();
  1         11154  
  1         45  
7 1     1   1114 use MooseX::Types::Data::GUID qw/GUID/;
  1         109237  
  1         10  
8 1     1   2527 use MooseX::Types::Path::Class qw/Dir File/;
  1         115277  
  1         8  
9              
10 1     1   1493 use namespace::autoclean;
  1         2  
  1         6  
11              
12             our $VERSION = '0.003003';
13              
14             has uuid => (is => 'ro', isa => GUID, coerce => 1, required => 1);
15             has dir => (is => 'ro', isa => Dir, coerce => 1);
16              
17             has path => (is => 'ro', isa => File, coerce => 1, lazy_build => 1);
18             has _meta_file => (is => 'ro', isa => File, coerce => 1, lazy_build => 1);
19              
20             has metadata => (
21             traits => ['Hash'],
22             is => 'rw',
23             isa => 'HashRef',
24             lazy_build => 1,
25             trigger => sub { shift->write_metadata },
26             handles => {
27             'get_property' => 'get',
28             'set_property' => 'set',
29             'has_property' => 'exists',
30             'clear_property', => 'delete',
31             },
32             );
33              
34             sub _build_path {
35 0     0     my $self = shift;
36 0           $self->dir->file( $self->uuid->as_string );
37             }
38              
39             sub _build__meta_file {
40 0     0     my $self = shift;
41 0           $self->dir->file((join '.', $self->uuid->as_string, 'meta' ));
42             }
43              
44             sub _build_metadata {
45 0     0     my $self = shift;
46 0           my $file = $self->_meta_file;
47 0 0         if( my $json = $file->slurp ){
48 0 0         if( my $perl = eval { JSON::from_json( $json ) }){
  0            
49 0           return $perl;
50             }
51 0           die("Failed to parse contents of meta file $file: ${@}");
52             }
53 0           die("Failed to read file $file: ${!}");
54             }
55              
56             sub write_metadata {
57 0     0 1   my $self = shift;
58 0           my $file = $self->_meta_file;
59 0 0 0       if (my $json = JSON::to_json( $self->metadata || {} ) ){
60 0 0         if( my $fh = $file->openw ){
61 0           print $fh $json;
62 0           return 1;
63             }
64 0           die("Failed to write meta file '${file}' Contents: '${json}': ${!}");
65             }
66 0           die("Failed to serialize metadata");
67             }
68              
69             sub remove {
70 0     0 1   my $self = shift;
71 0           my $uuid = $self->uuid;
72 0           my $file_path = $self->path;
73 0           my $meta_path = $self->_meta_file;
74              
75 0 0 0       if( -e $meta_path && !$meta_path->remove ){
76 0           die("Can't remove '${uuid}': Failed to delete '${meta_path}': ${!}");
77             }
78 0 0 0       if (-e $file_path && !$file_path->remove){
79 0           die("Can't remove '${uuid}': Failed to delete '${file_path}': ${!}");
80             }
81              
82 0   0       return ! (-e $self->path || -e $self->_meta_file);
83             }
84              
85             sub exists {
86 0     0 1   my $self = shift;
87 0 0         return -e $self->path and -e $self->_meta_file;
88             }
89              
90             sub extension {
91 0     0 1   my $self = shift;
92 0 0         if( my $type = $self->mime_type ){
93 0 0         if( my $ext = File::MimeInfo::extensions( $type ) ){
94 0           return $ext;
95             }
96             }
97 0 0         if( $self->has_property('original_name') ){
98 0 0         if( $self->get_property('original_name') =~ /\.(\w+)$/){
99 0           return $1;
100             }
101             }
102 0           return '';
103             }
104              
105             sub mime_type {
106 0     0 1   my $self = shift;
107 0 0         return $self->get_property('mime-type') if $self->has_property('mime-type');
108 0 0         if( my $type = File::MimeInfo::Magic::mimetype( $self->path->stringify ) ){
109 0           $self->set_property('mime-type', $type);
110 0           $self->write_metadata;
111 0           return $type;
112             }
113 0           return;
114             }
115              
116             __PACKAGE__->meta->make_immutable;
117              
118             1;
119              
120             __END__;
121              
122             =head1 NAME
123              
124             Cantella::Store::UUID::File - File represented by a UUID
125              
126             =head1 A NOTE ABOUT EXTENSIONS
127              
128             To make file location deterministic, files are stored under only their UUID,
129             along with their respective meta file which is named C<$UUID.meta> eg
130             (C<DD5EB40A-164B-11DE-9893-5FA9AE3835A0.meta>). The meta files may contain any
131             number of key/value pairs relevant to the file such as the original file name,
132             extension, MIME type, etc. Meta files are stored in JSON format.
133              
134             =head1 ATTRIBUTES
135              
136             C<Cantella::Store::UUID> is a subclass of L<Moose::Object>. It inherits the
137             C<new> object provided by L<Moose>. All attributes can be set using the C<new>
138             constructor method, or their respecitive writer method, if applicable.
139              
140             =head2 uuid
141              
142             =over 4
143              
144             =item B<uuid> - reader
145              
146             =back
147              
148             Required, read-only L<Data::GUID> object, will automatically coerce.
149              
150             =head2 dir
151              
152             =over 4
153              
154             =item B<dir> - reader
155              
156             =back
157              
158             Required, read-only L<Path::Class::File> object representing the directory
159             where this file is stored. Automatically coercing.
160              
161             =head2 path
162              
163             =over 4
164              
165             =item B<path> - reader
166              
167             =item B<has_path> - predicate
168              
169             =item B<_build_path> - builder
170              
171             =item B<clear_path> - clearer
172              
173             =back
174              
175             Lazy-building, read-only L<Path::Class::File> object representing the file
176             being stored under this UUID.
177              
178             =head2 metadata
179              
180             =over 4
181              
182             =item B<metadata> - accessor
183              
184             =item B<has_metadata> - predicate
185              
186             =item B<_build_metadata> - builder
187              
188             =item B<clear_metadata> - clearer
189              
190             =item B<has_property> - key predicate
191              
192             =item B<set_property> - key writer
193              
194             =item B<get_property> - key reader
195              
196             =item B<clear_property> - key clearer
197              
198             =back
199              
200             Lazy_building, read-write hashref which contains the file's metadata. Setting
201             it with the writer method will write the data to disk, modifying the
202             hashref directly, or via the key writer, will not.
203              
204             =head2 _meta_file
205              
206             =over 4
207              
208             =item B<_meta_file> - reader
209              
210             =item B<_has_meta_file> - predicate
211              
212             =item B<_build__meta_file> - builder
213              
214             =item B<clear__meta_file> - clearer
215              
216             =back
217              
218             Lazy-building, read-only L<Path::Class::File> object pointing at the meta file.
219              
220             =head1 METHODS
221              
222             =head2 new
223              
224             =over 4
225              
226             =item B<arguments:> C<\%arguments>
227              
228             =item B<return value:> C<$object_instance>
229              
230             =back
231              
232             Constructor.
233              
234             =head2 write_metadata
235              
236             =over 4
237              
238             =item B<arguments:> none
239              
240             =item B<return value:> none
241              
242             =back
243              
244             Write the contents of C<metadata> to the metadata file.
245              
246             =head2 remove
247              
248             =over 4
249              
250             =item B<arguments:> none
251              
252             =item B<return value:> C<$bool_success>
253              
254             =back
255              
256             Removes the file and metadata file from the store. Returns true if both are
257             removed successfully. An exception will be thrown if there is an error deleting
258             the files.
259              
260             =head2 exists
261              
262             =over 4
263              
264             =item B<arguments:> none
265              
266             =item B<return value:> C<$bool>
267              
268             =back
269              
270             Checks for existence of both the file and the metadata file. Returns true only
271             if both exist.
272              
273             =head2 mime_type
274              
275             =over 4
276              
277             =item B<arguments:> none
278              
279             =item B<return value:> C<$mime_type>
280              
281             =back
282              
283             Will return the mime-type for the file. If there is a value for the 'mime-type'
284             property, that value will be used. If the key isn't present, L<File::MimeInfo>
285             will be used to find the mime-type of the file and store in the meta file.
286              
287             If no mime-type can be determined, undef will be returned in scalr context and
288             an empty list in list context.
289              
290             =head2 extension
291              
292             =over 4
293              
294             =item B<arguments:> none
295              
296             =item B<return value:> C<$extension>
297              
298             =back
299              
300             Will return an appropriate extension for a file, by using it's mime-type, or
301             original file name if mime-type is unavailable. If no known extension is known
302             it returns an empty string.
303              
304             =head1 SEE ALSO
305              
306             L<Cantella::Store::UUID>
307              
308             =head1 AUTHOR
309              
310             Guillermo Roditi (groditi) E<lt>groditi@cpan.orgE<gt>
311              
312             =head1 COPYRIGHT AND LICENSE
313              
314             This software is copyright (c) 2009, 2010 by Guillermo Roditi.
315              
316             This is free software; you can redistribute it and/or modify it under
317             the same terms as the Perl 5 programming language system itself.
318              
319             =cut