view release on metacpan or search on metacpan
- Support version numbers prefixed with v in release tarball names, since
this appears to be the convention for Perl modules that use semantic
versions.
- Change the docknot.yaml field orphaned to unmaintained, and change the
templates to say the package is not maintained instead of orphaned.
This terminology is more precise and less metaphorical.
- Remove support information from the README.md and README templates for
packages that are no longer maintained, and adjust some of the wording
there and in the thread template.
- Add missing </address> closing tag in pages rendered from Markdown.
- Fix typo in README.md template for ExtUtils::MakeMaker packages.
- Fix the URL in the footer added by docknot spin to point to DocKnot
instead of my old web tools page.
7.01 - 2022-01-19
file with instructions for how to convert it to HTML. Via this
mechanism, support formatting Markdown files as HTML via docknot spin.
This support requires the pandoc program be installed. The path to
pandoc may be specified in the DocKnot global configuration file.
- Support *.spin pointers in addition to *.rpod pointers for external POD
files. The command-line flags used in *.rpod pointers are replaced by
the title and options key of the *.spin file. *.rpod files are
deprecated and support will be removed in a future version of DocKnot.
- Add spin_thread_output method to App::DocKnot::Spin::Thread, intended
to convert thread to HTML as part of a conversion pipeline of a
non-thread input file, while still using sitemap information and
generating the page footer. DocKnot now depends on Path::Tiny.
- Support creating distributions from branches named main rather than
master. The first of main or master that's found in the repository
will be used.
- Move some utility functions into a new App::DocKnot::Util module. This
is primarily intended for internal use by other App::DocKnot modules.
- Fix unintended localization of dates in RSS output, which are supposed
to be RFC 2822 dates and therefore always use English month and day of
week names. Thanks to Slaven ReziÄ for testing.
- Add load_yaml_file method to App::DocKnot, which loads a YAML file with
schema checking.
- Fix small whitespace problems in thread output.
5.00 - 2021-09-12
- Merge spin and spin-rss into this package, making it a full, if highly
idiosyncratic, static site generator built around a macro language
called thread. This adds new commands docknot spin, docknot spin-rss,
and docknot spin-thread, as well as modules App::DocKnot::Spin and
several submodules. docknot spin should be equivalent to the old spin
command except that \id support has been dropped. DocKnot now depends
on Date::Parse (from TimeDate), Git::Repository, Image::Size, and
Pod::Thread 3.00 or later.
- Support setting distribution.packaging.debian.package along with
distribution.packaging.debian.personal to specify the package name. Do
not generate links to Debian in that case.
- Ignore configure~ when checking distribution tarballs.
- Stop generating a developer link to a to-do list by default in thread
output. Not all of my packages use a TODO file, so the ones that do
should explicitly add this to the developer docs list.
4.01 - 2021-02-27
- DocKnot now supports a global configuration file. The default location
is $HOME/.config/docknot/config.yaml, but it honors the XDG environment
variables. Currently, this configuration file can be used to set the
distribution directory and signing PGP key for docknot dist.
submodules.
- Add new docknot dist command and App::DocKnot::Dist module, which runs
appropriate commands to create a distribution tarball. This command
will also run the test suite, with both Clang and GCC for packages
using Autoconf, and will also build and test with C++ if the package
indicates that it supports C++ via the build.cplusplus key in the
package metadata.
- Support orphaned warnings in the README and README.md output as well as
thread output.
- Add bug tracker links pointing to the CPAN RT installation to the
developer documentation links section of the thread template if
support.cpan is set.
- Output more helpful information about test failures on Windows and
other systems that don't have the diff command.
2.00 - 2019-01-12
- Move previous docknot command functionality to a new docknot generate
subcommand. All docknot actions in the future will be added as
subcommands.
1.06 - 2018-08-31
- When generating text output, put the footnotes containing URLs for
links immediately following the containing paragraph rather than the
end of the text block. This is both more readable and avoids odd
placement of the footnotes when a template adds further paragraphs to
the end of a text block containing footnotes.
- Do not wrap paragraphs in output that seem to be a bunch of short
lines, and add support for broken quotes (multiple short lines, such as
poetry) in quotes in the thread template.
- Adjust the README and README.md template to say that make warnings
requires either GCC or Clang, instead of only mentioning GCC.
- Adjust the README.md wording for the list information URL when package
releases are announced on a mailing list.
- Add support for additional developer documentation links in the thread
output template.
- Add support for contributed program documentation links in the thread
output template.
1.05 - 2018-05-05
- Add the Travis-CI badge to README.md and a link to Travis-CI to the
thread development links if the vcs.travis key is set in metadata.json.
- Add a Shields.io badge for the CPAN version to README.md and a link to
metacpan.org in the thread output if the distribution.cpan key is set
in metadata.json.
- Move the description of Lancaster Consensus environment variables into
the testing section of README and README.md instead of the requirements
section, since they're more about running the tests and less about
package requirements in general.
- Add support for a new packaging/extra metadata file (setting the
packaging.extra key in templates) and use it in the thread template.
- Correctly handle multi-paragraph debian.summary metadata in the thread
template.
- Fix formatting bug in the README template when additional bootstrap
documentation is provided.
- DocKnot now requires Perl 5.24 or later.
1.04 - 2018-03-24
- Fix SPDX test failure on Windows.
SPDX license list.
- Add SPDX-License-Identifier headers to all substantial source files,
and add a test to check for them.
1.02 - 2017-12-31
- Support quoted paragraphs (each line starting with ">") and turn them
into indentation when turning markup into plain text.
- Support numbered lists when converting to thread.
- Force long, unbreakable lines to be left intact when wrapping.
- When wrapping paragraphs, preserve two spaces after periods even when
the period comes before a closing parenthesis or quote mark.
- Support test/prefix metadata, which replaces the introduction to the
test instructions.
- Add new license text BSD-3-clause-or-GPL-1+ to support pam-krb5.
Support markdown formatting in license texts when converting to thread
to handle the numbered clauses.
- Support more complex quote attributions in thread output, and
automatically add \cite[] around work names if they don't contain
double quotes.
- Add security advisory support to the thread template.
1.01 - 2016-12-25
- Add build and test instructions for Autoconf packages, including
details about Kerberos configuration, to the README and README.md
templates. Allow a testing section in the metadata to override the
added testing section. Add a flag to indicate that the package is not
intended to be installed, which suppresses some of the template.
- Add support for license notices (the notices metadata file), which
should be appended to the end of the stock license statement wherever
it is generated.
- Add support for stock building and installation sections for Perl
module packages using either Module::Build or ExtUtils::MakeMaker.
- Add thread template support for annotating quotes shorter than 80
characters with the "short" class.
- Add support for additional bootstrapping requirements in a separate
bootstrap metadata file, appended to the Autotools bootstrapping
section (at least for right now).
- Allow markup in the license notices section.
1.00 - 2016-10-26
- Initial public release. Supports generating text and Markdown README
files and a thread index page. There is some automation of the
requirements section, but only automation for the build and test
section for Module::Build Perl modules.
share/schema/config.yaml
share/schema/docknot.yaml
share/schema/licenses.yaml
share/schema/pointer.yaml
share/templates/changes.tmpl
share/templates/html.tmpl
share/templates/index.tmpl
share/templates/readme-md.tmpl
share/templates/readme.tmpl
share/templates/rss.tmpl
share/templates/thread.tmpl
t/cli/errors.t
t/cli/generate.t
t/cli/spin.t
t/config/basic.t
t/data/dist/fake-gpg
t/data/dist/package/Build.PL
t/data/dist/package/docs/docknot.yaml
t/data/dist/package/lib/Empty.pm
t/data/dist/package/MANIFEST
t/data/dist/package/MANIFEST.SKIP
t/data/dist/package/t/api/empty.t.in
t/data/generate/ansicolor/docknot.yaml
t/data/generate/ansicolor/output/readme
t/data/generate/ansicolor/output/readme-md
t/data/generate/ansicolor/output/thread
t/data/generate/c-tap-harness/docknot.yaml
t/data/generate/c-tap-harness/output/readme
t/data/generate/c-tap-harness/output/readme-md
t/data/generate/c-tap-harness/output/thread
t/data/generate/control-archive/docknot.yaml
t/data/generate/control-archive/output/readme
t/data/generate/control-archive/output/readme-md
t/data/generate/control-archive/output/thread
t/data/generate/docknot/output/thread
t/data/generate/lbcd/docknot.yaml
t/data/generate/lbcd/output/readme
t/data/generate/lbcd/output/readme-md
t/data/generate/lbcd/output/thread
t/data/generate/pam-krb5/docknot.yaml
t/data/generate/pam-krb5/output/readme
t/data/generate/pam-krb5/output/readme-md
t/data/generate/pam-krb5/output/thread
t/data/generate/pgp-sign/docknot.yaml
t/data/generate/pgp-sign/output/readme
t/data/generate/pgp-sign/output/readme-md
t/data/generate/pgp-sign/output/thread
t/data/generate/pod-thread/docknot.yaml
t/data/generate/pod-thread/output/readme
t/data/generate/pod-thread/output/readme-md
t/data/generate/pod-thread/output/thread
t/data/generate/remctl/docknot.yaml
t/data/generate/remctl/output/readme
t/data/generate/remctl/output/readme-md
t/data/generate/remctl/output/thread
t/data/generate/rra-c-util/docknot.yaml
t/data/generate/rra-c-util/output/readme
t/data/generate/rra-c-util/output/readme-md
t/data/generate/rra-c-util/output/thread
t/data/generate/wallet/docknot.yaml
t/data/generate/wallet/output/readme
t/data/generate/wallet/output/readme-md
t/data/generate/wallet/output/thread
t/data/perl.conf
t/data/perlcriticrc
t/data/perltidyrc
t/data/regenerate-data
t/data/spin/errors/errors.th
t/data/spin/input/.rss
t/data/spin/input/.sitemap
t/data/spin/input/.versions
t/data/spin/input/index.th
t/data/spin/input/journal/.macros
t/lib/Test/RRA.pm
t/lib/Test/RRA/Config.pm
t/lib/Test/RRA/ModuleVersion.pm
t/metadata/licenses.t
t/release/basic.t
t/spin/errors.t
t/spin/file.t
t/spin/markdown.t
t/spin/sitemap.t
t/spin/text.t
t/spin/thread.t
t/spin/tree.t
t/spin/versions.t
t/style/coverage.t
t/style/critic.t
t/style/kwalitee.t
t/style/minimum-version.t
t/style/module-version.t
t/style/obsolete-strings.t
t/style/strict.t
t/update/basic.t
(Static web site and documentation generator)
Maintained by Russ Allbery <rra@cpan.org>
Copyright 1999-2024 Russ Allbery <rra@cpan.org>. This software is
distributed under a BSD-style license. Please see the section LICENSE
below for more information.
BLURB
DocKnot is a static web site generator built around a macro language
called thread, with special support for managing software releases. In
addition to building a web site, it can generate distribution tarballs
and consistent human-readable software package documentation from a YAML
metadata file and templates. The goal is to generate both web pages and
distributed documentation files (such as README) from the same source,
using templates for consistency across multiple packages.
DESCRIPTION
In 1999, I wrote a program named spin that implemented an idiosyncratic
macro language called thread. It slowly expanded into a static web site
generator and gained additional features to manage the journal entries,
book reviews, RSS feeds, and software releases. DocKnot is the latest
incarnation.
In addition to its static web site generator, DocKnot can use one
metadata file as its source information and generate all the various
bits of documentation for a software package. This allows me to make
any changes in one place and then regenerate the web page, included
documentation, and other files to incorporate those changes. It also
lets me make changes to the templates to improve shared wording and push
that out to every package I maintain without having to remember track
those changes in each package.
DocKnot is also slowly absorbing other tools that I use for software
distribution and web site maintenance, such as generating distribution
tarballs for software packages.
DocKnot was designed and written for my personal needs, and I'm not sure
it will be useful for anyone else. At the least, the template files are
rather specific to my preferences about how to write package
documentation, and the thread macro language is highly specialized for
my personal web site. I'm not sure if I'll have the time to make it a
more general tool. But you're certainly welcome to use it if you find
it useful, send pull requests to make it more general, or take ideas
from it for your own purposes.
REQUIREMENTS
Perl 5.24 or later and Module::Build are required to build this module.
The following additional Perl modules are required to use it:
[](https://tracker.debian.org/pkg/docknot)
Copyright 1999-2024 Russ Allbery <rra@cpan.org>. This software is
distributed under a BSD-style license. Please see the section
[License](#license) below for more information.
## Blurb
DocKnot is a static web site generator built around a macro language
called thread, with special support for managing software releases. In
addition to building a web site, it can generate distribution tarballs and
consistent human-readable software package documentation from a YAML
metadata file and templates. The goal is to generate both web pages and
distributed documentation files (such as `README`) from the same source,
using templates for consistency across multiple packages.
## Description
In 1999, I wrote a program named `spin` that implemented an idiosyncratic
macro language called thread. It slowly expanded into a static web site
generator and gained additional features to manage the journal entries,
book reviews, RSS feeds, and software releases. DocKnot is the latest
incarnation.
In addition to its static web site generator, DocKnot can use one metadata
file as its source information and generate all the various bits of
documentation for a software package. This allows me to make any changes
in one place and then regenerate the web page, included documentation, and
other files to incorporate those changes. It also lets me make changes to
the templates to improve shared wording and push that out to every package
I maintain without having to remember track those changes in each package.
DocKnot is also slowly absorbing other tools that I use for software
distribution and web site maintenance, such as generating distribution
tarballs for software packages.
DocKnot was designed and written for my personal needs, and I'm not sure
it will be useful for anyone else. At the least, the template files are
rather specific to my preferences about how to write package
documentation, and the thread macro language is highly specialized for my
personal web site. I'm not sure if I'll have the time to make it a more
general tool. But you're certainly welcome to use it if you find it
useful, send pull requests to make it more general, or take ideas from it
for your own purposes.
## Requirements
Perl 5.24 or later and Module::Build are required to build this module.
The following additional Perl modules are required to use it:
bin/docknot view on Meta::CPAN
B<docknot> release [B<-a> I<archivedir>] [B<-d> I<distdir>] [B<-m> I<metadata>]
B<docknot> spin [B<-d>] [B<-e> I<pattern> ...] [B<-s> I<url>] I<source>
I<output>
B<docknot> spin-rss [B<-b> I<base>] I<file>
B<docknot> spin-text [B<-lu>] [B<-s> I<url>] [B<-t> I<title>] [I<source>
[I<output>]]
B<docknot> spin-thread [B<-f>] [B<-s> I<url>] [I<source> [I<output>]]
B<docknot> update [B<-m> I<metadata>] [B<-o> I<output>]
B<docknot> update-spin [I<path>]
=head1 DESCRIPTION
B<docknot> is a static web site generator with special support for managing
the documentation and releases of software packages. Its actions are
organized into subcommands. The supported subcommands are:
bin/docknot view on Meta::CPAN
default output files are configured. This is a quick short-cut to generating
all documentation that's shipped with the package.
=item release
Copy a distribution tarball into a release area, archiving old versions, and
optionally updating configuration for C<spin>.
=item spin
Spin a tree of files written in the macro language thread into an HTML web
site. See L<App::DocKnot::Spin> for documentation on the input format and
details of the site generation.
=item spin-rss
Process a single F<.rss> file with L<App::DocKnot::Spin::RSS> (normally done
as part of using C<spin> to process a tree of files). See that module's
documentation for more details.
=item spin-text
Convert a text file to HTML.
=item spin-thread
Like C<spin>, but convert a single file written in thread to HTML.
=item update
Update the DocKnot package configuration from an older format.
=item update-spin
Update an input tree for C<spin> to the latest expectations. This will, for
example, convert old-style F<*.rpod> pointer files to new-style F<*.spin>
pointer files.
bin/docknot view on Meta::CPAN
this argument is given, no style sheet will be referred to in the generated
web page.
=item B<-t> I<title>, B<--title>=I<title>
Use I<title> as the page title rather than whatever may be determined from
looking at the input file.
=back
=head2 spin-thread
=over 4
=item B<-s> I<url>, B<--style-url>=I<url>
The base URL for style sheets. A style sheet specified in a C<\heading>
command will be considered to be relative to this URL and this URL will be
prepended to it. If this option is not given, the name of the style sheet
will be used verbatim as its URL, except with C<.css> appended.
docs/docknot.yaml view on Meta::CPAN
- name: api/app-docknot-spin
title: App::DocKnot::Spin
- name: api/app-docknot-spin-pointer
title: App::DocKnot::Spin::Pointer
- name: api/app-docknot-spin-rss
title: App::DocKnot::Spin::RSS
- name: api/app-docknot-spin-sitemap
title: App::DocKnot::Spin::Sitemap
- name: api/app-docknot-spin-text
title: App::DocKnot::Spin::Text
- name: api/app-docknot-spin-thread
title: App::DocKnot::Spin::Thread
- name: api/app-docknot-spin-versions
title: App::DocKnot::Spin::Versions
- name: api/app-docknot-update
title: App::DocKnot::Update
- name: api/app-docknot-util
title: App::DocKnot::Util
developer:
- name: todo
title: To-do list
user:
- name: docknot
title: docknot manual page
blurb: |
DocKnot is a static web site generator built around a macro language called
thread, with special support for managing software releases. In addition to
building a web site, it can generate distribution tarballs and consistent
human-readable software package documentation from a YAML metadata file and
templates. The goal is to generate both web pages and distributed
documentation files (such as `README`) from the same source, using templates
for consistency across multiple packages.
description: |
In 1999, I wrote a program named `spin` that implemented an idiosyncratic
macro language called thread. It slowly expanded into a static web site
generator and gained additional features to manage the journal entries, book
reviews, RSS feeds, and software releases. DocKnot is the latest
incarnation.
In addition to its static web site generator, DocKnot can use one metadata
file as its source information and generate all the various bits of
documentation for a software package. This allows me to make any changes in
one place and then regenerate the web page, included documentation, and
other files to incorporate those changes. It also lets me make changes to
the templates to improve shared wording and push that out to every package I
maintain without having to remember track those changes in each package.
DocKnot is also slowly absorbing other tools that I use for software
distribution and web site maintenance, such as generating distribution
tarballs for software packages.
DocKnot was designed and written for my personal needs, and I'm not sure it
will be useful for anyone else. At the least, the template files are rather
specific to my preferences about how to write package documentation, and the
thread macro language is highly specialized for my personal web site. I'm
not sure if I'll have the time to make it a more general tool. But you're
certainly welcome to use it if you find it useful, send pull requests to
make it more general, or take ideas from it for your own purposes.
requirements: |
Perl 5.24 or later and Module::Build are required to build this module.
The following additional Perl modules are required to use it:
* Date::Language (part of TimeDate)
* Date::Parse (part of TimeDate)
lib/App/DocKnot/Command.pm view on Meta::CPAN
maximum => 1,
},
'spin-text' => {
method => 'spin_text_file',
module => 'App::DocKnot::Spin::Text',
options => [
'modified|m', 'style|s=s', 'title|t=s', 'use-value|u',
],
maximum => 2,
},
'spin-thread' => {
method => 'spin_thread_file',
module => 'App::DocKnot::Spin::Thread',
options => ['style-url|s=s'],
maximum => 2,
},
update => {
method => 'update',
module => 'App::DocKnot::Update',
options => ['metadata|m=s', 'output|o=s'],
maximum => 0,
},
lib/App/DocKnot/Generate.pm view on Meta::CPAN
}
}
# Rejoin the paragraphs and return the result.
return join("\n\n", @paragraphs);
};
return $to_text;
}
# Returns code that converts metadata text (which is assumed to be in
# Markdown) to thread. This is not a complete Markdown formatter. It only
# supports the bits of markup that I've had some reason to use.
#
# This is constructed as a method returning a closure so that its behavior can
# be influenced by App::DocKnot configuration in the future, but it currently
# doesn't use any configuration.
#
# Returns: Code reference to a closure that takes a block of text and returns
# the converted thread
sub _code_for_to_thread {
my ($self) = @_;
my $to_thread = sub {
my ($text) = @_;
# Escape all backslashes.
$text =~ s{ \\ }{\\\\}xmsg;
# Rewrite triple backticks to \pre blocks and escape backticks inside
# them so that they're not turned into \code blocks.
$text =~ s{ ``` \w* (\s .*?) ``` }{
my $text = $1;
$text =~ s{ [\`] }{``}xmsg;
'\pre[' . $1 . ']';
}xmsge;
# Rewrite backticks to \code blocks.
$text =~ s{ ` ([^\`]+) ` }{\\code[$1]}xmsg;
# Undo backtick escaping.
$text =~ s{ `` }{\`}xmsg;
# Rewrite all Markdown links into thread syntax.
$text =~ s{ \[ ([^\]]+) \] [(] (\S+) [)] }{\\link[$2][$1]}xmsg;
# Rewrite long bullets. This is quite tricky since we have to grab
# every line from the first bulleted one to the point where the
# indentation stops.
$text =~ s{
( # capture whole contents
^ (\s*) # indent before bullet
[*] (\s+) # bullet and following indent
[^\n]+ \n # rest of line
lib/App/DocKnot/Generate.pm view on Meta::CPAN
# Rewrite compact bulleted lists.
$text =~ s{ \n ( (?: \s* [*] \s+ [^\n]+ \s* \n ){2,} ) }{
my $list = $1;
$list =~ s{ \n [*] \s+ ([^\n]+) }{\n\\bullet(packed)[$1]}xmsg;
"\n" . $list;
}xmsge;
# Done. Return the results.
return $text;
};
return $to_thread;
}
##############################################################################
# Helper methods
##############################################################################
# Word-wrap a paragraph of text. This is a helper function for _wrap, mostly
# so that it can be invoked recursively to wrap bulleted paragraphs.
#
# If the paragraph looks like regular text, which means indented by two or
lib/App/DocKnot/Generate.pm view on Meta::CPAN
$options_ref //= {};
my ($indent) = ($para =~ m{ \A ([ ]*) \S }xms);
# If the indent is longer than five characters and the ignore indent
# option is not set, leave it alone. Allow an indent of five characters
# since it may be a continuation of a numbered list entry.
if (length($indent) > 5 && !$options_ref->{ignore_indent}) {
return $para;
}
# If this looks like thread commands or URLs, leave it alone.
if ($para =~ m{ \A \s* (?: \\ | \[\d+\] ) }xms) {
return $para;
}
# If this starts with a bullet, strip the bullet off, wrap the paragraph,
# and then add it back in.
if ($para =~ s{ \A (\s*) [*] (\s+) }{$1 $2}xms) {
my $offset = length($1);
$para = $self->_wrap_paragraph($para, { ignore_indent => 1 });
substr($para, $offset, 1, q{*});
lib/App/DocKnot/Generate.pm view on Meta::CPAN
# Create the variable information for the template. Start with all
# metadata as loaded above.
my %vars = %{$data_ref};
# Add code references for our defined helper functions.
$vars{center} = $self->_code_for_center;
$vars{copyright} = $self->_code_for_copyright($data_ref->{copyrights});
$vars{indent} = $self->_code_for_indent;
$vars{to_text} = $self->_code_for_to_text;
$vars{to_thread} = $self->_code_for_to_thread;
# Run Template Toolkit processing.
$template = $self->appdata_path('templates', "${template}.tmpl");
my $tt = Template->new({ ABSOLUTE => 1, ENCODING => 'utf8' })
or croak(Template->error());
my $result;
$tt->process($template, \%vars, \$result) or croak($tt->error);
# Word-wrap the results to our width and return them.
return $self->_wrap($result);
lib/App/DocKnot/Generate.pm view on Meta::CPAN
=head1 NAME
App::DocKnot::Generate - Generate documentation from package metadata
=head1 SYNOPSIS
use App::DocKnot::Generate;
my $docknot
= App::DocKnot::Generate->new({ metadata => 'docs/docknot.yaml' });
my $readme = $docknot->generate('readme');
my $index = $docknot->generate('thread');
$docknot->generate_output('readme');
$docknot->generate_output('thread', 'www/index.th')
=head1 REQUIREMENTS
Perl 5.24 or later and the modules File::BaseDir, File::ShareDir, Kwalify,
Path::Tiny, Template (part of Template Toolkit), and YAML::XS, all of which
are available from CPAN.
=head1 DESCRIPTION
This component of DocKnot provides a system for generating consistent
lib/App/DocKnot/Spin.pm view on Meta::CPAN
# Static site builder supporting thread macro language.
#
# This module translates a tree of files, possibly written in thread (a custom
# macro language) into an HTML static site. It also handles formatting some
# other input types (text and POD, for example), copying other types of files
# to the output tree, and creating site navigation links.
#
# SPDX-License-Identifier: MIT
##############################################################################
# Modules and declarations
##############################################################################
lib/App/DocKnot/Spin.pm view on Meta::CPAN
if ($options) {
if ($options =~ m{ -c ( \s | \z ) }xms) {
$options{contents} = 1;
}
if ($options =~ m{ -t \s+ (?: '(.*)' | ( [^\'] \S+ ) ) }xms) {
$options{title} = $1 || $2;
}
} else {
$options{navbar} = 1;
}
my $podthread = Pod::Thread->new(%options);
# Grab the thread output.
my $data;
$podthread->output_string(\$data);
$podthread->parse_file("$source");
$data = decode('utf-8', $data);
# Spin that thread into HTML.
my $page = $self->{thread}->spin_thread($data);
# Push the result through _write_converter_output.
my $footer = sub {
my ($blurb) = @_;
my $link = '<a href="%URL%">spun</a>';
$self->_footer(
$source, $output, undef,
"Last modified and\n $link %MOD%",
"Last $link\n %NOW% from POD modified %MOD%",
);
lib/App/DocKnot/Spin.pm view on Meta::CPAN
my ($self, $input) = @_;
# Conversion rules for pointers. The key is the extension, the first
# value is the name of the command for the purposes of output, and the
# second is the name of the method to run.
#<<<
my %rules = (
changelog => ['cl2xhtml', '_cl2xhtml'],
faq => ['faq2html', '_faq2html'],
log => ['cvs2xhtml', '_cvs2xhtml'],
rpod => ['pod2thread', '_pod2html'],
);
#>>>
# Figure out what to do with the input.
if ($input->is_dir()) {
my $output = $self->_output_for_file($input);
$self->{generated}{"$output"} = 1;
if ($output->exists() && !$output->is_dir()) {
die "cannot replace $output with a directory\n";
} elsif (!$output->is_dir()) {
lib/App/DocKnot/Spin.pm view on Meta::CPAN
my $relative = $input->relative($self->{source});
my $time = $self->{versions}->latest_release($relative);
return
if is_newer($output, $input) && $output->stat()->[9] >= $time;
} else {
return if is_newer($output, $input);
}
# The output file is not newer. Respin it.
$self->_report_action('Spinning', $output);
$self->{thread}->spin_thread_file($input, $output);
} else {
my ($extension) = ($input->basename =~ m{ [.] ([^.]+) \z }xms);
if (defined($extension) && $rules{$extension}) {
my ($name, $sub) = $rules{$extension}->@*;
my $output = $self->_output_for_file($input, q{.} . $extension);
$self->{generated}{"$output"} = 1;
my ($source, $options, $style) = $self->_read_pointer($input);
return if is_newer($output, $input, $source);
$self->_report_action("Running $name for", $output);
$self->$sub($source, $output, $options, $style);
lib/App/DocKnot/Spin.pm view on Meta::CPAN
# Process all .rss files in the input tree first. This is done as a
# separate pass because Path::Iterator::Rule appears to not always re-read
# the directory when it's modified during the iteration.
my $rss = App::DocKnot::Spin::RSS->new({ base => $input });
my $rule = Path::Iterator::Rule->new()->name('.rss');
my $iter = $rule->iter("$input", { follow_symlinks => 0 });
while (defined(my $file = $iter->())) {
$rss->generate(path($file), path($file)->parent);
}
# Create a new thread converter object.
$self->{thread} = App::DocKnot::Spin::Thread->new(
{
output => $output,
sitemap => $self->{sitemap},
source => $input,
'style-url' => $self->{style_url},
versions => $self->{versions},
},
);
# Create the processor for pointers.
$self->{pointer} = App::DocKnot::Spin::Pointer->new(
{
output => "$output",
sitemap => $self->{sitemap},
'style-url' => $self->{style_url},
thread => $self->{thread},
},
);
# Process the input tree.
my %options = (follow_symlinks => 0, report_symlinks => 1);
$rule = Path::Iterator::Rule->new();
$rule = $rule->skip($rule->new()->name($self->{excludes}->@*));
$iter = $rule->iter("$input", \%options);
while (defined(my $file = $iter->())) {
$self->_process_file(path($file));
lib/App/DocKnot/Spin.pm view on Meta::CPAN
1;
__END__
=for stopwords
Allbery DocKnot MERCHANTABILITY NONINFRINGEMENT sublicense cvs2xhtml faq2html
cl2xhtml RSS
=head1 NAME
App::DocKnot::Spin - Static site builder supporting thread macro language
=head1 SYNOPSIS
use App::DocKnot::Spin;
my $spin = App::DocKnot::Spin->new({ delete => 1 });
$spin->spin('/path/to/input', '/path/to/output');
=head1 REQUIREMENTS
lib/App/DocKnot/Spin.pm view on Meta::CPAN
List::SomeUtils, Path::Iterator::Rule, Path::Tiny, Pod::Thread,
Sort::Versions, Template (part of Template Toolkit), and YAML::XS, all of
which are available from CPAN. Also expects to find B<faq2html>,
B<cvs2xhtml>, and B<cl2xhtml> on the user's PATH to convert certain types of
files.
=head1 DESCRIPTION
App::DocKnot::Spin is a static site builder that takes an input tree of files
and generates an output HTML site. It is built around the macro language
thread, which is designed for writing simple HTML pages using somewhat nicer
syntax, catering to my personal taste, and supporting variables and macros to
make writing pages less tedious.
Each file in the input tree is examined recursively and either copied verbatim
to the same relative path in the output tree (the default action), used as
instructions to an external program, or converted to HTML. When converted to
HTML, the output file will be named the same as the input file except the
extension will be replaced with C<.html>. Missing directories are created.
If the timestamp of the output file is the same as or newer than the timestamp
of the input file, it will be assumed to be up-to-date and will not be
regenerated. This optimization makes updating an existing static site much
quicker.
Most files in the input tree will normally be thread files ending in C<.th>.
These are processed into HTML using L<App::DocKnot::Spin::Thread>. See that
module's documentation for the details of the thread macro language.
Files that end in C<.spin> are pointers to external files that should be
converted to HTML and then written to the output tree. These pointers are
interpreted by L<App::DocKnot::Spin::Pointer>. See that module's
documentation for their format and effects.
Files that end in various other extensions are taken to be instructions to run
an external converter on a file. The first line of such a pointer file should
be the path to the source file, the second line any arguments to the
converter, and the third line the style sheet to use if not the default.
lib/App/DocKnot/Spin.pm view on Meta::CPAN
.log cvs log <file> | cvs2xhtml
.rpod Pod::Thread
All other files not beginning with a period are copied as-is, except that
files or directories named F<CVS>, F<Makefile>, or F<RCS> are ignored. As an
exception, F<.htaccess> files are also copied. This list of exclusions can
be added to with the C<exclude> constructor argument.
If there is a file named F<.sitemap> at the top of the input tree, it will be
parsed with L<App::DocKnot::Spin::Sitemap> and used for inter-page links and
the C<\sitemap> thread command. See that module's documentation for the
format of this file.
If there is a file named F<.versions> at the top of the input tree, it will be
parsed with L<App::DocKnot::Spin::Versions> and used to determine when to
regenerate certain pages and for the C<\release> and C<\version> thread
commands. See that module's documentation for the format of this file.
If there is a file named F<.rss> in any directory of the input tree, it will
be processed with L<App::DocKnot::Spin::RSS> to generate RSS and possibly
other files. This is done before processing the files in that directory so
that the generated files will then be processed as normal.
If there is a directory named F<.git> at the top of the input tree,
App::DocKnot::Spin will assume that the input tree is a Git repository and
will try to use C<git log> to determine the last modification date of files.
lib/App/DocKnot/Spin/Pointer.pm view on Meta::CPAN
my %options = (
contents => $data_ref->{options}{contents},
style => $data_ref->{style} // 'pod',
title => $data_ref->{title},
);
if (exists($data_ref->{options}{navbar})) {
$options{navbar} = $data_ref->{options}{navbar};
} else {
$options{navbar} = 1;
}
my $podthread = Pod::Thread->new(%options);
# Convert the POD to thread.
my $data;
$podthread->output_string(\$data);
$podthread->parse_file("$source");
$data = decode('utf-8', $data);
# Spin that page into HTML.
$self->{thread}->spin_thread_output($data, $source, 'POD', $output);
return;
}
# Convert a text file to HTML.
#
# $data_ref - Data form the pointer file
# options - Hash of conversion options
# modified - Whether to add a last modified subheader
# path - Path to the text file to convert
# style - Style sheet to use
lib/App/DocKnot/Spin/Pointer.pm view on Meta::CPAN
# Public interface
##############################################################################
# Create a new HTML converter for pointers. This object can (and should) be
# reused for all pointer conversions done while spinning a tree of files.
#
# $args - Anonymous hash of arguments with the following keys:
# output - Root of the output tree
# sitemap - App::DocKnot::Spin::Sitemap object
# style-url - Partial URL to style sheets
# thread - App::DocKnot::Spin::Thread object
#
# Returns: Newly created object
# Throws: Text exception on failure to initialize Template Toolkit
sub new {
my ($class, $args_ref) = @_;
# Get the configured path to pandoc, if any.
my $config_reader = App::DocKnot::Config->new();
my $global_config_ref = $config_reader->global_config();
my $pandoc = $global_config_ref->{pandoc} // 'pandoc';
lib/App/DocKnot/Spin/Pointer.pm view on Meta::CPAN
# Create and return the object.
my $tt = Template->new({ ABSOLUTE => 1, ENCODING => 'utf8' })
or croak(Template->error());
my $self = {
output => $args_ref->{output},
pandoc_path => $pandoc,
sitemap => $args_ref->{sitemap},
style_url => $style_url,
template => $tt,
thread => $args_ref->{thread},
};
bless($self, $class);
$self->{template_path} = $self->appdata_path('templates', 'html.tmpl');
return $self;
}
# Check if the result of a pointer file needs to be regenerated.
#
# $pointer - Path to pointer file
# $output - Path to corresponding output file
lib/App/DocKnot/Spin/Pointer.pm view on Meta::CPAN
An App::DocKnot::Spin::Sitemap object. This will be used to create inter-page
links. For inter-page links, the C<output> argument must also be provided.
=item style-url
The base URL for style sheets. A style sheet specified in a pointer file will
be considered to be relative to this URL and this URL will be prepended to it.
If this option is not given, the name of the style sheet will be used verbatim
as its URL, except with C<.css> appended.
=item thread
An App::DocKnot::Spin::Thread object, used for converting POD into HTML. It
should be configured with the same App::DocKnot::Spin::Sitemap object as the
C<sitemap> argument.
=back
=back
=head1 INSTANCE METHODS
lib/App/DocKnot/Spin/RSS.pm view on Meta::CPAN
# Generate RSS and thread from a feed description file.
#
# This module generates RSS feeds and thread indexes of newly-published pages
# or change notes for a web site maintained with App::DocKnot::Spin.
#
# SPDX-License-Identifier: MIT
##############################################################################
# Modules and declarations
##############################################################################
package App::DocKnot::Spin::RSS v8.0.1;
lib/App/DocKnot/Spin/RSS.pm view on Meta::CPAN
}
# Spin a file into HTML.
#
# $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 = $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) = @_;
lib/App/DocKnot/Spin/RSS.pm view on Meta::CPAN
# Write the result to the output file.
$file->spew_utf8($result);
return;
}
##############################################################################
# Thread output
##############################################################################
# Print out the thread version of the recent changes list.
#
# $file - Path::Tiny output path
# $metadata_ref - RSS feed metadata
# $entries_ref - Entries
sub _thread_output {
my ($self, $file, $metadata_ref, $entries_ref) = @_;
# The entries are in a flat list, but we want a two-level list of entries
# by month so that the template can add appropriate month headings.
# Restructure the entry list accordingly.
my (@entries_by_month, $last_month);
for my $entry_ref ($entries_ref->@*) {
my $month = strftime('%B %Y', localtime($entry_ref->{date}));
my $date = strftime('%Y-%m-%d', localtime($entry_ref->{date}));
lib/App/DocKnot/Spin/RSS.pm view on Meta::CPAN
my $month_ref = { heading => $month, entries => [$formatted_ref] };
push(@entries_by_month, $month_ref);
$last_month = $month;
} else {
push($entries_by_month[-1]{entries}->@*, $formatted_ref);
}
}
# Generate the RSS output using the template.
my %vars = (
prefix => $metadata_ref->{'thread-prefix'},
entries => \@entries_by_month,
);
my $result;
$self->{template}->process($self->{templates}{changes}, \%vars, \$result)
or croak($self->{template}->error());
# Write the result to the output file.
$file->spew_utf8($result);
return;
}
##############################################################################
# Index output
##############################################################################
# Translate the thread of a journal entry for inclusion in an index page.
#
# $file - Path::Tiny to the journal entry
#
# Returns: Thread to include in the index page
sub _index_journal {
my ($self, $file, $url) = @_;
my $fh = $file->openr_utf8();
# Skip to the first \h1 and exclude it.
while (defined(my $line = <$fh>)) {
lib/App/DocKnot/Spin/RSS.pm view on Meta::CPAN
while (defined(my $line = <$fh>)) {
last if $line =~ m{ \A \\date }xms;
$text .= $line;
}
# All done.
close($fh);
return $text;
}
# Translate the thread of a book review for inclusion into an index page.
#
# $file - Path::Tiny to the book review
#
# Returns: Thread to include in the index page
sub _index_review {
my ($self, $file) = @_;
my $title;
my $author;
# Regex to match a single "character" in a macro argument.
lib/App/DocKnot/Spin/RSS.pm view on Meta::CPAN
$text = $self->_index_journal($path);
} elsif ($entry_ref->{review}) {
my $path = path($entry_ref->{review})->absolute($base);
$text = $self->_index_review($path);
} else {
die "unknown entry type\n";
}
# Make all the URLs absolute and then convert images back to relative
# based on the URL of the file we're creating. This handles
# correcting links from thread from elsewhere in the tree.
$text =~ s{
( \\ (?: link | image ) \s* \[ ) ( [^\]]+ ) \]
}{ $1 . _absolute_url($2, $entry_ref->{link}) . ']' }xmsge;
$text =~ s{
( \\ image \s* \[ ) ( [^\]]+ ) \]
}{$1 . _relative_url($2, $metadata_ref->{'index-base'}) . ']' }xmsge;
# Add the entry to the list.
my $formatted_ref = {
date => $date,
lib/App/DocKnot/Spin/RSS.pm view on Meta::CPAN
my @entries;
if ($tags eq q{*}) {
@entries = $changes_ref->@*;
} else {
my @tags = split(m{ , }xms, $tags);
@entries
= grep { _intersect($_->{tags}, \@tags) } $changes_ref->@*;
}
# Write the output.
if ($format eq 'thread') {
$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});
}
$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});
}
lib/App/DocKnot/Spin/RSS.pm view on Meta::CPAN
1;
__END__
=for stopwords
Allbery DocKnot MERCHANTABILITY NONINFRINGEMENT RSS TimeDate YYYY-MM-DD
sublicense hoc rss
=head1 NAME
App::DocKnot::Spin::RSS - Generate RSS and thread from a feed description file
=head1 SYNOPSIS
use App::DocKnot::Spin::RSS;
my $rss = App::DocKnot::Spin::RSS->new({ base => 'path/to/tree' });
$rss->generate('path/to/tree/.rss');
=head1 REQUIREMENTS
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
App::DocKnot::Spin::RSS reads as input a feed description file consisting of
simple key/value pairs and writes out either thread (for input to
App::DocKnot::Spin::Thread) or RSS. The feed description consists of a
leading block of metadata and then one block per entry in the feed. Each
block can either include the content of the entry or can reference an external
thread file, in several formats, for the content. The feed description file
defines one or more output files in the Output field of the metadata.
Output files are only regenerated if they are older than the input feed
description file.
App::DocKnot::Spin::RSS is designed for use with App::DocKnot::Spin. It
relies on App::DocKnot::Spin::Thread to convert thread to HTML, both for
inclusion in RSS feeds and for post-processing of generated thread files.
App::DocKnot::Spin::RSS is invoked automatically by App::DocKnot::Spin when it
encounters an F<.rss> file in a directory it is processing.
See L<INPUT LANGUAGE> for the details of the language in which F<.rss> files
are written.
=head1 CLASS METHODS
=over 4
lib/App/DocKnot/Spin/RSS.pm view on Meta::CPAN
=item Description
The description of the feed, used only in the RSS output. This should always
be set if there are any RSS output files.
=item Index-Base
The base URL for output files of type C<index>. This is used to canonicalize
relative URLs and should be the URL to the directory containing the HTML file
that will result from processing the thread output. This should be set if
there are any output files of type C<index>; if it isn't set, relative links
may be rewritten incorrectly.
=item Index-Prefix
When generating output files of type C<index>, use the value as the initial
content of the generated thread. This field should almost always be set if
any output files of type C<index> are defined. It will contain such things as
the C<\heading> command, any prologue material, initial headings, and so
forth.
=item Index-Suffix
When generating output files of type C<index>, append the value to the end of
the generated thread. The C<\signature> command is always appended and should
not be included here. Set this field only if there is other thread that needs
to be appended (such as closing brackets for C<\div> commands).
=item Language
The language of the feed, used only in the RSS output. This should always be
set if there are any RSS output files. Use C<en-us> for US English.
=item Output
Specifies the output files for this input file in the form of a
lib/App/DocKnot/Spin/RSS.pm view on Meta::CPAN
the output file (always a relative path), I<type> is the type of output, and
I<tags> indicates which entries to include in this file. I<tags> is a
comma-separated list of tags or the special value C<*>, indicating all tags.
There are three types of output:
=over 4
=item index
Output thread containing all recent entries. This output file honors the
Recent field similar to RSS output and is used to generate something akin to a
journal or blog front page: an HTML version of all recent entries. It only
supports external entries (entries with C<Journal> or C<Review> fields). The
C<Index-Base> and C<Index-Prefix> (and possibly C<Index-Suffix>) fields should
be set.
For output for entries with simple descriptions included in the input file,
see the C<thread> output type.
=item rss
Output an RSS file. App::DocKnot::Spin::RSS only understands the RSS 2.0
output format. The C<Description>, C<Language>, C<RSS-Base>, and C<Title>
fields should be set to provide additional metadata for the output file.
=item thread
Output thread containing all entries in this input file. This should only be
used for input files where all entries have their description text inline in
the input file. Every entry will be included. The output will be divided
into sections by month, and each entry will be in a description list, with the
title prefixed by the date. The C<Thread-Prefix> field should be set.
For output that can handle entries from external files, see the C<index>
output type.
=back
=item Recent
Sets the number of recent entries to include in output files of type C<rss> or
C<index> (but not C<thread>, which will include the full contents of the
file). If this field is not present, the default is 15.
=item RSS-Base
The base URL for RSS files generated by this file. Each generated RSS file
should have a link back to itself, and that link will be formed by taking the
output name and prepending this field.
=item Thread-Prefix
When generating thread output from this file, use the value as the initial
content of the generated thread. This field should almost always be set if
any output files of type C<thread> are defined. It will contain such things
as the C<\heading> command, any prologue material, initial headings, and so
forth.
=item Title
The title of the feed, used only in the RSS output. This should always be set
if there are any RSS output files.
=back
lib/App/DocKnot/Spin/RSS.pm view on Meta::CPAN
=item Date
The date of this entry in ISO date format (YYYY-MM-DD HH:MM). This field is
required.
=item Description
The inline contents of this entry. One and only one of this field,
C<Journal>, or C<Review> should be present. C<Description> fields can only be
used with output types of C<rss> or C<thread>.
=item Journal
Specifies that the content of this entry should be read from an external
thread file given by the value of this field. The contents of that file are
expected to be in the thread format used by my journal entries: specifically,
everything is ignored up to the first C<\h1> and after the C<\date> macro, and
the C<\date> line is stripped off (the date information from the C<Date> field
is used instead).
One and only one of this field, C<Description>, or C<Review> should be
present.
=item Link
The link to the page referenced by this entry. The link is relative to the
C<Base> field set in the input file metadata. This field is required.
=item Review
Specifies that the content of this entry should be read from an external
thread file given by the value of this field. The contents of that file are
expected to be in the thread format used by my book reviews.
Many transformations are applied to entries of this sort based on the format
used by my book reviews and the URL layout they use, none of which is
documented at present. For the time being, see the source code for what
transformations are done. This support will require modification for use by
anyone else.
One and only one of this field, C<Description>, or C<Review> should be
present.
lib/App/DocKnot/Spin/Sitemap.pm view on Meta::CPAN
which are available from CPAN.
=head1 DESCRIPTION
App::DocKnot::Spin supports sitemap information stored in a C<.sitemap> file
at the top of the source directory. If this is present, it is used to add
navigation information to every generated page.
App::DocKnot::Spin::Sitemap encapsulates parsing of that file and generating
the HTML for inter-page links. It can also generate HTML for the entirety of
the sitemap to support the C<\sitemap> thread command.
The format of this file is one line per web page, with indentation showing the
tree structure. Each line should be formatted as a partial URL (relative to
the top of the site) starting with C</>, a colon, and a page description. The
partial URL should be for the generated pages, not the source files (so, for
example, should use an C<.html> extension). The top of the generated site
should have the URL of C</> at the top of the sitemap.
If two pages at the same level aren't related and shouldn't have next and
previous links to each other, they should be separated by three dashes on a
lib/App/DocKnot/Spin/Sitemap.pm view on Meta::CPAN
=item navbar(PAGE)
Generate the navigation bar for the provided PAGE, which should be a URL
relative to the top of the generated site and starting with C</>. The return
value is a list of HTML lines suitable for injecting into the output page.
=item sitemap()
Return the sitemap as a list of lines of formatted HTML, suitable for
inclusion in a generated web page. This is used to implement the C<\sitemap>
thread command.
=back
=head1 AUTHOR
Russ Allbery <rra@cpan.org>
=head1 COPYRIGHT AND LICENSE
Copyright 1999-2000, 2002-2004, 2008, 2021-2022 Russ Allbery <rra@cpan.org>
lib/App/DocKnot/Spin/Thread.pm view on Meta::CPAN
# Generate HTML from the macro language thread.
#
# Thread is a macro language designed for producing HTML pages. This module
# parses thread and generates the corresponding HTML.
#
# SPDX-License-Identifier: MIT
##############################################################################
# Modules and declarations
##############################################################################
package App::DocKnot::Spin::Thread v8.0.1;
use 5.024;
lib/App/DocKnot/Spin/Thread.pm view on Meta::CPAN
# as-is. (We will have already warned about this when defining the
# macro.)
my $expand = sub {
my ($n) = @_;
return ($n > scalar(@args)) ? "\\\\$n" : $args[$n - 1];
};
# Replace the substitution markers in the macro definition.
$definition =~ s{ \\(\d+) }{ $expand->($1) }xmsge;
# Now parse the result as if it were input thread and return the results.
return $self->_parse_context($definition, $block);
}
# Expand a given command into its representation. This function is mutually
# recursive with _parse_context and _macro.
#
# $command - Name of the command
# $text - Input text following the command
# $block - True if currently in block context (if so, and if the command
# doesn't generate its own container, it will need to be wrapped
lib/App/DocKnot/Spin/Thread.pm view on Meta::CPAN
# $text - Input text to parse
# $block - True if the parse is done in a block context
#
# Returns: HTML output corresponding to $text
sub _parse {
my ($self, $text, $block) = @_;
my ($output) = $self->_parse_context($text, $block);
return $output;
}
# The top-level function for parsing a thread document. Be aware that the
# working directory from which this function is run matters a great deal,
# since thread may contain relative paths to files that the spinning process
# needs to access.
#
# $thread - Thread to spin
# $in_path - Input file path as a Path::Tiny object, or undef
# $out_fh - Output file handle to which to write the HTML
# $out_path - Output file path as a Path::Tiny object, or undef
# $input_type - Optional one-word description of input type
sub _parse_document {
my ($self, $thread, $in_path, $out_fh, $out_path, $input_type) = @_;
# Parse the thread into paragraphs and reverse them to form a stack.
my @input = reverse($self->_split_paragraphs($thread));
# Initialize object state for a new document.
#<<<
$self->{input} = [[\@input, $in_path, 1]];
$self->{input_type} = $input_type // 'thread';
$self->{macro} = {};
$self->{out_fh} = $out_fh;
$self->{out_path} = $out_path;
$self->{rss} = [];
$self->{space} = q{};
$self->{state} = ['BLOCK'];
$self->{variable} = {};
#>>>
# Parse the thread file a paragraph at a time. _split_paragraphs takes
# care of ensuring that each paragraph contains the complete value of a
# command argument.
#
# The stack of parsed input is maintained in $self->{input} and the file
# being parsed at any given point is $self->{input}[-1]. _cmd_include
# will push new file information into this stack, and we pop off the top
# element of the stack when we exhaust its paragraphs.
while ($self->{input}->@*) {
while (defined(my $para = pop($self->{input}[-1][0]->@*))) {
my $result = $self->_parse(_escape($para), 1);
lib/App/DocKnot/Spin/Thread.pm view on Meta::CPAN
$output .= $self->_format_attr($format) . ' />';
return (1, $output);
}
# Include a file. Note that this includes a file after the current paragraph,
# not immediately, which may be a bit surprising.
sub _cmd_include {
my ($self, $file) = @_;
$file = $self->_file_path($self->_parse($file));
# Read the thread, split it on paragraphs, and reverse it to make a stack.
my $thread = $self->_read_file($file);
my @paragraphs = reverse($self->_split_paragraphs($thread));
# Add it to the file stack.
push($self->{input}->@*, [\@paragraphs, $file, 1]);
# Expand into empty output.
return (1, q{});
}
# A link to a URL or partial URL.
#
lib/App/DocKnot/Spin/Thread.pm view on Meta::CPAN
$self->_warning(qq(no version known for "$package"));
return (0, q{});
}
return (0, $version);
}
##############################################################################
# Public interface
##############################################################################
# Create a new thread to HTML converter. This object can (and should) be
# reused for all thread conversions done while spinning a tree of files.
#
# $args - Anonymous hash of arguments with the following keys:
# output - Root of the output tree
# sitemap - App::DocKnot::Spin::Sitemap object
# source - Root of the source tree
# style-url - Partial URL to style sheets
# versions - App::DocKnot::Spin::Versions object
#
# Returns: Newly created object
sub new {
lib/App/DocKnot/Spin/Thread.pm view on Meta::CPAN
repository => $repository,
sitemap => $args_ref->{sitemap},
source => $source,
style_url => $style_url,
versions => $args_ref->{versions},
};
bless($self, $class);
return $self;
}
# Convert thread to HTML and return the output as a string. The working
# directory still matters for file references in the thread.
#
# $thread - Thread to spin
# $input - Optional input file path (for relative path and timestamps)
#
# Returns: Resulting HTML
sub spin_thread {
my ($self, $thread, $input) = @_;
my $result;
open(my $out_fh, '>:raw:encoding(utf-8)', \$result);
$self->_parse_document($thread, $input, $out_fh, undef);
close($out_fh);
return decode('utf-8', $result);
}
# Spin a single file of thread to HTML.
#
# $input - Input file (if not given, assumes standard input)
# $output - Output file (if not given, assumes standard output)
#
# Raises: Text exception on processing error
sub spin_thread_file {
my ($self, $input, $output) = @_;
my $out_fh;
my $thread;
# Read the input file.
if (defined($input)) {
$input = path($input)->realpath();
$thread = $input->slurp_utf8();
} else {
$thread = slurp(\*STDIN);
}
# Open the output file.
if (defined($output)) {
$output = path($output)->absolute();
$out_fh = $output->openw_utf8();
} else {
open($out_fh, '>&:raw:encoding(utf-8)', 'STDOUT');
}
# Do the work.
$self->_parse_document($thread, $input, $out_fh, $output);
# Clean up.
close($out_fh);
return;
}
# Convert thread to HTML and write it to the given output file. This is used
# when the thread isn't part of the input tree but instead is intermediate
# output from some other conversion process.
#
# $thread - Thread to spin
# $input - Original input file path (for relative path and timestamps)
# $input_type - One-word description of input type for the page footer
# $output - Output file
#
# Returns: Resulting HTML
sub spin_thread_output {
my ($self, $thread, $input, $input_type, $output) = @_;
$input = path($input);
# Open the output file.
my $out_fh;
if (defined($output)) {
$output = path($output)->absolute();
$out_fh = $output->openw_utf8();
} else {
open($out_fh, '>&:raw:encoding(utf-8)', 'STDOUT');
}
# Do the work.
$self->_parse_document($thread, $input, $out_fh, $output, $input_type);
# Clean up and restore the working directory.
close($out_fh);
return;
}
##############################################################################
# Module return value and documentation
##############################################################################
1;
__END__
=for stopwords
Allbery DocKnot MERCHANTABILITY NONINFRINGEMENT sublicense NARGS RCS RSS
preformatted respun
=head1 NAME
App::DocKnot::Spin::Thread - Generate HTML from the macro language thread
=head1 SYNOPSIS
use App::DocKnot::Spin::Thread;
my $input = 'some thread';
my $thread = App::DocKnot::Spin::Thread->new();
my $output = $thread->spin_thread($input);
use App::DocKnot::Spin::Sitemap;
use App::DocKnot::Spin::Versions;
my $sitemap = App::DocKnot::Spin::Sitemap->new('/input/.sitemap');
my $versions = App::DocKnot::Spin::Versions->new('/input/.versions');
$thread = App::DocKnot::Spin::Thread->new({
source => '/input',
output => '/output',
sitemap => $sitemap,
versions => $versions,
});
$thread->spin_thread_file('/input/file.th', '/output/file.html');
$thread->spin_thread_output(
$input, '/path/to/file.pod', 'POD', '/output/file.html'
);
=head1 REQUIREMENTS
Perl 5.24 or later and the modules Git::Repository, Image::Size,
List::SomeUtils, and Path::Tiny, all of which are available from CPAN.
=head1 DESCRIPTION
This component of DocKnot implements the macro language thread, which is
designed for writing simple HTML pages using somewhat nicer syntax, catering
to my personal taste, and supporting variables and macros to make writing
pages less tedious.
For the details of the thread language, see L<THREAD LANGUAGE> below.
=head1 CLASS METHODS
=over 4
=item new(ARGS)
Create a new App::DocKnot::Spin::Thread object. A single converter object
can be used repeatedly to convert a tree of files, or can convert a single
file. ARGS should be a hash reference with one or more of the following
lib/App/DocKnot/Spin/Thread.pm view on Meta::CPAN
data for the C<\release> and C<\version> commands.
=back
=back
=head1 INSTANCE METHODS
=over 4
=item spin_thread(THREAD[, INPUT])
Convert the given thread to HTML, returning the result. When run via this
API, App::DocKnot::Spin::Thread will not be able to obtain sitemap information
even if a sitemap was provided and therefore will not add inter-page links.
INPUT, if given, is the full path to the original source file, used for
relative paths and modification time information.
=item spin_thread_file([INPUT[, OUTPUT]])
Convert a single thread file to HTML. INPUT is the path of the thread file
and OUTPUT is the path of the output file. OUTPUT or both INPUT and OUTPUT
may be omitted, in which case standard input or standard output, respectively,
will be used.
If OUTPUT is omitted, App::DocKnot::Spin::Thread will not be able to obtain
sitemap information even if a sitemap was provided and therefore will not add
inter-page links.
=item spin_thread_output(THREAD, INPUT, TYPE[, OUTPUT])
Convert the given thread to HTML, writing the result to OUTPUT. If OUTPUT is
not given, write the results to standard output. This is like spin_thread()
but does use sitemap information and adds inter-page links. It should be used
when the thread input is the result of an intermediate conversion step of a
known input file. INPUT should be the full path to the original source file,
used for relative paths and modification time information. TYPE should be set
to a one-word description of the format of the input file and is used for the
page footer.
=back
=head1 THREAD LANGUAGE
=head2 Basic Syntax
A thread file is Unicode text with a blank line between paragraphs.
There is no need to explicitly mark paragraphs; paragraph boundaries will be
inferred from the blank line between them and the appropriate C<< <p> >> tags
will be added to the HTML output.
There is no need to escape any character except C<\> (which should be written
as C<\\>) and an unbalanced C<[> or C<]> (which should be written as
C<\entity[91]> or C<\entity[93]> respectively). Escaping C<[> or C<]> is not
necessary if the brackets are balanced within the paragraph, and therefore is
only rarely needed.
lib/App/DocKnot/Spin/Thread.pm view on Meta::CPAN
appending navigation links, closing any open blocks, and any other cleanup
that has to happen at the end of a generated HTML page.
You can include other files with the C<\include> command, although it has a
few restrictions. The C<\include> command must appear either at the beginning
of the file or after a blank line, and should be followed by a blank line. Be
careful not to include the same file recursively as there is no current
protection against infinite loops.
Thread files will not be automatically respun when included files change, so
you will need touch the thread file to force the corresponding output file to
be regenerated.
All further thread commands are divided into block commands and inline
commands. These roughly correspond to HTML 5's "flow content" and "phrasing
content" respectively.
=head2 Block Commands
Block commands are commands that should occur in a paragraph by themselves,
not contained in a paragraph with other text. They indicate high-level
structural elements of the page. C<\heading> and C<\include> were already
discussed above, but here is a complete list. Any argument of TEXT can be
multiple paragraphs and contain other embedded block commands (so you can nest
lib/App/DocKnot/Spin/Thread.pm view on Meta::CPAN
=item \version[PACKAGE]
If the C<versions> argument was provided, replaced with the latest version of
PACKAGE.
=back
=head2 Defining Variables and Macros
One of the reasons to use thread instead of HTML is the ability to define new
macros on the fly. If there are constructs that are used more than once in
the page, you can define a macro at the top of that page and then use it
throughout the page.
A variable can be defined with the command:
\=[VARIABLE][VALUE]
where VARIABLE is the name that will be used (can only be alphanumerics plus
underscore) and VALUE is the value that string will expand into. Any later
lib/App/DocKnot/Spin/Thread.pm view on Meta::CPAN
\==[NAME][NARGS][DEFINITION]
where NAME is the name of the macro (again consisting only of alphanumerics or
underscore), NARGS is the number of arguments that it takes, and DEFINITION is
the definition of the macro.
When the macro is expanded, any occurrence of C<\1> in the definition is
replaced with the first argument, any occurrence of C<\2> with the second
argument, and so forth, and then the definition with those substitutions is
parsed as thread, as if it were written directly in the source page.
For example:
\==[bolddesc] [2] [\desc[\bold[\1]][\2]]
defines a macro C<\bolddesc> that takes the same arguments as the regular
C<\desc> command but always wraps the first argument, the heading, in C<<
<strong> >>.
=head1 AUTHOR
lib/App/DocKnot/Spin/Versions.pm view on Meta::CPAN
The date of the latest release of that package in YYYY-MM-DD format in the
local time zone.
=item <time>
The time of the latest release of that package in HH:MM:SS format in the local
time zone.
=item <files>
Any number of thread input files affected by this release, separated by
spaces. The file names should be relative to the top of the source tree for
the web site.
=back
The <files> field can be continued on the following line by starting the line
with whitespace. Each whitespace-separated word in a continuation line is
taken as an additional affected file for the previous line.
This information is used for the C<\version> and C<\release> thread commands
and to force regeneration of files affected by a release with a timestamp
newer than the timestamp of the corresponding output file.
=head1 CLASS METHODS
=over 4
=item new(PATH)
Create a new App::DocKnot::Spin::Versions object for the F<.versions> file
share/templates/thread.tmpl view on Meta::CPAN
[Debian package tracker] \break[% END %]
]
[% IF unmaintained %]
\h2[Warning]
\class(alert)[This package is not maintained.]
[% unmaintained %]
[% END %]
\h2[Blurb]
[% to_thread(blurb) %]
\h2[Description]
[% to_thread(description) %]
\h2[Requirements]
[% to_thread(requirements) %]
[% IF build.autotools %]
To bootstrap from a Git checkout, or if you change the Automake files and
need to regenerate Makefile.in, you will need Automake
[% build.automake %] or later. For bootstrap or if you change
configure.ac or any of the m4 files it includes and need to regenerate
configure or config.h.in, you will need Autoconf [% build.autoconf %] or
later.[% IF build.manpages %] Perl is also required to generate manual
pages from a fresh Git checkout.[% END %][% IF build.bootstrap %]
[% to_thread(build.bootstrap) %][% END %]
[% END %]
\h2[Download]
The distribution:
\table[][
\program[[% name %]][[% distribution.version %]]
[[% distribution.section %]/[% distribution.tarname %]-\version[[% distribution.version %]]]
]
An \link[https://archives.eyrie.org/software/ARCHIVE/[% distribution.tarname %]/]
[archive of older releases] is also available.[% IF advisories %]
\class(alert)[Versions older than [% advisories.0.threshold %] have known
security vulnerabilities and should not be used.][% END %]
[% IF distribution.packaging.debian.summary %]
[% IF distribution.packaging.debian.personal %][% to_thread(distribution.packaging.debian.summary) %]
[% ELSE %][% to_thread(distribution.packaging.debian.summary) | trim %][% IF distribution.packaging.debian.summary.match('\n\n') %]
[% ELSE %] [% END %]See the \link[https://tracker.debian.org/pkg/[%
distribution.packaging.debian.package %]][Debian package tracker] for more
information.
[% END %][% ELSIF distribution.packaging.debian.personal %]
A Debian package [% IF distribution.packaging.debian.package %]([% distribution.packaging.debian.package %]) [% END %]is available from my \link[../debian.html][personal
repository].
[% END %][% IF distribution.cpan %]
[% name %] is available from CPAN as the
\link[https://metacpan.org/release/[% distribution.cpan %]]
[[% distribution.cpan %] distribution].
[% END %]
[% IF distribution.packaging.extra %]
[% to_thread(distribution.packaging.extra) %]
[% END %][% name %] [% IF unmaintained %]was[% ELSE %]is[% END %]
maintained using the [% vcs.type %] version control system. To check out
the current development tree, [% IF vcs.github %]see
\link[https://github.com/[% vcs.github %]][GitHub] or [% END %]clone:
\pre[ [% vcs.url %]]
[% IF vcs.github && !unmaintained %]Pull requests on GitHub are welcome.
[% END %]You can also \link[[% vcs.browse %]][browse the current
development source].
share/templates/thread.tmpl view on Meta::CPAN
[% END %]
The [% name %] package as a whole is covered by the following copyright
and license:
\block[
[% FOREACH copr IN copyrights %]
Copyright [% copr.years %]
[% copr.holder %]
[% END %]
[% indent(to_thread(license.text), 4) %]
[% IF license.notices %]
[% indent(to_thread(license.notices), 4) %]
[% END %]
]
Some individual source files are covered by other, compatible licenses.
For complete copyright and license information, see the file
\link[license.html][LICENSE] in the [% name %] source distribution.
\signature
t/cli/spin.t view on Meta::CPAN
isa_ok($docknot, 'App::DocKnot::Command');
# Create a temporary directory for test output.
my $tempdir = Path::Tiny->tempdir();
# Spin a single file.
my $datadir = path('t', 'data', 'spin');
my $input = $datadir->child('input', 'index.th');
my $expected = $datadir->child('output', 'index.html');
my $output = $tempdir->child('index.html');
$docknot->run('spin-thread', '-s', '/~eagle/styles', "$input", "$output");
is_spin_output($output, $expected, 'spin-thread (output specified)');
# Spin a single file to standard output.
my $stdout = capture_stdout {
$docknot->run('spin-thread', '-s', '/~eagle/styles', "$input");
};
$output->spew($stdout);
is_spin_output($output, $expected, 'spin-thread (standard output)');
# Copy the input tree to a new temporary directory since .rss files generate
# additional thread files.
my $indir = Path::Tiny->tempdir();
$input = $datadir->child('input');
dircopy($input, $indir)
or die "Cannot copy $input to $indir: $!\n";
fix_pointers($indir, $input);
# Spin a tree of files.
$expected = $datadir->child('output');
capture_stdout {
$docknot->run('spin', '-s', '/~eagle/styles', "$indir", "$tempdir");
};
my $count = is_spin_output_tree($tempdir, $expected, 'spin');
# Spin a file with warnings. The specific warnings are checked in
# t/spin/errors.t; here, we just check the rewrite of the warning.
my $errors = $datadir->child('errors', 'errors.th')->realpath();
my $stderr;
($stdout, $stderr) = capture {
$docknot->run('spin-thread', "$errors");
};
like(
$stderr, qr{ \A \Q$0\E [ ] spin-thread : \Q$errors\E : 1 : }xms,
'warnings are properly rewritten',
);
# Report the end of testing.
done_testing($count + 6);
t/data/generate/docknot/output/thread view on Meta::CPAN
[Git repository] \break
\link[https://metacpan.org/release/App-DocKnot]
[MetaCPAN] \break
\link[https://tracker.debian.org/pkg/docknot]
[Debian package tracker] \break
]
\h2[Blurb]
DocKnot is a static web site generator built around a macro language
called thread, with special support for managing software releases. In
addition to building a web site, it can generate distribution tarballs and
consistent human-readable software package documentation from a YAML
metadata file and templates. The goal is to generate both web pages and
distributed documentation files (such as \code[README]) from the same
source, using templates for consistency across multiple packages.
\h2[Description]
In 1999, I wrote a program named \code[spin] that implemented an
idiosyncratic macro language called thread. It slowly expanded into a
static web site generator and gained additional features to manage the
journal entries, book reviews, RSS feeds, and software releases. DocKnot
is the latest incarnation.
In addition to its static web site generator, DocKnot can use one metadata
file as its source information and generate all the various bits of
documentation for a software package. This allows me to make any changes
in one place and then regenerate the web page, included documentation, and
other files to incorporate those changes. It also lets me make changes to
the templates to improve shared wording and push that out to every package
I maintain without having to remember track those changes in each package.
DocKnot is also slowly absorbing other tools that I use for software
distribution and web site maintenance, such as generating distribution
tarballs for software packages.
DocKnot was designed and written for my personal needs, and I'm not sure
it will be useful for anyone else. At the least, the template files are
rather specific to my preferences about how to write package
documentation, and the thread macro language is highly specialized for my
personal web site. I'm not sure if I'll have the time to make it a more
general tool. But you're certainly welcome to use it if you find it
useful, send pull requests to make it more general, or take ideas from it
for your own purposes.
\h2[Requirements]
Perl 5.24 or later and Module::Build are required to build this module.
The following additional Perl modules are required to use it:
t/data/generate/docknot/output/thread view on Meta::CPAN
\doc[api/app-docknot-command.html][App::DocKnot::Command]
\doc[api/app-docknot-config.html][App::DocKnot::Config]
\doc[api/app-docknot-dist.html][App::DocKnot::Dist]
\doc[api/app-docknot-generate.html][App::DocKnot::Generate]
\doc[api/app-docknot-release.html][App::DocKnot::Release]
\doc[api/app-docknot-spin.html][App::DocKnot::Spin]
\doc[api/app-docknot-spin-pointer.html][App::DocKnot::Spin::Pointer]
\doc[api/app-docknot-spin-rss.html][App::DocKnot::Spin::RSS]
\doc[api/app-docknot-spin-sitemap.html][App::DocKnot::Spin::Sitemap]
\doc[api/app-docknot-spin-text.html][App::DocKnot::Spin::Text]
\doc[api/app-docknot-spin-thread.html][App::DocKnot::Spin::Thread]
\doc[api/app-docknot-spin-versions.html][App::DocKnot::Spin::Versions]
\doc[api/app-docknot-update.html][App::DocKnot::Update]
\doc[api/app-docknot-util.html][App::DocKnot::Util]
]
\h2(after)[License]
The DocKnot package as a whole is covered by the following copyright and
license:
t/data/generate/pod-thread/docknot.yaml view on Meta::CPAN
format: v1
name: Pod::Thread
maintainer: Russ Allbery <rra@cpan.org>
version: '2.00'
synopsis: Format POD source into thread, an HTML macro language
license:
name: Expat
copyrights:
- holder: Russ Allbery <rra@cpan.org>
years: 2002, 2008-2009, 2013, 2021
build:
type: Module::Build
distribution:
cpan: Pod-Thread
packaging:
debian:
package: libpod-thread-perl
personal: true
section: web
tarname: Pod-Thread
version: pod-thread
support:
email: rra@cpan.org
github: rra/pod-thread
web: https://www.eyrie.org/~eagle/software/pod-thread/
vcs:
browse: https://git.eyrie.org/?p=web/pod-thread.git
github: rra/pod-thread
status:
workflow: build
type: Git
url: https://git.eyrie.org/web/pod-thread.git
docs:
api:
- name: pod-thread
title: Pod::Thread
user:
- name: pod2thread
title: pod2thread manual page
blurb: |
Pod::Thread translates POD source into thread, a macro language processed by
spin. It supports optionally adding a table of contents and a navigation
bar to the genenerated file. This package also includes the pod2thread
driver script, invoked automatically by spin for POD files and pointers to
POD files.
description: |
This package contains a module to translate POD into thread, an HTML macro
language. As such, it's not very useful without
[spin](https://www.eyrie.org/~eagle/software/web/), a separate program to
convert thread into HTML. I wrote this module for my personal needs and it
may not be (and in fact probably isn't) suitable for more general use as
yet.
The eventual intention is to incorporate spin into
[DocKnot](https://www.eyrie.org/~eagle/software/docknot/), at which point
this module will provide the POD support for DocKnot as a static site
generator. I have no estimate for when that work will be done.
The conversion done by this module is mostly straightforward. The only
notable parts are the optional generation of a table of contents or a
t/data/generate/pod-thread/output/readme view on Meta::CPAN
Pod::Thread 2.00
(Format POD source into thread, an HTML macro language)
Maintained by Russ Allbery <rra@cpan.org>
Copyright 2002, 2008-2009, 2013, 2021 Russ Allbery <rra@cpan.org>. This
software is distributed under a BSD-style license. Please see the
section LICENSE below for more information.
BLURB
Pod::Thread translates POD source into thread, a macro language
processed by spin. It supports optionally adding a table of contents
and a navigation bar to the genenerated file. This package also
includes the pod2thread driver script, invoked automatically by spin for
POD files and pointers to POD files.
DESCRIPTION
This package contains a module to translate POD into thread, an HTML
macro language. As such, it's not very useful without spin [1], a
separate program to convert thread into HTML. I wrote this module for
my personal needs and it may not be (and in fact probably isn't)
suitable for more general use as yet.
[1] https://www.eyrie.org/~eagle/software/web/
The eventual intention is to incorporate spin into DocKnot [2], at which
point this module will provide the POD support for DocKnot as a static
site generator. I have no estimate for when that work will be done.
[2] https://www.eyrie.org/~eagle/software/docknot/
t/data/generate/pod-thread/output/readme view on Meta::CPAN
sanity-check the release, set the environment variable RELEASE_TESTING
to a true value. To enable tests that may be sensitive to the local
environment or that produce a lot of false positives without uncovering
many problems, set the environment variable AUTHOR_TESTING to a true
value.
SUPPORT
The Pod::Thread web page at:
https://www.eyrie.org/~eagle/software/pod-thread/
will always have the current version of this package, the current
documentation, and pointers to any additional resources.
For bug tracking, use the issue tracker on GitHub:
https://github.com/rra/pod-thread/issues
Please be aware that I tend to be extremely busy and work projects often
take priority. I'll save your report and get to it as soon as I can,
but it may take me a couple of months.
SOURCE REPOSITORY
Pod::Thread is maintained using Git. You can access the current source
on GitHub at:
https://github.com/rra/pod-thread
or by cloning the repository at:
https://git.eyrie.org/web/pod-thread.git
or view the repository via the web at:
https://git.eyrie.org/?p=web/pod-thread.git
The eyrie.org repository is the canonical one, maintained by the author,
but using GitHub is probably more convenient for most purposes. Pull
requests are gratefully reviewed and normally accepted.
LICENSE
The Pod::Thread package as a whole is covered by the following copyright
statement and license:
t/data/generate/pod-thread/output/readme-md view on Meta::CPAN
# Pod::Thread
[](https://github.com/rra/pod-thread/actions)
[](https://metacpan.org/release/Pod-Thread)
[](https://github.com/rra/pod-thread/blob/master/LICENSE)
Copyright 2002, 2008-2009, 2013, 2021 Russ Allbery <rra@cpan.org>. This
software is distributed under a BSD-style license. Please see the section
[License](#license) below for more information.
## Blurb
Pod::Thread translates POD source into thread, a macro language processed
by spin. It supports optionally adding a table of contents and a
navigation bar to the genenerated file. This package also includes the
pod2thread driver script, invoked automatically by spin for POD files and
pointers to POD files.
## Description
This package contains a module to translate POD into thread, an HTML macro
language. As such, it's not very useful without
[spin](https://www.eyrie.org/~eagle/software/web/), a separate program to
convert thread into HTML. I wrote this module for my personal needs and
it may not be (and in fact probably isn't) suitable for more general use
as yet.
The eventual intention is to incorporate spin into
[DocKnot](https://www.eyrie.org/~eagle/software/docknot/), at which point
this module will provide the POD support for DocKnot as a static site
generator. I have no estimate for when that work will be done.
The conversion done by this module is mostly straightforward. The only
notable parts are the optional generation of a table of contents or a
t/data/generate/pod-thread/output/readme-md view on Meta::CPAN
To enable tests that don't detect functionality problems but are used to
sanity-check the release, set the environment variable `RELEASE_TESTING`
to a true value. To enable tests that may be sensitive to the local
environment or that produce a lot of false positives without uncovering
many problems, set the environment variable `AUTHOR_TESTING` to a true
value.
## Support
The [Pod::Thread web
page](https://www.eyrie.org/~eagle/software/pod-thread/) will always have
the current version of this package, the current documentation, and
pointers to any additional resources.
For bug tracking, use the [issue tracker on
GitHub](https://github.com/rra/pod-thread/issues). Please be aware that I
tend to be extremely busy and work projects often take priority. I'll
save your report and get to it as soon as I can, but it may take me a
couple of months.
## Source Repository
Pod::Thread is maintained using Git. You can access the current source on
[GitHub](https://github.com/rra/pod-thread) or by cloning the repository
at:
https://git.eyrie.org/web/pod-thread.git
or [view the repository on the
web](https://git.eyrie.org/?p=web/pod-thread.git).
The eyrie.org repository is the canonical one, maintained by the author,
but using GitHub is probably more convenient for most purposes. Pull
requests are gratefully reviewed and normally accepted.
## License
The Pod::Thread package as a whole is covered by the following copyright
statement and license:
t/data/generate/pod-thread/output/thread view on Meta::CPAN
[signature])\break
Released \release[\2]]
\heading[Pod::Thread][software]
\h1[Pod::Thread]
\div(sidebar)[
\h2[Download]
\download[Pod::Thread][pod-thread]
[web/Pod-Thread-\version[pod-thread]]
\link[https://archives.eyrie.org/software/ARCHIVE/Pod-Thread/]
[Archive]
\h2[Documentation]
\link[readme.html][General overview] \break
\link[news.html][Change summary] \break
\link[pod2thread.html][pod2thread manual page]
\h2[Development]
\link[https://github.com/rra/pod-thread]
[GitHub] \break
\link[https://github.com/rra/pod-thread/issues]
[Bug tracker] \break
\link[https://git.eyrie.org/?p=web/pod-thread.git]
[Git repository] \break
\link[https://metacpan.org/release/Pod-Thread]
[MetaCPAN] \break
]
\h2[Blurb]
Pod::Thread translates POD source into thread, a macro language processed
by spin. It supports optionally adding a table of contents and a
navigation bar to the genenerated file. This package also includes the
pod2thread driver script, invoked automatically by spin for POD files and
pointers to POD files.
\h2[Description]
This package contains a module to translate POD into thread, an HTML macro
language. As such, it's not very useful without
\link[https://www.eyrie.org/~eagle/software/web/][spin], a separate
program to convert thread into HTML. I wrote this module for my personal
needs and it may not be (and in fact probably isn't) suitable for more
general use as yet.
The eventual intention is to incorporate spin into
\link[https://www.eyrie.org/~eagle/software/docknot/][DocKnot], at which
point this module will provide the POD support for DocKnot as a static
site generator. I have no estimate for when that work will be done.
The conversion done by this module is mostly straightforward. The only
notable parts are the optional generation of a table of contents or a
t/data/generate/pod-thread/output/thread view on Meta::CPAN
\h2[Requirements]
Perl 5.24 or later and Pod::Parser 3.06 or later. As mentioned above,
it's also not particularly useful without spin.
\h2[Download]
The distribution:
\table[][
\program[Pod::Thread][pod-thread]
[web/Pod-Thread-\version[pod-thread]]
]
An \link[https://archives.eyrie.org/software/ARCHIVE/Pod-Thread/] [archive
of older releases] is also available.
A Debian package (libpod-thread-perl) is available from my
\link[../debian.html][personal repository].
Pod::Thread is available from CPAN as the
\link[https://metacpan.org/release/Pod-Thread] [Pod-Thread distribution].
Pod::Thread is maintained using the Git version control system. To check
out the current development tree, see
\link[https://github.com/rra/pod-thread][GitHub] or clone:
\pre[ https://git.eyrie.org/web/pod-thread.git]
Pull requests on GitHub are welcome. You can also
\link[https://git.eyrie.org/?p=web/pod-thread.git][browse the current
development source].
\h2[Documentation]
\div(left)[
\class(first)[User documentation:]
\doc[readme.html][README]
\doc[news.html][Change summary]
\doc[pod2thread.html][pod2thread manual page]
\doc[license.html][License and copyright]
Developer documentation:
\doc[https://github.com/rra/pod-thread]
[GitHub]
\doc[https://github.com/rra/pod-thread/issues]
[Bug tracker]
]
\div(right)[
\class(first)[API documentation:]
\doc[pod-thread.html][Pod::Thread]
]
\h2(after)[License]
The Pod::Thread package as a whole is covered by the following copyright
and license:
\block[
Copyright 2002, 2008-2009, 2013, 2021
t/data/regenerate-data view on Meta::CPAN
}
# Regenerate the README and README.md for DocKnot itself.
my $docknot = App::DocKnot::Generate->new();
$docknot->generate_all();
# The test of spinning a tree of files uses a reference to App::DocKnot's own
# POD documentation. Regenerate the expected output in case the POD has
# changed.
my $source = path('lib', 'App', 'DocKnot.pm');
my $podthread = Pod::Thread->new(navbar => 1);
my $spin = App::DocKnot::Spin::Thread->new();
my $thread;
$podthread->output_string(\$thread);
$podthread->parse_file("$source");
my $html = $spin->spin_thread($thread);
# Add the additional metadata that should be added by spin.
my $links = <<'EOD';
<link rel="stylesheet" href="/~eagle/styles/pod.css" type="text/css" />
<link rel="next" href="app-docknot-command.html"
title="App::DocKnot::Command" />
<link rel="up" href="../" title="DocKnot" />
<link rel="top" href="../../../" />
EOD
my $comment = '<!-- Spun from DocKnot.pm by DocKnot %VERSION% on %DATE% -->';
t/data/regenerate-data view on Meta::CPAN
<a href="../../../">Russ Allbery</a>
> <a href="../../">Software</a>
> <a href="../">DocKnot</a>
</td>
<td class="navright"><a href="app-docknot-command.html">App::DocKnot::Command</a> ></td>
</tr></table>
EOD
my $address = <<'EOD';
<address>
Last <a href="https://www.eyrie.org/~eagle/software/docknot/">spun</a>
%DATE% from thread modified %DATE%
</address>
EOD
$html =~ s{ (</head>) }{$links$1}xms;
$html =~ s{ <!-- [ ] Spun .*? [ ] --> }{$comment}xms;
$html =~ s{ (<body> \n) }{$1$navbar}xms;
$html =~ s{ (</body>) }{$navbar\n$address$1}xms;
# Replace the expected data file.
my $output = path(
't', 'data', 'spin', 'output', 'software', 'docknot', 'api',
t/data/spin/input/.rss view on Meta::CPAN
Base: https://www.eyrie.org/~eagle/
RSS-Base: https://www.eyrie.org/~eagle/
Title: Changes to Russ Allbery's Web Pages
Description: Recent changes to Russ Allbery's web pages.
Language: en-us
Output: *:thread:changes.th *:rss:changes.rss
Thread-Prefix:
\rss[changes.rss][Changes to Russ Allbery's Web Pages]
.
\heading[Recent Changes][indent]
.
\h1[Recent Changes]
.
\quote(broken)[
Put up in a place
where it's easy to see
t/data/spin/input/.rss view on Meta::CPAN
Date: 2021-03-28 17:07
Title: Add sample INN init script and systemd unit file
Link: software/inn/
Description:
In the documentation pages for INN CURRENT, 2.6, and 2.5, add the sample
init script and systemd unit.
Date: 2021-03-28 09:21
Title: Pod::Thread 2.00
Link: software/pod-thread/
Description:
Handle the navbar and table of contents internally in the module rather
than via a pre-scanning pass. Honor CVS Id strings found anywhere in the
document. Don't title-case words in section titles in the navbar that
contain an underscore.
Date: 2021-03-21 19:58
Title: cvs2xhtml 1.15
Link: software/web/
Description:
t/data/spin/input/.rss view on Meta::CPAN
Support formatting of dense bullet lists with line continuations but no
blank lines between bullets. Update my email address.
Date: 2021-03-21 19:03
Title: Remove DocKnot from my personal Debian repository list
Link: software/debian.html
Description: DocKnot has now been uploaded to Debian proper.
Date: 2021-03-21 18:57
Title: Add separate web pages for Pod::Thread
Link: software/pod-thread/
Description:
As the first step in cleaning up my static site generator pages, and in
prepration for uploading it to CPAN in its own right, move Pod::Thread to
its own separate web pages.
Date: 2021-03-20 13:35
Title: pam-krb5 4.10
Link: software/pam-krb5/
Description:
Fix use-after-free if krb5_cc_get_principal fails on the newly-created
t/data/spin/input/software/docknot/index.th view on Meta::CPAN
wording and push that out to every package I maintain during its next
release, without having to remember which changes I wanted to make.
DocKnot is also slowly absorbing other tools that I use for software
distribution and web site maintenance, such as generating distribution
tarballs for software packages.
DocKnot was designed and written for my personal needs, and I'm not sure
it will be useful for anyone else. At the least, the template files are
rather specific to my preferences about how to write package
documentation, and the web page output is in my personal thread language
as opposed to HTML. I'm not sure if I'll have the time to make it a more
general tool. But you're certainly welcome to use it if you find it
useful, send pull requests to make it more general, or take ideas from it
for your own purposes.
Currently included in this package are just the App::DocKnot module and
its submodules, a small docknot driver program, and the templates I use
for my own software. Over time, it may include more of my web publishing
framework, time permitting.
t/data/spin/input/software/index.th view on Meta::CPAN
[Netnews control message processing and archiving]
\package[inn/][INN]
[Full-featured, flexible and configurable news server]
\package[gateway/][News::Gateway]
[General toolkit for mail gatewaying and moderation]
\package[postfaq/][postfaq]
[Post FAQs and periodic postings with superseding]
]
\section[web][Web][
\package[pod-thread/][Pod::Thread]
[Format POD source into thread, an HTML macro language]
\package[web/][Web tools]
[Web page generation from a macro language and other sources]
]
\h2(#other)[Other Links]
\desclink[scripts/][Scripts][
Some smaller scripts that don't warrant their own set of pages. Some
larger or more generally useful scripts get their own pages above;
these are scripts with a narrower or specialized purpose, or scripts
t/data/spin/markdown/output/other.html view on Meta::CPAN
<table class="navbar"><tr>
<td class="navleft">< <a href="foo.html">Some document</a></td>
<td>
<a href="./">Test Root</a>
</td>
<td class="navright"><a href="test.html">Test Markdown</a> ></td>
</tr></table>
<address>
Last <a href="https://www.eyrie.org/~eagle/software/docknot/">spun</a>
%DATE% from thread modified %DATE%
</address>
</body>
</html>
t/data/spin/markdown/output/test.html view on Meta::CPAN
<table class="navbar"><tr>
<td class="navleft">< <a href="other.html">Other test Markdown</a></td>
<td>
<a href="./">Test Root</a>
</td>
<td class="navright"></td>
</tr></table>
<address>
Last <a href="https://www.eyrie.org/~eagle/software/docknot/">spun</a>
%DATE% from thread modified %DATE%
</address>
</body>
</html>
t/data/spin/output/changes.html view on Meta::CPAN
</p></dd>
<dt>2021-03-28 â
<a href="https://www.eyrie.org/~eagle/software/inn/">Add sample INN init script and systemd unit file</a></dt>
<dd><p>
In the documentation pages for INN CURRENT, 2.6, and 2.5, add the sample
init script and systemd unit.
</p></dd>
<dt>2021-03-28 â
<a href="https://www.eyrie.org/~eagle/software/pod-thread/">Pod::Thread 2.00</a></dt>
<dd><p>
Handle the navbar and table of contents internally in the module rather
than via a pre-scanning pass. Honor CVS Id strings found anywhere in the
document. Don't title-case words in section titles in the navbar that
contain an underscore.
</p></dd>
<dt>2021-03-21 â
<a href="https://www.eyrie.org/~eagle/software/web/">cvs2xhtml 1.15</a></dt>
<dd><p>
t/data/spin/output/changes.html view on Meta::CPAN
blank lines between bullets. Update my email address.
</p></dd>
<dt>2021-03-21 â
<a href="https://www.eyrie.org/~eagle/software/debian.html">Remove DocKnot from my personal Debian repository list</a></dt>
<dd><p>
DocKnot has now been uploaded to Debian proper.
</p></dd>
<dt>2021-03-21 â
<a href="https://www.eyrie.org/~eagle/software/pod-thread/">Add separate web pages for Pod::Thread</a></dt>
<dd><p>
As the first step in cleaning up my static site generator pages, and in
prepration for uploading it to CPAN in its own right, move Pod::Thread to
its own separate web pages.
</p></dd>
<dt>2021-03-20 â
<a href="https://www.eyrie.org/~eagle/software/pam-krb5/">pam-krb5 4.10</a></dt>
<dd><p>
Fix use-after-free if krb5_cc_get_principal fails on the newly-created
t/data/spin/output/changes.html view on Meta::CPAN
<dt>2021-01-01 â
<a href="https://www.eyrie.org/~eagle/reviews/year/2020.html">2020 reading in review</a></dt>
<dd><p>
Add an overview of my 2020 reading, main book recommendations, and
reading statistics.
</p></dd>
</dl>
<address>
Last <a href="https://www.eyrie.org/~eagle/software/docknot/">spun</a>
%DATE% from thread modified %DATE%
</address>
</body>
</html>
t/data/spin/output/index.html view on Meta::CPAN
standards-compliant browser. And somewhere herein is, I hope, something
that will capture your attention for more than five seconds.
</p>
<p>
Welcome.
</p>
<address>
Last <a href="https://www.eyrie.org/~eagle/software/docknot/">spun</a>
%DATE% from thread modified %DATE%
</address>
</body>
</html>
t/data/spin/output/journal/2011-08/006.html view on Meta::CPAN
I need to re-read this and write long reviews of them, since I have a
lot to say about them. But they need to be read in the context of the
Christian faith to make any sense.</li>
<div class="date"><p>
Posted: %DATE% 00:09 — <span class="no-comment"><a href="/~eagle/faqs/comments.html">Why no comments?</a></span>
</p></div>
</ol>
<address>
Last <a href="https://www.eyrie.org/~eagle/software/docknot/">spun</a>
%DATE% from thread modified %DATE%
</address>
</body>
</html>