File Coverage

blib/lib/Mango/GridFS/Writer.pm
Criterion Covered Total %
statement 15 79 18.9
branch 0 26 0.0
condition 0 6 0.0
subroutine 5 19 26.3
pod 3 3 100.0
total 23 133 17.2


line stmt bran cond sub pod time code
1             package Mango::GridFS::Writer;
2 9     9   30 use Mojo::Base -base;
  9         10  
  9         53  
3              
4 9     9   1178 use Carp 'croak';
  9         25  
  9         360  
5 9     9   29 use List::Util 'first';
  9         10  
  9         477  
6 9     9   33 use Mango::BSON qw(bson_bin bson_doc bson_oid bson_time);
  9         9  
  9         379  
7 9     9   29 use Mojo::IOLoop;
  9         8  
  9         66  
8              
9             has chunk_size => 261120;
10             has [qw(content_type filename gridfs metadata)];
11              
12             sub close {
13 0     0 1   my ($self, $cb) = @_;
14              
15             # Already closed
16 0 0         if ($self->{closed}++) {
17 0           my $files_id = $self->_files_id;
18 0 0         return $files_id unless $cb;
19 0     0     return Mojo::IOLoop->next_tick(sub { $self->$cb(undef, $files_id) });
  0            
20             }
21              
22 0           my @index = (bson_doc(files_id => 1, n => 1), {unique => \1});
23 0           my $gridfs = $self->gridfs;
24 0           my $command = bson_doc filemd5 => $self->_files_id, root => $gridfs->prefix;
25              
26             # Non-blocking
27 0           my $chunks = $gridfs->chunks;
28 0           my $bulk = $chunks->bulk;
29 0           my $files = $gridfs->files;
30             return Mojo::IOLoop->delay(
31 0     0     sub { $self->_chunk($bulk)->execute(shift->begin) },
32             sub {
33 0     0     my ($delay, $err) = @_;
34 0 0         return $delay->pass($err) if $err;
35 0           $files->ensure_index({filename => 1} => $delay->begin);
36 0           $chunks->ensure_index(@index => $delay->begin);
37             },
38             sub {
39 0     0     my ($delay, $files_err, $chunks_err) = @_;
40 0 0 0       if (my $err = $files_err || $chunks_err) { return $delay->pass($err) }
  0            
41 0           $gridfs->db->command($command => $delay->begin);
42             },
43             sub {
44 0     0     my ($delay, $err, $doc) = @_;
45 0 0         return $delay->pass($err) if $err;
46 0           $files->insert($self->_meta($doc->{md5}) => $delay->begin);
47             },
48 0     0     sub { shift; $self->$cb(shift, $self->_files_id) }
  0            
49 0 0         ) if $cb;
50              
51             # Blocking
52 0           $self->_chunk($bulk)->execute;
53 0           $files->ensure_index({filename => 1});
54 0           $chunks->ensure_index(@index);
55 0           my $md5 = $gridfs->db->command($command)->{md5};
56 0           $files->insert($self->_meta($md5));
57 0           return $self->_files_id;
58             }
59              
60 0     0 1   sub is_closed { !!shift->{closed} }
61              
62             sub write {
63 0     0 1   my ($self, $chunk, $cb) = @_;
64              
65             # Already closed
66 0 0         if ($self->is_closed) {
67 0 0         croak 'File already closed' unless $cb;
68 0     0     return Mojo::IOLoop->next_tick(sub { $self->$cb('File already closed') });
  0            
69             }
70              
71 0           $self->{buffer} .= $chunk;
72 0           $self->{len} += length $chunk;
73              
74 0           my $bulk = $self->gridfs->chunks->bulk->ordered(0);
75 0           my $size = $self->chunk_size;
76 0           $self->_chunk($bulk) while length $self->{buffer} >= $size;
77              
78             # Non-blocking
79 0 0   0     return $bulk->execute(sub { shift; $self->$cb(shift) }) if $cb;
  0            
  0            
80              
81             # Blocking
82 0           $bulk->execute;
83 0           return $self;
84             }
85              
86             sub _chunk {
87 0     0     my ($self, $bulk) = @_;
88              
89 0           my $chunk = substr $self->{buffer}, 0, $self->chunk_size, '';
90 0 0         return $bulk unless length $chunk;
91              
92 0           my $n = $self->{n}++;
93 0           return $bulk->insert(
94             {files_id => $self->_files_id, n => $n, data => bson_bin($chunk)});
95             }
96              
97 0   0 0     sub _files_id { shift->{files_id} //= bson_oid }
98              
99             sub _meta {
100 0     0     my ($self, $md5) = @_;
101              
102             my $doc = {
103             _id => $self->_files_id,
104             length => $self->{len},
105 0           chunkSize => $self->chunk_size,
106             uploadDate => bson_time,
107             md5 => $md5
108             };
109 0 0         if (my $name = $self->filename) { $doc->{filename} = $name }
  0            
110 0 0         if (my $type = $self->content_type) { $doc->{contentType} = $type }
  0            
111 0 0         if (my $data = $self->metadata) { $doc->{metadata} = $data }
  0            
112              
113 0           return $doc;
114             }
115              
116             1;
117              
118             =encoding utf8
119              
120             =head1 NAME
121              
122             Mango::GridFS::Writer - GridFS writer
123              
124             =head1 SYNOPSIS
125              
126             use Mango::GridFS::Writer;
127              
128             my $writer = Mango::GridFS::Writer->new(gridfs => $gridfs);
129              
130             =head1 DESCRIPTION
131              
132             L writes files to GridFS.
133              
134             =head1 ATTRIBUTES
135              
136             L implements the following attributes.
137              
138             =head2 chunk_size
139              
140             my $size = $writer->chunk_size;
141             $writer = $writer->chunk_size(1024);
142              
143             Chunk size in bytes, defaults to C<261120> (255KB).
144              
145             =head2 content_type
146              
147             my $type = $writer->content_type;
148             $writer = $writer->content_type('text/plain');
149              
150             Content type of file.
151              
152             =head2 filename
153              
154             my $name = $writer->filename;
155             $writer = $writer->filename('foo.txt');
156              
157             Name of file.
158              
159             =head2 gridfs
160              
161             my $gridfs = $writer->gridfs;
162             $writer = $writer->gridfs(Mango::GridFS->new);
163              
164             L object this writer belongs to.
165              
166             =head2 metadata
167              
168             my $data = $writer->metadata;
169             $writer = $writer->metadata({foo => 'bar'});
170              
171             Additional information.
172              
173             =head1 METHODS
174              
175             L inherits all methods from L and
176             implements the following new ones.
177              
178             =head2 close
179              
180             my $oid = $writer->close;
181              
182             Close file. You can also append a callback to perform operation non-blocking.
183              
184             $writer->close(sub {
185             my ($writer, $err, $oid) = @_;
186             ...
187             });
188             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
189              
190             =head2 is_closed
191              
192             my $success = $writer->is_closed;
193              
194             Check if file has been closed.
195              
196             =head2 write
197              
198             $writer = $writer->write('hello world!');
199              
200             Write chunk. You can also append a callback to perform operation non-blocking.
201              
202             $writer->write('hello world!' => sub {
203             my ($writer, $err) = @_;
204             ...
205             });
206             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
207              
208             =head1 SEE ALSO
209              
210             L, L, L.
211              
212             =cut