mowyw - Macro processor and Offline Content Management System
$ mowyw $ mowyw --make --source-prefix sp --destination-prefix dp --menu-prefix mp --includes-prefix=ip --postifx=.html --quiet
mowyw is a text processor for building static websites. It supports includes, menus, external datasources (like XML files or databases) and syntax hilighting.
To use mowyw you need three directories called source, online and includes. source holds the source files that are to be interpreted by mowyw. When you run mowyw, it will walk (recursively) through the source dir, and for each file it either copies it verbatimely to online, or, if the file looks like HTML (ends on .html, .shtml, .htm etc.) it is processed by mowyw, and the output is written into the online dir.
source
online
In includes/ are include files (how surprising), header, footer, global include files, and optionally data source files.
includes/
You should always execute mowyw from the parent directory of these three directories.
The name of these three directories can be overridden with command line options.
Only process files with a newer timestamp than the corresponding online/ file. Note that this does not account for dependencies on include files and menu files.
online/
Don't produce diagnostics or progress output
Append the given postfix to the filename of all included file names (and menu files). Defaults to the empty string.
Change the search path for include files. Defaults to includes/.
Change the search for menu files. Defaults to includes/menu-.
includes/menu-
Change the path for the output files. Defaults to online/.
Change the search path for source files. Defaults to source/. Must be a directory.
source/
Interpret all input files to have that encoding. Defaults to UTF-8. (Conjecture: should default to the current locale... what to do?).
All options except --make can be specified in a config file, with more customization options.
--make
mowyw processes files in several steps:
The file *mowyw.conf* is read (if it exists)
The source directory is traversed
For all source file that is actually processed
the file includes/global is read, if it exists
includes/global
the current file is read, all markup is processed
the file includes/header is processed and prepended to the current output file
includes/header
the file includes/footer is processed and appended to the current output file
includes/footer
Normal text (including HTML markup) is copied verbatimely to the output file.
Special directives are delimited either with [% ... %] (preferred) or with [[[ ... ]]] (deprecated, but still supported for compatibility).
[% ... %]
[[[ ... ]]]
Many elements take options, which are usually whitespace delimited, for example [% include includefile.html %].
[% include includefile.html %]
Inline elements consist only of one tag, while block elements run until the matching end tag.
The following inline elements are supported:
include includes a file, and processes it. Example: `[% include coypright.html %].
include
`[% include coypright.html %]
menu refers to a menu. See section "MENUES" below.
menu
item defines a menu item. See section "MENUES" below.
item
option sets an option, for example [% option no-header %]. See section "OPTIONS" below.
option
[% option no-header %]
comment ignores everything up to the tag end. Example: [% comment this comments appears nowhere in the output %].
comment
[% comment this comments appears nowhere in the output %]
setvar sets a variable. Example: [% setvar title Title of this page%].
setvar
[% setvar title Title of this page%]
readvar reads and outputs a variable. You can optionally specify an escape mechanism. Example: <h1>[% readvar title %]</h1> or [% readvar db.column escape:html %].
readvar
<h1>[% readvar title %]</h1>
[% readvar db.column escape:html %]
bind binds a variable to a datasource. See section "DATA SOURCES" below.
bind
The following block elements are supported:
verbatim reads the file until it finds an endverbatim tag. It expects a marker, which must be present at the end tag as well. Everything inbetween is taken verbatimly, i.e. it is not processed at all. Keywords are not allowed as markers. Example: [% verbatim foo %] Some [% non-processed %] markup [% endverbatim foo %]
verbatim
endverbatim
[% verbatim foo %] Some [% non-processed %] markup [% endverbatim foo %]
syntax uses vim(1) to do syntax hilighting. Expects the name of the language or configuration as its only argument. Example: `<pre>[%syntax perl%]print "Hello, world\n";[%endsyntax%]</pre>`. The pseudo-syntax escape HTML-escapes the string and doesn't to any other syntax hilighting.
syntax
escape
for iterates of a datasource. See section <<datasource,DATA SOURCES>> below.
for
ifvar executes the block only if the variable is defined. Example: [% ifvar title %]<h1[%readvar title%]</h1>[% endifvar %]>.
ifvar
[% ifvar title %]<h1
mowyw has special menu support. To define a menu named foo, create a file source/menu-foo with the following contents:
foo
source/menu-foo
<h2>Your menu title</h2> <ul> [% item home <li><a {{class="active"}} href="/">Home</a></li> %] [% item first <li><a {{class="active"}} href="/first">First</a></li> %] [% item sec <li><a {{class="active"}} href="/sec/">First</a> {{ [% comment this is a sub menu that will only be visible inside the `sec' menu %] [% item subitem1 <p>More menu markup</p> %] [% item subitem2 <p>Even more menu markup</p> %] }} </li> %] </ul>
(The menu name is purely for your internal use, it will appear nowhere in the output)
Now in your index page you can include that menu with the directive [% menu home %]. This will produce the menu contents with the markup stripped, and insde the home all of the contents will appear, the other menu items only contribute the parts that are not enclosed in double curly braces.
[% menu home %]
home
Menus can ben nested, a nested menu entry can be accessed for example with [% menu sec subitem1 %].
[% menu sec subitem1 %]
It's best to take a look at the examples in the distribution, which should nicely illustrate the menu mechanism.
Syntax hilighting requires vim (see http://www.vim.org/) and Text::VimColor to be installed (otherwise the code is just HTML escaped, not hilighted).
Since you can't tell vim which encoding a source file is in, non-ASCII-characters might not survive the round trip to vim if the locales don't fit. On my systems UTF-8 locales worked, everything else didn't. So use with caution.
Currently only two options are supported, no-header and no-footer. If they are set in a file via [% option no-header %], the inclusion of header or footer files will be omitted.
no-header
no-footer
You can access external data sources by first bind'ing a variable to a data source, and then iterating with a for loop over that source.
This is best illustrated with a short example.
File includes/news.xml:
includes/news.xml
<rootTag> <item> <headline>China buys Google</headline> <status>April's fool joke</stoke> <date>2007</date> </item> <item> <headline>Perl and Python join forces: Larry Wall and Guido von Rossum announce 'parrot'</headline> <status>April's fool joke</stoke> <date>Very old</date> </item> </rootTag>
Now you can access the contents of this XML file in your source files:
[% bind news_variable type:xml file:news.xml root:item %] [% comment and iterate over news_variable %] [% for i in news_variable %] <h2>Breaking news: [% readvar i.headline %]</h2> <p>Status: [% readvar i.status %]</p> [% endfor %]
Data sources are handled via plugins. Currently XML and DBI are supported.
The XML source is explained by the example above. The only additional option is 'limit', which can be set to a positive number and which limits the number of iterations. This plugin is quite limited in that the file structure always has be the same: one root tag that contains a list of secondary tags, each of which many only contain distinct tags. Nested tags might work, but aren't officially supported.
DBI is perls generic database interface. You can use it to access a database. This has some limitations, for example you can't reuse database connections, so every bind statement actually opens a database connection on its own.
For the brave, here is an example of how to use it:
[% bind my_db type:dbi dsn:DBI:mysql:database=yourdatabse;host=dbhost username:your_db_user password:you_db_password encoding:latin1 sql:'SELECT headline, status FROM news LIMIT 10' %] [% for i in my_db %] <h2>Breaking news: [% readvar i.headline escape:html %]</h2> <p>Status: [% readvar i.status escape:html %]</p> [% endfor %]
The options are as follows:
The dsn option is the "data source name" that DBI's connect method accepts. It always starts with DBI:, then followed by the driver name like mysql or Pg (for Postgres) and the driver options.
dsn
DBI
connect
DBI:
mysql
Pg
The encoding option is optional and defaults to utf-8. You can use any character encoding here that Perl's cool Encode module supports, which is quite a many.
encoding
utf-8
Encode
the password and username options can be omitted if your database doesn't ask for them
password
username
This plugin may seem weird if you don't know Perl and its database module. If that's the case, consider toying around with Perl and DBI first (it's really worth a try).
Mowyw tries to read a file called mowyw.conf. Within that file you can configure include pathes and file names on a per-file base, and you can specify which files should be processed.
mowyw.conf
An example config file might look like this:
MATCH[german] = \.de POSTFIX[german] = .de MATCH[english] = \.en POSTFIX[english] = .en INCLUDE[10] = \.xhtml$ EXCLUDE[50] = _private
The first part defines two sections called "german" and "english". If a file matches the regular expression \.de, it is treated as being in section "german". All include files will have the postfix '.de', which means that if a file is called source/index.html.de, the header file will be includes/header.de, a menu foo will be searched for in file includes/menu-foo.de etc.
\.de
source/index.html.de
includes/header.de
includes/menu-foo.de
This configuration mechanism is primarily intented for creating mutilingual sites, but might be useful for other things as well.
The second part consists of INCLUDE and EXCLUDE statements with their priority and the regex they match. Higher priority rules are tested first. When a rule matches the file is either processed (in the case of an INCLUDE option) or just verbatimly copied (in case of an EXCLUDE option).
INCLUDE
EXCLUDE
Due to an limition of the module that reads the config file ([mod://Config::File]) the only way to specify rules with the same priority is to prefix them with distinct strings:
INCLUDE[a5] = foo INCLUDE[b5] = bar
is equivalent to
INCLUDE[5] = foo|bar
Don't give INCLUDE and EXCLUDE-options with the same priority, the behaviour in that case is undefined.
If something isn't clear to you, please take a look into the README file and the examples in the source tarball. If that doesn't answer your questions, don't hesitate to contact the author.
README
The official website can be found here: http://perlgeek.de/en/software/mowyw.
Error messages from the lexer and parser aren't very useful for the uninitiated, though the line number makes it pretty obvious where to search for for the error.
Disk space: mowyw keeps two complete copies of a project on disk, the source files and the resulting online files. This might not be optimal, and consumes unnecessary space for files that are not processed anyway.
Memory: mowyw is written in Perl, which is not know to be terribly memory efficient. Additionally it slurps the files into memory before parsing, which isn't terribly efficient either. But it's simple, and if it's not enough for you, go get a bigger machine (or contribute a stream tokenizer and parser).
There are some limitations due to multi-pass parsing. For example you can't include the string %] in SQL statements, because it is considered to be a tag closer by the lexer. A future rewrite might change that.
%]
Written by Moritz Lenz, http://perlgeek.de/, mailto:moritz@faui2k3.org.
Copyright 2006 - 2011 by Moritz Lenz.
This is free software. You may redistribute copies of it under the terms of the Artistic License 2 as published by The Perl Foundation.
(If this license is not permissive enough for you, please contact the author).
There is NO WARRANTY, to the extent permitted by law.
To install App::Mowyw, copy and paste the appropriate command in to your terminal.
cpanm
cpanm App::Mowyw
CPAN shell
perl -MCPAN -e shell install App::Mowyw
For more information on module installation, please visit the detailed CPAN module installation guide.