summaryrefslogtreecommitdiff
path: root/lib/App/DocKnot/Spin/RSS.pm
diff options
context:
space:
mode:
authorRuss Allbery <rra@cpan.org>2021-12-31 17:23:11 -0800
committerRuss Allbery <rra@cpan.org>2021-12-31 17:23:11 -0800
commit84cd6a8fb1982d80132cfa675f397c0cb59148ab (patch)
tree8f94b307c3b6b0200b1d59bc6e5d792b217a67cb /lib/App/DocKnot/Spin/RSS.pm
parent5c26c088aa5963a1000a437c291f8542ed947eb4 (diff)
Use Path::Tiny and Path::Iterator::Rule
docknot spin now uses Path::Iterator::Rule and Path::Tiny to construct its paths, which eliminates the need to change the working directory while processing input files.
Diffstat (limited to 'lib/App/DocKnot/Spin/RSS.pm')
-rw-r--r--lib/App/DocKnot/Spin/RSS.pm135
1 files changed, 72 insertions, 63 deletions
diff --git a/lib/App/DocKnot/Spin/RSS.pm b/lib/App/DocKnot/Spin/RSS.pm
index 2a7460a..c63222b 100644
--- a/lib/App/DocKnot/Spin/RSS.pm
+++ b/lib/App/DocKnot/Spin/RSS.pm
@@ -18,10 +18,9 @@ use warnings;
use App::DocKnot;
use App::DocKnot::Spin::Thread;
use App::DocKnot::Util qw(print_checked print_fh);
-use Cwd qw(getcwd);
use Date::Language ();
use Date::Parse qw(str2time);
-use File::Basename qw(fileparse);
+use Path::Tiny qw(path);
use Perl6::Slurp qw(slurp);
use POSIX qw(strftime);
@@ -123,23 +122,30 @@ sub _relative_url {
return ('../' x scalar(@base)) . $url;
}
-# Spin a file into HTML, changing directories to the directory of that file so
-# that relative file references resolve correctly.
+# Spin a file into HTML.
#
-# $file - Path to the file
+# $file - Path::Tiny path to the file
#
# Returns: Rendered HTML as a list with one element per line
sub _spin_file {
my ($self, $file) = @_;
- my $source = slurp($file);
- my $cwd = getcwd();
- my (undef, $dir) = fileparse($file);
- chdir($dir);
- my $page = $self->{spin}->spin_thread($source);
- chdir($cwd);
+ my $source = $file->slurp_utf8();
+ my $page = $self->{spin}->spin_thread($source, $file);
return map { "$_\n" } split(m{ \n }xms, $page);
}
+# Report an action to standard output.
+#
+# $action - String description of the action
+# $output - Output file generated
+# $base - Base path for all output
+sub _report_action {
+ my ($self, $action, $output) = @_;
+ my $shortout = $output->relative($self->{base} // path());
+ print_checked("$action .../$shortout\n");
+ return;
+}
+
##############################################################################
# Parsing
##############################################################################
@@ -158,7 +164,7 @@ sub _read_rfc2822_file {
# Parse the file. $key holds the last key seen, used to append
# continuation values to the previous key. $current holds the current
# block being parsed and @blocks all blocks seen so far.
- open(my $fh, '<', $file);
+ my $fh = $file->openr_utf8();
while (defined(my $line = <$fh>)) {
if ($line =~ m{ \A \s* \z }xms) {
if ($key) {
@@ -208,7 +214,7 @@ sub _read_rfc2822_file {
# the changes into the provided array reference. Each element of the array
# will be a hash with keys title, date, link, and description.
#
-# $file - File to read
+# $file - Path::Tiny path to file to read
#
# Returns: List of reference to metadata hash and reference to a list of
# hashes of changes
@@ -273,7 +279,7 @@ sub _parse_changes {
# Format a journal post into HTML for inclusion in an RSS feed. This depends
# heavily on my personal layout for journal posts.
#
-# $file - Path to the journal post
+# $file - Path::Tiny path to the journal post
#
# Returns: HTML suitable for including in an RSS feed
sub _rss_journal {
@@ -303,7 +309,7 @@ sub _rss_journal {
# Format a review into HTML for inclusion in an RSS feed. This depends even
# more heavily on my personal layout for review posts.
#
-# $file - Path to the review
+# $file - Path::Tiny path to the review
#
# Returns: HTML suitable for inclusion in an RSS feed
sub _rss_review {
@@ -373,12 +379,13 @@ sub _rss_review {
# time as <lastBuildDate>; it's not completely clear to me that this is
# correct.
#
-# $fh - Output file handle
-# $file - Name of the output file
+# $file - Path::Tiny path to the output file
+# $base - Base Path::Tiny path for input files
# $metadata_ref - Hash of metadata for the RSS feed
# $entries_ref - Array of entries in the RSS feed
sub _rss_output {
- my ($self, $fh, $file, $metadata_ref, $entries_ref) = @_;
+ my ($self, $file, $base, $metadata_ref, $entries_ref) = @_;
+ my $fh = $file->openw_utf8();
my $version = '1.25';
# Determine the current date and latest publication date of all of the
@@ -405,7 +412,7 @@ sub _rss_output {
<generator>DocKnot $App::DocKnot::VERSION</generator>
EOC
if ($metadata_ref->{'rss-base'}) {
- my ($name) = fileparse($file);
+ my $name = $file->basename();
my $url = $metadata_ref->{'rss-base'} . $name;
print_fh(
$fh,
@@ -427,9 +434,11 @@ EOC
$description =~ s{ \A (\s*) }{$1<p>}xms;
$description =~ s{ \n* \z }{</p>\n}xms;
} elsif ($entry_ref->{journal}) {
- $description = $self->_rss_journal($entry_ref->{journal});
+ my $path = path($entry_ref->{journal})->absolute($base);
+ $description = $self->_rss_journal($path);
} elsif ($entry_ref->{review}) {
- $description = $self->_rss_review($entry_ref->{review});
+ my $path = path($entry_ref->{review})->absolute($base);
+ $description = $self->_rss_review($path);
}
# Make all relative URLs absolute.
@@ -464,6 +473,7 @@ EOC
# Close the RSS structure.
print_fh($fh, $file, " </channel>\n</rss>\n");
+ close($fh);
return;
}
@@ -473,12 +483,12 @@ EOC
# Print out the thread version of the recent changes list.
#
-# $fh - File handle to which to output
-# $file - Name of the file for error reporting
+# $file - Path::Tiny output path
# $metadata_ref - RSS feed metadata
# $entries_ref - Entries
sub _thread_output {
- my ($self, $fh, $file, $metadata_ref, $entries_ref) = @_;
+ my ($self, $file, $metadata_ref, $entries_ref) = @_;
+ my $fh = $file->openw_utf8();
# Page prefix.
if ($metadata_ref->{'thread-prefix'}) {
@@ -520,6 +530,7 @@ sub _thread_output {
# Print out the end of the page.
print_fh($fh, $file, "\\signature\n");
+ close($fh);
return;
}
@@ -529,12 +540,12 @@ sub _thread_output {
# Translate the thread of a journal entry for inclusion in an index page.
#
-# $file - Path to the journal entry
+# $file - Path::Tiny to the journal entry
#
# Returns: Thread to include in the index page
sub _index_journal {
my ($self, $file, $url) = @_;
- open(my $fh, '<', $file);
+ my $fh = $file->openr_utf8();
# Skip to the first \h1 and exclude it.
while (defined(my $line = <$fh>)) {
@@ -558,7 +569,7 @@ sub _index_journal {
# Translate the thread of a book review for inclusion into an index page.
#
-# $file - Path to the book review
+# $file - Path::Tiny to the book review
#
# Returns: Thread to include in the index page
sub _index_review {
@@ -571,7 +582,7 @@ sub _index_review {
# Scan for the author information and save it. Handle the case where the
# \header or \edited line is continued on the next line.
- open(my $fh, '<', $file);
+ my $fh = $file->openr_utf8();
while (defined(my $line = <$fh>)) {
if ($line =~ m{ \\ (?:header|edited) \s* \[ $char+ \] \s* \z }xms) {
$line .= <$fh>;
@@ -619,12 +630,13 @@ sub _index_review {
# Print out the index version of the recent changes list.
#
-# $fh - File handle to which to output
-# $file - Name of the file for error reporting
+# $file - Path::Tiny path to the output file
+# $base - Base Path::Tiny path for input files
# $metadata_ref - RSS feed metadata
# $entries_ref - Entries
sub _index_output {
- my ($self, $fh, $file, $metadata_ref, $entries_ref) = @_;
+ my ($self, $file, $base, $metadata_ref, $entries_ref) = @_;
+ my $fh = $file->openw_utf8();
# Output the prefix.
if ($metadata_ref->{'index-prefix'}) {
@@ -640,9 +652,11 @@ sub _index_output {
# Get the text of the entry.
my $text;
if ($entry_ref->{journal}) {
- $text = $self->_index_journal($entry_ref->{journal});
+ my $path = path($entry_ref->{journal})->absolute($base);
+ $text = $self->_index_journal($path);
} elsif ($entry_ref->{review}) {
- $text = $self->_index_review($entry_ref->{review});
+ my $path = path($entry_ref->{review})->absolute($base);
+ $text = $self->_index_review($path);
} else {
die "unknown entry type\n";
}
@@ -674,6 +688,7 @@ sub _index_output {
print_fh($fh, $file, $metadata_ref->{'index-suffix'}, "\n");
}
print_fh($fh, $file, "\\signature\n");
+ close($fh);
return;
}
@@ -683,8 +698,8 @@ sub _index_output {
# Create a new RSS generator object.
#
-# $args - Anonymous hash of arguments with the following keys:
-# base - Base path for output files
+# $args_ref - Anonymous hash of arguments with the following keys:
+# base - Path::Tiny base path for output files
#
# Returns: Newly created object
sub new {
@@ -692,7 +707,7 @@ sub new {
# Create and return the object.
my $self = {
- base => $args_ref->{base},
+ base => defined($args_ref->{base}) ? path($args_ref->{base}) : undef,
spin => App::DocKnot::Spin::Thread->new(),
};
bless($self, $class);
@@ -701,14 +716,13 @@ sub new {
# Generate specified output files from an .rss input file.
#
-# $source - Path to the .rss file
-# $base - Optional base path for output
+# $source - Path::Tiny path to the .rss file
+# $base - Optional Path::Tiny base path for output
sub generate {
my ($self, $source, $base) = @_;
+ $source = path($source);
$base //= $self->{base};
- if ($base) {
- $base =~ s{ /* \z}{/}xms;
- }
+ $base = defined($base) ? path($base) : path();
# Read in the changes.
my ($metadata_ref, $changes_ref) = $self->_parse_changes($source);
@@ -722,14 +736,13 @@ sub generate {
# Iterate through each specified output file.
for my $output (@output) {
my ($tags, $format, $file) = split(m{ : }xms, $output);
- my $path = ($base && $file !~ m{ \A / }xms) ? "$base$file" : $file;
- my $prettyfile = $path;
- if ($prettyfile !~ m{ \A / }xms) {
- $prettyfile = ".../$prettyfile";
+ $file = path($file);
+ if ($file->is_relative()) {
+ $file = $file->absolute($base);
}
# If the output file is newer than the input file, do nothing.
- next if (-e $path && -M $path <= -M $source);
+ next if ($file->exists() && -M "$file" <= -M "$source");
# Find all the changes of interest to this output file.
my @entries;
@@ -743,26 +756,21 @@ sub generate {
# Write the output.
if ($format eq 'thread') {
- print_checked("Generating thread file $prettyfile\n");
- open(my $fh, '>', $path);
- $self->_thread_output($fh, $path, $metadata_ref, \@entries);
- close($fh);
+ $self->_report_action('Generating thread file', $file);
+ $self->_thread_output($file, $metadata_ref, \@entries);
} elsif ($format eq 'rss') {
if (scalar(@entries) > $metadata_ref->{recent}) {
splice(@entries, $metadata_ref->{recent});
}
- print_checked("Generating RSS file $prettyfile\n");
- open(my $fh, '>', $path);
- $self->_rss_output($fh, $path, $metadata_ref, \@entries);
- close($fh);
+ $self->_report_action('Generating RSS file', $file);
+ $self->_rss_output($file, $base, $metadata_ref, \@entries);
} elsif ($format eq 'index') {
if (scalar(@entries) > $metadata_ref->{recent}) {
splice(@entries, $metadata_ref->{recent});
}
- print_checked("Generating index file $prettyfile\n");
- open(my $fh, '>', $path);
- $self->_index_output($fh, $path, $metadata_ref, \@entries);
- close($fh);
+ $self->_report_action('Generating index file', $file);
+ my $index_base = $source->parent();
+ $self->_index_output($file, $index_base, $metadata_ref, \@entries);
}
}
return;
@@ -792,9 +800,9 @@ App::DocKnot::Spin::RSS - Generate RSS and thread from a feed description file
=head1 REQUIREMENTS
-Perl 5.006 or later and the modules Date::Language, Date::Parse (both part of
-the TimeDate distribution), List::SomeUtils, and Perl6::Slurp, both of which
-are available from CPAN.
+Perl 5.24 or later and the modules Date::Language, Date::Parse (both part of
+the TimeDate distribution), List::SomeUtils, Path::Tiny, and Perl6::Slurp,
+both of which are available from CPAN.
=head1 DESCRIPTION
@@ -834,7 +842,7 @@ with one or more of the following keys, all of which are optional:
By default, App::DocKnot::Spin::RSS output files are relative to the current
working directory. If the C<base> argument is given, output files will be
relative to the value of C<base> instead. Output files specified as absolute
-paths will not be affected.
+paths will not be affected. C<base> may be a string or a Path::Tiny object.
=back
@@ -848,7 +856,8 @@ paths will not be affected.
Parse the input file FILE and generate the output files that it specifies.
BASE, if given, specifies the root directory for output files specified with
-relative paths, and overrides any C<base> argument given to new().
+relative paths, and overrides any C<base> argument given to new(). Both FILE
+and BASE may be strings or Path::Tiny objects.
=back