File Coverage

blib/lib/SoggyOnion.pm
Criterion Covered Total %
statement 31 33 93.9
branch n/a
condition n/a
subroutine 11 11 100.0
pod n/a
total 42 44 95.4


line stmt bran cond sub pod time code
1             package SoggyOnion;
2 1     1   41729 use strict;
  1         3  
  1         77  
3 1     1   6 use warnings;
  1         3  
  1         34  
4 1     1   74 use Carp;
  1         2  
  1         290  
5              
6             our $VERSION = '0.04';
7              
8             # soggyonion and the default plugins use the Template Toolkit
9 1     1   2468 use Template;
  1         34096  
  1         38  
10              
11             # which template file to use for this module
12 1     1   11 use constant TEMPLATE_FILE => 'main.tt2';
  1         2  
  1         60  
13              
14             # Parallel::ForkManager threads the page creation. i sure hope that
15             # Cache::File does the right thing in terms of locking..
16 1     1   3904 use Parallel::ForkManager;
  1         62120  
  1         53  
17              
18             # how many pages to create at once
19 1     1   11 use constant MAX_THREADS => 10;
  1         2  
  1         84  
20              
21             # use Cache::FileCache to cache the results of plugins
22 1     1   1043 use Cache::FileCache;
  1         58652  
  1         66  
23              
24             # use IO::Scalar to buffer the output of threads so the output appears
25             # in the correct order
26 1     1   2950 use IO::Scalar;
  1         8465  
  1         48  
27              
28             #------------------------------------------------------------------------
29              
30             # preload base classes
31 1     1   738 use SoggyOnion::Plugin;
  1         3  
  1         32  
32 1     1   569 use SoggyOnion::Resource;
  0            
  0            
33              
34             # a simple accessor for our options -- makes the code look particularly
35             # neat in plugins
36             our $OPTIONS;
37              
38             sub options {
39             my ( $self, $value ) = @_;
40             if ($value) {
41             croak "configuration error: options isn't a hash\n"
42             unless ref $value eq 'HASH';
43             $OPTIONS = $value;
44             }
45             return $OPTIONS;
46             }
47              
48             # hopefully plugins that get pages will use this as the User-Agent. it
49             # can be set by exporting the $ua variable for LWP::Simple
50             # ($LWP::Simple::ua) or setting the agent option for LWP::UserAgent
51             # objects.
52             sub useragent {
53             return "SoggyOnion/$VERSION";
54             }
55              
56             # here's the meaty subroutine that is called from the executable. the
57             # executable's job is to parse a configuration file, retrieve a hash of
58             # options and call SoggyOnion->options with it, then retrieve a hash
59             # that is the layout of the pages and call SoggyOnion->generate with it.
60             sub generate {
61             my ( $self, $layout ) = @_;
62             die "configuration error: options isn't defined\n"
63             unless $self->options;
64             die "configuration error: layout isn't an array\n"
65             unless ref $layout eq 'ARRAY';
66              
67             # determine resource class
68             $self->options->{resourceclass} ||= 'SoggyOnion::Resource';
69              
70             # initialize our cache
71             my $cache;
72             eval {
73             $cache = Cache::FileCache->new(
74             { cache_root => $self->options->{cachedir}, } );
75             };
76             die "error creating cache: $@\n"
77             if $@;
78              
79             # initialize templates
80             my $template = Template->new(
81             INCLUDE_PATH => $self->options->{templatedir},
82             OUTPUT_PATH => $self->options->{outputdir},
83             );
84              
85             # initialize thread manager
86             my $fm = Parallel::ForkManager->new(MAX_THREADS);
87              
88             # process all pages
89             foreach my $page (@$layout) {
90              
91             # fork off!
92             my $pid = $fm->start and next;
93              
94             # set up buffer so that the output for each page generation is
95             # only printed at the end of the thread. note: i ran into
96             # problems when trying to capture STDERR to the same filehandle,
97             # and that's probably the problem :)
98             my $output;
99             my $output_fh = IO::Scalar->new( \$output );
100             local *REAL_STDOUT = *STDOUT;
101             local *STDOUT = $output_fh;
102              
103             # a little feedback
104             print "creating page $page->{name} ($page->{title})\n";
105              
106             # construct the body for each page by grabbing the content
107             # from each item
108             my $body = '';
109             unless ( ref $page->{items} eq 'ARRAY' ) {
110             warn "Error: items for page $page->{name} isn't an array\n";
111             $fm->finish;
112             next;
113             }
114             foreach my $item ( @{ $page->{items} } ) {
115              
116             # let SoggyOnion/Resource.pm figure out what kind of
117             # resource this is and simply return an object. this is
118             # i made it easy to specify a "resourceclass" option in the
119             # config, so that you can extend SoggyOnion::Resource to
120             # automatically determine a class using your plugins
121             my $resource = $self->options->{resourceclass}->new($item);
122             unless ( ref $resource ) {
123             warn "\t\tcouldn't find appropriate handler for item $item\n";
124             $fm->finish;
125             next;
126             }
127             my $id = $resource->id;
128             print "\tprocessing item ", $id, "\n";
129              
130             # is the resource already cached? check and see by
131             # comparing the resource's modification time against the
132             # modification time of the cached copy. if the resource's
133             # mod time is newer, regenerate the content.
134             # (see Cache::Cache and Cache::Object for more info)
135             my $content = 'empty content';
136             if ( $cache->get_object($id)
137             && $cache->get_object($id)->get_created_at
138             >= $resource->mod_time )
139             {
140             print "\t\tcache is up to date\n";
141             $content = $cache->get($id);
142             }
143             else {
144             $content = eval { $resource->content };
145             if ($@) {
146             $content
147             = "error generating this resource:
$@
";
148             warn "\t\terror generating: $@\n";
149             $fm->finish;
150             next;
151             }
152             $cache->set( $id => $content );
153             }
154              
155             # add the item to the body, set the
's id attribute to
156             # the id of the item
157             $body .= qq(
\n);
158             $body .= $content;
159             $body .= qq(\n);
160             }
161              
162             # create the output file
163             $template->process(
164             TEMPLATE_FILE,
165             { thispage => $page,
166             allpages => $layout,
167             content => $body,
168             },
169             $page->{name},
170             )
171             or warn "\t\t" . $template->error . "\n";
172              
173             # print buffer to output
174             print REAL_STDOUT $output;
175              
176             # finish with fork
177             $fm->finish;
178             }
179              
180             # cleanup all the children
181             $fm->wait_all_children;
182              
183             print "done!\n";
184             }
185              
186             1;
187             __END__