=pod
=head1 NAME
App::WRT - WRiting Tool, a static site generator and related utilities
=for HTML
=head1 SYNOPSIS
$ wrt display 2016 > 2016.html
Or:
$ wrt render
Or:
#!/usr/bin/env perl
use App::WRT;
my $w = App::WRT->new(
entry_dir => 'archives',
url_root => '/',
# etc.
);
print $w->display(@ARGV);
=head1 INSTALLING
It's possible this may run on a Perl as old as 5.10.0, although in practice I
imagine that at least some of its dependencies have more recent requirements.
In practice, I know that it works under 5.20.2. It should work on any
reasonably modern Linux distribution, and may also be fine on MacOS or a BSD of
your choosing.
$ perl Build.PL
$ ./Build installdeps
$ ./Build test
$ ./Build install
=head1 DESCRIPTION
This started life as C, a simple script to concatenate fragments of
handwritten HTML by date. It has since haphazardly accumulated several of the
usual weblog features (lightweight markup, feed generation, embedded Perl,
poetry tools, image galleries, and ill-advised dependencies), but the basic
idea hasn't changed that much.
The C utility now generates static HTML files, instead of expecting to
run as a CGI script. This is a better idea, for the most part.
The C module will work with FastCGI, if called from the appropriate
wrapper script, such as C.
By default, entries are stored in a simple directory tree under C.
Like:
archives/2001/1/1
archives/2001/1/1/sub_entry
It is possible (although not as flexible as it ought to be) to redefine the
directory layout. More about this after a bit.
An entry may be either a plain text file, or a directory containing several
files. If it's a directory, a file named "index" will be treated as the text
of the entry, and all other lower-case filenames without extensions will be
treated as sub-entries or documents within that entry, and displayed
accordingly. Links to certain other filetypes will be displayed as well.
Directories may be nested to an arbitrary depth, although it's probably not a
good idea to go very deep with the current display logic.
A PNG or JPEG file with a name like
2001/1/1.icon.png
2001/1/1/index.icon.png
2001/1/1/whatever.icon.png
2001/1/1/whatever/index.icon.png
will be treated as an icon for the appropriate entry file.
=head2 MARKUP
Entries may consist of hand-written HTML (to be passed along without further
interpretation), a supported form of lightweight markup, or some combination
thereof. Actually, an entry may consist of any darn thing you please, as long
as Perl will agree that it is text, but presumably you're going to be feeding
this to a browser.
Special markup is indicated by a variety of HTML-like container tags.
B - evaluated and replaced by whatever value you return
(evaluated in a scalar context):
my $dog = "Ralph."; return $dog;
This code is evaluated before any other processing is done, so you can return
any other markup understood by the script and have it handled appropriately.
B - actually keys to the hash underlying the App::WRT
object, for the moment:
$self->title("About Ralph, My Dog"); return '';
The title is ${title}.
This will change.
Embedded code and variables are intended for use in the F file, where
it's handy to drop in titles or conditionalize aspects of a layout. You want to
be careful with this sort of thing - it's useful in small doses, but it's also
a maintainability nightmare waiting to happen. (WordPress, I am looking at
you.)
B:
John Gruber's Markdown, by way of
Text::Markdown
Dean Allen's Textile, via Brad Choate's
Text::Textile.
An easy way to
get properly broken lines
plus -- en and em dashes ---
for poetry and such.
B:
filename.ext
alt text, if any
one list item
another list item
As it stands, freeverse, image, and list are not particularly robust.
=head1 CONFIGURATION
=over
=item options
See F for a sample configuration.
=item entry_map(\%map)
Takes a hashref which will dispatch entries matching various regexen to
the appropriate output methods. The default looks something like this:
nnnn/[nn/nn/]doc_name - a document within a day.
nnnn/nn/nn - a specific day.
nnnn/nn - a month.
nnnn - a year.
doc_name - a document in the root directory.
You can re-map things to an arbitrary archive layout.
Since the entry map is a hash, and handle() simply loops over its keys, there
is no guaranteed precedence of patterns. Be extremely careful that no entry
will match more than one pattern, or you will wind up with unexpected behavior.
A good way to ensure that this does not happen is to use patterns like:
qr(
^ # start of string
[0-9/]{4}/ # year
[0-9]{1,2}/ # month
[0-9]{1,2] # day
$ # end of string
)x
...always marking the start and end of the string explicitly.
This may eventually be rewritten to use an array so that the order can be
explicitly specified.
=item entry_descriptions(\%descriptions)
Takes a hashref which contains a map of entry titles to entry descriptions.
=back
=head1 METHODS
For no bigger than this thing is, it gets a little convoluted.
=over
=item new_from_file($config_file)
Takes a filename to pull JSON config data out of, and returns a new App::WRT
instance with the parameters set in that file.
=item new(%params)
Get a new WRT object with the specified parameters set.
=item display($entry1, $entry2, ...)
Return a string containing the given entries, which are in the form of
date/entry strings. If no parameters are given, default to default_entry().
display() expands aliases ("new" and "all", for example) as necessary, collects
output from handle($entry), and wraps the whole thing in a template file.
=item handle($entry)
Return the text of an individual entry.
=begin digression
=item A digression about each()
I once spent a lot of time chasing down a bug caused by a while loop in this
method. Specifically, I was using while to iterate over the entry_map hash.
Since C<$self->entry_map> returns a reference to the same hash each time, every
other request was finding C mid-way through iterating over this hash.
I initially solved this by copying the hash into a local one called C<%map>
every time C was called. I could also have called C or
C on the anonymous hash, as these reset C.
Presently I'm not using each() or an explicit loop, so this probably doesn't
make a whole lot of sense in the context of the existing code.
=end digression
=item expand_option($option)
Expands/converts 'all' and 'new' to appropriate values.
=item recent_month()
Tries to find the most recent month in the archive.
If a year file is text, returns that instead.
=item fulltext
The full text of all entries, in order.
=item link_bar(@extra_links)
Returns a little context-sensitive navigation bar.
=item month_before($this_month)
Return the month before the given month in the archive.
Very naive; there has got to be a smarter way.
=item year($year)
List out the updates for a year.
=item month($month)
Prints the entries in a given month (nnnn/nn).
=item entry_wrapped
Wraps entry() in entry_markup.
=item entry_stamped
Wraps entry() + a datestamp in entry_markup()
=item entry_topic_list
Get a list of topics (by tag-* files) for the entry. This hardcodes a
p1k3-specific thing, and is dumb.
=item entry($entry)
Returns the contents of a given entry. Calls dir_list
and icon_markup. Recursively calls itself.
=item icon_markup
Check if an icon exists for a given entry if so, return markup to include it.
Icons are PNG or JPEG image files following a specific naming convention:
index.icon.[png|jp(e)g] for directories
[filename].icon.[png|jp(e)g] for flat text files
Calls image_size, uses filename to determine type.
=item datestamp
Returns a nice html datestamp / breadcrumbs for a given entry.
=item fragment_slurp
Read a text fragment, call line_parse() and eval_perl() to take care of funky
markup and interpreting embedded code, and then return it as a string. Takes
one parameter, the name of the file, and returns '' if it's not an extant text
file.
This might be the place to implement an in-memory cache for FastCGI or mod_perl
environments. The trick is that the results for certain files shouldn't be
cached because they contain embedded code.
=item month_name
Turn numeric dates into English.
=item root_locations($file)
Given a file/entry, return the appropriate concatenations with
entry_dir and url_root.
=item local_path
Return an absolute path for a given file. Called by root_locations.
Arguably this is stupid and inefficient.
=item feed_print
Return an Atom feed of entries for a month. Defaults to the most
recent month in the archive.
Called from handle(), requires XML::Atom::SimpleFeed.
=back
=head1 SEE ALSO
walawiki.org, Blosxom, rassmalog, Text::Textile, XML::Atom::SimpleFeed,
Image::Size, CGI::Fast, and about a gazillion static site generators.
=head1 AUTHOR
Copyright 2001-2017 Brennen Bearnes
=head1 LICENSE
wrt is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .