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. =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;