File Coverage

blib/lib/Catmandu/FileBag.pm
Criterion Covered Total %
statement 57 77 74.0
branch 11 22 50.0
condition 4 12 33.3
subroutine 10 11 90.9
pod 4 5 80.0
total 86 127 67.7


line stmt bran cond sub pod time code
1             package Catmandu::FileBag;
2              
3             our $VERSION = '1.16';
4              
5 13     13   257274 use Catmandu::Sane;
  13         200752  
  13         106  
6 13     13   3902 use IO::String;
  13         5414  
  13         381  
7 13     13   92 use Catmandu::Util qw(:is :check);
  13         29  
  13         5650  
8 13     13   92 use Moo::Role;
  13         31  
  13         88  
9 13     13   7321 use namespace::clean;
  13         31  
  13         109  
10              
11             sub stream {
12 5     5 1 1082 my ($self, $io, $data) = @_;
13 5         22 check_hash_ref($data);
14 5         601 check_invocant($io);
15 5         469 $data->{_stream}->($io);
16             }
17              
18             sub as_string {
19 0     0 1 0 my ($self, $data) = @_;
20 0         0 check_hash_ref($data);
21 0         0 my $str;
22 0         0 my $io = IO::String->new($str);
23 0         0 $data->{_stream}->($io);
24 0         0 $str;
25             }
26              
27             sub as_string_utf8 {
28 5     5 1 1503 my ($self, $data) = @_;
29 5         36 check_hash_ref($data);
30 5         159 my $str;
31 5         49 my $io = IO::String->new($str);
32 5         330 $data->{_stream}->($io);
33 5         23 utf8::decode($str);
34 5         27 $str;
35             }
36              
37             sub upload {
38 17     17 1 33044 my ($self, $io, $id) = @_;
39 17         138 check_string($id);
40 17         1014 check_invocant($io);
41              
42 17         2291 my $file = {_id => $id, _stream => $io};
43              
44 17         351 $self->add($file);
45              
46             # The add() method of FileBags should inline data the passed $file with
47             # file metadata. Use a get($id) when this inline update wasn't implemented
48             # by the Bag.
49 17 50       125 if (exists $file->{size}) {
50              
51             # all ok
52             }
53             else {
54 0         0 $self->log->warn(
55             "$self doesn't inline update \$data in add(\$data) method");
56 0         0 $file = $self->get($id);
57             }
58              
59 17 50       89 if (!defined($file)) {
    50          
60 0         0 return 0;
61             }
62             elsif (is_hash_ref($file)) {
63 17         157 return $file->{size};
64             }
65             else {
66 0         0 $self->log->error("expecting a HASH but got `$file'");
67 0         0 return 0;
68             }
69             }
70              
71             # Helper method to implement file streaming in Dancer contexts
72             sub file_streamer {
73 9     9 0 39 my ($self,$file,$out) = @_;
74 9         17 my $bytes = 0;
75 9   33     55 my $data = IO::File->new($file, "r")
76             || Catmandu::Error->throw("$file not readable");
77              
78 9 50       1020 Catmandu::Error->throw("no io defined or not writable")
79             unless defined($out);
80              
81 9 50       187 Catmandu::Error->throw("$out doesn't support syswrite!")
82             unless $out->can('syswrite');
83              
84 9         62 OUTER: while (!$data->eof) {
85 9         248 my $buffer;
86 9         60 my $n_read = $data->read($buffer, 1024);
87              
88 9 50       163 if (!defined($n_read)) {
    50          
89 0         0 $self->log->error("read error for $file : $!");
90 0         0 Catmandu::Error->throw("read error for $file : $!");
91             }
92             elsif ($n_read == 0) {
93             # end-of-file
94 0         0 last;
95             }
96             else {
97             # all is ok
98             }
99              
100 9         20 my $bytes_to_write = $n_read;
101 9         24 my $bytes_written = 0;
102            
103 9         43 while ($bytes_to_write) {
104 9         40 my $n_write = $out->syswrite($buffer,$bytes_to_write);
105              
106 9 50 33 1   402 if ($!{EINTR} || $!{EWOULDBLOCK} || $!{EAGAIN}) {
  1 50 33     2238  
  1 50 33     1345  
  1 50       9  
107             # no data read, try again
108 0         0 next;
109             }
110             elsif ($!) {
111 0         0 $self->log->error("write failed : $!");
112 0         0 last OUTER;
113             }
114             elsif (!defined($n_write) || $n_write == 0) {
115 0         0 $self->log->error("write failed : no bytes written");
116 0         0 last OUTER;
117             }
118             elsif ($n_read < $n_write) {
119             # incomplete write
120 0         0 $buffer = substr($buffer,$n_write);
121             }
122             else {
123             # all is ok
124             }
125              
126 9         424 $bytes_to_write -= $n_write;
127 9         27 $bytes_written = $n_write;
128             }
129              
130 9         41 $bytes += $bytes_written;
131             }
132              
133 9         89 $out->close();
134 9         244 $data->close();
135              
136 9         180 $bytes;
137             }
138              
139             1;
140              
141             __END__
142              
143             =pod
144              
145             =head1 NAME
146              
147             Catmandu::FileBag - A Catmandu::FileStore compartment to persist binary data
148              
149             =head1 SYNOPSIS
150              
151             use Catmandu;
152              
153             my $store = Catmandu->store('Simple' , root => 't/data');
154              
155             # List all containers
156             $store->bag->each(sub {
157             my $container = shift;
158              
159             print "%s\n" , $container->{_id};
160             });
161              
162             # Add a new folder
163             $store->bag->add({ _id => '1234' });
164              
165             # Get the files
166             my $files = $store->bag->files('1234');
167              
168             # Add a file to the files
169             $files->upload(IO::File->new('<foobar.txt'), 'foobar.txt');
170              
171             # Stream the contents of a file
172             my $file = $files->get('foobar.txt');
173             $files->stream(IO::File->new('>foobar.txt'), $file);
174              
175             # Delete a file
176             $files->delete('foobar.txt');
177              
178             # Delete a folder
179             $store->index->delete('1234');
180              
181              
182             =head1 DESCRIPTION
183              
184             Each L<Catmandu::FileBag> is a L<Catmandu::Bag> and inherits all its methods.
185              
186             =head1 METHODS
187              
188             =head2 upload($io, $file_name)
189              
190             An helper application to add an IO::Handle $io to the L<Catmandu::FileBag>. Returns
191             the number of bytes written.
192              
193             =head2 stream($io, $file)
194              
195             A helper application to stream the contents of a L<Catmandu::FileBag> item
196             to an IO::Handle. Returns the number of bytes written.
197              
198             =head2 as_string($file)
199              
200             Return the contents of the L<Catmandu::FileBag> item as a string.
201              
202             =head2 as_string_utf8($file)
203              
204             Return the contents of the L<Catmandu::FileBag> item as an UTF-8 string.
205              
206             =head1 SEE ALSO
207              
208             L<Catmandu::FileStore> ,
209             L<Catmandu::FileBag::Index>
210              
211             =cut