File Coverage

blib/lib/Mandel/Collection.pm
Criterion Covered Total %
statement 38 107 35.5
branch 8 34 23.5
condition 2 15 13.3
subroutine 9 25 36.0
pod 10 10 100.0
total 67 191 35.0


line stmt bran cond sub pod time code
1             package Mandel::Collection;
2              
3             =head1 NAME
4              
5             Mandel::Collection - A collection of Mandel documents
6              
7             =head1 SYNOPSIS
8              
9             my $connection = MyModel->connect("mongodb://localhost/my_db");
10             my $persons = $connection->collection("person");
11              
12             $persons->count(sub {
13             my($persons, $err, $int) = @_;
14             });
15              
16             # ...
17              
18             =head1 DESCRIPTION
19              
20             This class is used to describe a group of mongodb documents.
21              
22             =cut
23              
24 19     19   12592 use Mojo::Base -base;
  19         34  
  19         177  
25 19     19   12468 use Mandel::Iterator;
  19         210  
  19         161  
26 19     19   15471 use Mango::BSON ':bson';
  19         839396  
  19         4683  
27 19     19   172 use Scalar::Util 'blessed';
  19         40  
  19         819  
28 19     19   100 use Carp 'confess';
  19         39  
  19         1517  
29 19 50   19   138 use constant DEBUG => $ENV{MANDEL_CURSOR_DEBUG} ? eval 'require Data::Dumper;1' : 0;
  19         42  
  19         35272  
30              
31             =head1 ATTRIBUTES
32              
33             =head2 connection
34              
35             An object that inherit from L.
36              
37             =head2 model
38              
39             An object that inherit from L.
40              
41             =cut
42              
43             has connection => sub { confess "connection required in constructor" };
44             has model => sub { confess "model required in constructor" };
45              
46             has _storage_collection => sub {
47             my $self = shift;
48             $self->connection->_storage_collection($self->model->collection_name);
49             };
50              
51             =head1 METHODS
52              
53             =head2 all
54              
55             $self = $self->all(sub { my($self, $err, $docs) = @_; });
56             $docs = $self->all;
57              
58             Retrieves all documents from the database that match the given L
59             query.
60              
61             =cut
62              
63             sub all {
64 0     0 1 0 my ($self, $cb) = @_;
65              
66              
67 0         0 my $c = $self->_new_cursor;
68 0 0       0 return [map { $self->_new_document($_, 1) } @{$c->all}] unless $cb;
  0         0  
  0         0  
69              
70             $c->all(
71             sub {
72 0     0   0 my ($cursor, $err, $docs) = @_;
73 0         0 return $self->$cb($err, [map { $self->_new_document($_, 1) } @$docs]);
  0         0  
74             }
75 0         0 );
76 0         0 return $self;
77             }
78              
79             =head2 create
80              
81             $document = $self->create;
82             $document = $self->create(\%args);
83              
84             Returns a new object of a given type. This object is NOT inserted into the
85             mongodb collection. You need to call L for that to
86             happen.
87              
88             C<%args> is used to set the fields in the new document, NOT the attributes.
89              
90             =cut
91              
92             sub create {
93 5 50   5 1 2539 my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
94 5         9 my $self = shift;
95              
96 5   100     28 $self->_new_document(shift || undef, 0);
97             }
98              
99             =head2 count
100              
101             $self = $self->count(sub { my($self, $err, $int) = @_; });
102             $int = $self->count;
103              
104             Used to count how many documents the current L query match.
105              
106             =cut
107              
108             sub count {
109 0     0 1 0 my ($self, $cb) = @_;
110              
111 0         0 my $c = $self->_new_cursor;
112 0 0       0 return $c->count unless $cb;
113              
114 0     0   0 $c->count(sub { shift; $self->$cb(@_) });
  0         0  
  0         0  
115 0         0 return $self;
116             }
117              
118             =head2 distinct
119              
120             $self = $self->distinct("field_name", sub { my($self, $err, $values) = @_; });
121             $values = $self->distinct("field_name");
122              
123             Get all distinct values for key in this collection.
124              
125             =cut
126              
127             sub distinct {
128 0     0 1 0 my ($self, $field, $cb) = @_;
129              
130 0         0 my $c = $self->_new_cursor;
131 0 0       0 return $c->distinct($field) unless $cb;
132              
133 0     0   0 $c->distinct(sub { shift; $self->$cb(@_) });
  0         0  
  0         0  
134 0         0 return $self;
135             }
136              
137             =head2 iterator
138              
139             $iterator = $self->iterator;
140              
141             Returns a L object based on the L performed.
142              
143             =cut
144              
145             sub iterator {
146 0     0 1 0 my $self = shift;
147              
148 0         0 Mandel::Iterator->new(cursor => $self->_new_cursor, model => $self->model,);
149             }
150              
151             =head2 patch
152              
153             $self = $self->patch(\%changes, sub { my($self, $err, $doc) = @_ });
154             $self = $self->patch(\%changes);
155              
156             This method can be used to add C<%changes> to multiple documents
157             in the collection. Which documents to update will be decided by the
158             C<%query> given to L.
159              
160             C<%extra> arguments default to:
161              
162             =over 4
163              
164             =item * upsert: false
165              
166             =item * multi: true
167              
168             =back
169              
170             =cut
171              
172             sub patch {
173 0     0 1 0 my ($self, $changes, $cb) = @_;
174 0         0 my $extra = $self->{extra};
175              
176             warn '[Mandel::Collection::patch] ',
177 0         0 Data::Dumper->new([$changes, $self->{query}, $extra])->Indent(1)->Sortkeys(1)->Terse(1)->Dump
178             if DEBUG;
179             $self->_storage_collection->update(
180             $self->{query} || {},
181             {'$set' => $changes},
182             {upsert => $extra->{upsert} // bson_false, multi => $extra->{multi} // bson_true,},
183 0 0 0 0   0 $cb ? (sub { $self->$cb($_[1]) }) : (),
  0   0     0  
      0        
184             );
185              
186 0         0 $self;
187             }
188              
189             =head2 remove
190              
191             $self = $self->remove(sub { my($self, $err) = @_; });
192             $self = $self->remove;
193              
194             Remove the documents that query given to L.
195              
196             =cut
197              
198             sub remove {
199 0 0   0 1 0 my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
200 0         0 my $self = shift;
201 0         0 my $c = $self->_storage_collection;
202 0         0 my @args = $self->{query};
203              
204 0         0 warn '[Mandel::Collection::remove] ', Data::Dumper->new([$self->{query}])->Indent(1)->Sortkeys(1)->Terse(1)->Dump
205             if DEBUG;
206 0     0   0 push @args, sub { $self->$cb($_[1]) }
207 0 0       0 if $cb;
208              
209 0         0 $c->remove(@args);
210 0         0 $self;
211             }
212              
213             =head2 save
214              
215             $self = $self->save(\%document, sub { my($self, $err, $doc) = @_; );
216             $doc = $self->save(\%document);
217              
218             Used to save a document. The callback receives a L.
219              
220             =cut
221              
222             sub save {
223 0     0 1 0 my ($self, $raw, $cb) = @_;
224 0         0 my $c = $self->_storage_collection;
225              
226 0   0     0 $raw->{_id} ||= bson_oid;
227              
228 0         0 warn '[Mandel::Collection::save] ', Data::Dumper->new([$raw])->Indent(1)->Sortkeys(1)->Terse(1)->Dump if DEBUG;
229              
230 0 0       0 unless ($cb) {
231 0         0 $c->save($raw);
232 0         0 return $self->_new_document($raw, 1);
233             }
234              
235             $c->save(
236             $raw,
237             sub {
238 0     0   0 my ($collection, $err, $doc) = @_;
239 0         0 $self->$cb($err, $self->_new_document($raw, 1));
240             }
241 0         0 );
242              
243 0         0 return $self;
244             }
245              
246             =head2 search
247              
248             $clone = $self->search(\%query, \%extra);
249              
250             Return a clone of the given collection, but with different C<%search> and
251             C<%extra> parameters. You can chain these calls to make the query more
252             precise.
253              
254             C<%extra> will be used to set extra parameters on the L, where
255             all the keys need to match the L.
256              
257             =cut
258              
259             sub search {
260 1     1 1 3 my ($self, $query, $extra) = @_;
261 1         5 my $class = blessed $self;
262 1         5 my $clone = $class->new(%$self);
263              
264 1 50       10 @{$clone->{extra}}{keys %$extra} = values %$extra if $extra;
  0         0  
265 1 50       5 @{$clone->{query}}{keys %$query} = values %$query if $query;
  1         4  
266 1         4 $clone;
267             }
268              
269             =head2 single
270              
271             $self = $self->single(sub { my($self, $err, $doc) = @_; });
272             $doc = $self->single;
273              
274             Will return the first object found in the callback, matching the given
275             C<%search> query.
276              
277             =cut
278              
279             sub single {
280 0     0 1 0 my ($self, $cb) = @_;
281              
282 0         0 my $c = $self->_new_cursor->limit(-1);
283 0 0       0 unless ($cb) {
284 0 0       0 my $doc = $c->next or return;
285 0         0 return $self->_new_document($doc, 1);
286             }
287              
288             $c->next(
289             sub {
290 0     0   0 my ($cursor, $err, $doc) = @_;
291 0 0       0 $self->$cb($err, $doc ? $self->_new_document($doc, 1) : undef);
292             }
293 0         0 );
294 0         0 return $self;
295             }
296              
297             sub _new_cursor {
298 0     0   0 my $self = shift;
299 0   0     0 my $extra = $self->{extra} || {};
300 0         0 my $cursor = $self->_storage_collection->find;
301              
302 0 0       0 $cursor->query($self->{query}) if $self->{query};
303 0         0 $cursor->$_($extra->{$_}) for keys %$extra;
304              
305 0         0 if (DEBUG) {
306             local $cursor->{collection}{db} = $cursor->{collection}{db}{name}; # hide big data structure
307             warn '[', +(caller 1)[3], '] ', Data::Dumper->new([$cursor])->Indent(1)->Sortkeys(1)->Terse(1)->Dump;
308             }
309              
310 0         0 $cursor;
311             }
312              
313             sub _new_document {
314 5     5   10 my ($self, $doc, $from_storage) = @_;
315 5         14 my $model = $self->model;
316 4         29 my @extra;
317              
318 4 100       14 if ($doc) {
319 3         7 push @extra, data => $doc;
320 3         9 push @extra, dirty => {map { $_, 1 } keys %$doc};
  3         12  
321             }
322 4 100       16 if (my $connection = $self->{connection}) {
323 2         4 push @extra,
324             connection => $connection,
325             ;
326             }
327              
328 4         14 $model->document_class->new(model => $model, in_storage => $from_storage, @extra,);
329             }
330              
331             =head1 SEE ALSO
332              
333             L, L, L
334              
335             =head1 AUTHOR
336              
337             Jan Henning Thorsen - C
338              
339             =cut
340              
341             1;