Asciidoctor extension for JBake

NOTE

English is not my mother language so if some part of this post is not clear don’t hesitate and let me know and I’ll try to explain better

JBake is an static site generator written in Java (there are a lot of similar static site generator written in Python, Javascript, Ruby …​). As a Java/Groovy developer point of view it has a lot of interesting features as:

  • run into the JVM machine, so you can integrate into your Java project

  • different template engines (freemarker, groovy, thymeleaf, jade…​)

  • markdown and asciidoctor support

  • open source

  • gradle plugins

As an example, this full blog is written in Asciidoctor and rendered by JBake as a static site who doesn’t require database, php, etc so I can deploy it in a lot of free platform as Github, Gitlab, Netlify, …​

WARNING

I use Gradle as build tool because it allows me to write code and customize the build process. Not sure if this post can be or how to adapted to Maven for example.

Asciidoctor

I like write post in asciidoctor because I can maintain the focus in the content of the post an forget the presentation.

For example, I can write in a post at some part of it:

img::image/path1/path2/image.png[]

and Asciidoctor+Jbake will render the HTML similar to

<image src="image/path1/path2/image.png">

Img is part of asciidoctor’s specification. Also you can use paragraphs, links, tables, etc but always from a semantic point of view, I mean, you don’t need to pay attention about "visual" details

Asciidoctor extension

One of the best features of Asciidoctor is the capabilitie of write extension. You can write extension in Ruby, Java or Javascript for example and "attach" these extensions to the Asciidoctor engine and use it into your document.

I’ve written some simple extensions in Java as a Discuss block, a Google Analitycs preprocessor or a QRCode generator. For example, with the QRCode extension you can write into your blog:

barcode:qrcode[https://puravida-software.gitlab.io/asciidoctor-extensions/,300,300]

and the extensión will generate the image and include the required code into the document to visualize them.

https://puravida-software.gitlab.io/asciidoctor-extensions/

Asciidoctor extension + Jbake

If you want to use some Asciidoctor extension, for example the previous barcode extension, you need to include it into the classpath.

WARNING

Pay attention you need to include them into the buildScript dependencies section because the dependencies section in Gradle is dedicated to compile

As I use Gradle this is how my build.gradle looks :

buildscript {
    repositories {
        jcenter()
        maven{ url  "http://dl.bintray.com/puravida-software/repo" }
    }

    dependencies {
        classpath 'org.scilab.forge:jlatexmath:1.0.7'
        classpath 'org.asciidoctor:asciidoctorj-diagram:2.0.5'
        classpath 'org.asciidoctor:asciidoctorj:2.2.0'
        classpath 'com.puravida.asciidoctor:asciidoctor-barcode:2.4.1'
    }
}

plugins {
    id 'org.jbake.site' version '5.2.0'
}

dependencies {

}

bakePreview {
    port = '8090'
}

bakePreview.dependsOn bake

jbake{
    version = "2.6.5"
    configuration['asciidoctor.option.requires'] = "asciidoctor-diagram"
    configuration['asciidoctor.attributes'] = [
            "stem=", "icons=font",
            "imagesoutdir="+file('build/jbake/images').absolutePath,
            "imagesdir=/images",
            "source-highlighter=prettify",
    ].join(',')
}

build.dependsOn  bake

Your own Asciidoctor extension

It’s great when someone has been written and published an usefull extension and you can use it into your blog without more effort that include it into the `build.gradle`file, but sometimes you want to write your own extension without the complexity of create a repo (maybe in Bintray), versioning, publishing, etc when all you want is to use the extension into the same blog

For this situation I’ll explain how I manage to write custom extensions only accesible in the blog and versioned together.

This extension will render a calendar for an specified month as an HTML table and looks as:

    .Agenda 2020/10
    [calendar,year=2020, month=10]
    ----
    1 Arrive
    2 Keynotes
    6 Visit to Madrid
    9 Close and party
    ----

It’s a block with an unique identifier (calendar) with two attributes (year and month) where every line will start with a number followed by a String. The extension will parse this block and generate a table similar to

Table 1. Agenda 2020/10
L M X J V S D

1

Arrive

2

Keynotes

3

4

5

6

Visit to Madrid

7

8

9

Close and party

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

buildSrc

Our extension will reside into the buildSrc/src/main/groovy/jorge/aguilera/soy folder and we’ll follow the steps indicate into the guide https://github.com/asciidoctor/asciidoctorj

Briefly these steps are:

  • Create a CalendarBlock

CalendarBlock.groovy
package jorge.aguilera.soy

// a lot of imports

@Name("calendar")
@Contexts([Contexts.LISTING])
public class CalendarBlock extends BlockProcessor {

    @Override
    public Object process(StructuralNode parent, Reader reader, Map<String, Object> attributes) {
        // main logic to parse and render
    }
]

Basically the extension will read the lines and parse all of them following the specified structure.

It will instantiate a Calendar object with the year and month set in the attributes (required) and navigate accross the month adding strings to an array in order to have an "asciidoctor table" as if the user written it.

At the end the extension will call to the asciidoctor engine to render an array os strings similar to

    |===
    | | | 30 | 1 | 2
    | 3 a line | 4 another line
    |===

using the method parseContent:

    Block block = createBlock(parent, "open", (String) null);
    parseContent(block, content);
    return block;
  • register the extension using the JorgeExtensionRegistry.groovy

package jorge.aguilera.soy
// some imports
class JorgeExtensionRegistry implements ExtensionRegistry {
    void register(Asciidoctor asciidoctor) {
        asciidoctor.javaExtensionRegistry().with{
            block 'calendar', CalendarBlock
        }
    }
}

Dependencies

We will include the dependencies required by our extension into the buildSrc/build.gradle file. Pay attention is not the build.gradle file in the root of the project.

repositories {
    jcenter()
}

dependencies {
    compile 'org.asciidoctor:asciidoctorj:2.2.0'
}

Build

Now when Gradle will run the build task it will compile and use the buildSrc as classpath dependency so when JBake instantiate the Asciidoctor engine will have all the artifacts required to understand your new extension

Simple execute

./gradlew build bakePreview

and preview your blog at localhost:8090

SPARQL extension

I’m playing with a new idea for a SPARQL asciidoctor-extension how execute a sentence and dump the results as a table (similar to the native capability of asciidoctor to render a CSV file for example)

The idea is to have a new block where you write the sentence and specify wich fields you want to dump.

You can see it in action at sparql.html

Others extensions

In this way you can have more extensions as:

  • write some content in yellow to remark some ideas

  • encript some parts of your post and reveal it with a key

  • create a quizz with questions and answers

  • …​

Follow comments at Telegram groupOr subscribe to the Channel Telegram channel

2019 - 2020 | Mixed with Bootstrap | Baked with JBake v2.6.5