File Coverage

blib/lib/App/Jiffy/TimeEntry.pm
Criterion Covered Total %
statement 18 67 26.8
branch 0 20 0.0
condition n/a
subroutine 6 13 46.1
pod 7 7 100.0
total 31 107 28.9


line stmt bran cond sub pod time code
1             package App::Jiffy::TimeEntry;
2              
3 2     2   1404725 use strict;
  2         11  
  2         55  
4 2     2   9 use warnings;
  2         5  
  2         54  
5              
6 2     2   10 use Scalar::Util qw( blessed );
  2         4  
  2         98  
7 2     2   528 use MongoDB;
  2         1538661  
  2         55  
8 2     2   836 use MongoDB::OID;
  2         6063  
  2         54  
9              
10 2     2   13 use Moo;
  2         4  
  2         7  
11              
12             my $collection = 'timeEntry';
13              
14             has id => (
15             is => 'rw',
16             isa => sub {
17             die 'id must be a MongoDB::OID'
18             unless blessed $_[0] and $_[0]->isa('MongoDB::OID');
19             },
20             );
21              
22             has start_time => (
23             is => 'rw',
24             isa => sub {
25             die 'start_time is not a DateTime object'
26             unless blessed $_[0] and $_[0]->isa('DateTime');
27             },
28             default => sub {
29             DateTime->now();
30             },
31             );
32              
33             has title => (
34             is => 'rw',
35             required => 1,
36             );
37              
38             has _duration => ( is => 'rw', );
39              
40             has cfg => ( is => 'ro', );
41              
42             =head2 db
43              
44             C<db> get the db handler from MongoDB
45              
46             =cut
47              
48             sub db {
49 0     0 1   my $self = shift;
50 0           my $client = MongoDB::MongoClient->new;
51 0 0         return $client->get_database( ( $self->cfg ) ? $self->cfg->{db} : 'jiffy' );
52             }
53              
54             =head2 save
55              
56             C<save> will commit the current state of the C<TimeEntry> to the database.
57              
58             =cut
59              
60             sub save {
61 0     0 1   my $self = shift;
62 0           my $_id = $self->id;
63 0           my $document = {
64             start_time => $self->start_time,
65             title => $self->title,
66             };
67 0 0         if ($_id) {
68              
69             # Update the existing record
70 0           $self->db->get_collection($collection)
71             ->update_one( { _id => $self->id }, { '$set' => $document } );
72             } else {
73              
74             # Insert new record
75 0           my $result = $self->db->get_collection($collection)->insert_one($document);
76              
77             # Update id
78 0           $self->id( $result->inserted_id );
79             }
80             }
81              
82             =head2 duration
83              
84             C<duration> returns the time between this entry and the next.
85              
86             =cut
87              
88             sub duration {
89 0     0 1   my $self = shift;
90 0           my $set = shift;
91              
92 0 0         if ($set) {
93 0           $self->_duration($set);
94             }
95              
96 0 0         if ( $self->_duration ) {
97 0           return $self->_duration;
98             }
99              
100 0           my @entry_after = App::Jiffy::TimeEntry::search(
101             $self->cfg,
102             query => {
103             start_time => {
104             '$gt' => $self->start_time,
105             },
106             },
107             sort => {
108             start_time => 1,
109             },
110             limit => 1,
111             );
112              
113 0 0         if (@entry_after) {
114 0           return $entry_after[0]->start_time->subtract_datetime( $self->start_time );
115             } else {
116 0           return DateTime->now->subtract_datetime( $self->start_time );
117             }
118             }
119              
120             =head2 find
121              
122             C<find> will return a single document. The query will use the provided C<_id>.
123              
124             =cut
125              
126             sub find {
127 0     0 1   my $cfg = shift;
128 0           my $_id = shift;
129              
130 0           my $client = MongoDB::MongoClient->new;
131 0           my $entry = $client->get_database( $cfg->{db} )->get_collection($collection)
132             ->find_one( { _id => $_id } );
133 0 0         return unless $entry;
134             return App::Jiffy::TimeEntry->new(
135             id => $entry->{_id},
136             title => $entry->{title},
137             start_time => $entry->{start_time},
138 0           cfg => $cfg,
139             );
140             }
141              
142             =head2 search C<$config> C<%options>...
143              
144             C<search> will return an array of TimeEntry that fit the given C<%options>.
145              
146             C<%options> can include the following:
147              
148             =over
149              
150             =item C<query>
151              
152             This is the query syntax available via L<MongoDB::Collection>.
153              
154             =item C<sort>
155              
156             If present, the results will be sorted via the hashref given by this option.
157              
158             =item C<limit>
159              
160             If present, the results will limited to the number provided.
161              
162             =back
163              
164             =cut
165              
166             sub search {
167 0     0 1   my $cfg = shift;
168 0           my %options = @_;
169 0           my $query = $options{query};
170 0           my $sort = $options{sort};
171 0           my $limit = $options{limit};
172              
173 0           my $client = MongoDB::MongoClient->new;
174 0           my $entries = $client->get_database( $cfg->{db} )->get_collection($collection)
175             ->find($query);
176              
177 0 0         if ($sort) {
178 0           $entries = $entries->sort($sort);
179             }
180              
181 0 0         if ($limit) {
182 0           $entries = $entries->limit($limit);
183             }
184              
185             # Return undef if nothing was found
186 0 0         return unless $entries;
187              
188 0           my $LocalTZ = DateTime::TimeZone->new( name => 'local' ); # For caching
189              
190             # @TODO create a subclass of the MongoDB cursor that allows chaining of results like MongoDB
191             map {
192 0           $_->{start_time}->set_time_zone($LocalTZ); # Convert to local tz
  0            
193             App::Jiffy::TimeEntry->new(
194             id => $_->{_id},
195             title => $_->{title},
196             start_time => $_->{start_time},
197 0           cfg => $cfg,
198             )
199             } $entries->all;
200             }
201              
202             =head2 last_entry
203              
204             C<last_entry> will return the last TimeEntry in the db.
205              
206             =cut
207              
208             sub last_entry {
209 0     0 1   my $cfg = shift;
210              
211 0           my $client = MongoDB::MongoClient->new;
212 0           my $entry = $client->get_database( $cfg->{db} )->get_collection($collection)
213             ->find->sort( { start_time => -1 } )->limit(1)->next;
214 0 0         return unless $entry;
215             return App::Jiffy::TimeEntry->new(
216             id => $entry->{_id},
217             title => $entry->{title},
218             start_time => $entry->{start_time},
219 0           cfg => $cfg,
220             );
221             }
222              
223             =head2 TO_JSON
224              
225             C<TO_JSON> will return the entry as a JSON convertible hash.
226              
227             =cut
228              
229             sub TO_JSON {
230 0     0 1   my $self = shift;
231              
232             {
233 0           start_time => $self->start_time->iso8601,
234             title => $self->title,
235             duration => { $self->duration->deltas },
236             };
237             }
238              
239             1;