File Coverage

blib/lib/Text/Todo/Entry.pm
Criterion Covered Total %
statement 128 133 96.2
branch 29 42 69.0
condition 9 12 75.0
subroutine 25 25 100.0
pod 12 12 100.0
total 203 224 90.6


line stmt bran cond sub pod time code
1             package Text::Todo::Entry;
2              
3             # $AFresh1: Entry.pm,v 1.30 2010/02/16 01:13:12 andrew Exp $
4              
5 5     5   49688 use warnings;
  5         12  
  5         167  
6 5     5   31 use strict;
  5         13  
  5         168  
7 5     5   27 use Carp;
  5         11  
  5         392  
8              
9 5     5   2147 use Class::Std::Utils;
  5         7917  
  5         36  
10              
11 5     5   293 use version; our $VERSION = qv('0.2.0');
  5         10  
  5         49  
12              
13             {
14              
15             my @attr_refs = \(
16             my %text_of,
17              
18             my %tags_of,
19             my %priority_of,
20             my %completion_status_of,
21             my %known_tags_of,
22             );
23              
24             # XXX Should the completion (x) be case sensitive?
25             my $priority_completion_regex = qr{
26             ^ \s*
27             (?i:(x \s* [\d-]* ) \s*)?
28             (?i:\( ([A-Z]) \) \s*)?
29             }xms;
30              
31             sub new {
32 2     2 1 818 my ( $class, $options ) = @_;
33              
34 2         13 my $self = bless anon_scalar(), $class;
35 2         20 my $ident = ident($self);
36              
37 2         9 $text_of{$ident} = q{};
38              
39 2 100       15 if ( !ref $options ) {
    50          
40 1         3 $options = { text => $options };
41             }
42             elsif ( ref $options ne 'HASH' ) {
43 0         0 croak 'Invalid parameter passed!';
44             }
45              
46 2         9 my %tags = (
47             context => q{@},
48             project => q{+},
49             );
50              
51 2 100 66     17 if ( exists $options->{tags} && ref $options->{tags} eq 'HASH' ) {
52 1         4 %tags = ( %tags, %{ $options->{tags} } );
  1         6  
53             }
54              
55 2         10 for my $tag ( keys %tags ) {
56 5         21 $self->learn_tag( $tag, $tags{$tag} );
57             }
58              
59 2         11 $self->replace( $options->{text} );
60              
61 2         9 return $self;
62             }
63              
64             sub _parse_entry {
65 17     17   30 my ($self) = @_;
66 17         44 my $ident = ident($self);
67              
68 17         191 delete $tags_of{$ident};
69 17         43 delete $completion_status_of{$ident};
70 17         38 delete $priority_of{$ident};
71              
72 17   100     48 my $text = $self->text || q{};
73 17   50     67 my $known_tags = $self->known_tags || {};
74              
75 17         28 foreach my $tag ( keys %{$known_tags} ) {
  17         53  
76 36 50       7308 next if !defined $known_tags->{$tag};
77 36 50       88 next if !length $known_tags->{$tag};
78              
79 36         78 my $sigal = quotemeta $known_tags->{$tag};
80 50         264 $tags_of{$ident}{$tag}
81 36         1603 = { map { $_ => q{} } $text =~ / (?:^|\s) $sigal (\S*)/gxms };
82             }
83              
84 17         317 my ( $completed, $priority )
85             = $text =~ / $priority_completion_regex /xms;
86              
87 17         45 $completion_status_of{$ident} = _clean_completed($completed);
88 17         31 $priority_of{$ident} = $priority;
89              
90 17         92 return 1;
91             }
92              
93             sub _clean_completed {
94 17     17   26 my ($completed) = @_;
95              
96 17   100     76 $completed ||= q{};
97 17         32 $completed =~ s/^\s+|\s+$//gxms;
98              
99 17 100       73 if ( !$completed ) {
100 16         45 return;
101             }
102              
103 1 50       7 if ( $completed =~ s/(x)\s*//ixms ) {
104 1         3 my $status = $1;
105 1 50       4 if ($completed) {
106 1         4 return $completed;
107             }
108             else {
109 0         0 return $status;
110             }
111             }
112              
113 0         0 return;
114             }
115              
116             sub replace {
117 11     11 1 25 my ( $self, $text ) = @_;
118 11         32 my $ident = ident($self);
119              
120 11 100       37 $text = defined $text ? $text : q{};
121              
122 11         26 $text_of{$ident} = $text;
123              
124 11         29 return $self->_parse_entry;
125             }
126              
127             sub learn_tag {
128 6     6 1 14 my ( $self, $tag, $sigal ) = @_;
129 6         27 $known_tags_of{ ident $self}{$tag} = $sigal;
130              
131             ## no critic strict
132 5     5   5451 no strict 'refs'; # Violates use strict, but allows code generation
  5         14  
  5         6101  
133             ## use critic
134              
135 6 50       75 if ( !$self->can( $tag . 's' ) ) {
136 6         27 *{ $tag . 's' } = sub {
137 35     35   2040 my ($self) = @_;
138 35         85 return $self->_tags($tag);
139 6         25 };
140             }
141              
142 6 50       49 if ( !$self->can( 'in_' . $tag ) ) {
143 6         22 *{ 'in_' . $tag } = sub {
144 20     20   861 my ( $self, $item ) = @_;
145 20         70 return $self->_is_in( $tag . 's', $item );
146 6         31 };
147             }
148              
149 6         580 return $self->_parse_entry;
150             }
151              
152             sub _tags {
153 35     35   45 my ( $self, $tag ) = @_;
154 35         86 my $ident = ident($self);
155              
156 35         46 my @tags;
157 35 50       119 if ( defined $tags_of{$ident}{$tag} ) {
158 35         40 @tags = sort keys %{ $tags_of{$ident}{$tag} };
  35         189  
159             }
160 35 50       217 return wantarray ? @tags : \@tags;
161             }
162              
163             sub _is_in {
164 20     20   30 my ( $self, $tags, $item ) = @_;
165 20 50       49 return if !defined $item;
166 20         47 foreach ( $self->$tags ) {
167 27 100       114 return 1 if $_ eq $item;
168             }
169 9         50 return 0;
170             }
171              
172             sub pri {
173 2     2 1 5 my ( $self, $new_pri ) = @_;
174 2         10 my $ident = ident($self);
175              
176 2 50       16 if ( $new_pri !~ /^[a-zA-Z]?$/xms ) {
177 0         0 croak "Invalid priority [$new_pri]";
178             }
179              
180 2         6 $priority_of{$ident} = $new_pri;
181              
182 2         7 return $self->prepend();
183             }
184              
185             sub prepend {
186 4     4 1 11 my ( $self, $addition ) = @_;
187              
188 4         13 my $new = $self->text;
189 4         8 my @new;
190              
191 4         40 $new =~ s/$priority_completion_regex//xms;
192              
193 4 100       16 if ( $self->done ) {
194 1 50       3 if ( $self->done !~ /^x/ixms ) {
195 1         11 push @new, 'x';
196             }
197 1         4 push @new, $self->done;
198             }
199              
200 4 100       11 if ( $self->priority ) {
201 2         6 push @new, '(' . $self->priority . ')';
202             }
203              
204 4 100 66     19 if ( defined $addition && length $addition ) {
205 1         3 push @new, $addition;
206             }
207              
208 4         19 return $self->replace( join q{ }, @new, $new );
209             }
210              
211             sub append {
212 3     3 1 9 my ( $self, $addition ) = @_;
213 3         13 return $self->replace( join q{ }, $self->text, $addition );
214             }
215              
216             ## no critic 'homonym'
217             sub do { # This is what it is called in todo.sh
218             ## use critic
219 1     1 1 3 my ($self) = @_;
220 1         4 my $ident = ident($self);
221              
222 1 50       4 if ( $self->done ) {
223 0         0 return 1;
224             }
225              
226 1         58 $completion_status_of{$ident} = sprintf "%04d-%02d-%02d",
227             ( (localtime)[5] + 1900 ),
228             ( (localtime)[4] + 1 ),
229             ( (localtime)[3] );
230              
231 1         7 return $self->prepend();
232             }
233              
234             sub done {
235 9     9 1 16 my ($self) = @_;
236 9         54 return $completion_status_of{ ident($self) };
237             }
238 20     20 1 789 sub known_tags { my ($self) = @_; return $known_tags_of{ ident($self) }; }
  20         108  
239 13     13 1 25 sub priority { my ($self) = @_; return $priority_of{ ident($self) }; }
  13         71  
240 34     34 1 832 sub text { my ($self) = @_; return $text_of{ ident($self) }; }
  34         439  
241 1     1 1 3 sub depri { my ($self) = @_; return $self->pri(q{}) }
  1         4  
242              
243             sub DESTROY {
244 2     2   1250 my ($self) = @_;
245 2         9 my $ident = ident $self;
246 2         7 foreach my $attr_ref (@attr_refs) {
247 10         281 delete $attr_ref->{$ident};
248             }
249             }
250             }
251             1; # Magic true value required at end of module
252             __END__