File Coverage

blib/lib/Catmandu/Store/File/Simple/Bag.pm
Criterion Covered Total %
statement 56 57 98.2
branch 3 4 75.0
condition n/a
subroutine 16 17 94.1
pod 0 5 0.0
total 75 83 90.3


line stmt bran cond sub pod time code
1             package Catmandu::Store::File::Simple::Bag;
2              
3 8     8   56 use Catmandu::Sane;
  8         92  
  8         54  
4              
5             our $VERSION = '1.16';
6              
7 8     8   1556 use Moo;
  8         19  
  8         52  
8 8     8   2922 use Carp;
  8         22  
  8         611  
9 8     8   53 use IO::File;
  8         22  
  8         1388  
10 8     8   60 use Path::Tiny;
  8         23  
  8         423  
11 8     8   48 use File::Spec;
  8         16  
  8         213  
12 8     8   4085 use File::Copy;
  8         18881  
  8         579  
13 8     8   63 use Catmandu::Util qw(content_type);
  8         16  
  8         352  
14 8     8   3793 use URI::Escape;
  8         11939  
  8         475  
15 8     8   60 use namespace::clean;
  8         21  
  8         65  
16              
17             with 'Catmandu::Bag';
18             with 'Catmandu::FileBag';
19             with 'Catmandu::Droppable';
20              
21             has _path => (is => 'lazy');
22              
23             sub _build__path {
24 44     44   383 my $self = shift;
25 44         741 $self->store->directory_index->add($self->name)->{_path};
26             }
27              
28             sub generator {
29 13     13 0 2708 my ($self) = @_;
30 13         255 my $path = $self->_path;
31              
32             sub {
33 32     32   492 state $children = [path($path)->children];
34              
35 32         2494 my $child = shift @$children;
36              
37 32 100       277 return undef unless $child;
38              
39 19         144 my ($volume, $directories, $file) = File::Spec->splitpath($child);
40              
41 19 50       245 next if index($file, ".") == 0;
42              
43 19         57 my $unpacked_key = $self->unpack_key($file);
44              
45 19         472 return $self->get($unpacked_key);
46 13         131 };
47             }
48              
49             sub exists {
50 4     4 0 3264 my ($self, $id) = @_;
51 4         88 my $path = $self->_path;
52              
53 4         36 my $packed_key = $self->pack_key($id);
54              
55 4         75 my $file = File::Spec->catfile($path, $packed_key);
56              
57 4         97 -f $file;
58             }
59              
60             sub get {
61             my ($self, $id) = @_;
62             my $path = $self->_path;
63              
64             my $packed_key = $self->pack_key($id);
65              
66             my $file = File::Spec->catfile($path, $packed_key);
67              
68             return undef unless -f $file;
69              
70             my $stat = [stat($file)];
71              
72             my $size = $stat->[7];
73             my $modified = $stat->[9];
74             my $created = $stat->[10]; # no real creation time exists on Unix
75              
76             my $content_type = content_type($id);
77              
78             return {
79             _id => $id,
80             size => $size,
81             md5 => '',
82             content_type => $content_type,
83             created => $created,
84             modified => $modified,
85             _stream => sub {
86             $self->file_streamer($file,shift);
87             }
88             };
89             }
90              
91             sub add {
92             my ($self, $data) = @_;
93             my $path = $self->_path;
94              
95             my $id = $data->{_id};
96             my $io = $data->{_stream};
97              
98             return $self->get($id) unless $io;
99              
100             my $packed_key = $self->pack_key($id);
101              
102             my $file = File::Spec->catfile($path, $packed_key);
103              
104             if (Catmandu::Util::is_invocant($io)) {
105             copy($io, $file)
106             || Catmandu::Error->throw("failed to write file : $!");
107             }
108             else {
109             Catmandu::Util::write_file($file, $io)
110             || Catmandu::Error->throw("failed to write file : $!");
111             }
112              
113             my $new_data = $self->get($id);
114              
115             $data->{$_} = $new_data->{$_} for keys %$new_data;
116              
117             1;
118             }
119              
120             sub delete {
121             my ($self, $id) = @_;
122             my $path = $self->_path;
123              
124             my $packed_key = $self->pack_key($id);
125              
126             my $file = File::Spec->catfile($path, $packed_key);
127              
128             return undef unless -f $file;
129              
130             unlink $file;
131             }
132              
133             sub delete_all {
134             my ($self) = @_;
135              
136             $self->each(
137             sub {
138             my $key = shift->{_id};
139             $self->delete($key);
140             }
141             );
142              
143             1;
144             }
145              
146             sub drop {
147 0     0 0 0 $_[0]->delete_all;
148             }
149              
150             sub commit {
151             return 1;
152             }
153              
154             sub pack_key {
155 95     95 0 170 my $self = shift;
156 95         141 my $key = shift;
157 95         301 utf8::encode($key);
158 95         257 uri_escape($key);
159             }
160              
161             sub unpack_key {
162 19     19 0 35 my $self = shift;
163 19         32 my $key = shift;
164 19         63 my $str = uri_unescape($key);
165 19         237 utf8::decode($str);
166 19         40 $str;
167             }
168              
169             1;
170              
171             __END__
172              
173             =pod
174              
175             =head1 NAME
176              
177             Catmandu::Store::File::Simple::Bag - Index of all "files" in a Catmandu::Store::File::Simple "folder"
178              
179             =head1 SYNOPSIS
180              
181             use Catmandu;
182              
183             my $store = Catmandu->store('File::Simple' , root => 't/data');
184              
185             my $index = $store->index;
186              
187             # List all containers
188             $index->each(sub {
189             my $container = shift;
190              
191             print "%s\n" , $container->{_id};
192             });
193              
194             # Add a new folder
195             $index->add({_id => '1234'});
196              
197             # Delete a folder
198             $index->delete(1234);
199              
200             # Get a folder
201             my $folder = $index->get(1234);
202              
203             # Get the files in an folder
204             my $files = $index->files(1234);
205              
206             $files->each(sub {
207             my $file = shift;
208              
209             my $name = $file->{_id};
210             my $size = $file->{size};
211             my $content_type = $file->{content_type};
212             my $created = $file->{created};
213             my $modified = $file->{modified};
214              
215             $file->stream(IO::File->new(">/tmp/$name"), file);
216             });
217              
218             # Add a file
219             $files->upload(IO::File->new("<data.dat"),"data.dat");
220              
221             # Retrieve a file
222             my $file = $files->get("data.dat");
223              
224             # Stream a file to an IO::Handle
225             $files->stream(IO::File->new(">data.dat"),$file);
226              
227             # Delete a file
228             $files->delete("data.dat");
229              
230             # Delete a folders
231             $index->delete("1234");
232              
233             =head1 DESCRIPTION
234              
235             A L<Catmandu::Store::File::Simple::Bag> contains all "files" available in a
236             L<Catmandu::Store::File::Simple> FileStore "folder". All methods of L<Catmandu::Bag>,
237             L<Catmandu::FileBag::Index> and L<Catmandu::Droppable> are
238             implemented.
239              
240             Every L<Catmandu::Bag> is also an L<Catmandu::Iterable>.
241              
242             =head1 FOLDERS
243              
244             All files in a L<Catmandu::Store::File::Simple> are organized in "folders". To add
245             a "folder" a new record needs to be added to the L<Catmandu::Store::File::Simple::Index> :
246              
247             $index->add({_id => '1234'});
248              
249             The C<_id> field is the only metadata available in Simple stores. To add more
250             metadata fields to a Simple store a L<Catmandu::Plugin::SideCar> is required.
251              
252             =head1 FILES
253              
254             Files can be accessed via the "folder" identifier:
255              
256             my $files = $index->files('1234');
257              
258             Use the C<upload> method to add new files to a "folder". Use the C<download> method
259             to retrieve files from a "folder".
260              
261             $files->upload(IO::File->new("</tmp/data.txt"),'data.txt');
262              
263             my $file = $files->get('data.txt');
264              
265             $files->download(IO::File->new(">/tmp/data.txt"),$file);
266              
267             =head1 INHERITED METHODS
268              
269             This Catmandu::Bag implements:
270              
271             =over 3
272              
273             =item L<Catmandu::Bag>
274              
275             =item L<Catmandu::FileBag>
276              
277             =item L<Catmandu::Droppable>
278              
279             =back
280              
281             =cut