Almost-minimal filesystem based blog.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

188 lines
4.1 KiB

package App::WRT::Renderer;
use strict;
use warnings;
use 5.10.0;
use Carp;
use File::Basename;
=pod
=head1 NAME
App::WRT::Renderer - render a wrt repo to publishable HTML
=head1 SYNOPSIS
use App::WRT;
use App::WRT::Renderer;
my $wrt = App::WRT::new_from_file($config_file);
my $renderer = App::WRT::Renderer->new(
$wrt,
sub { say $_[0]; }
);
$renderer->render();
=head1 METHODS
=over
=item new($class, $entry_dir, $logger, $io)
Get a new Renderer. Takes an instance of App::WRT, a logging callback, and a
App::WRT::FileIO or similar object to be used for the actual intake and
mangling of things on the filesystem.
=cut
sub new {
my $class = shift;
my ($wrt, $logger, $io) = @_;
ref($logger) eq 'CODE' or
croak("Error: Renderer expects an anonymous function for logging");
my %params = (
wrt => $wrt,
logger => $logger,
io => $io,
);
my $self = \%params;
bless $self, $class;
}
=item write($path, $contents)
Write $contents to $path, using the FileIO object passed into the constructor
above.
=cut
sub write {
my ($self, $file, $contents) = @_;
$self->{io}->file_put_contents($file, $contents)
}
=item render($class, $entry_dir)
Render entries to F<publish_dir>.
=cut
sub render {
my $self = shift;
my $entry_dir = $self->{wrt}->{entry_dir};
my $publish_dir = $self->{wrt}->{publish_dir};
# Ensure that publication path exists and is a directory:
if (-e $publish_dir) {
unless (-d $publish_dir) {
croak("$publish_dir exists but is not a directory");
}
} else {
$self->log("Attempting to create $publish_dir");
unless ($self->dir_make_logged($publish_dir)) {
croak("Could not create $publish_dir");
}
}
# Handle the front page. With no entries given, display() will use the
# configured default, which is probably "new" unless the user has changed it.
$self->write(
"${publish_dir}/index.html",
$self->{wrt}->display()
);
# Handle feed formats:
my $feed_alias = $self->{wrt}->{feed_alias};
my $xml_feed_content = $self->{wrt}->feed_print_recent();
my $json_feed_content = $self->{wrt}->feed_print_json_recent();
$self->write("${publish_dir}/${feed_alias}", $xml_feed_content);
$self->write("${publish_dir}/${feed_alias}.xml", $xml_feed_content);
$self->write("${publish_dir}/${feed_alias}.json", $json_feed_content);
# Handle any other paths that aren't derived directly from files:
my @meta_paths = qw(all);
my $rendered_count = 0;
my $copied_count = 0;
for my $target ($self->{wrt}->{entries}->all(), @meta_paths)
{
# Skip index files - these are the text content of an entry, not
# a sub-entry:
next if $target =~ m{/index$};
# Lowercase and alphanumeric + underscores + dashes, no dots - an entry:
if ($self->{wrt}->{entries}->is_renderable($target)) {
$self->dir_make_logged("$publish_dir/$target");
my $rendered = $self->{wrt}->display($target);
my $target_file = "$publish_dir/$target/index.html";
$self->log("[write] $target_file " . length($rendered));
$self->write($target_file, $rendered);
$rendered_count++;
next;
}
# A directory - no-op:
if (-d "$entry_dir/$target") {
$self->log("[directory] $entry_dir/$target");
next;
}
# Some other file - a static asset of some kind:
my $dirname = dirname($target);
$self->log("[copy] archives/$target -> $publish_dir/$target");
$self->dir_make_logged("$publish_dir/$dirname");
$self->{io}->file_copy("$entry_dir/$target", "$publish_dir/$target");
$copied_count++;
}
$self->log("rendered $rendered_count entries");
$self->log("copied $copied_count static files");
# Presumed success:
return 1;
}
=item dir_make_logged($path)
Make a directory path or log an error.
=cut
sub dir_make_logged {
my ($self, $path) = @_;
my $path_err;
$self->log("[create] $path");
$self->{io}->dir_make($path);
# XXX: surface these somehow
# $self->log(Dumper($path_err)) if @{ $path_err };
}
=item log(@log_items)
Call logging callback with passed parameters.
=cut
sub log {
my ($self) = shift;
$self->{logger}->(@_);
}
=back
=cut
1;