File Coverage

blib/lib/WebService/HabitRPG/Task.pm
Criterion Covered Total %
statement 60 66 90.9
branch 11 22 50.0
condition n/a
subroutine 16 16 100.0
pod 2 3 66.6
total 89 107 83.1


line stmt bran cond sub pod time code
1             package WebService::HabitRPG::Task;
2 5     5   88758 use v5.010;
  5         17  
3 5     5   24 use strict;
  5         9  
  5         96  
4 5     5   21 use warnings;
  5         8  
  5         103  
5 5     5   507 use autodie;
  5         21539  
  5         29  
6 5     5   25050 use JSON::MaybeXS qw(is_bool);
  5         21540  
  5         251  
7 5     5   634 use Moo;
  5         15177  
  5         29  
8 5     5   3316 use Scalar::Util qw(looks_like_number);
  5         10  
  5         209  
9 5     5   1408 use POSIX qw(strftime);
  5         17819  
  5         50  
10 5     5   5406 use Carp qw(croak);
  5         10  
  5         203  
11 5     5   1979 use Data::Dumper;
  5         23401  
  5         293  
12 5     5   2776 use DateTime;
  5         1782402  
  5         218  
13 5     5   2044 use DateTime::Format::ISO8601;
  5         394283  
  5         314  
14              
15 5         4202 use constant HRPG_REPEAT_MAP => qw(
16             m t w th f s su
17 5     5   51 );
  5         9  
18              
19             # TODO: croak provides poor error messages in here, possibly due to
20             # it not knowing about Moo properly. Still, they're good enough for
21             # getting stack backtraces when needed.
22              
23             # ABSTRACT: A HabitRPG task
24              
25             our $VERSION = '0.30'; # VERSION: Generated by DZP::OurPkg:Version
26              
27              
28             # Validation functions
29              
30             my $Bool = sub {
31             my ( $value ) = @_;
32             $value = 0 + $value if is_bool($value);
33             croak "$value must be 0|1" unless $value =~ /^[01]$/;
34             };
35              
36             my $Num = sub {
37             croak "$_[0] isn't a number" unless looks_like_number $_[0];
38             };
39              
40             my $Type = sub {
41             croak "$_[0] is not habit|todo|daily|reward"
42             unless $_[0] =~ /^(?:habit|todo|daily|reward)$/;
43             };
44              
45             my $NonEmpty = sub {
46             croak "Empty or undef parameter" unless length($_[0] // "");
47             };
48              
49             has 'text' => ( is => 'ro', required => 1, isa => $NonEmpty);
50             has 'id' => ( is => 'ro', required => 1, isa => $NonEmpty);
51             has 'up' => ( is => 'ro', default => sub { 0 }, isa => $Bool);
52             has 'down' => ( is => 'ro', default => sub { 0 }, isa => $Bool);
53             has 'value' => ( is => 'ro', required => 1, isa => $Num);
54             has 'type' => ( is => 'ro', required => 1, isa => $Type);
55             has 'history' => ( is => 'ro' ); # TODO: Objectify
56             has 'repeat' => ( is => 'ro' ); # TODO: Objectify
57             has 'completed' => ( is => 'ro' );
58             has 'tags' => ( is => 'ro' ); # Hashref to uuid => True pairs.
59             has 'streak' => ( is => 'ro' );
60             has '_raw' => ( is => 'rw' );
61              
62             # Debugging hooks in case things go weird.
63              
64             around BUILDARGS => sub {
65             my $orig = shift;
66             my $class = shift;
67              
68             if ($WebService::HabitRPG::DEBUG) {
69             warn "Building task with:\n";
70             warn Dumper(\@_), "\n";
71             }
72              
73             return $class->$orig(@_);
74             };
75              
76             sub BUILD {
77 6     6 0 55 my ($self, $args) = @_;
78              
79             # Since we're usually being called directly with the results of
80             # a JSON parse, we want to record that original structure here.
81              
82 6         32 $self->_raw($args);
83             }
84              
85              
86             sub active_today {
87 5     5 1 273 my ($self) = @_;
88              
89 5 100       26 return 1 if $self->type ne 'daily';
90              
91 4         12 my $frequency = $self->_raw->{frequency};
92              
93 4 100       15 if ($frequency eq 'weekly') {
    50          
94 2 50       7 return unless $self->repeat;
95              
96 2         9 my $today_short = (HRPG_REPEAT_MAP)[ DateTime->now->set_time_zone('local')->day_of_week - 1 ];
97 2         1754 return $self->repeat->{$today_short};
98             }
99             elsif ($frequency eq 'daily') {
100 2         5 my $every_x = $self->_raw->{everyX};
101 2 50       5 return unless $every_x;
102              
103 2         15 my $start_date = DateTime::Format::ISO8601->new->parse_datetime($self->_raw->{startDate})->truncate(to => 'day');
104 2         1361 my $days_since_start = DateTime->today->delta_days($start_date)->in_units('days');
105              
106 2         1138 return $days_since_start % $every_x == 0;
107             }
108             else {
109 0         0 return;
110             }
111             }
112              
113              
114             sub format_task {
115 1     1 1 2525 my ($task) = @_;
116              
117 1         2 my $formatted = "";
118              
119 1 50       10 if ($task->type =~ /^(?:daily|todo)$/) {
    50          
120 0 0       0 if ($task->completed) {
    0          
121 0         0 $formatted .= '[X] ';
122             }
123             elsif (not $task->active_today) {
124 0         0 $formatted .= '[-] ';
125             }
126             else {
127 0         0 $formatted .= '[ ] ';
128             }
129             }
130             elsif ($task->type eq 'habit') {
131 1         2 $formatted .= ' ';
132 1 50       4 $formatted .= $task->{up} ? "+" : " " ;
133 1 50       3 $formatted .= $task->{down} ? "- " : " " ;
134             }
135             else {
136 0         0 $formatted .= " * ";
137             }
138              
139 1         4 $formatted .= $task->text;
140              
141 1         2 return $formatted;
142             }
143              
144             1;
145              
146             __END__
147              
148             =pod
149              
150             =encoding UTF-8
151              
152             =head1 NAME
153              
154             WebService::HabitRPG::Task - A HabitRPG task
155              
156             =head1 VERSION
157              
158             version 0.30
159              
160             =head1 SYNOPSIS
161              
162             my $task = WebService::HabitRRG::Task->new(
163             text => 'Floss teeth',
164             id => 'a670fc50-4e04-4b0f-9583-e4ee55fced02',
165             up => 1,
166             down => 0,
167             value => 0,
168             type => 'habit',
169             tags => { work_uuid => 1, home_uuid => 0 },
170             );
171              
172             say "Task name: ", $task->text;
173              
174             =head1 DESCRIPTION
175              
176             Represents a HabitRPG task object. All properties shown in the
177             synopsis are checked for sanity and are available as methods.
178              
179             The C<history>, C<completed> and C<repeat> attributes may also
180             be provided at build-time, but are optional. No checking is done
181             on them (yet).
182              
183             =head1 METHODS
184              
185             =head2 active_today()
186              
187             if ( $task->active_today ) { # This task is active today! }
188              
189             Returns a true value if this task is due today. To check if it's
190             already completed, check C< $task->completed >.
191              
192             =head2 format_task()
193              
194             say $task->format_task;
195              
196             Generated a formatted form of the task, including check-boxes for
197             C<todo> and C<daily> tasks, and C<+> and C<-> signs for habits.
198              
199             =for Pod::Coverage BUILDARGS BUILD text id up down value type history completed repeat tags streak
200              
201             =head1 AUTHOR
202              
203             Paul Fenwick <pjf@cpan.org>
204              
205             =head1 COPYRIGHT AND LICENSE
206              
207             This software is copyright (c) 2013 by Paul Fenwick.
208              
209             This is free software; you can redistribute it and/or modify it under
210             the same terms as the Perl 5 programming language system itself.
211              
212             =cut