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.040';
3             # ABSTRACT: central depot for App::JobLog configuration parameters and controller allowing their modification
4              
5              
6 6     6   115905 use Exporter 'import';
  6         12  
  6         444  
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   4376 };
  6         32747  
47 6     6   8811 use autouse 'File::Path' => qw(mkpath);
  6         3972  
  6         35  
48 6     6   1271 use autouse 'Cwd' => qw(abs_path);
  6         12  
  6         23  
49 6     6   914 use autouse 'Term::ReadKey' => qw(GetTerminalSize);
  6         8  
  6         41  
50 6     6   1678 use Modern::Perl;
  6         7724  
  6         51  
51              
52             # default precision
53 6     6   866 use constant PRECISION => 2;
  6         10  
  6         445  
54              
55             # default pay period
56 6     6   31 use constant PERIOD => 14;
  6         8  
  6         272  
57              
58             # hours worked in day
59 6     6   27 use constant HOURS => 8;
  6         10  
  6         281  
60              
61             # whether Sunday is the first day of the week
62             # otherwise it's Monday, as in DateTime
63 6     6   50 use constant SUNDAY_BEGINS_WEEK => 1;
  6         11  
  6         268  
64              
65             # environment variables
66              
67             # identifies directory to write files into
68 6     6   36 use constant DIRECTORY => 'JOB_LOG_DIRECTORY';
  6         10  
  6         273  
69              
70             # expected abbreviations for working days in week
71 6     6   28 use constant WORKDAYS => 'MTWHF';
  6         8  
  6         315  
72              
73             # expected abbreviations for weekdays
74 6     6   28 use constant DAYS => 'S' . WORKDAYS . 'A';
  6         11  
  6         262  
75              
76             # default level of merging
77 6     6   26 use constant MERGE => 'adjacent same tags';
  6         9  
  6         261  
78              
79             # name of hide nothing "column"
80 6     6   26 use constant NONE_COLUMN => 'none';
  6         9  
  6         348  
81              
82             # array of hidable columns
83 6         346 use constant HIDABLE_COLUMNS => [
84             NONE_COLUMN, qw(
85             date
86             description
87             duration
88             tags
89             time
90             )
91 6     6   26 ];
  6         10  
92              
93             # default time zone; necessary because Cygwin doesn't support local
94 6 50   6   29 use constant TIME_ZONE => $^O eq 'cygwin' ? 'floating' : 'local';
  6         9  
  6         11270  
95              
96              
97             sub init_file {
98 4     4 1 12 my ($path) = @_;
99 4 50       71 unless ( -e $path ) {
100 4         77 my ( $volume, $directories, $file ) = File::Spec->splitpath($path);
101 4         52 my $dir = File::Spec->catfile( $volume, $directories );
102 4 100       81 mkpath( $dir, { verbose => 0, mode => 0711 } ) unless -d $dir;
103 4 50       190 unless ( -e readme() ) {
104 4 50       15 my $fh = FileHandle->new( readme(), 'w' )
105             or die 'could not create file ' . readme();
106 4         9906 my $executable = abs_path($0);
107              
108             # to protect against refactoring
109 4         64 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         30 $fh->close;
123             }
124             }
125             }
126              
127              
128             my $dir;
129              
130             sub dir {
131 14   100 14 1 94 $dir ||= $ENV{ DIRECTORY() };
132 14   66     62 $dir ||= File::Spec->catfile( File::HomeDir->my_home, '.joblog' );
133 14         22812 return $dir;
134             }
135              
136              
137             my $log;
138              
139             sub log {
140 193   66 193 1 142030 $log ||= File::Spec->catfile( dir(), 'log' );
141 193         3752 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   44 $config_file ||= File::Spec->catfile( dir(), 'config.ini' );
157 6         32 return $config_file;
158             }
159              
160              
161             my $vacation_file;
162              
163             sub vacation {
164 13   66 13 1 84 $vacation_file ||= File::Spec->catfile( dir(), 'vacation' );
165 13         492 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   19194 if ($config_changed) {
173 1         5 init_file( _config_file() );
174 1         94 $config->write( _config_file() );
175             }
176             }
177              
178             # construct configuration object as necessary
179             sub _config {
180 38 100   38   122 unless ($config) {
181 4         16 my $f = _config_file();
182 4 50       243 $config = -e $f ? Config::Tiny->read($f) : Config::Tiny->new;
183             }
184 38         10149 return $config;
185             }
186              
187              
188             sub precision {
189 2     2 1 6 my ($value) = @_;
190 2         8 return _param( 'precision', PRECISION, 'summary', $value );
191             }
192              
193             sub merge {
194 4     4 0 9 my ($value) = @_;
195 4         18 return _param( 'merge', MERGE, 'summary', $value );
196             }
197              
198              
199             sub day_length {
200 2     2 1 7 my ($value) = @_;
201 2         14 return _param( 'day-length', HOURS, 'time', $value );
202             }
203              
204              
205             sub pay_period_length {
206 9     9 1 185 my ($value) = @_;
207 9         18 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         19 return _param( 'sunday-begins-week', SUNDAY_BEGINS_WEEK, 'time', $value );
214             }
215              
216              
217             sub start_pay_period {
218 8     8 1 191 my ($value) = @_;
219 8         47 require DateTime;
220 8 100       21 if ( ref $value eq 'DateTime' ) {
221 1         5 $value = sprintf '%d %d %d', $value->year, $value->month, $value->day;
222             }
223 8         30 $value = _param( 'start-pay-period', undef, 'time', $value );
224 8 50       19 if ($value) {
225 8         23 my @parts = split / /, $value;
226 8         21 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   95 my ( $param, $default, $section, $new_value ) = @_;
239 38   50     105 $section ||= 'main';
240 38         93 my $config = _config();
241 38         128 my $value = $config->{$section}->{$param};
242 38 100       108 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     148 return $value if defined $value && $value eq $new_value;
247 1         3 $config_changed = 1;
248 1         6 return $config->{$section}->{$param} = $new_value;
249             }
250             else {
251 36 100       9731 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 83 my ($cols) = GetTerminalSize;
265 12   50     271724 $cols ||= 76;
266 12         165 return $cols;
267             }
268              
269              
270             sub workdays {
271 2     2 1 6 my ($value) = @_;
272 2         9 return _param( 'workdays', WORKDAYS, 'time', $value );
273             }
274              
275              
276             my %workdays;
277              
278             sub is_workday {
279 18995     18995 1 26717 my ($date) = @_;
280              
281             # initialize map
282 18995 100       54115 unless (%workdays) {
283 2         18 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         8 for ( 0 .. $#days ) {
289 14         33 $day_map{ $days[$_] } = $_ + 1;
290             }
291 2         8 for ( split //, workdays() ) {
292 10         31 $workdays{ $day_map{$_} } = 1;
293             }
294             }
295 18995         50969 return $workdays{ $date->day_of_week };
296             }
297              
298              
299             sub hidden_columns {
300 2     2 1 5 my ($value) = @_;
301 2         31 return _param( 'hidden_columns', NONE_COLUMN, 'summary', $value );
302             }
303              
304              
305             my %hidden_columns;
306              
307             sub is_hidden {
308 73     73 1 202 my ($value) = @_;
309 73 100       270 unless (%hidden_columns) {
310 2         10 %hidden_columns = map { $_ => 1 } split / /, hidden_columns();
  2         12  
311             }
312 73         736 return $hidden_columns{$value};
313             }
314              
315              
316             sub time_zone {
317 2     2 1 8 my ($value) = @_;
318 2         12 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   61245 if ( !defined $tz ) {
326 2         26 require DateTime::TimeZone;
327 2         8 eval { $tz = DateTime::TimeZone->new( name => time_zone() ) };
  2         9  
328 2 50       10800 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         106763 return $tz;
336             }
337              
338             1;
339              
340             __END__