File Coverage

blib/lib/App/Dapper.pm
Criterion Covered Total %
statement 73 234 31.2
branch 4 88 4.5
condition 0 14 0.0
subroutine 22 33 66.6
pod 3 12 25.0
total 102 381 26.7


line stmt bran cond sub pod time code
1             package App::Dapper;
2              
3             =head1 NAME
4              
5             App::Dapper - A publishing tool for static websites.
6              
7             =cut
8              
9 2     2   40044 use utf8;
  2         23  
  2         12  
10 2     2   2306 use open ':std', ':encoding(UTF-8)';
  2         3135  
  2         15  
11 2     2   41766 use 5.8.0;
  2         12  
  2         92  
12 2     2   9 use strict;
  2         4  
  2         59  
13 2     2   8 use warnings FATAL => 'all';
  2         4  
  2         72  
14              
15 2     2   15 use vars '$VERSION';
  2         3  
  2         97  
16              
17 2     2   10 use Exporter qw(import);
  2         3  
  2         57  
18 2     2   1743 use IO::Dir;
  2         58425  
  2         137  
19              
20             #use Template;
21 2     2   2202 use Template::Alloy;
  2         64465  
  2         17  
22 2     2   2140 use Template::Constants qw( :debug );
  2         4962  
  2         472  
23              
24 2     2   2436 use Text::MultiMarkdown 'markdown';
  2         101859  
  2         186  
25 2     2   2434 use HTTP::Server::Brick;
  2         231552  
  2         74  
26 2     2   2128 use YAML::Tiny qw(LoadFile Load Dump);
  2         10591  
  2         139  
27 2     2   1725 use File::Spec::Functions qw/ canonpath /;
  2         1542  
  2         128  
28 2     2   12 use File::Path qw(make_path);
  2         3  
  2         102  
29              
30 2     2   3986 use Data::Dumper;
  2         14431  
  2         227  
31             $Data::Dumper::Indent = 1;
32             $Data::Dumper::Sortkeys = 1;
33              
34 2     2   3843 use DateTime;
  2         399572  
  2         93  
35              
36 2     2   1403 use App::Dapper::Init;
  2         9  
  2         111  
37 2     2   16 use App::Dapper::Utils;
  2         5  
  2         44  
38 2     2   1546 use App::Dapper::Defaults;
  2         7  
  2         75  
39 2     2   7019 use App::Dapper::Filters;
  2         9  
  2         11377  
40              
41             my $DEFAULT_PORT = 8000;
42             my $ID = 0;
43              
44             =head1 VERSION
45              
46             Version 0.18
47              
48             =cut
49              
50             our $VERSION = '0.18';
51              
52             our @EXPORT = qw($VERSION);
53              
54             =head1 SYNOPSIS
55              
56             B allows you to transform simple text files into static websites. By installing the App::Dapper Perl module, an executable named C will be available to you in your terminal window. You can use this executable in a number of ways:
57              
58             # Initialize the current directory with a fresh skeleton of a site
59             $ dapper [-solc] init
60              
61             # Build the site
62             $ dapper [-solc] build
63              
64             # Serve the site locally at http://localhost:8000
65             $ dapper [-solc] serve
66              
67             # Rebuild the site if anything (source, layout dirs; config file) changes
68             $ dapper [-solc] watch
69              
70             # Get help on usage and switches
71             $ dapper -h
72              
73             # Print the version
74             $ dapper -v
75              
76             Additionally, B may be used as a perl module directly from a script. Examples:
77              
78             use App::Dapper;
79              
80             # Create a Dapper object
81             my $d = App::Dapper->new();
82              
83             # Initialize a new website in the current directory
84             $d->init();
85              
86             # Build the site
87             $d->build();
88              
89             # Serve the site locally at http://localhost:8000
90             $d->serve();
91              
92             =head1 DESCRIPTION
93              
94             Dapper helps you build static websites. To get you started, you can use the
95             C command to initialize a directory. After running this command,
96             the following directory structure will be created:
97              
98             _config.yml
99             _layout/
100             index.html
101             _source/
102             index.md
103              
104             In that same directory, you may then build the site using the C
105             command, which will combine the source files and the layout files and place
106             the results in the output directory (default: C<_output>). After you build
107             the default site, you'll then have the following directory structure:
108              
109             _config.yml
110             _layout/
111             index.html
112             _source/
113             index.md
114             _output/
115             index.html
116              
117             To see what your website looks like, run the C command which
118             will spin up a development webserver and serve the static files located in
119             the output directory (default: C<_output>) at L.
120              
121             Now, let's walk through each file:
122              
123             =over 4
124              
125             =item B<_config.yml>
126              
127             The configuration file is a YAML file that specifies key configuration
128             elements for your static website. The default configuration file is as
129             follows:
130              
131             ---
132             name : My Site
133              
134             If you want to use a separate source, layout, or output directory, you may
135             specify it in this file. For instance:
136              
137             ---
138             name : My Site
139             source : _source
140             layout : _layout
141             output : _output
142              
143             All of the configurations in this file are available in layout templates,
144             based on the Liquid template system. For instance, C in the
145             configuration file may be used in a template as follows:
146              
147             {{ site.name }}
148              
149             =item B<_source/index.md>
150              
151             A sample markdown file is available in the _source directory. Contents:
152              
153             ---
154             layout: index
155             title: Welcome
156             ---
157              
158             Hello world.
159              
160             There are a few things to note about this file:
161              
162             =over 4
163              
164             =item 1. There is a YAML configuration block at the start of the file.
165              
166             =item 2. The I configuration specifies which layout to use.
167              
168             =item 3. The C layout indicates that C<_layout/index.html> should be used.
169              
170             =item 4. The C configuration is the name of the post/page. It is optional. </td> </tr> <tr> <td class="h" > <a name="171">171</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="172">172</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item 5. All of the configurations may be used in the corresponding layout file. </td> </tr> <tr> <td class="h" > <a name="173">173</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="174">174</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> <!-- Example use of "name" in a layout file --> </td> </tr> <tr> <td class="h" > <a name="175">175</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> <h1>{{ page.name }}</h1> </td> </tr> <tr> <td class="h" > <a name="176">176</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="177">177</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =back </td> </tr> <tr> <td class="h" > <a name="178">178</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="179">179</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item B<_layout/index.html> </td> </tr> <tr> <td class="h" > <a name="180">180</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="181">181</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Layout files are processed using the Liquid template system. The initial layout </td> </tr> <tr> <td class="h" > <a name="182">182</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> file that is given after you run the C<dapper init> command, is this: </td> </tr> <tr> <td class="h" > <a name="183">183</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="184">184</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> </td> </tr> <tr> <td class="h" > <a name="185">185</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> <html> </td> </tr> <tr> <td class="h" > <a name="186">186</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> <head> </td> </tr> <tr> <td class="h" > <a name="187">187</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> <title>{{ page.title }}
188            
189            
190              
191            
192              
193             {{ page.content }}
194              
195            
196            
197              
198             The main content of the text file that is being rendered with this template
199             is available using C<{{ page.content }}>.
200              
201             Definitions specified in the C<_config.yml> file can be referenced under the
202             "site" namespace (e.g. {{ site.name }}. Definitions specified in the YAML
203             portion of text files can be referenced under the "page" namespace (e.g.
204             {{ page.title }}.
205              
206             =item B<_output/index.html>
207              
208             The output file that is created is a mix of the input file and the layout that
209             is specified by the input file. For the default site, the following output
210             file is created:
211              
212            
213            
214            
215             Welcome
216            
217            
218              
219            
220              
221            

Hello world.

222              
223            
224            
225              
226             =back
227              
228             B provides a number of optional command line switches:
229              
230             =head2 Options
231              
232             =over 4
233              
234             =item B<-s>, B<--source>=I
235              
236             Specify the directory containing source files to process. If this command line option is not present, it defaults to "_source".
237              
238             =item B<-o>, B<--output>=I
239              
240             Specify the directory to place the output files in. If this command line option is not present, it defaults to "_output".
241              
242             =item B<-l>, B<--layout>=I
243              
244             Specify the directory containing source files to process. If this command line option is not present, it defaults to "_layout".
245              
246             =item B<-h>
247              
248             Get help on available commands and options.
249              
250             =item B<-v>
251              
252             Print the version and exit.
253              
254             =back
255              
256             =cut
257              
258             =head1 METHODS
259              
260             Dapper may be used directly from a script as well. The following methods are available:
261              
262             =head2 new
263              
264             Create a new B object. Example:
265              
266             my $d = App::Dapper->new();
267              
268             Alternatively, the source dir, output dir, layout dir, and configuration file
269             may be specified. Example:
270              
271             my $d = App::Dapper->new("_source", "_output", "_layout", "_config.yml");
272              
273             After creating a B object, the followin hash elements may be accessed:
274              
275             use App::Dapper;
276              
277             my $d = App::Dapper->new();
278              
279             print "Source directory: $d->{source}\n";
280             print "Output directory: $d->{output}\n";
281             print "Layout directory: $d->{layout}\n";
282             print "Config file: $d->{config}\n";
283             print "Object instantiation time: $d->{site}->{time}\n";
284              
285             =cut
286              
287             sub new {
288 1     1 1 335 my $class = shift;
289 1         10 my $self = {
290             created=> 1,
291             source => shift,
292             output => shift,
293             layout => shift,
294             config => shift,
295             };
296              
297 1         6 $self->{site} = App::Dapper::Defaults::get_defaults();
298 1         865 $self->{site}->{time} = DateTime->now( time_zone => DateTime::TimeZone->new( name => 'local' ) );
299 1 50       12401 $self->{source} = "_source" unless defined($self->{source});
300 1 50       5 $self->{output} = "_output" unless defined($self->{output});
301 1 50       7 $self->{layout} = "_layout" unless defined($self->{layout});
302 1 50       4 $self->{config} = "_config.yml" unless defined($self->{config});
303              
304 1         5 bless $self, $class;
305 1         4 return $self;
306             }
307              
308             =head2 init
309              
310             Initializes a new skeleton project in the current directory (of the calling script, and uses the defined source dir, output dir, layout dir, and config file. Example usage:
311              
312             use App::Dapper;
313              
314             my $d = App::Dapper->new();
315             $d->init();
316              
317             =cut
318              
319             sub init {
320 0     0 1   my ($self) = @_;
321              
322 0           App::Dapper::Init::init(
323             $self->{source},
324             $self->{output},
325             $self->{layout},
326             $self->{config}
327             );
328              
329 0           print "Project initialized.\n";
330             }
331              
332             =head2 build
333              
334             Build the site. Example:
335              
336             use App::Dapper;
337            
338             my $d = App::Dapper->new();
339             $d->build();
340              
341             When the site is built, it is done in three steps:
342              
343             1. Parse. In this step, the configuration file is read. In addition, all the source files in the source directory as well as the layout files in the layout directory are reach and stored in the site hash.
344            
345             2. Transform. Combine source and layout files.
346              
347             3. Render. Save output files to the output directory.
348              
349             =cut
350              
351             sub build {
352 0     0 1   my($self) = @_;
353            
354 0           $ID = 0;
355              
356 0           $self->parse();
357 0           $self->transform();
358 0           $self->render();
359              
360 0           print "Project built.\n";
361             }
362              
363             # sub parse - Parse the source directory, config file, and templates.
364              
365             sub parse {
366 0     0 0   my ($self) = @_;
367              
368             # load program and project configuration
369 0           $self->read_project();
370              
371             # replaces the values of h_template with actual content
372 0           $self->read_templates();
373             }
374              
375             # sub transform - Walk the source and output directories and transform the text into the output files.
376              
377             sub transform {
378 0     0 0   my ($self) = @_;
379              
380             # recurse through the project tree and generate output (combine src with templates)
381 0           $self->walk($self->{source}, $self->{output});
382             }
383              
384             # sub render - Render the internal hash of source files, config file, and layouts into the actual output files on disk.
385              
386             sub render {
387 0     0 0   my ($self) = @_;
388              
389 0   0       my $tt = Template::Alloy->new({
390             INCLUDE_PATH => $self->{layout},
391             ANYCASE => 1,
392             ENCODING => 'utf8',
393             #STRICT => 1,
394             FILTERS => {
395             'xml_escape' => \&App::Dapper::Filters::xml_escape,
396             'date_to_xmlschema' => \&App::Dapper::Filters::date_to_xmlschema,
397             'replace_last' => \&App::Dapper::Filters::replace_last,
398             'smart' => \&App::Dapper::Filters::smart,
399             'json' => \&App::Dapper::Filters::json,
400             },
401             #DEBUG => DEBUG_ALL,
402             }) || die "$Template::ERROR\n";
403              
404 0           for my $page (@{$self->{site}->{pages}}) {
  0            
405              
406             #print Dump $page->{content};
407              
408 0 0         if (not $page->{layout}) { $page->{layout} = "index"; }
  0            
409              
410 0           my $layout = $self->{layout_content}->{$page->{layout}};
411              
412 0           my %tags = ();
413 0           $tags{site} = $self->{site};
414 0           $tags{page} = $page;
415             #$tags{page}->{content} = $content;
416              
417 0           my $destination1;
418              
419 0 0         $tt->process(\$layout, \%tags, \$destination1)
420             || die $tt->error(), "\n";
421              
422             # Parse and render once more to make sure that any liquid statments
423             # In the source file also gets rendered
424 0           my $destination;
425              
426 0 0         $tt->process(\$destination1, \%tags, \$destination)
427             || die $tt->error(), "\n";
428              
429 0 0         if ($page->{filename}) {
430 0           make_path($page->{dirname}, { verbose => 1 });
431 0 0         open(DESTINATION, ">$page->{filename}") or die "error: could not open destination file:$page->{filename}: $!\n";
432 0 0         print(DESTINATION $destination) or die "error: could not print to $page->{filename}: $!\n";
433 0 0         close(DESTINATION) or die "error: could not close $page->{filename}: $!\n";
434              
435 0           print "Wrote $page->{filename}\n";
436             #print Dumper $page;
437             }
438             else {
439 0           print Dumper "No filename specified\n";
440             }
441             }
442              
443             #print Dumper $self->{site};
444              
445             #print Dumper($self->{site});
446             # copy additional files and directories
447 0           $self->copy(".", $self->{output});
448             }
449              
450             # sub serve - Serve the site locally. Pass in the port number. The port number will be used to serve the site contents from the output directory like this: http://localhost:. Here is an example, using the default port 8000:
451             #
452             # use App::Dapper;
453             #
454             # my $d = App::Dapper->new();
455             # $d->serve("8000");
456             #
457             # The following is equivalent:
458             #
459             # $d->serve();
460              
461             sub serve {
462 0     0 0   my($self, $port) = @_;
463              
464 0 0         $port = $DEFAULT_PORT unless $port;
465              
466 0           my $s = HTTP::Server::Brick->new(port=>$port);
467 0           $s->add_type('text/html' => qw(^[^\.]+$));
468 0           $s->mount("/"=>{ path => $self->{output} });
469              
470 0           $s->start
471             }
472              
473             # sub read_project - Read the project file.
474              
475             sub read_project {
476 0     0 0   my ($self) = @_;
477              
478 0 0         my $config = LoadFile($self->{config}) or die "error: could not load \"$self->{config}\": $!\n";
479              
480             # Graft together
481 0           @{$self->{site}}{keys %{$config}} = values %$config;
  0            
  0            
482              
483 0 0         die "error: \"source\" must be defined in project file\n" unless defined $self->{source};
484 0 0         die "error: \"output\" must be defined in project file\n" unless defined $self->{output};
485 0 0         die "error: \"layout\" must be defined in project file\n" unless defined $self->{layout};
486              
487             #print Dump($self->{site});
488             }
489              
490             # sub read_templates - Read the content of the templates specified in the project configuration file.
491              
492             sub read_templates {
493 0     0 0   my ($self) = @_;
494 0           my ($key, $ckey);
495              
496 0 0         opendir(DIR, $self->{layout}) or die $!;
497 0           my @files = sort(grep(!/^\..*$/, readdir(DIR)));
498              
499 0           for my $file (@files) {
500 0           my $stem = App::Dapper::Utils::filter_stem($file);
501 0           $file = $self->{layout} . "/" . $file;
502 0           $self->{layout_content}->{$stem} = App::Dapper::Utils::read_file($file);
503             #print "$stem layout content:\n";
504             #print $self->{layout_content}->{$stem};
505             }
506              
507             # Expand sub layouts
508 0           for my $key (keys %{$self->{layout_content}}) {
  0            
509 0           my $value = $self->{layout_content}->{$key};
510 0           my $frontmatter;
511             my $content;
512              
513 0           $value =~ /(---.*?)---(.*)/s;
514              
515 0 0         if (not defined $1) { next; }
  0            
516 0 0         if (not defined $2) { next; }
  0            
517              
518 0           $frontmatter = Load($1);
519 0           $content = $2;
520              
521 0 0         if (not defined $frontmatter->{layout}) { next; }
  0            
522 0 0         if (not defined $self->{layout_content}->{$frontmatter->{layout}}) { next; }
  0            
523              
524 0           my $master = $self->{layout_content}->{$frontmatter->{layout}};
525 0           $master =~ s/\[\% *page\.content *\%\]/$content/g;
526 0           $self->{layout_content}->{$key} = $master;
527              
528             #print "$key Result:\n" . $self->{layout_content}->{$frontmatter->{layout}} . "\n\n\n\n\n\n";
529             }
530             }
531              
532             # sub walk - Walk the source directory and output directory and build the site hash.
533              
534             sub walk {
535 0     0 0   my ($self, $source_dir, $output_dir) = @_;
536 0           my $source_handle = new IO::Dir "$source_dir";;
537 0           my $output_handle = new IO::Dir "$output_dir";;
538 0           my $directory_element;
539              
540             # if the directory does not exist, create it
541 0 0         if (!(defined $output_handle)) {
542 0           mkdir($output_dir);
543 0           $output_handle = new IO::Dir "$output_dir";
544             }
545              
546 0 0         if (defined $source_handle) {
547              
548             # cycle through each element in the current directory
549 0           while(defined($directory_element = $source_handle->read)) {
550              
551             # print "directory element:$source/$directory_element\n";
552 0 0 0       if(-d "$source_dir/$directory_element" and $directory_element ne "." and $directory_element ne "..") {
    0 0        
      0        
      0        
553 0           $self->walk("$source_dir/$directory_element", "$output_dir/$directory_element");
554             }
555             elsif(-f "$source_dir/$directory_element" and $directory_element ne "." and $directory_element ne "..") {
556            
557             # Skip dot files
558 0 0         if ($directory_element =~ /^\./) { next; }
  0            
559            
560             # Construct output file name, which is a combination
561             # of the stem of the source file and the extension of the template.
562             # Example:
563             # - Source: index.md
564             # - Template: layout.html
565             # - Destination: index.html
566 0           my $source = "$source_dir/$directory_element";
567 0           my $output = "$output_dir/$directory_element";
568            
569 0           $self->build_inventory($source, $output);
570             }
571             }
572 0           undef $source_handle;
573             }
574             else {
575 0           die "error: could not get a handle on $source_dir/$directory_element";
576             }
577             #undef %b_ddm;
578             }
579              
580             # sub build_inventory - Build the internal hash of files, configurations, and layouts.
581              
582             sub build_inventory {
583 0     0 0   my ($self, $source_file_name, $destination_file_name) = @_;
584              
585 0           my %page = ();
586              
587 0           my $source_content = App::Dapper::Utils::read_file($source_file_name);
588              
589 0           $source_content =~ /(---.*?)---(.*)/s;
590              
591 0           my ($frontmatter) = Load($1);
592 0           $page{content} = $2;
593              
594 0           for my $key (keys %{$frontmatter}) {
  0            
595 0           $page{$key} = $frontmatter->{$key};
596             }
597              
598 0           $page{slug} = App::Dapper::Utils::slugify($page{title});
599              
600 0 0         if (not $page{date}) {
601 0           my $date = App::Dapper::Utils::get_modified_time($source_file_name);
602             #print "Didn't find date for $source_file_name. Setting to file modified date of $date\n";
603 0           $page{date} = $date;
604             }
605            
606 0 0         if($page{date} =~ /^(\d\d\d\d)-(\d\d)-(\d\d) (\d\d)\:(\d\d)\:(\d\d)$/) {
607 0           $page{year} = $1;
608 0           $page{month} = $2;
609 0           $page{day} = $3;
610 0           $page{hour} = $4;
611 0           $page{minute} = $5;
612 0           $page{second} = $6;
613 0           $page{nanosecond} = 0;
614             }
615              
616 0 0         if(not $page{timezone}) {
617 0           $page{timezone} = DateTime::TimeZone->new( name => 'local' );
618             }
619              
620 0           $page{date} = DateTime->new(
621             year => $page{year},
622             month => $page{month},
623             day => $page{day},
624             hour => $page{hour},
625             minute => $page{minute},
626             second => $page{second},
627             nanosecond => $page{nanosecond},
628             time_zone => $page{timezone},
629             );
630              
631 0 0         $page{url} = defined $page{urlpattern} ? $page{urlpattern} : $self->{site}->{urlpattern};
632 0 0         $page{url} =~ s/\:category/$page{categories}/g unless not defined $page{categories};
633 0 0         $page{url} =~ s/\:year/$page{year}/g unless not defined $page{year};
634 0 0         $page{url} =~ s/\:month/$page{month}/g unless not defined $page{month};
635 0 0         $page{url} =~ s/\:day/$page{day}/g unless not defined $page{day};
636 0 0         $page{url} =~ s/\:hour/$page{hour}/g unless not defined $page{hour};
637 0 0         $page{url} =~ s/\:minute/$page{minute}/g unless not defined $page{minute};
638 0 0         $page{url} =~ s/\:second/$page{second}/g unless not defined $page{second};
639 0 0         $page{url} =~ s/\:slug/$page{slug}/g unless not defined $page{slug};
640              
641 0           $page{id} = $page{url};
642              
643 0           $page{id} = ++$ID; #$page{url};
644              
645 0 0         if (not defined $page{extension}) { $page{extension} = $self->{site}->{extension}; }
  0            
646              
647 0           $page{source_file_extension} = App::Dapper::Utils::filter_extension($source_file_name);
648              
649 0           $page{filename} = App::Dapper::Utils::filter_stem("$destination_file_name") . $page{extension};
650            
651 0 0         if(defined $page{categories}) {
652 0           my $filename = $self->{site}->{output} . $page{url};
653 0           $filename =~ s/\/$/\/index.html/;
654 0           $page{filename} = canonpath $filename;
655             }
656              
657 0           my ($volume, $dirname, $file) = File::Spec->splitpath( $page{filename} );
658 0           $page{dirname} = $dirname;
659              
660 0 0         if ($page{source_file_extension} eq ".md") {
661 0           $page{content} = markdown($page{content});
662              
663             # Save first paragraph of content as excerpt
664 0           $page{content} =~ /(

.*?<\/p>)/s;

665 0           $page{excerpt} = $1;
666             }
667             else {
668 0           print "Did not run markdown on $page{filename} since the extension was not .md\n";
669             }
670              
671             # Remove leading spaces and newline
672 0           $page{content} =~ s/^[ \n]*//;
673            
674 0 0         if ($page{categories}) {
675 0           push @{$self->{site}->{categories}->{$page{categories}}}, \%page;
  0            
676             }
677              
678 0           push @{$self->{site}->{pages}}, \%page;
  0            
679             }
680              
681             # sub copy(sourcedir, outputdir) - Copies files and directories from to as long as they do not made what is contained in $self->{site}->{ignore}.
682              
683             sub copy {
684 0     0 0   my ($self, $dir, $output) = @_;
685              
686 0 0         opendir(DIR, $dir) or die $!;
687              
688 0           DIR: while (my $file = readdir(DIR)) {
689 0           for my $i (@{ $self->{site}->{ignore} }) {
  0            
690 0 0         next DIR if ($file =~ m/$i/);
691             }
692              
693 0           $output =~ s/\/$//;
694 0           my $command = "cp -r $file $output";
695 0           print $command . "\n";
696 0           system($command);
697             }
698              
699 0           closedir(DIR);
700             }
701              
702             =head1 AUTHOR
703              
704             Mark Benson, C<< >>
705              
706             =head1 BUGS
707              
708             Please report any bugs or feature requests to C, or through
709             the web interface at L. I will be notified, and then you'll
710             automatically be notified of progress on your bug as I make changes.
711              
712             =head1 SUPPORT
713              
714             You can find documentation for this module with the perldoc command.
715              
716             perldoc App::Dapper
717              
718             You can also look for information at:
719              
720             =over 4
721              
722             =item * RT: CPAN's request tracker (report bugs here)
723              
724             L
725              
726             =item * AnnoCPAN: Annotated CPAN documentation
727              
728             L
729              
730             =item * CPAN Ratings
731              
732             L
733              
734             =item * Search CPAN
735              
736             L
737              
738             =back
739              
740             =head1 LICENSE AND COPYRIGHT
741              
742             The MIT License (MIT)
743              
744             Copyright (c) 2002-2014 Mark Benson
745              
746             Permission is hereby granted, free of charge, to any person obtaining a copy
747             of this software and associated documentation files (the "Software"), to deal
748             in the Software without restriction, including without limitation the rights
749             to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
750             copies of the Software, and to permit persons to whom the Software is
751             furnished to do so, subject to the following conditions:
752              
753             The above copyright notice and this permission notice shall be included in all
754             copies or substantial portions of the Software.
755              
756             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
757             IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
758             FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
759             AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
760             LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
761             OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
762             SOFTWARE.
763              
764             =cut
765              
766             1; # End of App::Dapper
767