The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
<?xml version="1.0" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rev="made" href="mailto:root@localhost" />
</head>

<body>



<ul id="index">
  <li><a href="#NAME">NAME</a></li>
  <li><a href="#SYNOPSIS">SYNOPSIS</a></li>
  <li><a href="#INSTALLING">INSTALLING</a></li>
  <li><a href="#DESCRIPTION">DESCRIPTION</a>
    <ul>
      <li><a href="#MARKUP">MARKUP</a></li>
      <li><a href="#TEMPLATES">TEMPLATES</a></li>
      <li><a href="#CONFIGURATION">CONFIGURATION</a></li>
      <li><a href="#METHODS-AND-INTERNALS">METHODS AND INTERNALS</a></li>
    </ul>
  </li>
  <li><a href="#SEE-ALSO">SEE ALSO</a></li>
  <li><a href="#AUTHOR">AUTHOR</a></li>
  <li><a href="#LICENSE">LICENSE</a></li>
</ul>

<h1 id="NAME">NAME</h1>

<p>App::WRT - WRiting Tool, a static site generator and related utilities</p>

<a href="https://travis-ci.org/brennen/wrt"><img src="https://travis-ci.org/brennen/wrt.svg?branch=master"></a>

<h1 id="SYNOPSIS">SYNOPSIS</h1>

<p>Using the commandline tools:</p>

<pre><code>    $ mkdir project
    $ cd project
    $ wrt init         # set up some defaults
    $ wrt display new  # print html for new posts to stdout
    $ wrt render-all   # publish html to project/public/</code></pre>

<p>Using App::WRT in library form:</p>

<pre><code>    #!/usr/bin/env perl

    use App::WRT;
    my $w = App::WRT-&gt;new(
      entry_dir =&gt; &#39;archives&#39;,
      url_root  =&gt; &#39;/&#39;,
      # etc.
    );
    print $w-&gt;display(@ARGV);</code></pre>

<h1 id="INSTALLING">INSTALLING</h1>

<p>It&#39;s possible but not likely this would run on a Perl as old as 5.10.0. In practice, I know that it works under 5.20.2. It should be fine on any reasonably modern Linux distribution, and may also be fine on MacOS or a BSD of your choosing.</p>

<p>To install the latest development version from the main repo:</p>

<pre><code>    $ git clone https://github.com/brennen/wrt.git
    $ cd wrt
    $ perl Build.PL
    $ ./Build installdeps
    $ ./Build test
    $ ./Build install</code></pre>

<p>To install the latest version released on CPAN:</p>

<pre><code>    $ cpanm App::WRT</code></pre>

<p>Or:</p>

<pre><code>    $ cpan -i App::WRT</code></pre>

<p>You will likely need to use <code>sudo</code> or <code>su</code> to get a systemwide install.</p>

<h1 id="DESCRIPTION">DESCRIPTION</h1>

<p>This started life as <code>display.pl</code>, a simple script to concatenate fragments of handwritten HTML by date. It has since 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&#39;t changed that much.</p>

<p>The <code>wrt</code> utility now generates static HTML files, instead of expecting to run as a CGI script. This is a better idea, for the most part.</p>

<p>The <code>App::WRT</code> module will work with FastCGI, if called from the appropriate wrapper script, such as <code>wrt-fcgi</code>.</p>

<p>By default, entries are stored in a simple directory tree under <code>entry_dir</code>.</p>

<p>Like:</p>

<pre><code>     archives/2001/1/1
     archives/2001/1/2/index
     archives/2001/1/2/sub_entry</code></pre>

<p>Which will publish files like so:</p>

<pre><code>     public/2001/index.html
     public/2001/1/index.html
     public/2001/1/1/index.html
     public/2001/1/2/index.html
     public/2001/1/2/sub_entry/index.html</code></pre>

<p>It&#39;s possible (although not as flexible as it ought to be) to redefine the directory layout. More about this after a bit.</p>

<p>An entry may be either a plain text file, or a directory containing several files. If it&#39;s a directory, a file named &quot;index&quot; 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.</p>

<p>Directories may be nested to an arbitrary depth, although it&#39;s probably not a good idea to go very deep with the current display logic.</p>

<p>A PNG or JPEG file with a name like</p>

<pre><code>    2001/1/1.icon.png
    2001/1/1/index.icon.png
    2001/1/1/whatever.icon.png
    2001/1/1/whatever/index.icon.png</code></pre>

<p>will be treated as an icon for the corresponding entry file.</p>

<h2 id="MARKUP">MARKUP</h2>

<p>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&#39;re going to be feeding this to a browser.</p>

<p>Header tags (&lt;h1&gt;, &lt;h2&gt;, etc.) will be used to display titles in feeds and other places.</p>

<p>Other special markup is indicated by a variety of HTML-like container tags.</p>

<p><b>Embedded Perl</b> - evaluated and replaced by whatever value you return (evaluated in a scalar context):</p>

<pre><code>     &lt;perl&gt;my $dog = &quot;Ralph.&quot;; return $dog;&lt;/perl&gt;</code></pre>

<p>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.</p>

<p><b>Interpolated variables</b> - actually keys to the hash underlying the App::WRT object, for the moment:</p>

<pre><code>     &lt;perl&gt;$self-&gt;title(&quot;About Ralph, My Dog&quot;); return &#39;&#39;;&lt;/perl&gt;

     &lt;p&gt;The title is &lt;em&gt;${title}&lt;/em&gt;.&lt;/p&gt;</code></pre>

<p>This is likely to change at some point, so don&#39;t build anything too elaborate on it.</p>

<p>Embedded code and variables are intended only for use in the <i>template</i> file, where it&#39;s handy to drop in titles or conditionalize aspects of a layout. You want to be careful with this sort of thing - it&#39;s useful in small doses, but it&#39;s also a maintainability nightmare waiting to happen. (WordPress, I am looking at you.)</p>

<p><b>Several forms of lightweight markup</b>:</p>

<pre><code>     &lt;markdown&gt;John Gruber&#39;s Markdown, by way of
     Text::Markdown&lt;/markdown&gt;

     &lt;textile&gt;Dean Allen&#39;s Textile, via Brad Choate&#39;s
     Text::Textile.&lt;/textile&gt;

     &lt;freeverse&gt;An easy way to
     get properly broken lines
     plus -- en and em dashes ---
     for poetry and such.&lt;/freeverse&gt;</code></pre>

<p><b>And a couple of shortcuts</b>:</p>

<pre><code>     &lt;image&gt;filename.ext
     alt text, if any&lt;/image&gt;

     &lt;list&gt;
     one list item

     another list item
     &lt;/list&gt;</code></pre>

<p>As it stands, freeverse, image, and list are not particularly robust.</p>

<h2 id="TEMPLATES">TEMPLATES</h2>

<p>A single template, specified with the</p>

<h2 id="CONFIGURATION">CONFIGURATION</h2>

<p>Configuration is read from a <i>wrt.json</i> in the directory where the <code>wrt</code> utility is invoked, or can (usually) be specified with the <code>--config</code> option.</p>

<p>See <i>example/wrt.json</i> for a sample configuration.</p>

<p>Under the hood, configuration is done by combining a hash called <code>%default</code> with values pulled out of the JSON file. Most defaults can be overwritten from the config file, but changing some would require writing Perl, since they contain things like subroutine references.</p>

<dl>

<dt id="default">%default</dt>
<dd>

<p>Here&#39;s a verbatim copy of <code>%default</code>, with some commentary about values.</p>

<pre><code>    my %default = (
      root_dir       =&gt; &#39;.&#39;,         # dir for wrt repository
      entry_dir      =&gt; &#39;archives&#39;,  # dir for entry files
      publish_dir    =&gt; &#39;public&#39;,    # dir to publish site to
      url_root       =&gt; &quot;$0?&quot;,       # root URL for building links
      image_url_root =&gt; &#39;&#39;,          # same for images
      template_dir   =&gt; &#39;templates&#39;, # dir for template files
      template       =&gt; &#39;default&#39;,   # template to use
      title          =&gt; &#39;&#39;,          # current title (used in template)
      title_prefix   =&gt; &#39;&#39;,          # a string to slap in front of titles
      stylesheet_url =&gt; undef,       # path to a CSS file (used in template)
      favicon_url    =&gt; undef,       # path to a favicon (used in template)
      feed_alias     =&gt; &#39;feed&#39;,      # what entry path should correspond to feed?
      author         =&gt; undef,       # author name (used in template, feed)
      description    =&gt; undef,       # site description (used in template)
      content        =&gt; undef,       # place to stash content for templates
      embedded_perl  =&gt; 1,           # evaluate embedded &lt;perl&gt; tags?
      default_entry  =&gt; &#39;new&#39;,       # what to display if no entry specified

      # A license string for site content:
      license        =&gt; &#39;public domain&#39;, 

      # List of years for the menu:
      year_list      =&gt; [ reverse(1997..(get_date(&#39;year&#39;) + 1900)) ],

      # What gets considered an entry _path_:
      entrypath_expr =&gt; qr/^ ([a-z0-9_\/-]+) $/x,

      # What gets considered a subentry file (slightly misleading
      # terminology here):
      subentry_expr =&gt; qr/^[0-9a-z_-]+(\.(tgz|zip|tar[.]gz|gz|txt))?$/,

      # We&#39;ll show links for these, but not display them inline:
      binfile_expr   =&gt; qr/[.](tgz|zip|tar[.]gz|gz|txt|pdf)$/,
    );</code></pre>

</dd>
<dt id="default-entry_map">$default{entry_map}</dt>
<dd>

<p>A hash which will dispatch entries matching various regexen to the appropriate output methods. The default looks something like this:</p>

<pre><code>    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.</code></pre>

<p>You can re-map things to an arbitrary archive layout.</p>

<p>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:</p>

<pre><code>    qr(
        ^           # start of string
        [0-9/]{4}/  # year
        [0-9]{1,2}/ # month
        [0-9]{1,2]  # day
        $           # end of string
      )x</code></pre>

<p>...always marking the start and end of the string explicitly.</p>

<p>This may eventually be rewritten to use an array so that the order can be explicitly specified.</p>

</dd>
<dt id="default-entry_descriptions">$default{entry_descriptions}</dt>
<dd>

<p>A hashref which contains a map of entry titles to entry descriptions.</p>

</dd>
</dl>

<h2 id="METHODS-AND-INTERNALS">METHODS AND INTERNALS</h2>

<p>For no bigger than this thing is, the internals are convoluted. Because it&#39;s spaghetti code originally written in a now-archaic language by a teenager who didn&#39;t know how to program.</p>

<dl>

<dt id="new_from_file-config_file">new_from_file($config_file)</dt>
<dd>

<p>Takes a filename to pull JSON config data out of, and returns a new App::WRT instance with the parameters set in that file.</p>

</dd>
<dt id="new-params">new(%params)</dt>
<dd>

<p>Get a new WRT object with the specified parameters set.</p>

</dd>
<dt id="display-entry1-entry2">display($entry1, $entry2, ...)</dt>
<dd>

<p>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().</p>

<p>display() expands aliases (&quot;new&quot; and &quot;all&quot;, for example) as necessary, collects output from handle($entry), and wraps the whole thing in a template file.</p>

</dd>
<dt id="handle-entry">handle($entry)</dt>
<dd>

<p>Return the text of an individual entry.</p>

</dd>
<dt id="expand_option-option">expand_option($option)</dt>
<dd>

<p>Expands/converts &#39;all&#39; and &#39;new&#39; to appropriate values.</p>

</dd>
<dt id="recent_month">recent_month()</dt>
<dd>

<p>Tries to find the most recent month in the archive.</p>

<p>If a year file is text, returns that instead.</p>

</dd>
<dt id="fulltext">fulltext()</dt>
<dd>

<p>Returns the full text of all entries, in order.</p>

</dd>
<dt id="link_bar-extra_links">link_bar(@extra_links)</dt>
<dd>

<p>Returns a little context-sensitive navigation bar.</p>

</dd>
<dt id="month_before-this_month">month_before($this_month)</dt>
<dd>

<p>Return the month before the given month in the archive.</p>

<p>Very naive; there has got to be a smarter way.</p>

</dd>
<dt id="year-year">year($year)</dt>
<dd>

<p>List out the updates for a year.</p>

</dd>
<dt id="month-month">month($month)</dt>
<dd>

<p>Prints the entries in a given month (nnnn/nn).</p>

</dd>
<dt id="entry_wrapped-entry-level">entry_wrapped($entry, $level)</dt>
<dd>

<p>Wraps entry() in entry_markup.</p>

</dd>
<dt id="entry_stamped-entry-level">entry_stamped($entry, $level)</dt>
<dd>

<p>Wraps entry() + a datestamp in entry_markup()</p>

</dd>
<dt id="entry_topic_list-entry">entry_topic_list($entry)</dt>
<dd>

<p>Get a list of topics (by tag-* files) for the entry. This hardcodes a p1k3-specific thing, and is dumb.</p>

</dd>
<dt id="entry-entry">entry($entry)</dt>
<dd>

<p>Returns the contents of a given entry. Calls dir_list and icon_markup. Recursively calls itself.</p>

</dd>
<dt id="get_sub_entries-entry_loc">get_sub_entries($entry_loc)</dt>
<dd>

<p>Returns &quot;sub entries&quot; based on the <code>subentry_expr</code> regexp.</p>

</dd>
<dt id="list_contents-entry-entries">list_contents($entry, @entries)</dt>
<dd>

<p>Returns links (potentially with icons) for a set of sub-entries within an entry.</p>

</dd>
<dt id="icon_markup-entry-alt">icon_markup($entry, $alt)</dt>
<dd>

<p>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:</p>

<pre><code>  index.icon.[png|jp(e)g] for directories
  [filename].icon.[png|jp(e)g] for flat text files</code></pre>

<p>Calls image_size, uses filename to determine type.</p>

</dd>
<dt id="datestamp-entry">datestamp($entry)</dt>
<dd>

<p>Returns a nice html datestamp / breadcrumbs for a given entry.</p>

</dd>
<dt id="fragment_slurp-file">fragment_slurp($file)</dt>
<dd>

<p>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 &#39;&#39; if it&#39;s not an extant text file.</p>

<p>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&#39;t be cached because they contain embedded code.</p>

</dd>
<dt id="month_name-number">month_name($number)</dt>
<dd>

<p>Turn numeric dates into English.</p>

</dd>
<dt id="root_locations-file">root_locations($file)</dt>
<dd>

<p>Given a file/entry, return the appropriate concatenations with entry_dir and url_root.</p>

</dd>
<dt id="local_path-file">local_path($file)</dt>
<dd>

<p>Return an absolute path for a given file. Called by root_locations.</p>

<p>Arguably this is stupid and inefficient.</p>

</dd>
<dt id="feed_print-month">feed_print($month)</dt>
<dd>

<p>Return an Atom feed of entries for a month. Defaults to the most recent month in the archive.</p>

<p>Called from handle(), requires XML::Atom::SimpleFeed.</p>

</dd>
</dl>

<h1 id="SEE-ALSO">SEE ALSO</h1>

<p>walawiki.org, Blosxom, rassmalog, Text::Textile, XML::Atom::SimpleFeed, Image::Size, CGI::Fast, and about a gazillion static site generators.</p>

<h1 id="AUTHOR">AUTHOR</h1>

<p>Copyright 2001-2017 Brennen Bearnes</p>

<h1 id="LICENSE">LICENSE</h1>

<pre><code>    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 &lt;http://www.gnu.org/licenses/&gt;.</code></pre>


</body>

</html>