File Coverage

lib/App/Followme/Module.pm
Criterion Covered Total %
statement 79 89 88.7
branch 17 20 85.0
condition 1 8 12.5
subroutine 19 20 95.0
pod 8 9 88.8
total 124 146 84.9


line stmt bran cond sub pod time code
1             package App::Followme::Module;
2              
3 11     11   758 use 5.008005;
  11         45  
4 11     11   75 use strict;
  11         20  
  11         264  
5 11     11   55 use warnings;
  11         23  
  11         273  
6 11     11   51 use integer;
  11         19  
  11         66  
7 11     11   349 use lib '../..';
  11         23  
  11         74  
8              
9 11     11   1432 use IO::File;
  11         22  
  11         1826  
10 11         1100 use File::Spec::Functions qw(abs2rel catfile file_name_is_absolute
11 11     11   77 no_upwards splitdir updir);
  11         23  
12 11     11   88 use App::Followme::FIO;
  11         40  
  11         1077  
13 11     11   5309 use App::Followme::NestedText;
  11         26  
  11         933  
14 11     11   5431 use App::Followme::Web;
  11         32  
  11         1038  
15              
16 11     11   93 use base qw(App::Followme::ConfiguredObject);
  11         24  
  11         5340  
17              
18             our $VERSION = "2.03";
19              
20             #----------------------------------------------------------------------
21             # Read the default parameter values
22              
23             sub parameters {
24 172     172 1 326 my ($self) = @_;
25              
26             return (
27 172         916 template_file => '',
28             exclude_index => 0,
29             web_extension => 'html',
30             configuration_file => 'followme.cfg',
31             template_directory => '_templates',
32             data_pkg => 'App::Followme::WebData',
33             template_pkg => 'App::Followme::Template',
34             );
35             }
36              
37             #----------------------------------------------------------------------
38             # Main method of all module subclasses (stub)
39              
40             sub run {
41 0     0 0 0 my ($self, $folder, $base_folder, $top_folder) = @_;
42 0   0     0 $base_folder ||= $folder;
43 0   0     0 $top_folder ||= $base_folder;
44              
45 0         0 my $pkg = ref $self;
46 0         0 die "Run method not implemented by $pkg\n";
47             }
48              
49             #----------------------------------------------------------------------
50             # Check for error and warn if found
51              
52             sub check_error {
53 43     43 1 136 my($self, $error, $folder) = @_;
54 43 50       157 return 1 unless $error;
55              
56 0         0 my $pkg = ref $self;
57 0         0 my $filename = $self->to_file($folder);
58 0         0 warn "$pkg $filename: $error";
59              
60 0         0 return;
61             }
62              
63             #----------------------------------------------------------------------
64             # Find an file to serve as a prototype for updating other files
65              
66             sub find_prototype {
67 33     33 1 1651 my ($self, $directory, $uplevel) = @_;
68              
69 33 50       132 $uplevel = 0 unless defined $uplevel;
70 33         123 ($directory) = fio_split_filename($directory);
71 33         197 my @path = splitdir(abs2rel($directory, $self->{top_directory}));
72              
73 33         2225 for (;;) {
74 45         201 my $dir = catfile($self->{top_directory}, @path);
75              
76 45 100       184 if ($uplevel) {
77 8         37 $uplevel -= 1;
78             } else {
79 37         109 my $pattern = "*.$self->{web_extension}";
80             my $file = fio_most_recent_file($dir, $pattern,
81 37         139 $self->{exclude_index});
82 37 100       209 return $file if $file;
83             }
84              
85 13 100       42 last unless @path;
86 12         26 pop(@path);
87             }
88              
89 1         4 return;
90             }
91              
92             #----------------------------------------------------------------------
93             # Get the full template name
94              
95             sub get_template_name {
96 51     51 1 804 my ($self, $template_file) = @_;
97              
98             my $template_directory = fio_full_file_name($self->{top_directory},
99 51         198 $self->{template_directory});
100              
101 51         280 my @directories = ($self->{base_directory}, $template_directory);
102              
103 51         103 foreach my $directory (@directories) {
104 92         269 my $template_name = fio_full_file_name($directory, $template_file);
105 92 100       1605 return $template_name if -e $template_name;
106             }
107              
108 0         0 die "Couldn't find template: $template_file\n";
109             }
110              
111             #----------------------------------------------------------------------
112             # Read the configuration from a file
113              
114             sub read_configuration {
115 20     20 1 307 my ($self, $filename, %configuration) = @_;
116            
117 20         68 foreach my $name (qw(run_before run_after)) {
118 40   50     210 $configuration{$name} ||= [];
119             }
120              
121 20         95 my %new_configuration = nt_parse_almost_yaml_file($filename);
122 20         91 my $final_configuration = nt_merge_items(\%configuration,
123             \%new_configuration);
124              
125 20         182 return %$final_configuration;
126             }
127              
128             #----------------------------------------------------------------------
129             # Reformat the contents of an html file using one or more prototypes
130              
131             sub reformat_file {
132 25     25 1 1245 my ($self, @files) = @_;
133              
134 25         123 my $page;
135 25         62 my $section = {};
136 25         74 foreach my $file (reverse @files) {
137 74 100       195 if (defined $file) {
138 73 100       979 if ($file =~ /\n/) {
    100          
139 24         109 $page = web_substitute_sections($file, $section);
140             } elsif (-e $file) {
141 38         191 $page = web_substitute_sections(fio_read_page($file), $section);
142             }
143             }
144             }
145              
146 25         131 return $page;
147             }
148              
149             #----------------------------------------------------------------------
150             # Render the data contained in a file using a template
151              
152             sub render_file {
153 25     25 1 766 my ($self, $template_file, $file) = @_;
154              
155 25         84 $template_file = $self->get_template_name($template_file);
156 25         134 my $template = fio_read_page($template_file);
157              
158 25         161 my $renderer = $self->{template}->compile($template);
159 25         545 return $renderer->($self->{data}, $file);
160             }
161              
162             #----------------------------------------------------------------------
163             # Convert filename to index file if it is a directory
164              
165             sub to_file {
166 64     64 1 142 my ($self, $file) = @_;
167              
168 64 50       1609 $file = catfile($file, "index.$self->{web_extension}") if -d $file;
169 64         304 return $file;
170             }
171              
172             1;
173              
174             =pod
175              
176             =encoding utf-8
177              
178             =head1 NAME
179              
180             App::Followme::Module - Base class for modules invoked from configuration
181              
182             =head1 SYNOPSIS
183              
184             use Cwd;
185             use App::Followme::Module;
186             my $obj = App::Followme::Module->new();
187             my $directory = getcwd();
188             $obj->run($directory);
189              
190             =head1 DESCRIPTION
191              
192             This module serves as the basis of all the computations
193             performed by App::Followme, and thus is used as the base class for all its
194             modules. It contains a few methods used by the modules and is not meant to
195             be invoked itself.
196              
197             =head1 METHODS
198              
199             Packages loaded as modules get a consistent behavior by subclassing
200             App::Followme:Module. It is not invoked directly. It provides methods for i/o,
201             handling templates and prototypes.
202              
203             A template is a file containing commands and variables for making a web page.
204             First, the template is compiled into a subroutine and then the subroutine is
205             called with a metadata object as an argument to fill in the variables and
206             produce a web page. A prototype is the most recently modified web page in a
207             directory. It is combined with the template so that the web page has the same
208             look as the other pages in the directory.
209              
210             =over 4
211              
212             =item $flag = $self->check_error($error, $folder);
213              
214             Provides common error formatting and checking for modules. It generates a
215             warning message if $error is set. $folder is the name of the file or folder
216             that the operation that generated the error was invoked on. The return value
217             is Perl true if $error was set.
218              
219             =item $filename = $self->find_prototype($directory, $uplevel);
220              
221             Return the name of the most recently modified web page in a directory. If
222             $uplevel is defined, search that many directory levels up from the directory
223             passed as the first argument.
224              
225             =item $filename = $self->get_template_name($template_file);
226              
227             Searches in the standard places for a template file and returns the full
228             filename if it is found. Throws an error if the template is not found.
229              
230             =item %configuration = $self->read_configuration($filename, %configuration);
231              
232             Update the configuraion parameters by reading the contents of a configuration
233             file.
234              
235             =item $page = $self->reformat_file(@files);
236              
237             Reformat a file using one or more prototypes. The first file is the
238             prototype, the second, the subprototype, and the last file is the file to
239             be updated.
240              
241             =item $page = $self->render_file($template_file, $file);
242              
243             Render a file as html using a template. The data subpackage is used to
244             retrieve the data from the file.
245              
246             =item $file = $self->to_file($file);
247              
248             A convenience method that converts a folder name to an index file name,
249             otherwise pass the file name unchanged.
250              
251             =back
252              
253             =head1 CONFIGURATION
254              
255             The following fields in the configuration file are used in this class and every
256             class based on it:
257              
258             =over 4
259              
260             =item template_file
261              
262             The name of the template file used by this module.
263              
264             =item exclude_index
265              
266             Do not include index file when searching for the file most recently changed
267             in a directory.
268              
269             =item web_extension
270              
271             The extension used by web files. The default value is 'html'.
272              
273             =item configuration_file
274              
275             The name of the file containing the configuration. The default value is
276             'followme.cfg'.
277              
278             =item template_directory
279              
280             The name of the directory containing the template files. The name is relative
281             to the top directory of the web site. The default value is '_templates'.
282              
283             =item data_pkg
284              
285             The name of the Perl module that generates data to be put into the template.
286             It should be a subclass of App::Followme::BaseData. The default value is
287             'App::Followme::WebData'.
288              
289             =item template_pkg
290              
291             The name of the Perl Module used to generate web pages from templates.
292             The default value is 'App::Followme::Template'.
293              
294             =back
295              
296             =head1 LICENSE
297              
298             Copyright (C) Bernie Simon.
299              
300             This library is free software; you can redistribute it and/or modify
301             it under the same terms as Perl itself.
302              
303             =head1 AUTHOR
304              
305             Bernie Simon E<lt>bernie.simon@gmail.comE<gt>
306              
307             =cut