11 July 2015

Recently, we have begun using Asciidoctor for system documentation at a project for the company I work for. I like Asciidoctor a lot, it is mature and the toolchain support is good.

However, we were having a hard time to set up navigation between documents. Basically, we would for instance like to be able to include a navigation bar in the header. The auto-generated table of contents is just for one document, and it didn’t make sense that all content was served as one big document, it was unwieldy. In the course of trying to figure out how to do this, I found this article on stackoverflow. The recommended way to handle this is to use some site generation tool, e.g. Awestruct. This is how I came about to have a look at Awestruct in combination with Asciidoctor.

This article is about how to set up Awestruct, Asciidoctor, and JRuby. The platform is Ubuntu 12.04 LTS, with a history, various bits and pieces already installed. Probably, some steps might not work for you, but the main purpose of this article is to give an overview over how to set up both the stack as well as the integration between Asciidoctor and Awestruct.

Of course, there exists information on the internet about this and related topics which I learned from. I will refer to other websites, blogs, and tutorials as I go along. For instance, I started by following "Running Awestruct with Asciidoc on Windows with JRuby" by Markus Eisele.

The entire source code is available on GitHub.

Download JRuby and follow the installation instructions. I used JRuby 1.7.21, and extracted it to ~/bin/jruby-1.7.21. The active Java version on my machine was Oracle 1.8.0_25.

Run

jruby -S gem install awestruct bundler

Verify that awestruct is installed, by running awestruct -v. My version was 0.5.5.

Create a project directory to hold the new website, I called it ~/aablog-foundation.

Initialize a git repository in this directory, if you like (I did).

cd to the project folder, and issue

awestruct --init --framework foundation

Several frameworks are possible, I tried also bootstrap. It appears to me that there is less work to do if foundation is chosen, as Asciidoctor uses foundation as well. (And I am no Sass/CSS expert.)

I added all files and directories to the git repositories in the initial commit. Some of them will possibly be removed later, but that makes it easier to understand what was removed, and I do not need to write down every detail here. In other words: check out the git repository!

Edit the Gemfile, and uncomment the entries for

gem 'coffee-script'
gem 'asciidoctor'
gem 'kramdown'
gem 'uglifier'
gem 'htmlcompressor'

Install the gems by running

bundle install

Build and serve the website by running

awestruct -g -s -P development

Note that

awestruct -d

did not start the webserver on my PC. Actually, the implicitly added --auto option seems to prevent the server from running. I could not figure out why.

In the project folder, create a file called restapi.adoc, with the following content:

= REST API
REST Architect <rest-architect@example.com>

An introduction to
https://en.wikipedia.org/wiki/Representational_state_transfer[REST].

== Introduction

* Verbs
* Status Codes
* ...

== Code Example

[[source,Java]]
----
@Path(Environment.PATH)
@SuppressWarnings(SuppressedWarnings.UNUSED)
public class Environment {

  public static final String STATUS_OK = "OK";
  public static final String PATH = "/environment";

  @GET
  @Produces({ MediaType.APPLICATION_JSON  })
  @SuppressWarnings(SuppressedWarnings.UNUSED)
  public Response getEnvironment() {
    return Response.ok()
     .entity(new EnvironmentSummary(STATUS_OK))
     .build();
  }
}
----

Rebuild and go to http://localhost:4242/restapi.html and observe that the page was rendered, but that it is just a single Asciidoctor document. There is no navigation bar, and the code sample is not formatted correctly. We will fix this first.

To continue from here, the following websites and blogs were helpful: graphitefriction/oscon-2013-docs-workshop by Dan Allan and Sarah White, "Setup a Blog with Awestruct" by Dan Allan, "Advanced integration of Jekyll and AsciiDoctor" by Evgeny Shepelyuk, and the Asciidoctor project site source.

We continue by integrating the Asciidoctor document with the site generation tool, Awestruct, in order to have the latter control the layout of the former.

In restapi.adoc, amend the top of the document so that it reads:

= REST API
REST Architect <rest-architect@example.com>
:awestruct-layout: base
:source-highlighter: coderay
:coderay-css: class
:linkcss:

An introduction to
https://en.wikipedia.org/wiki/Representational_state_transfer[REST].

This is part of the site _{sitename}_.

== Introduction

The attribute :awestruct-layout: base causes Awestruct to use the base template, which is defined in layouts/base.html.haml. Adding _:source-highlighter: coderay will fix the syntax highlighting issue.

Amend the _config/site.yml to read

name: Fancy Solution
title: Baked with Awestruct
org: ExampleCom
base_url: ''

asciidoctor:
 :safe: safe
 :attributes:
   sitename: Fancy Solution
   base_url: ''
   ctx_path: ''
   idprefix: ''
   idseparator: '-'
   sectanchors: ''
   icons: font

Finally, add

%link{:rel=>'stylesheet',:href=>"#{site.base_url}/stylesheets/asciidoctor.css"}
%link{:rel=>'stylesheet',:href=>"#{site.base_url}/stylesheets/coderay-asciidoctor.css"}

to the other stylesheet links in _layouts/base.html.haml (the easiest way to obtain these 2 css-files is to run asciidoctor on the restapi.adoc file directly).

Rebuild and verify that http://localhost:4242/restapi.html is both wrapped by the base Awestruct template, and styled correctly.

Also observe how the sitename attribute specified in the site.yml file is available in the document.

Finally, _layouts/base.html.haml is changed to include a menu entry pointing to the restapi page (the changes necessary are shown as diff):

@@ -27,9 +27,9 @@
%li.divider
%li
%a{:href=&gt;"#{site.base_url}/"} Home
-        -#%li.divider
-        -#  %li
-        -#    %a{:href=&gt;"#{site.base_url}/blog"} Blog
+        %li.divider
+          %li
+            %a{:href=&gt;"#{site.base_url}/restapi.html"} REST API
.row
.large-12.columns
~ content

A lot can be done to improve this example. There are also a few features that do not seem to work, for instance is the variable site.org, defined in _config/site.yml, not picked up in the template, it always defaults to Java::Org. Just use another variable name.

Note: when using Ruby instead of JRuby the variable site.org is propagated correctly. I posted a question about this on the newsgroup and will create an issue in github.

The approach of separating the site structure and the page contents using a site generation tool and a document generation tool makes sense. It is important that the approach is developer friendly, that the generation can be automated and that everything can be version controlled. That is the case with these tools. Often, a lot of time needs to be used to set up and configure the toolchain. They challenges tend to be different each time this has to be done on a new PC. The most popular approach to handle these for once and all is of course to use virtualisation and create appropriate images, e.g. using Docker. Several Awestruct and Asciidoctor related docker images can be found in the Docker Hub Registry.