File Coverage

blib/lib/HiD.pm
Criterion Covered Total %
statement 170 237 71.7
branch 23 66 34.8
condition 10 30 33.3
subroutine 32 37 86.4
pod 1 1 100.0
total 236 371 63.6


line stmt bran cond sub pod time code
1             # ABSTRACT: Static website publishing framework
2              
3              
4             package HiD;
5             our $AUTHORITY = 'cpan:GENEHACK';
6             $HiD::VERSION = '1.991';
7 12     12   2696246 use Moose;
  12         2074963  
  12         123  
8 12     12   78290 use namespace::autoclean;
  12         36539  
  12         1372  
9             # note: we also do 'with HiD::Role::DoesLogging', just later on because reasons.
10              
11 12     12   963 use 5.014; # strict, unicode_strings
  12         48  
12 12     12   3092 use utf8;
  12         110  
  12         60  
13 12     12   2774 use autodie;
  12         109209  
  12         54  
14 12     12   68450 use warnings;
  12         27  
  12         435  
15 12     12   69 use warnings qw/ FATAL utf8 /;
  12         23  
  12         473  
16 12     12   3114 use open qw/ :std :utf8 /;
  12         8835  
  12         68  
17 12     12   4045 use charnames qw/ :full /;
  12         215195  
  12         92  
18              
19 12     12   2809 use Class::Load qw/ try_load_class /;
  12         24  
  12         701  
20 12     12   7032 use DateTime;
  12         4037464  
  12         578  
21 12     12   4797 use File::Find::Rule;
  12         73526  
  12         87  
22 12     12   1260 use Path::Tiny;
  12         8310  
  12         604  
23 12     12   67 use Try::Tiny;
  12         21  
  12         528  
24 12     12   1372 use YAML::Tiny;
  12         13873  
  12         593  
25              
26 12     12   4107 use HiD::File;
  12         38  
  12         569  
27 12     12   5928 use HiD::Layout;
  12         43  
  12         452  
28 12     12   4345 use HiD::Page;
  12         44  
  12         493  
29 12     12   5031 use HiD::Pager;
  12         5095  
  12         491  
30 12     12   4659 use HiD::Post;
  12         47  
  12         464  
31 12     12   88 use HiD::Types;
  12         24  
  12         49343  
32              
33              
34             has categories => (
35             is => 'ro' ,
36             isa => 'Maybe[HashRef[ArrayRef[HiD::Post]]]' ,
37             lazy => 1 ,
38             builder => '_build_categories' ,
39             );
40              
41             sub _build_categories {
42 0     0   0 my $self = shift;
43              
44 0 0       0 return undef unless $self->posts;
45              
46 0         0 my $categories_hash = {};
47 0         0 foreach my $post ( @{$self->posts} ) {
  0         0  
48 0         0 push @{ $categories_hash->{$_} }, $post for @{ $post->categories };
  0         0  
  0         0  
49             }
50              
51 0         0 return $categories_hash;
52             }
53              
54              
55             has cli_opts => (
56             is => 'ro' ,
57             isa => 'HashRef' ,
58             lazy => 1 ,
59             default => sub {{}} ,
60             );
61              
62              
63             has config => (
64             is => 'ro' ,
65             isa => 'HashRef' ,
66             traits => [ 'Hash' ] ,
67             lazy => 1 ,
68             builder => '_build_config' ,
69             handles => { get_config => 'get' } ,
70             );
71              
72             sub _build_config {
73 26     26   51 my $self = shift;
74              
75 26         53 my( $config , $config_loaded );
76              
77 26 100       533 if ( my $file = $self->config_file ) {
78             try {
79 24   50 24   986 $config = YAML::Tiny->read( $file )->[0] // {};
80 19 50       10730 ref $config eq 'HASH' or die $!;
81 19         60 $config_loaded++;
82 24         208 };
83             }
84              
85 26 100 33     3864 $config_loaded or $config = {}
86             and warn( "Could not read configuration. Using defaults (and options).\n" );
87              
88             return {
89 26         659 %{ $self->default_config } ,
90             %$config ,
91 26         364 %{ $self->cli_opts } ,
  26         542  
92             };
93             }
94              
95             # this is down here so it will see the 'get_config' delegation...
96             with 'HiD::Role::DoesLogging';
97              
98              
99             has config_file => (
100             is => 'ro' ,
101             isa => 'Str' ,
102             );
103              
104              
105             has default_config => (
106             is => 'ro' ,
107             isa => 'HashRef' ,
108             traits => [ 'Hash' ] ,
109             init_arg => undef ,
110             default => sub{{
111             default_author => 'your name here!' ,
112             destination => '_site' ,
113             include_dir => '_includes',
114             layout_dir => '_layouts' ,
115             plugin_dir => '_plugins' ,
116             posts_dir => '_posts' ,
117             drafts_dir => '_drafts' ,
118             source => '.' ,
119             }},
120             );
121              
122              
123             has destination => (
124             is => 'ro' ,
125             isa => 'HiD_DirPath' ,
126             lazy => 1 ,
127             default => sub {
128             my $dest = shift->get_config( 'destination' );
129             path( $dest )->mkpath() unless -e -d $dest;
130             return $dest;
131             },
132             );
133              
134              
135             has draft_post_file_regex => (
136             is => 'ro' ,
137             isa => 'RegexpRef' ,
138             default => sub { qr/^(?:.+?)\.(?:mk|mkd|mkdn|markdown|md|mmd|text|textile|html)$/ },
139             );
140              
141              
142             has excerpt_separator => (
143             is => 'ro' ,
144             isa => 'Str' ,
145             lazy => 1 ,
146             default => sub { shift->get_config( 'excerpt_separator' ) // "\n\n" }
147             );
148              
149              
150             has include_dir => (
151             is => 'ro' ,
152             isa => 'Maybe[HiD_DirPath]' ,
153             lazy => 1 ,
154             default => sub {
155             my $self = shift;
156             my $dir = $self->get_config( 'include_dir' );
157             ( -e -d '_includes' ) ? $dir : undef;
158             },
159             );
160              
161              
162             has inputs => (
163             is => 'ro' ,
164             isa => 'HashRef',
165             default => sub {{}} ,
166             traits => ['Hash'],
167             handles => {
168             add_input => 'set' ,
169             seen_input => 'exists' ,
170             },
171             );
172              
173              
174             has layout_dir => (
175             is => 'ro' ,
176             isa => 'HiD_DirPath' ,
177             lazy => 1 ,
178             default => sub { shift->get_config( 'layout_dir' ) },
179             );
180              
181              
182             has layouts => (
183             is => 'ro' ,
184             isa => 'HashRef[HiD::Layout]',
185             lazy => 1 ,
186             builder => '_build_layouts',
187             traits => ['Hash'] ,
188             handles => {
189             get_layout_by_name => 'get' ,
190             },
191             );
192              
193             sub _build_layouts {
194 16     16   23 my $self = shift;
195              
196 16         58 $self->INFO( "Building layouts" );
197              
198 16         561 my @layout_files = File::Find::Rule->file
199             ->in( $self->layout_dir );
200              
201 16         9997 my %layouts;
202 16         47 foreach my $layout_file ( @layout_files ) {
203 31         902 my $dir = $self->layout_dir;
204              
205 31         245 my( $layout_name , $extension ) = $layout_file
206             =~ m|^$dir/(.*)\.([^.]+)$|;
207              
208 31         595 $layouts{$layout_name} = HiD::Layout->new({
209             filename => $layout_file,
210             processor => $self->processor ,
211             });
212              
213 30         775 $self->add_input( $layout_file => 'layout' );
214              
215 30         136 $self->DEBUG( "* Added layout $layout_file" );
216             }
217              
218 15         256 foreach my $layout_name ( keys %layouts ) {
219 30         597 my $metadata = $layouts{$layout_name}->metadata;
220              
221 30 50       75 if ( my $embedded_layout = $metadata->{layout} ) {
222             die "FIXME embedded layout fail $embedded_layout"
223 0 0       0 unless $layouts{$embedded_layout};
224              
225             $layouts{$layout_name}->set_layout(
226 0         0 $layouts{$embedded_layout}
227             );
228             }
229             }
230              
231 15         262 return \%layouts;
232             }
233              
234              
235             has limit_posts => (
236             is => 'ro' ,
237             isa => 'HiD_PosInt' ,
238             );
239              
240              
241             has objects => (
242             is => 'ro' ,
243             isa => 'ArrayRef[Object]' ,
244             traits => [ 'Array' ] ,
245             default => sub {[]},
246             handles => {
247             add_object => 'push' ,
248             all_objects => 'elements' ,
249             },
250             );
251              
252              
253             has page_file_regex => (
254             is => 'ro' ,
255             isa => 'RegexpRef',
256             default => sub { qr/\.(mk|mkd|mkdn|markdown|mmd|textile|html|htm|xml|xhtml|xhtm|shtm|shtml|rss)$/ } ,
257             );
258              
259              
260             has pages => (
261             is => 'ro',
262             isa => 'Maybe[ArrayRef[HiD::Page]]',
263             lazy => 1 ,
264             builder => '_build_pages' ,
265             );
266              
267             sub _build_pages {
268 16     16   26 my $self = shift;
269              
270             # build posts before pages
271 16         301 $self->posts;
272              
273 15         52 $self->INFO( "Posts built." );
274              
275 15         510 my @potential_pages = File::Find::Rule->file->
276             name( $self->page_file_regex )->in( '.' );
277              
278 144         165 my @pages = grep { $_ } map {
279 15 100 100     21375 if ($self->seen_input( $_ ) or $_ =~ /^_/ ) { 0 }
  144         3915  
  109         187  
280             else {
281             try {
282 35     35   2150 my $page = HiD::Page->new({
283             dest_dir => $self->destination,
284             hid => $self ,
285             input_filename => $_ ,
286             layouts => $self->layouts ,
287             });
288 35         837 $page->content;
289 35         820 $self->add_input( $_ => 'page' );
290 35         927 $self->add_object( $page );
291 35         92 $page;
292             }
293 35     0   237 catch { 0 };
  0         0  
294             }
295             } @potential_pages;
296              
297 15         284 return \@pages;
298             }
299              
300              
301             has plugin_dir => (
302             is => 'ro',
303             isa => 'Maybe[HiD_DirPath]',
304             lazy => 1,
305             default => sub {
306             my $dir = shift->get_config('plugin_dir');
307             (-e -d $dir) ? $dir : undef;
308             },
309             );
310              
311              
312             has plugins => (
313             is => 'ro' ,
314             isa => 'ArrayRef[Pluginish]' ,
315             lazy => 1 ,
316             builder => '_build_plugins' ,
317             );
318              
319             sub _build_plugins {
320 15     15   31 my $self = shift;
321              
322 15         21 my @loaded_plugins;
323              
324 15 50       288 if ( my $plugin_list = $self->config->{plugins} ){
325 0 0       0 my @plugins = ( ref $plugin_list eq 'ARRAY' ) ? @$plugin_list
326             : (split /\s+/ , $plugin_list );
327              
328 0         0 foreach ( @plugins ) {
329              
330 0 0       0 my $plugin_name = ( /^\+/ ) ? $_ : "HiD::Generator::$_";
331 0         0 $self->INFO( "* Loading plugin $plugin_name" );
332 0 0       0 next unless _load_plugin_or_warn( $plugin_name );
333 0         0 push @loaded_plugins , $plugin_name->new;
334             }
335             }
336              
337 15 50       281 if ( my $plugin_dir = $self->plugin_dir ) {
338             # plugin modules in plugin_dir
339 0         0 my @mods = File::Find::Rule->file->
340             name( '*.pm' )->in( $plugin_dir );
341              
342 0         0 push @INC , $plugin_dir;
343              
344 0         0 foreach my $m ( @mods ) {
345 0         0 $m =~ s|$plugin_dir/?||;
346 0         0 $m =~ s|.pm$||;
347 0         0 $self->INFO("* Loading plugin $m" );
348 0 0       0 next unless _load_plugin_or_warn( $m );
349 0         0 push @loaded_plugins, $m->new;
350             }
351             }
352              
353 15         257 return \@loaded_plugins;
354             }
355              
356             sub _load_plugin_or_warn {
357 0     0   0 my $plugin = shift;
358              
359 0         0 my( $lrlt , $lerr ) = try_load_class( $plugin );
360              
361 0 0 0     0 warn "plugin $plugin cannot be loaded : $lerr \n" and return undef
362             unless $lrlt;
363              
364 0 0 0     0 ( $plugin->isa('HiD::Plugin') or
      0        
      0        
365             $plugin->does('HiD::Plugin') or
366             $plugin->does('HiD::Generator') )
367             or warn "plugin $plugin is not a valid plugin.\n"
368             and return undef;
369              
370 0         0 return 1;
371             }
372              
373              
374             has post_file_regex => (
375             is => 'ro' ,
376             isa => 'RegexpRef' ,
377             default => sub { qr/^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}-(?:.+?)\.(?:mk|mkd|mkdn|markdown|md|mmd|text|textile|html)$/ },
378             );
379              
380              
381             has posts_dir => (
382             is => 'ro' ,
383             isa => 'HiD_DirPath' ,
384             lazy => 1 ,
385             default => sub { shift->get_config( 'posts_dir' ) },
386             );
387              
388              
389             has posts => (
390             is => 'ro' ,
391             isa => 'ArrayRef[HiD_Post]' ,
392             traits => [ qw/ Array / ] ,
393             handles => { posts_size => 'count' } ,
394             lazy => 1 ,
395             builder => '_build_posts' ,
396             );
397              
398             sub _build_posts {
399 16     16   30 my $self = shift;
400              
401             # build layouts before posts
402 16         293 $self->layouts;
403              
404 15         57 $self->INFO("Layouts built.");
405              
406 15         206 $self->INFO("Building posts." );
407              
408 15         223 my $rule = File::Find::Rule->new;
409              
410 15         173 my @posts_directories = $rule->or(
411             $rule->new->directory->name( $self->get_config( 'posts_dir' )) ,
412             $rule->new->directory->name( $self->get_config( 'destination' ))->prune->discard ,
413             )->in( $self->source );
414              
415 15         18303 my @potential_posts = File::Find::Rule->file
416             ->name( $self->post_file_regex )->in( @posts_directories );
417              
418 15 50       10208 if ( $self->get_config( 'publish_drafts' )){
419 0         0 push @potential_posts , $self->_build_potential_draft_posts_list ,
420             }
421              
422 44         143 my @posts = grep { $_ } map {
423 15         40 try {
424 44     44   3422 $self->DEBUG( "* Trying to build post $_" );
425 44         1268 my $post = HiD::Post->new({
426             dest_dir => $self->destination,
427             hid => $self ,
428             input_filename => $_ ,
429             layouts => $self->layouts ,
430             });
431 43         962 $self->add_input( $_ => 'post' );
432 43         1022 $self->add_object( $post );
433 43         186 $self->DEBUG( "* Built post $_" );
434 43         640 $post;
435             }
436 44     1   732 catch { $self->ERROR( "ERROR: Post failed to build: $_" ) ; return 0 };
  1         22  
  1         315  
437             } @potential_posts;
438              
439 15         48 @posts = sort { $b->date <=> $a->date } @posts;
  121         8833  
440              
441 15 50       647 if ( my $limit = $self->limit_posts ) {
442 0 0       0 die "--limit_posts must be positive" if $limit < 1;
443 0         0 @posts = splice( @posts , -$limit , $limit );
444             }
445              
446 15         419 return \@posts;
447             }
448              
449             sub _build_potential_draft_posts_list {
450 0     0   0 my( $self ) = @_;
451              
452 0         0 my $rule = File::Find::Rule->new;
453              
454 0         0 my @posts_directories = $rule->or(
455             $rule->new->directory->name( $self->get_config( 'drafts_dir' )) ,
456             $rule->new->directory->name( $self->get_config( 'destination' ))->prune->discard ,
457             )->in( $self->source );
458              
459 0         0 my @potential_posts = File::Find::Rule->file
460             ->name( $self->draft_post_file_regex )->in( @posts_directories );
461              
462 0         0 return @potential_posts;
463             }
464              
465              
466             has processor => (
467             is => 'ro' ,
468             isa => 'HiD::Processor' ,
469             lazy => 1 ,
470             predicate => 'has_processor',
471             default => sub {
472             my $self = shift;
473              
474             my $processor_name = $self->get_config( 'processor_name' ) // 'Handlebars';
475              
476             my $processor_class = ( $processor_name =~ /^\+/ ) ? $processor_name
477             : "HiD::Processor::$processor_name";
478              
479             try_load_class( $processor_class );
480              
481             return $processor_class->new( $self->processor_args );
482             },
483             );
484              
485              
486             has processor_args => (
487             is => 'ro' ,
488             isa => 'ArrayRef|HashRef' ,
489             lazy => 1 ,
490             default => sub {
491             my $self = shift;
492              
493             my $processor_args = defined $self->get_config('processor_args') ? $self->get_config('processor_args') : {};
494              
495             if(ref $processor_args eq 'HASH' && !exists $processor_args->{path}) {
496             my @path = ( $self->layout_dir );
497             push @path , $self->include_dir
498             if defined $self->include_dir;
499             $processor_args->{path} = \@path;
500             }
501              
502             return $processor_args;
503             },
504             );
505              
506              
507             has regular_files => (
508             is => 'ro',
509             isa => 'Maybe[ArrayRef[HiD::File]]',
510             lazy => 1 ,
511             builder => '_build_regular_files' ,
512             );
513              
514             sub _build_regular_files {
515 16     16   37 my $self = shift;
516              
517             # build pages before regular files
518 16         306 $self->pages;
519              
520 15         53 $self->INFO( "Pages built" );
521              
522 15         523 my @potential_files = File::Find::Rule->file->in( '.' );
523              
524 206         215 my @files = grep { $_ } map {
525 15 100 100     20679 if ($self->seen_input( $_ ) or $_ =~ /^_/ ) { 0 }
  206 50 33     4757  
  181 50       324  
526 0         0 elsif( $_ =~ /^\.git/ ) { 0 }
527 0         0 elsif( $_ =~ /^\.svn/ or $_ =~ /\/\.svn\// ) { 0 }
528             else {
529 25         454 my $file = HiD::File->new({
530             dest_dir => $self->destination,
531             input_filename => $_ ,
532             });
533 25         644 $self->add_input( $_ => 'file' );
534 25         602 $self->add_object( $file );
535 25         50 $file;
536             }
537             } @potential_files;
538              
539 15         293 return \@files;
540             }
541              
542              
543             has remove_unwritten_files => (
544             is => 'ro' ,
545             isa => 'Bool' ,
546             lazy => 1 ,
547             default => sub {
548             my $self = shift;
549             return $self->get_config('remove_unwritten_files')
550             if defined $self->get_config('remove_unwritten_files');
551              
552             return 1;
553             },
554             );
555              
556              
557             has source => (
558             is => 'ro' ,
559             isa => 'HiD_DirPath' ,
560             lazy => 1 ,
561             default => sub {
562             my $self = shift;
563             my $source = $self->get_config( 'source' );
564             chdir $source or die $!;
565             return $source;
566             },
567             );
568              
569              
570             has 'tags' => (
571             is => 'ro',
572             isa => 'Maybe[HashRef[ArrayRef[HiD::Post]]]',
573             lazy => 1,
574             builder => '_build_tags',
575             );
576              
577             sub _build_tags {
578 0     0   0 my $self = shift;
579              
580 0 0       0 return undef unless $self->posts;
581              
582 0         0 my $tags_hash = {};
583 0         0 foreach my $post (@{$self->posts}) {
  0         0  
584 0         0 push @{$tags_hash->{$_}}, $post for @{$post->tags};
  0         0  
  0         0  
585             }
586 0         0 return $tags_hash;
587             }
588              
589              
590             has time => (
591             is => 'ro',
592             isa => 'DateTime' ,
593             init_arg => undef ,
594             default => sub { DateTime->now() } ,
595             );
596              
597              
598              
599             has written_files => (
600             is => 'ro' ,
601             isa => 'HashRef' ,
602             traits => [ qw/ Hash / ] ,
603             default => sub {{}},
604             handles => {
605             add_written_file => 'set' ,
606             all_written_files => 'keys' ,
607             wrote_file => 'defined' ,
608             },
609             );
610              
611              
612             sub publish {
613 16     16 1 1174 my( $self ) = @_;
614              
615 16 50 33     295 if ( -e $self->destination && $self->get_config( 'clean_destination' )){
616 0         0 my $path = path( $self->destination );
617 0         0 $path->remove_tree();
618 0         0 $self->INFO( "cleaned destination directory" );
619 0         0 $path->mkpath();
620             }
621              
622 16         73 $self->INFO( "publish" );
623              
624             # bootstrap data structures
625             # FIXME should have a more explicit way to do this
626 16         615 $self->regular_files;
627              
628 15         52 $self->INFO( "files bootstrapped" );
629              
630 15         438 $self->add_written_file( $self->destination => '_site_dir' );
631              
632 15         64 $self->INFO( "processing plugins for generate()" );
633              
634 15         186 foreach my $plugin ( @{ $self->plugins } ) {
  15         284  
635 0 0       0 if ( $plugin->does( 'HiD::Generator' )) {
636 0         0 $plugin->generate($self)
637             }
638             }
639              
640 15 50       258 if ( $self->config->{pagination} ){
641             my $entries_per_page = $self->config->{pagination}{entries}
642 0 0       0 or die "Must set 'pagination.entries' key in pagination config";
643              
644             my $page_fstring = $self->config->{pagination}{page}
645 0 0       0 or die "Must set 'pagination.page' key in pagination config";
646              
647             my $template = $self->config->{pagination}{template}
648 0 0       0 or die "Must set 'pagination.template' key in pagination config";
649              
650 0         0 my $pager = HiD::Pager->new({
651             entries => $self->posts ,
652             entries_per_page => $entries_per_page ,
653             hid => $self ,
654             page_pattern => $page_fstring
655             });
656              
657 0         0 while ( my $page_data = $pager->next ) {
658             my $page = HiD::Page->new({
659             dest_dir => $self->destination ,
660             hid => $self ,
661             input_filename => $template ,
662             layouts => $self->layouts ,
663             url => $page_data->{current_page_url} ,
664 0         0 });
665 0         0 $page->metadata->{page_data} = $page_data;
666              
667 0         0 $self->add_input( "Paged page $page_data->{page_number}" => 'page' );
668 0         0 $self->add_object( $page );
669             }
670             }
671              
672              
673 15         381 foreach my $file ( $self->all_objects ) {
674 103         4933 $file->publish;
675              
676 103         50564 $self->INFO( sprintf "* Published %s" , $file->output_filename );
677              
678 103         3993 my $path = path( $file->output_filename );
679 103         3160 while ( $path ne '.' ) {
680 370         15263 $self->add_written_file( $path->stringify() => 1 );
681 370         945 $path = $path->parent;
682             }
683             }
684              
685 15 50       983 if ( $self->remove_unwritten_files ) {
686 15         279 foreach ( File::Find::Rule->in( $self->destination )) {
687 213 100       21867 next if $self->wrote_file($_);
688              
689 1         5 my $path = path( $_ );
690 1 50       26 $path->is_dir ? $path->remove_tree : $path->remove;
691             }
692             }
693              
694 15 50       297 return 1 unless $self->plugins;
695              
696 15         59 $self->INFO( "processing plugins for after_publish()" );
697              
698 15         222 foreach my $plugin ( @{ $self->plugins } ) {
  15         243  
699 0 0 0     0 if ( $plugin->does( 'HiD::Plugin' ) or
700             ### FIXME remove after 13 Nov 2014
701             $plugin->isa( 'HiD::Plugin' )) {
702 0         0 $plugin->after_publish($self)
703             }
704             }
705              
706 15         60 1;
707             }
708              
709              
710              
711             __PACKAGE__->meta->make_immutable;
712             1;
713              
714             __END__
715              
716             =pod
717              
718             =encoding UTF-8
719              
720             =head1 NAME
721              
722             HiD - Static website publishing framework
723              
724             =head1 SYNOPSIS
725              
726             HÄ«D is a blog-aware, GitHub-friendly, static website generation system
727             inspired by Jekyll.
728              
729             =head1 DESCRIPTION
730              
731             HiD users probably want to look at the documentation for the L<hid> command.
732              
733             Subsequent documentation in this file describes internal details that are only
734             useful or interesting for people that are trying to modify or extend HiD.
735              
736             =head1 ATTRIBUTES
737              
738             =head2 categories
739              
740             Categories hash, contains (category, post) pairs
741              
742             =head2 cli_opts
743              
744             Hashref of command line options to integrate into the config.
745              
746             (L<HiD::App::Command>s should pass in the C<$opt> variable to this.)
747              
748             =head2 config
749              
750             Hashref of configuration information.
751              
752             =head2 config_file
753              
754             Path to a configuration file.
755              
756             =head2 default_config
757              
758             Hashref of standard configuration options. The default config is:
759              
760             destination => '_site' ,
761             include_dir => '_includes',
762             layout_dir => '_layouts' ,
763             plugin_dir => '_plugins' ,
764             posts_dir => '_posts' ,
765             drafts_dir => '_drafts' ,
766             source => '.' ,
767              
768             =head2 destination
769              
770             Directory to write output files into.
771              
772             B<N.B.:> If it doesn't exist and is needed, it will be created.
773              
774             =head2 draft_post_file_regex
775              
776             Regular expression for which files will be recognized as draft blog posts.
777              
778             FIXME should this be configurable?
779              
780             FIXME this and post_file_regex should probably be built based on a common
781             underlying "post_extensions_regex" attr...
782              
783             =head2 excerpt_separator
784              
785             String that distinguishes initial excerpt from "below the fold" content
786              
787             Defaults to "\n\n"
788              
789             =head2 include_dir
790              
791             Directory for template "include" files
792              
793             =head2 inputs
794              
795             Hashref of input files. Keys are file paths; values are what type of file the
796             system has classified that path as.
797              
798             =head2 layout_dir
799              
800             Directory where template files are located.
801              
802             =head2 layouts
803              
804             Hashref of L<HiD::Layout> objects, keyed by layout name.
805              
806             =head2 limit_posts
807              
808             If set, only this many blog post files will be processed during publishing.
809              
810             Setting this can significantly speed up publishing for sites with many blog posts.
811              
812             =head2 objects
813              
814             Array of objects (pages, posts, files) created during site processing.
815              
816             =head2 page_file_regex
817              
818             Regular expression for identifying "page" files.
819              
820             # FIXME should it be possible to set this from the config?
821              
822             =head2 pages
823              
824             Arrayref of L<HiD::Page> objects, populated during processing.
825              
826             =head2 plugin_dir
827              
828             Directory for plugins, which will be called after publish.
829              
830             =head2 plugins
831              
832             Plugins, which consume either of the L<HiD::Plugin> or L<HiD::Generator> roles.
833              
834             Plugins used to subclass L<HiD::Plugin>, but that behavior is deprecated and
835             will be removed on or after 13 Nov 2014.
836              
837             =head2 post_file_regex
838              
839             Regular expression for which files will be recognized as blog posts.
840              
841             FIXME should this be configurable?
842              
843             =head2 posts_dir
844              
845             Directory where blog posts are located.
846              
847             =head2 posts
848              
849             Arrayref of L<HiD::Post> objects, populated during processing.
850              
851             =head2 processor
852              
853             Slot to hold the L<HiD::Processor> object that will be used during the
854             publication process.
855              
856             =head2 processor_args
857              
858             Arguments to use when instantiating the L<processor> attribute.
859              
860             Can be an arrayref or a hashref.
861              
862             Defaults to appropriate Template Toolkit arguments.
863              
864             =head2 regular_files
865              
866             ArrayRef of L<HiD::File> objects, populated during processing.
867              
868             =head2 remove_unwritten_files ( Boolean )
869              
870             Boolean value controlling whether files found in the C<dest_dir> that weren't
871             produced by HiD should be removed. In other words, when this is true, after a
872             C<hid publish> run, only files produced by HiD will be found in the
873             C<dest_dir>.
874              
875             Defaults to true.
876              
877             =head2 source
878              
879             Base directory that all other paths are calculated relative to.
880              
881             =head2 tags
882              
883             Tags hash, contains (tag, posts) pairs
884              
885             =head2 time
886              
887             DateTime object from the start of the latest run of the system.
888              
889             Cannot be set via argument.
890              
891             =head2 written_files
892              
893             Hashref of files written out during the publishing process.
894              
895             =head1 METHODS
896              
897             =head2 get_config
898              
899             my $config_key_value = $self->get_config( $config_key_name );
900              
901             Given a config key name, returns a config key value.
902              
903             =head2 add_input
904              
905             $self->add_input( $input_file => $input_type );
906              
907             Record what input type a particular input file is.
908              
909             =head2 seen_input
910              
911             if( $self->seen_input( $input_file )) { ... }
912              
913             Check to see if a particular input file has been seen.
914              
915             =head2 get_layout_by_name
916              
917             my $hid_layout_obj = $self->get_layout_by_name( $name );
918              
919             Given a layout name (e.g., 'default') returns the corresponding L<HiD::Layout> object.
920              
921             =head2 add_object
922              
923             $self->add_object( $generated_object );
924              
925             Add an object to the set of objects generated during site processing.
926              
927             =head2 all_objects
928              
929             my @objects = $self->all_objects;
930              
931             Returns the list of all objects that have been generated.
932              
933             =head2 add_written_file
934              
935             $self->add_written_file( $file => 1 );
936              
937             Record that a file was written.
938              
939             =head2 all_written_files
940              
941             my @files = $self->all_written_files;
942              
943             Return the list of all files that were written out.
944              
945             =head2 wrote_file
946              
947             if( $self->wrote_file( $file )) { ... }
948              
949             Check to see if a particular file has been written out.
950              
951             =head2 publish
952              
953             $self->publish;
954              
955             Process files and generate output per the active configuration.
956              
957             =head1 CONTRIBUTORS
958              
959             =over 4
960              
961             =item *
962              
963             ChinaXing
964              
965             =item *
966              
967             reyjrar
968              
969             =item *
970              
971             Yanick Champoux
972              
973             =item *
974              
975             Jake Goldsborough
976              
977             =item *
978              
979             Trey Bianchini
980              
981             =back
982              
983             =head1 SEE ALSO
984              
985             =over 4
986              
987             =item *
988              
989             L<jekyll|http://jekyllrb.com/>
990              
991             =item *
992              
993             L<Papery>
994              
995             =item *
996              
997             L<StaticVolt>
998              
999             =back
1000              
1001             =head1 VERSION
1002              
1003             version 1.991
1004              
1005             =head1 AUTHOR
1006              
1007             John SJ Anderson <genehack@genehack.org>
1008              
1009             =head1 COPYRIGHT AND LICENSE
1010              
1011             This software is copyright (c) 2015 by John SJ Anderson.
1012              
1013             This is free software; you can redistribute it and/or modify it under
1014             the same terms as the Perl 5 programming language system itself.
1015              
1016             =cut