「はじめてのRake」の第4回です。 今回はRakeの応用です。
Pandocを使ってマークダウンからHTMLを作る例を紹介します。
はじめてこのブログを見る方は「はじめてのRake(1)」からご覧になってください。
文中に[R]という記号で始まる段落は、「Ruby上級者向けの解説」です。
上級とは、ほぼ「クラスを記述できるレベル」を指します。
上級以外の方はこの部分を飛ばしてください。
Pandoc
まず、Pandocがどのようなアプリケーションなのかを説明します。
Pandocは、文書の形式を変換するアプリケーションです。
例えば、
これ以外にも多数の文書形式がサポートされています。
詳しくはPandocのウェブサイトをご覧ください。
例としてexample.docx
というワードファイルをHTMLにしてみましょう。
ワードファイルはこんな感じです。
$ pandoc -so example.html example.docx
これにより、example.html
というファイルができます。
ダブルクリックするとブラウザで内容が表示されます。
画面の見栄えはともかく、ワードで書いた内容がHTMLとして表示されていることが確認できるでしょう。
では、どのようなHTMLが生成されたのでしょうか。
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>example</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
</style>
</head>
<body>
<h2 id="pandocのインストール"><strong>P</strong>andocのインストール</h2>
<p>以下ではUbuntu22.04でのインストールを説明する。</p>
<p>端末から、apt-getを使ってインストールする。</p>
<p>$ sudo apt-get install pandoc</p>
<p>これでインストールできるPandocはたいていの場合、最新版ではない。Pandocの最新版は、
そのホームページからダウンロードできる。インストーラがあるので、それを用いるのが簡単である。</p>
<h2 id="rubyのインストール">Rubyのインストール</h2>
<p>端末から、apt-getを使ってインストールする。</p>
<p>$ sudo apt-get install ruby</p>
<p>最新版のRubyをインストールにはrbenvが良いが、rbenvをマスターするには時間がかかる。
詳しくは<a href="https://github.com/rbenv/rbenv">rbenv</a>と
<a href="https://github.com/rbenv/ruby-build">ruby-build</a>のウェブサイトを参照してほしい。</p>
</body>
</html>
HTMLのソースコードから分かることで最も重要なことは、ヘッダが追加されていることです。
これはpandocに-s
オプションをつけたからです。
-s
をつけなければ、bodyタグで挟まれた本文の部分だけが生成されます。
マークダウンをHTMLに変換する
ここからは、マークダウンをHTMLに変換し、さらにRakeで作業を自動化する方法を学びます。
ソースファイルはすべてカレントディレクトリにあるとします。
生成するHTMLはdocsディレクトリに作成します。
マークダウンファイルは、「sec1.md」「sec2.md」「sec3.md」「sec4.md」ですが、将来ファイルが増えても対応できるようにRakefileを作ります。
※ 「sec1.md」は「はじめてのRake(1)」の編集画面に書き込んだマークダウンファイルに手を加えたものです。
「sec2.md」「sec3.md」「sec4.md」も同様に「はじめてのRake」のマークダウンファイルです。
これらのソースコードとほぼ同じものがGithubのRake-tutorial-for-beginners-jpレポジトリにアップロードしてあります。
Rakefileを試すときにはそこからダウンロードしてください。
RakefileのコードはGithubとブログでは若干違います。
そのため、試すときはダウンロードした方のRakefileで試してください。
また、できあがったHTMLがGithub Pagesで確認できます。
Pandocでは、最初に%とともにメタデータを書きます。
これは、タイトル、著者、日付を表します。
% はじめてのRake
% ToshioCP
% 2022/7/25
タイトルはHTMLのヘッダのタイトル・タグの内容にもなります。
PandocでHTMLにすると画面全面を使うので、横幅のあるPCでは広がりすぎて読みにくくなります。
それを解消するために、次のCSSファイル「style.css」を用意しました。
body {
padding-right: 0.75rem;
padding-left: 0.75rem;
margin-right: auto;
margin-left: auto;
}
@media (min-width: 576px) {
body {
max-width: 540px;
}
}
@media (min-width: 768px) {
body {
max-width: 720px;
}
}
@media (min-width: 992px) {
body {
max-width: 960px;
}
}
@media (min-width: 1200px) {
body {
max-width: 1140px;
}
}
@media (min-width: 1400px) {
body {
max-width: 1320px;
}
}
このCSSはBootstrapのcontainerクラスの定義を参考に作りました。
CSSの内容説明は省略します。
これをstyle.css
という名前のファイルにしてRakefileのあるディレクトリに保存します。
Pandocで-c
オプションを使うと、生成されたHTMLのヘッダでstyle.css
を取り込むようになります。
それでは、「sec1.md」から「sec4.md」までの4つのファイルからHTMLファイルを作るRakefileを作ってみましょう。
ここで、2つの考え方があります。
- sec1からsec4までを別々のHTMLファイルにし、それらをリンクでつなぐ。
目次を含むトップページはそれらとは別に作る
- sec1.mdからsec4.mdまでを一つのファイルにつなげ、それをHTMLにする。
どちらにも一長一短があります。
ここでは、作成の簡単な2番目の方法を採用しましょう。
sources = FileList["sec*.md"]
task default: %w[docs/はじめてのRake.html docs/style.css]
file "docs/はじめてのRake.html" => %w[はじめてのRake.md docs] do |t|
sh "pandoc -s --toc -c style.css -o #{t.name} #{t.source}"
end
file "はじめてのRake.md" => sources do |t|
firstrake = t.sources.inject("") {|s1, s2| s1 << File.read(s2) + "\n"}
File.write("はじめてのRake.md", firstrake)
end
file "docs/style.css" => %w[style.css docs] do |t|
cp t.source, t.name
end
directory "docs"
少しタスクの間の関連が複雑になっています。
- デフォルトのタスク「default」の事前タスクは「docs/はじめてのRake.html」と「docs/style.css」です
- 「docs/はじめてのRake.html」はマークダウン「はじめてのRake.md」とディレクトリ「docs」に依存しています
- 「はじめてのRake.md」は4つのファイル(「sec1.md」から「sec4.md」)に依存しています
- 「docs/style.css」はそのコピー元の「style.css」とディレクトリ「docs」に依存しています
- 「docs」はディレクトリタスクで、directoryメソッドで定義されます
6行目のsh
は、Rubyのsystem
メソッドと似ています。
引数を外部コマンドとして実行します。
6行目の場合、シェルを介してpandocを起動します。
sh
メソッドはRakeがFileUtilsに拡張したもので、オリジナルのFileUtilsにはありません。
Pandocの--toc
オプションは目次を自動生成するオプションです。
デフォルトでは#
から###
までが目次になります。
10行目のinject
メソッドは畳み込みを行う、配列インスタンスのメソッドです。
引数を初期値として、配列の値を次々にs2に代入して計算し、結果を次のs1に代入します。
順を追って説明しましょう
- 初期値は引数の空文字列
""
です。
それがブロックのs1に代入されます
- s2には最初の配列の要素である「sec1.md」が代入され、ブロック本体の
s1 << File.read(s2) + "\n"
が実行されます。
これにより、s1には「sec1.mdの内容+改行」が代入され、それが次のブロックのs1に代入されます。
- 2回目のブロック実行で、s1は「sec1.mdの内容+改行」、s2には次の配列要素の「sec2.md」が代入されます。
ブロック本体が実行され、「s1」には「sec2.mdの内容+改行」が追加されます。
その結果、s1は「se1.mdの内容+改行+sec2.mdの内容+改行」となります。
これが次のs1に代入されます。
- 3回目のブロック実行で、s1には前回実行の結果、s2には次の配列要素の「sec3.md」が代入されます。
前と同様に「sec3.mdの内容+改行」が追加されます。
- 4回目(最後)のブロック実行で、s1には前回実行の結果、s2には次の配列要素の「sec4.md」が代入されます。
前と同様に「sec4.mdの内容+改行」が追加されます。
- 以上の結果、firstrakeには「sec1.mdの内容+改行+sec2.mdの内容+改行+sec3.mdの内容+改行+sec4.mdの内容+改行」が代入されます。
要するに、4つのファイルを改行を挟んで結合した文字列になります。
11行目でそれがファイル「はじめてのRake.md」として保存されます。
改行をファイルの末尾に足したのは、一般に「テキストファイルの末尾は改行がある場合とない場合がある」からです。
改行が無い場合に次のファイルを接続すると、2番めのファイルの先頭の文字が行頭に来ません。
すると、見出しの「#」が行頭からずれて見出しでなくなるということが起こりえます。
これを避けるために改行を足しているのです。
cleanとclobber
この処理において「はじてのRake.md」というファイルは中間ファイルです。
重要なのはソースファイルと結果ファイルだと考えれば、処理後に中間ファイルは削除したいと思うかもしれません。
そのような操作を行うのがcleanタスクです。
cleanタスクを使うには
rake/clean
をrequireする
- 定数
CLEAN
の指すファイルリスト・オブジェクト(それも「CLEAN」と呼ぶことにします)に中間ファイルを追加する。
ファイルリストには配列と同様のメソッドが備わっているので、<<
またはappend
、push
メソッドで追加ができる。
また、結果ファイルも含めて全て生成ファイルを消去するタスクがclobberです。
- clobberタスクは、CLEANに登録されたファイルを削除する
- さらに、ファイルリストCLOBBERに登録されたファイルも削除する
以上を付け加えたRakefileは次のようになります。
require 'rake/clean'
sources = FileList["sec*.md"]
task default: %w[docs/はじめてのRake.html docs/style.css]
file "docs/はじめてのRake.html" => %w[はじめてのRake.md docs] do |t|
sh "pandoc -s --toc -c style.css -o #{t.name} #{t.source}"
end
CLEAN << "はじめてのRake.md"
file "はじめてのRake.md" => sources do |t|
firstrake = t.sources.inject("") {|s1, s2| s1 << File.read(s2) + "\n"}
File.write("はじめてのRake.md", firstrake)
end
file "docs/style.css" => %w[style.css docs] do |t|
cp t.source, t.name
end
directory "docs"
CLOBBER << "docs"
中間ファイルを削除するには
$ rake clean
生成ファイル全てを削除するには
$ rake clobber
とします。
以上で4回にわたった「はじめてのRake」を終わります。
ここでは基本的な事項に限定して説明しましたが、それだけでもRakefileを書くのに十分な知識は得られたと思います。
このブログで触れなかった事項には、
- タスクをマルチスレッドで実行し、高速化するための
multitask
メソッド
- Rakefileをカレントディレクトリ以外に置くこと、複数のRakefileを使うこと
- タスクの名前空間
- タスクの説明をするための
describe
メソッド
rake
コマンドのオプション、とくにトレースなどのデバッグに使えるもの
などがあります。
今後はこれらの学習をしていくと良いと思います。
また、Rakeの機能を学習するだけでなく、ビルドをどのように構成するかについての学習も大事です。
これには経験を重ねることが一番大事だと思います。