一歩前進

プログラミングに関する雑多なメモ

数式とUMLが記述できる静的サイト生成 Middleman + Asciidoctor の連携

PlantUMLや数式が記述できる静的サイト生成の環境が欲しいと思い調べたところ、Middleman + Asciidotor + asciidoctor-diagramで実現できたのでメモします。 (Hugoが気に入っていたのですが、現時点ではAsciiDocには対応していませんでした。)

AsciiDocはMarkdownよりも表現力が高く、リッチな文書を記述するのに向いています。 例えば以下の記事 [1]では、書籍 "Pro Git" をAsciidoctorで作成したとあります。

postd.cc

このAsciiDocですが、仕様が2つあります。 1つはオリジナルのAsciiDocで、その変換ツールPythonで作られています。 もう1つは後発のAsciidoctorで、ツールRuby製です。 両者の仕様の違いはこちら[2]。 今回は後者を使います。

インストール

環境:

  • OS X 10.10.3
  • Homebrew 0.9.5
  • Ruby 2.2.2p95
  • Git

インストールするもの:

  • JDK 1.8.0
  • Graphviz 2.38.0
  • PlantUML 8024
  • rbenv 0.4.0
  • bundle 1.10.4
  • Middleman 3.3.12
  • Asciidoctor 1.5.2
  • asciidoctor-diagram 1.3.0.preview.1
  • 他、依存パッケージ

事前準備

(1) 図の生成に必要なツール
GraphvizとPlantUMLをインストールします。 なお、PlantUMLはJava製のため、Javaをインストールしておく必要があります。

$ brew update
$ brew cask install java  # 既にJDKをインストール済みの場合は不要
$ brew install graphviz
$ brew install plantuml

(2) rbenv, bundle
gemを直接使わず、rbenvとbundleを使うので、先にそれらをインストールします。 インストール方法は以下の記事 [3]が参考になります。 ここではRuby 2.2.2を使いました。なお、Railsはここでは使いません。

qiita.com

既にrbenvとbundlerがインストール済みの場合

既にrbenvとbundlerがインストールしてある場合は、パッケージの依存関係による問題を回避するため、すべてのグローバルなgemを削除してローカルなgemで運用することをお勧めします。

グローバルなgemを削除する:

$ for i in `bundle exec gem list --no-versions | grep -v bundler`; do bundle exec gem uninstall -aIx $i; done

このとき、デフォルトのgemはアンインストールできない旨のエラーメッセージが出ますが、問題は無いので無視して先へ進みます。

Middlemanのインストール

(1) プロジェクトフォルダの作成
まず、任意の場所にプロジェクト用のフォルダを作成します。ここではmysiteというフォルダ名にします。

$ mkdir ~/mysite
$ cd ~/mysite

(2) asciidoctor-diagramリポジトリの取得 [暫定対処]
次にasciidoctor-diagramをインストールします。 gemからインストールしたいところですが、現時点でrubygemsに登録されているバージョン1.2.1は、Middleman経由で画像を生成するとParmission Deniedエラーが発生します。 バージョン1.3.0.preview.1で試したところ、エラーを回避出来ていたので、今回はこのバージョンを使います。

$ mkdir -p vendor/repo
$ cd vendor/repo
$ git clone https://github.com/asciidoctor/asciidoctor-diagram.git
$ cd asciidoctor-diagram
$ git checkout v1.3.0.preview.1

注意:
バージョンアップなどで、将来的には挙動が変わるかもしれません。 エラーの原因については、後述の「おまけ:エラーの原因」を参照してください。

(3) Gemfileの編集
Gemfileを生成するために、まずmysiteフォルダでbundle initを実行します。

$ cd ~/mysite
$ bundle init

ここでGemfileが作成されるので、次のように編集します。

# If you do not have OpenSSL installed, update
# the following line to use "http://" instead
source 'https://rubygems.org'

gem "middleman", "~>3.3.12"

# Live-reloading plugin
gem "middleman-livereload", "~> 3.1.1"

# For faster file watcher updates on Windows:
gem "wdm", "~> 0.1.0", :platforms => [:mswin, :mingw]

# Windows does not come with time zone data
gem "tzinfo-data", platforms: [:mswin, :mingw, :jruby]

# Activate support for AsciiDoc
gem "asciidoctor", "~> 1.5.2"

# Enable to embed diagrams in AsciiDoc documents.
gem 'asciidoctor-diagram', "=1.3.0.preview.1", :path => "vendor/repo/asciidoctor-diagram"

(4) gemのインストール
Gemfileの編集が終わったらgemをインストールしていきます。 --pathオプションを指定するのを忘れないで下さい。

$ bundle install --path vendor/bundle

これでインストールは完了です。

Middlemanのセットアップ

(1) サイトの生成
まずGemfileのバックアップをとり、mysiteフォルダにてmiddleman initコマンドでサイトを作ります。このときGemfileが上書きされるので、バックアップから元に戻しておきます。

$ cd ~/mysite
$ cp Gemfile Gemfile.bk
$ bundle exec middleman init .
$ mv Gemfile.bk Gemfile

(2) config.rb
次に設定ファイル(config.rb)のconfigure :build do ... endブロックの中に、次の行を追加します。 Asciidoctorでは文書の様々な属性を指定できますが、共通的に指定しておきたいものはここで設定しておきます。

config.rb:

...
configure :build do
  ...
  # Asciidoctor
  set :asciidoc_attributes, %w(source-highlighter=coderay coderay-css=style)
end

(3) MathJaxの設定
Asciidoctorで記述した数式は、HTMLに変換されるときにMathJaxのコードになります。 数式が表示されるように、MathJax.jsを読み込むようlayout.erbを編集します。 source/layouts/layout.erb を開き、<head> ... </head>ブロックの中に以下の行を追記します。

MathJaxにはいくつかの設定パラメータがありますので[4]、必要に応じて設定してください。

    <script type="text/javascript"
  src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML">
    </script>

(4) サーバ起動
それではサーバを起動してみます。

$ bundle exec middleman server

次の画面が表示されたら成功です。 f:id:succzero:20150626214914p:plain

(5) サーバ終了
サーバの終了はCtrl+Cで行います。

ページを作る

ページファイルはsourceフォルダに格納します。必要に応じて任意のサブフォルダに格納することもできます。 他のSSGのように_postフォルダに置く、といった決まりはありません。 また、ページファイルのフォーマットもMarkdownやAsciidoctorだけでなく、ERBやHaml(エクステンションを追加すればSlimも)が使えるので柔軟なWebサイトの構成が可能です。

サンプルページ

では、Asciidoctor形式のページを作ります。 sourceフォルダ直下に、次のファイルを置いてください。 ファイル中のditaaやUML図は公式ドキュメント [5]のサンプルを使いました。

sample.adoc:

:stem: latexmath
= ドキュメントタイトル

これは本文です。

== 数式を書く

インラインの数式 latexmath:[\lambda \vec{x}.M].

.ブロック形式の数式
[latexmath]
++++
\begin{align*}
e &::= c \mid x \\
  &\phantom{::}\mid \lambda x.e \mid e\;e
\end{align*}
++++



== 図を書く

.ditaaによる図
[ditaa, dia-ditaa, png]
....
                   +-------------+
                   | Asciidoctor |-------+
                   |   diagram   |       |
                   +-------------+       | PNG out
                       ^                 |
                       | ditaa in        |
                       |                 v
 +--------+   +--------+----+    /---------------\
 |        | --+ Asciidoctor +--> |               |
 |  Text  |   +-------------+    |   Beautiful   |
 |Document|   |   !magic!   |    |    Output     |
 |     {d}|   |             |    |               |
 +---+----+   +-------------+    \---------------/
     :                                   ^
     |          Lots of work             |
     +-----------------------------------+
....



.PlantUMLによるクラス図
[plantuml, dia-classes, png]
....
class BlockProcessor
class DiagramBlock
class DitaaBlock
class PlantUmlBlock

BlockProcessor <|-- DiagramBlock
DiagramBlock <|-- DitaaBlock
DiagramBlock <|-- PlantUmlBlock
....

ページをビルドする

middleman build コマンドを使います。 ただしPlantUMLやditaaなどで図を記述した場合、今のところLaTeXのように2回ビルドする必要があります。

$ bundle exec middleman build
$ bundle exec middleman build

これは1回目で画像ファイルを生成し、2回目で生成された画像ファイルを検知してbuildフォルダへ移すためです。 ビルド中にasciidoctor-diagramが生成したものを、Middlemanが検知できていないのだと思います。

結果

先ほどと同じようにmiddleman serverコマンドでサーバを起動し、ページを開きます。 sample.adocの場合はsourceフォルダの直下においているので、URLはhttp://localhost:4567/sample.htmlとなります。

sample.adocをhtmlに変換した結果は次のようになります。

f:id:succzero:20150626214935p:plain

sample.htmlが表示されない場合

sample.htmlが表示されず、buildフォルダにsample.adocが格納されている場合、 Asciidoctorとasciidoctor-diagramがインストールされていない可能性があります。 もう一度Gemfileを確認して、bundle install --path vendor/bundle を実行してください。

おまけ:エラーの原因

現行のasciidoctor-diagram-1.2.1とMiddleman 3を連携させると、画像の生成でエラーになります。 画像の出力フォルダをimagesとした場合、Middlemanはそれをサイトルート相対パスとして/imagesというパスで扱います。しかし、asciidoctor-diagram-1.2.1では、そのパスをそのまま画像の出力フォルダとして扱います。 つまり、ルートディレクトリ直下にあるimagesフォルダとなるので、書き込み権限がなくてエラーになります。

この画像出力の問題で、過去にimagesoutdirなる属性が追加されたり、いまは消えていたりと、現時点では公式な仕様がまだ確定されていません。[6]

今回は不安定なバージョンである1.3.0.preview.1を使いましたが、将来的に挙動が変わる可能性があります。 安定版である1.2.1を使いたい場合は、以下にパッチを置いておきますので、利用を検討してみてください。

https://gist.github.com/succzero/34e57c01209ed9581fd9

どちらの方法をとるにせよ自己責任でお願いします。

参考ページ

[1] テクニカルライティングの将来 ー GitHub上のAsciidocで技術書Pro Gitを協働執筆. http://postd.cc/living-the-future-of-technical-writing/

[2] Differences between Asciidoctor and AsciiDoc. http://asciidoctor.org/docs/asciidoc-asciidoctor-diffs/

[3] Rails開発環境の構築(複数バージョン共存可能)(Homebrew編). http://qiita.com/emadurandal/items/e43c4896be1df60caef0

[4] Loading and Configuring MathJax. http://docs.mathjax.org/en/latest/configuration.html

[5] Asciidoctor Diagram. http://asciidoctor.org/docs/asciidoctor-diagram/

[6] Missing documentation on imagesoutdir attribute #59. https://github.com/asciidoctor/asciidoctor-diagram/issues/59