nanoc 3.3.xを使うときのメモ

これまでrestructured textでコンテンツを記述していたサイトを
リプレースしようということで、nanocを使ってみた。


Rubyで書かれていて、それなりにメンテされていて、ドキュメントもある、という観点でnanocを選択。


同様のプロダクトについては以下にいろいろあるのでnanoc以外を調べたい人には参考になるかも知れない。
ただし、あんまり活発ではないのも結構含まれている。
http://nanoc.stoneship.org/docs/1-introduction/


さて、静的なHTMLを生成するのに以下のようなことができればとりあえず満足。

  • 適当な記法でコンテンツを記述したい(nanocだとMarkdown)
  • 英語/日本語のコンテンツを用意したい
  • 言語ごとにディレクトリを切らずに同一階層に置きたい
  • パンくずリストは必須

nanocでMarkdownを有効にするには生成されるRulesを以下のように編集すれば良い

compile '*' do
  if item.binary?
    # don't filter binary items
  else
    filter :kramdown
    filter :erb
    layout 'default'
  end
end

英語/日本語のコンテンツを同階層に置きたい、というのはちょっとやっかい。
というのも、そもそもnanocはディレクトリに複数のHTMLが生成されるのを基本的には良しとしないポリシーらしいから。
そのあたりは nanoc create_itemを実行して雛形を生成してみるとよくわかる。
各ディレクトリにはindex.htmlだけが許容される。

以前はconfig.yamlにno_dirsというオプションが指定できたようなのだが、3.xではバッサリ削除されている。

ゆえにnanocとしては例えば/about.htmlと/about/index.htmlはitem.identifierだと同じ扱いになってしまう。
それでは今回は困るので、以下のようにしてコンパイル元のファイルを:content_filenameから取得して
/about.htmlになるのか、/about/index.htmlが生成されるのかを判定してあげるコードを追加することにした。以下は厳密には正しくないけどおおむね目的は達成できる。
ちなみにこれもRulesに記述する。

route '*' do
  if item.binary?
    # Write item with identifier /foo/ to /foo.ext
    p item
    item.identifier.chop + '.' + item[:extension]
  else
    # Write item with identifier /foo/ to /foo/index.html
    if item.identifier == "/" then
      ret = "/index.html"
    elsif item.attributes[:content_filename] =~ /^content(\/.*index.ja)\.md$/ then
      ret = $1 + ".html"
    elsif item.attributes[:content_filename] =~ /^content\/(.+index)\.md$/ then
      ret = item.identifier  + "index.html"
    else 
      ret = item.identifier.chop + ".html"
    end 
    ret
  end
end

ちなみにnanocのサイトには多言語サイトの例があるけど、そっちは
en/とかディレクトリを切る方法なので今回は採用しなかった。
http://nanoc.stoneship.org/docs/6-guides/#creating-multilingual-sites

config.yamlに allow_periods_in_identifiers: trueを追記しておかないと、foo.ja.mdなんかはnanoc compileで処理できないので注意。


あとはパンくずリストの実現。とりあえず以下のようなのをlayouts/default.htmlに入れておけば
上位階層からそれっぽいのが生成される。
これを入れるときはlib/default.rbにてinclude Nanoc::Helpers::Breadcrumbsを忘れずに。

<% breadcrumbs_trail.each do |crumb| %>
	<% if crumb != breadcrumbs_trail[0] %>
	&gt;
	<% end %>
	&nbsp;<%= link_to(crumb[:title], crumb.reps.find { |r| r.name == :default }) %>
	<% end %>