Nanocで記事ごとにUUIDを付けてリンク作ってPermalinkとする
静的ファイルジェネレータの弱点(と僕が勝手に思っている)は、URL=ファイルのパスというところです。 何が困るかと言うと、移動した場合にURLが変わってしまう。
これを解消するために、記事ごとにUUIDをつけて、実際のパスへのシンボリックリンクを作り、それをPermalinkとして使えば良いということです。
メタデータを利用する
Nanocのドキュメントの上部には、メタデータを入れることができます。
--- KEY: VALUE ---
のような感じです。ここに、UUID用に
--- NANOC_UUID: "....." ---
というのを、挿入します。こうすると、コンパイル時に attributeとして使えるようになります。layoutをhamlで書いていたとすると、
@item[:NANOC_UUID]
で、参照できます。filter内であれば、下記のように参照できます。
@item.attributes[:NANOC_UUID]
こうしておけば、permalinkようのURLとして、上記のUUIDを使ったURLを作るとか、 index tree を作るような処理のなかで、通常のパスの代わりに、UUIDのリンクの方を使うようにすればよいかと思います。
コード
最初 filter
で作っていたのですが、こういうのはRules
内のpreprocess
でやるべきでした。filter
内だと、attribute
の変更はできませんが、preprocess
であれば、attribute
の変更ができます。
preprocess do @items.each do |item| path = item.raw_filename.sub(/^.+\/content\//, "content/") # markdownのみ対象 next if path != nil && !path.match(/\.md$/); content = '' File.open(path, 'r') do |f| content += f.read end if content.match(/^NANOC_UUID: (.+)$/) # すでにある場合それを作る(とり方がちょっと雑ですが) uuid = $1 else # 新しく作成して先頭に書き込む uuid = SecureRandom.uuid if (content.match(/\A---/)) content = content.sub(/\A---/, "---\nNANOC_UUID: " + uuid + "\n") else content = "---\nNANOC_UUID: " + uuid + "\n---\n" + content end File.open(path, "w") do |f| f.puts(content) end # attribute に追加する item.attributes[:NANOC_UUID] = uuid end if (uuid) link = 'output/' + uuid + '.html' if !File.exist?(link) # output のルートに、実際のパスへのリンクを作る File.symlink(path.sub(/^content\/(.+)\.md$/, "\\1.html"), link) end end end end
結果
こんな感じのリンクが出来上がるので、permlink として、b247771c-a07a-4cb6-835d-ee8b2d461e49.html
の方を使えばいい感じです。
lrwxrwxrwx 1 ktat ktat 10 Apr 29 16:01 b247771c-a07a-4cb6-835d-ee8b2d461e49.html -> index.html -rw-r--r-- 1 ktat ktat 12169 Apr 29 16:02 index.html
元のファイルにメタデータを追加しているので、nanoc コマンドのラッパーのshell scriptかなんかをかいて、git commit
するようにしとくのが良いのではないかなと思います。
元のパスをURL欄からコピーされるのを防ぎたければ、パスにUUIDが含まれていない場合は、JavaScriptで強制的に移動させれば良いんじゃないかなと思います。
注意
filter :relativize_path
を使っていると、相対パスになってしまうので、元ファイルの場所がルートに存在しない場合、シンボリックリンクをルートに置いている関係上、相性が悪いです。
cssやjsは、絶対パスで指定するようにする必要があります。