File Coverage

blib/lib/App/JobLog/Config.pm
Criterion Covered Total %
statement 139 145 95.8
branch 27 36 75.0
condition 20 31 64.5
subroutine 39 40 97.5
pod 17 18 94.4
total 242 270 89.6


line stmt bran cond sub pod time code
1             package App::JobLog::Config;
2             $App::JobLog::Config::VERSION = '1.039';
3             # ABSTRACT: central depot for App::JobLog configuration parameters and controller allowing their modification
4              
5              
6 6     6   117737 use Exporter 'import';
  6         12  
  6         419  
7             our @EXPORT_OK = qw(
8             columns
9             day_length
10             dir
11             editor
12             hidden_columns
13             init_file
14             is_hidden
15             is_workday
16             log
17             merge
18             pay_period_length
19             precision
20             readme
21             start_pay_period
22             sunday_begins_week
23             time_zone
24             _tz
25             vacation
26             workdays
27             DAYS
28             DIRECTORY
29             HIDABLE_COLUMNS
30             HOURS
31             MERGE
32             NONE_COLUMN
33             PERIOD
34             PRECISION
35             SUNDAY_BEGINS_WEEK
36             TIME_ZONE
37             WORKDAYS
38             );
39              
40 6         43 use Class::Autouse qw{
41             File::HomeDir
42             File::Spec
43             Config::Tiny
44             FileHandle
45             App::JobLog::Command::info
46 6     6   4638 };
  6         33921  
47 6     6   9071 use autouse 'File::Path' => qw(mkpath);
  6         3861  
  6         36  
48 6     6   1249 use autouse 'Cwd' => qw(abs_path);
  6         13  
  6         26  
49 6     6   937 use autouse 'Term::ReadKey' => qw(GetTerminalSize);
  6         11  
  6         25  
50 6     6   1905 use Modern::Perl;
  6         8296  
  6         53  
51              
52             # default precision
53 6     6   948 use constant PRECISION => 2;
  6         11  
  6         366  
54              
55             # default pay period
56 6     6   31 use constant PERIOD => 14;
  6         9  
  6         265  
57              
58             # hours worked in day
59 6     6   29 use constant HOURS => 8;
  6         10  
  6         299  
60              
61             # whether Sunday is the first day of the week
62             # otherwise it's Monday, as in DateTime
63 6     6   51 use constant SUNDAY_BEGINS_WEEK => 1;
  6         12  
  6         280  
64              
65             # environment variables
66              
67             # identifies directory to write files into
68 6     6   34 use constant DIRECTORY => 'JOB_LOG_DIRECTORY';
  6         11  
  6         276  
69              
70             # expected abbreviations for working days in week
71 6     6   29 use constant WORKDAYS => 'MTWHF';
  6         10  
  6         310  
72              
73             # expected abbreviations for weekdays
74 6     6   26 use constant DAYS => 'S' . WORKDAYS . 'A';
  6         12  
  6         263  
75              
76             # default level of merging
77 6     6   28 use constant MERGE => 'adjacent same tags';
  6         10  
  6         257  
78              
79             # name of hide nothing "column"
80 6     6   27 use constant NONE_COLUMN => 'none';
  6         7  
  6         356  
81              
82             # array of hidable columns
83 6         358 use constant HIDABLE_COLUMNS => [
84             NONE_COLUMN, qw(
85             date
86             description
87             duration
88             tags
89             time
90             )
91 6     6   29 ];
  6         8  
92              
93             # default time zone; necessary because Cygwin doesn't support local
94 6 50   6   28 use constant TIME_ZONE => $^O eq 'cygwin' ? 'floating' : 'local';
  6         11  
  6         11692  
95              
96              
97             sub init_file {
98 4     4 1 9 my ($path) = @_;
99 4 50       71 unless ( -e $path ) {
100 4         78 my ( $volume, $directories, $file ) = File::Spec->splitpath($path);
101 4         50 my $dir = File::Spec->catfile( $volume, $directories );
102 4 100       81 mkpath( $dir, { verbose => 0, mode => 0711 } ) unless -d $dir;
103 4 50       192 unless ( -e readme() ) {
104 4 50       13 my $fh = FileHandle->new( readme(), 'w' )
105             or die 'could not create file ' . readme();
106 4         9836 my $executable = abs_path($0);
107              
108             # to protect against refactoring
109 4         65 my $command = App::JobLog::Command::info->name;
110 4         52 print $fh <
111              
112             Job Log
113              
114             This directory holds files used by Job Log to maintain
115             a work log. For more details type
116              
117             $executable $command
118              
119             on the command line.
120              
121             END
122 4         27 $fh->close;
123             }
124             }
125             }
126              
127              
128             my $dir;
129              
130             sub dir {
131 14   100 14 1 95 $dir ||= $ENV{ DIRECTORY() };
132 14   66     48 $dir ||= File::Spec->catfile( File::HomeDir->my_home, '.joblog' );
133 14         21880 return $dir;
134             }
135              
136              
137             my $log;
138              
139             sub log {
140 193   66 193 1 144754 $log ||= File::Spec->catfile( dir(), 'log' );
141 193         3373 return $log;
142             }
143              
144              
145             my $readme;
146              
147             sub readme {
148 8   66 8 1 33 $readme ||= File::Spec->catfile( dir(), 'README' );
149 8         164 return $readme;
150             }
151              
152             # configuration file for basic parameters
153             my $config_file;
154              
155             sub _config_file {
156 6   66 6   37 $config_file ||= File::Spec->catfile( dir(), 'config.ini' );
157 6         28 return $config_file;
158             }
159              
160              
161             my $vacation_file;
162              
163             sub vacation {
164 13   66 13 1 90 $vacation_file ||= File::Spec->catfile( dir(), 'vacation' );
165 13         486 return $vacation_file;
166             }
167              
168             # configuration object and whether any changes need to be written to this file
169             my ( $config, $config_changed );
170              
171             END {
172 6 100   6   161027 if ($config_changed) {
173 1         4 init_file( _config_file() );
174 1         87 $config->write( _config_file() );
175             }
176             }
177              
178             # construct configuration object as necessary
179             sub _config {
180 38 100   38   101 unless ($config) {
181 4         13 my $f = _config_file();
182 4 50       207 $config = -e $f ? Config::Tiny->read($f) : Config::Tiny->new;
183             }
184 38         9200 return $config;
185             }
186              
187              
188             sub precision {
189 2     2 1 6 my ($value) = @_;
190 2         9 return _param( 'precision', PRECISION, 'summary', $value );
191             }
192              
193             sub merge {
194 4     4 0 7 my ($value) = @_;
195 4         18 return _param( 'merge', MERGE, 'summary', $value );
196             }
197              
198              
199             sub day_length {
200 2     2 1 6 my ($value) = @_;
201 2         10 return _param( 'day-length', HOURS, 'time', $value );
202             }
203              
204              
205             sub pay_period_length {
206 9     9 1 176 my ($value) = @_;
207 9         23 return _param( 'pay-period-length', PERIOD, 'time', $value );
208             }
209              
210              
211             sub sunday_begins_week {
212 7     7 1 13 my ($value) = @_;
213 7         21 return _param( 'sunday-begins-week', SUNDAY_BEGINS_WEEK, 'time', $value );
214             }
215              
216              
217             sub start_pay_period {
218 8     8 1 189 my ($value) = @_;
219 8         48 require DateTime;
220 8 100       22 if ( ref $value eq 'DateTime' ) {
221 1         4 $value = sprintf '%d %d %d', $value->year, $value->month, $value->day;
222             }
223 8         32 $value = _param( 'start-pay-period', undef, 'time', $value );
224 8 50       20 if ($value) {
225 8         23 my @parts = split / /, $value;
226 8         23 return DateTime->new(
227             year => $parts[0],
228             month => $parts[1],
229             day => $parts[2],
230             time_zone => _tz(),
231             );
232             }
233 0         0 return;
234             }
235              
236             # abstracts out code for maintaining config file
237             sub _param {
238 38     38   94 my ( $param, $default, $section, $new_value ) = @_;
239 38   50     109 $section ||= 'main';
240 38         88 my $config = _config();
241 38         113 my $value = $config->{$section}->{$param};
242 38 100       141 if ( defined $new_value ) {
243 2 50 66     16 if ( defined $default && $new_value eq $default && !defined $value ) {
      66        
244 1         4 return $new_value;
245             }
246 1 50 33     73 return $value if defined $value && $value eq $new_value;
247 1         3 $config_changed = 1;
248 1         5 return $config->{$section}->{$param} = $new_value;
249             }
250             else {
251 36 100       9689 return defined $value ? $value : $default;
252             }
253             }
254              
255              
256             sub editor {
257 0     0 1 0 my ($value) = @_;
258 0         0 $value = _param( 'editor', undef, 'external', $value );
259 0         0 return $value;
260             }
261              
262              
263             sub columns {
264 12     12 1 71 my ($cols) = GetTerminalSize;
265 12   50     537538 $cols ||= 76;
266 12         203 return $cols;
267             }
268              
269              
270             sub workdays {
271 2     2 1 4 my ($value) = @_;
272 2         8 return _param( 'workdays', WORKDAYS, 'time', $value );
273             }
274              
275              
276             my %workdays;
277              
278             sub is_workday {
279 18917     18917 1 26704 my ($date) = @_;
280              
281             # initialize map
282 18917 100       53500 unless (%workdays) {
283 2         13 my @days = split //, DAYS;
284              
285             # move Sunday into DateTime's expected position
286 2         5 push @days, shift @days;
287 2         4 my %day_map;
288 2         12 for ( 0 .. $#days ) {
289 14         30 $day_map{ $days[$_] } = $_ + 1;
290             }
291 2         8 for ( split //, workdays() ) {
292 10         31 $workdays{ $day_map{$_} } = 1;
293             }
294             }
295 18917         54648 return $workdays{ $date->day_of_week };
296             }
297              
298              
299             sub hidden_columns {
300 2     2 1 5 my ($value) = @_;
301 2         7 return _param( 'hidden_columns', NONE_COLUMN, 'summary', $value );
302             }
303              
304              
305             my %hidden_columns;
306              
307             sub is_hidden {
308 73     73 1 158 my ($value) = @_;
309 73 100       240 unless (%hidden_columns) {
310 2         9 %hidden_columns = map { $_ => 1 } split / /, hidden_columns();
  2         12  
311             }
312 73         735 return $hidden_columns{$value};
313             }
314              
315              
316             sub time_zone {
317 2     2 1 5 my ($value) = @_;
318 2         6 return _param( 'time_zone', TIME_ZONE, 'time', $value );
319             }
320              
321             our $tz;
322              
323             # removed from App::JobLog::Time to prevent dependency cycle
324             sub _tz {
325 25818 100   25818   59501 if ( !defined $tz ) {
326 2         14 require DateTime::TimeZone;
327 2         4 eval { $tz = DateTime::TimeZone->new( name => time_zone() ) };
  2         6  
328 2 50       31911 if ($@) {
329 0         0 print STDERR 'DateTime::TimeZone doesn\'t like the time zone '
330             . time_zone()
331             . "\nreverting to floating time\n full error: $@";
332 0         0 $tz = DateTime::TimeZone->new( name => 'floating' );
333             }
334             }
335 25818         104345 return $tz;
336             }
337              
338             1;
339              
340             __END__