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