File Coverage

blib/lib/App/Toodledo/Task.pm
Criterion Covered Total %
statement 45 45 100.0
branch n/a
condition n/a
subroutine 29 29 100.0
pod n/a
total 74 74 100.0


line stmt bran cond sub pod time code
1             package App::Toodledo::Task;
2 10     10   100958 use strict;
  10         20  
  10         290  
3 10     10   53 use warnings;
  10         21  
  10         462  
4              
5             our $VERSION = '1.02';
6              
7 10     10   159 BEGIN { $PPI::XS_DISABLE = 1 } # PPI::XS throws deprecation warnings in 5.16
8              
9 10     10   47 use Carp;
  10         21  
  10         592  
10 10     10   640 use Moose;
  10         710490  
  10         59  
11 10     10   59982 use MooseX::Method::Signatures;
  10         2225446  
  10         88  
12 10     10   5034 use App::Toodledo::TaskInternal;
  10         29  
  10         453  
13 10     10   725 use App::Toodledo::Util qw(toodledo_decode toodledo_encode);
  10         22  
  10         621  
14              
15 10     10   63 use Moose::Util::TypeConstraints;
  10         21  
  10         93  
16             with 'MooseX::Log::Log4perl';
17 10     10   21314 BEGIN { class_type 'App::Toodledo' };
18              
19             extends 'App::Toodledo::InternalWrapper';
20              
21             my %ENUM_STRING = ( status => {
22             0 => 'None',
23             1 => 'Next Action',
24             2 => 'Active',
25             3 => 'Planning',
26             4 => 'Delegated',
27             5 => 'Waiting',
28             6 => 'Hold',
29             7 => 'Postponed',
30             8 => 'Someday',
31             9 => 'Canceled',
32             10 => 'Reference',
33             },
34             priority => {
35             -1 => 'Negative',
36             0 => 'Low',
37             1 => 'Medium',
38             2 => 'High',
39             3 => 'Top',
40             }
41             );
42             my %ENUM_INDEX = (
43             status => { reverse %{ $ENUM_STRING{status} } },
44             priority => { reverse %{ $ENUM_STRING{priority} } },
45             );
46              
47             # TODO: Figure out how to put this attribute in the wrapper:
48             has object => ( is => 'ro', isa => 'App::Toodledo::TaskInternal',
49             default => sub { App::Toodledo::TaskInternal->new },
50             handles => sub { __PACKAGE__->internal_attributes( $_[1] ) } );
51              
52 10     10   833680 method tag ( @args ) {
53             toodledo_decode( $self->object->tag( @args ) );
54             }
55              
56 10     10   459855 method title ( @args ) {
57             toodledo_decode( $self->object->title( @args ) );
58             }
59              
60 10     10   444830 method note ( @args ) {
61             toodledo_decode( $self->object->note( @args ) );
62             }
63              
64              
65 10     10   471577 method status_str ( Item $new_status? ) {
66             $self->set_enum( status => $new_status );
67             }
68              
69 10     10   465773 method priority_str ( Item $new_priority? ) {
70             $self->set_enum( priority => $new_priority );
71             }
72              
73 10     10   686405 method set_enum ( Str $type!, Item $new_value? ) {
74             my @args;
75             if ( $new_value )
76             {
77             defined( my $index = $ENUM_INDEX{$type}{$new_value} )
78             or $self->log->logdie("$type $new_value not valid");
79             push @args, $index;
80             }
81             my $index = $self->object->$type( @args );
82             my $string = $ENUM_STRING{$type}{$index}
83             or $self->log->logdie("Toodledo returned invalid $type index $index");
84             $string;
85             }
86              
87              
88             # XXX Factor out duplication in next 4 methods
89 10     10   685770 method folder_name ( App::Toodledo $todo!, Item $new_folder? ) {
90             $self->set_name( $todo, folder => $new_folder );
91             }
92              
93 10     10   677296 method context_name ( App::Toodledo $todo!, Item $new_context? ) {
94             $self->set_name( $todo, context => $new_context );
95             }
96              
97 10     10   699246 method goal_name ( App::Toodledo $todo!, Item $new_goal? ) {
98             $self->set_name( $todo, goal => $new_goal );
99             }
100              
101 10     10   709301 method location_name ( App::Toodledo $todo!, Item $new_location? ) {
102             $self->set_name( $todo, location => $new_location );
103             }
104              
105              
106             our $can_use_cache; # See App::Toodledo::foreach()
107             my %cache;
108              
109 10     10   923218 method set_name( App::Toodledo $todo!, Str $type!, Item $new_string? ) {
110             my @args;
111             my $class = "App::Toodledo::\u$type";
112             eval "require $class";
113             my @objs;
114             if ( $can_use_cache )
115             {
116             @objs = @{ $cache{$type} };
117             $self->log->debug( "Using cached ${type}s\n" );
118             }
119             else
120             {
121             $self->log->debug( "Fetching ${type}s\n" );
122             @objs = $todo->get( $type.'s' );
123             $cache{$type} = \@objs;
124             $can_use_cache = 0;
125             }
126             if ( defined $new_string ) # Find the new object in list of available
127             {
128             my $id;
129             if ( $new_string eq '' )
130             {
131             $id = 0;
132             }
133             else
134             {
135             my ($obj) = grep { $_->name eq $new_string } @objs
136             or $self->log->logdie("Could not find a $type with name '$new_string'");
137             $id = $obj->id;
138             }
139             $self->object->$type( $id );
140             return $new_string;
141             }
142              
143             my $id = $self->$type or return '';
144             my ($obj) = grep { $_->id == $id } @objs
145             or $self->log->logdie( "Could not find existing $type $id in global list!");
146             $obj->name;
147             }
148              
149              
150 10     10   482887 method tags ( Str @new_tags ) {
151             if ( @new_tags )
152             {
153             $self->tag( join ', ', @new_tags );
154             return @new_tags;
155             }
156             split /,/, $self->tag;
157             }
158              
159              
160 10     10   433054 method has_tag ( Str $tag! ) {
161             grep { $_ eq $tag } $self->tags;
162             }
163              
164              
165 10     10   427640 method add_tag ( Str $tag! ) {
166             my $new_tag = $self->tag ? $self->tag . ", $tag" : $tag;
167             $self->tag( $new_tag ) unless $self->has_tag( $tag );
168             }
169              
170              
171 10     10   429686 method remove_tag ( Str $tag! ) {
172             return unless $self->has_tag( $tag );
173             my @new_tags = grep { $_ ne $tag } $self->tags;
174             $self->tags( @new_tags );
175             }
176              
177              
178             # Return id of added task
179 10     10   428003 method add ( App::Toodledo $todo! ) {
180             my %param = %{ $self->object };
181             $param{$_} = toodledo_encode( $param{$_} )
182             for grep { $param{$_} } qw(title tag note);
183             my $added_ref = $todo->call_func( tasks => add => { tasks => \%param } );
184             $added_ref->[0]{id};
185             }
186              
187              
188 10     10   407162 method optional_attributes ( $class: ) {
189             my @attrs = $class->attribute_list;
190             grep { ! /\A(?:id|title|modified|completed)\z/ } @attrs;
191             }
192              
193              
194 10     10   682801 method edit ( App::Toodledo $todo!, App::Toodledo::Task @more ) {
195             if ( @more )
196             {
197             my @edited = map { +{ %{ $_->object } } } ( $self, @more );
198             my $edited_ref = $todo->call_func( tasks => edit => { tasks => \@edited } );
199             return map { $_->{id} } @$edited_ref;
200             }
201             else
202             {
203             my %param = %{ $self->object };
204             my $edited_ref = $todo->call_func( tasks => edit => { tasks => \%param } );
205             return $edited_ref->[0]{id};
206             }
207             }
208              
209              
210 10     10   436289 method delete ( App::Toodledo $todo! ) {
211             my $id = $self->id;
212             my $deleted_ref = $todo->call_func( tasks => delete => { tasks => [$id] } );
213             $deleted_ref->[0]{id} == $id or $self->log->logdie("Did not get ID back from delete");
214             }
215              
216              
217             1;
218              
219             __END__
220              
221             =head1 NAME
222              
223             App::Toodledo::Task - class encapsulating a Toodledo task
224              
225             =head1 SYNOPSIS
226              
227             $task = App::Toodledo::Task->new;
228             $task->title( 'Put the cat out' );
229              
230             =head1 DESCRIPTION
231              
232             This class provides accessors for the properties of a Toodledo task.
233             The attributes of a task are defined in the L<App::Toodledo::TaskRole>
234             module.
235              
236             =head1 METHODS
237              
238             =head2 @tags = $task->tags( [@tags] )
239              
240             Return the tags of the task as a list (splits the attribute on comma).
241             If a list is provided, set the tags to that list.
242              
243             =head2 $task->has_tag( $tag )
244              
245             Return true if the tag C<$tag> is in the list returned by C<tags()>.
246              
247             =head2 $task->add_tag( $tag )
248              
249             Add the given tag. No-op if the task already has that tag.
250              
251             =head2 $task->remove_tag( $tag )
252              
253             Remove the given tag. No-op if the task doesn't have that tag.
254              
255             =head2 $task->edit( @tasks )
256              
257             This is the method called by:
258              
259             App::Toodledo::edit( $task )
260              
261             You can pass multiple tasks to it:
262              
263             $todo->edit( @tasks )
264              
265             and they will all be updated.
266             The current maximum number of tasks you can send to Toodledo for
267             editing is 50. B<This method does not check for that.> (They
268             might raise the limit in the future.) Bounds checking is the caller's
269             responsibility.
270              
271             =head2 $task->status_str, $task->priority_str
272              
273             Each of these methods operates on the string defined at
274             http://api.toodledo.com/2/tasks/index.php, not the integer.
275             The string will be turned into the integer going into Toodledo
276             and the integer will get turned into the string coming out.
277             Examples:
278              
279             $task->priority_str( 'Top' )
280             $task->status_str eq 'Hold' and ...
281              
282             Each method can be used in a App::Toodledo::select call.
283              
284             =head2 $task->folder_name, $task->context_name, $task->location_name, $task->goal_name
285              
286             Each of these methods returns and optionally sets the given attribute via
287             its name rather than the indirect ID that is stored in the task. An
288             exception is thrown if no object with that name exists when setting it.
289             Examples:
290              
291             $task->folder_name( $todo, 'Later' );
292             $task->context_name( $todo ) eq 'Home' and ...
293              
294             If the value is null, returns the empty string rather than the Toodledo
295             display value of "No <Whatever>".
296              
297             NOTE: An App::Toodledo object must be passed as the first parameter
298             so it can look up the mapping of objects to names.
299              
300             =head1 CAVEAT
301              
302             This is a very basic implementation of Toodledo tasks. It is missing
303             much that would be helpful with dealing with repeating tasks. Patches
304             welcome.
305              
306             =head1 AUTHOR
307              
308             Peter J. Scott, C<< <cpan at psdt.com> >>
309              
310             =head1 SEE ALSO
311              
312             Toodledo: L<http://www.toodledo.com/>.
313              
314             Toodledo API documentation: L<http://www.toodledo.com/info/api_doc.php>.
315              
316             =head1 COPYRIGHT & LICENSE
317              
318             Copyright 2009-2011 Peter J. Scott, all rights reserved.
319              
320             This program is free software; you can redistribute it and/or modify it
321             under the same terms as Perl itself.
322              
323             =cut