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   736 use 5.008005;
  11         46  
4 11     11   68 use strict;
  11         18  
  11         222  
5 11     11   52 use warnings;
  11         19  
  11         364  
6 11     11   63 use integer;
  11         19  
  11         95  
7 11     11   416 use lib '../..';
  11         32  
  11         63  
8              
9 11     11   1454 use IO::File;
  11         23  
  11         1863  
10 11         792 use File::Spec::Functions qw(abs2rel catfile file_name_is_absolute
11 11     11   134 no_upwards splitdir updir);
  11         30  
12 11     11   86 use App::Followme::FIO;
  11         29  
  11         1075  
13 11     11   5501 use App::Followme::NestedText;
  11         46  
  11         1032  
14 11     11   5850 use App::Followme::Web;
  11         49  
  11         1185  
15              
16 11     11   90 use base qw(App::Followme::ConfiguredObject);
  11         24  
  11         5983  
17              
18             our $VERSION = "2.02";
19              
20             #----------------------------------------------------------------------
21             # Read the default parameter values
22              
23             sub parameters {
24 172     172 1 374 my ($self) = @_;
25              
26             return (
27 172         882 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 136 my($self, $error, $folder) = @_;
53 44 50       158 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 33     33 1 905 my ($self, $directory, $uplevel) = @_;
67              
68 33 50       1101 $uplevel = 0 unless defined $uplevel;
69 33         162 ($directory) = fio_split_filename($directory);
70 33         167 my @path = splitdir(abs2rel($directory, $self->{top_directory}));
71              
72 33         2343 for (;;) {
73 45         215 my $dir = catfile($self->{top_directory}, @path);
74              
75 45 100       122 if ($uplevel) {
76 8         21 $uplevel -= 1;
77             } else {
78 37         97 my $pattern = "*.$self->{web_extension}";
79 37         127 my $file = fio_most_recent_file($dir, $pattern);
80 37 100       224 return $file if $file;
81             }
82              
83 13 100       45 last unless @path;
84 12         26 pop(@path);
85             }
86              
87 1         4 return;
88             }
89              
90             #----------------------------------------------------------------------
91             # Get the full template name
92              
93             sub get_template_name {
94 54     54 1 806 my ($self, $template_file) = @_;
95              
96             my $template_directory = fio_full_file_name($self->{top_directory},
97 54         244 $self->{template_directory});
98              
99 54         224 my @directories = ($self->{base_directory}, $template_directory);
100              
101 54         119 foreach my $directory (@directories) {
102 98         281 my $template_name = fio_full_file_name($directory, $template_file);
103 98 100       1844 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 294 my ($self, $filename, %configuration) = @_;
114            
115 20         65 foreach my $name (qw(run_before run_after)) {
116 40   50     192 $configuration{$name} ||= [];
117             }
118              
119 20         97 my %new_configuration = nt_parse_almost_yaml_file($filename);
120 20         86 my $final_configuration = nt_merge_items(\%configuration,
121             \%new_configuration);
122              
123 20         164 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 26     26 1 1141 my ($self, @files) = @_;
131              
132 26         47 my $page;
133 26         52 my $section = {};
134 26         66 foreach my $file (reverse @files) {
135 77 100       191 if (defined $file) {
136 76 100       1165 if ($file =~ /\n/) {
    100          
137 25         119 $page = web_substitute_sections($file, $section);
138             } elsif (-e $file) {
139 40         193 $page = web_substitute_sections(fio_read_page($file), $section);
140             }
141             }
142             }
143              
144 26         129 return $page;
145             }
146              
147             #----------------------------------------------------------------------
148             # Render the data contained in a file using a template
149              
150             sub render_file {
151 26     26 1 803 my ($self, $template_file, $file) = @_;
152              
153 26         83 $template_file = $self->get_template_name($template_file);
154 26         175 my $template = fio_read_page($template_file);
155              
156 26         151 my $renderer = $self->{template}->compile($template);
157 26         566 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 156 my ($self, $file) = @_;
165              
166 66 50       1705 $file = catfile($file, "index.$self->{web_extension}") if -d $file;
167 66         302 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