[ruby][Middleman] カスタムなレンダラ

 性的な、もとい静的なサイトを構築するツールとして有名なjekyllがあります。ちょろっとMarkdown書くだけでそれっぽいページが作れるのですが、どうしても扱いにくい部分があったので今回は二番手の「Middleman」というツールを使ってみました。あ、原田泰造は関係無いです。
 今回はこのMiddlemanで、レンダリング前にテキストに変更を加える方法をまとめます。結構力技な感じもするので正しい方法があったら教えて欲しいです。ググっても見つからなかったので…

jekyllのここがよくない!

 よくない、というわけではないのですが、jekyllはサーバー上でページを見ること前提のコードを出力します。要するに、パスが絶対パスになっているので、ローカルにファイルをおいているだけではページが見れません。相対パスで出力する方法も色々ググったんですが良さそうな方法が見つからなかったので、今回はMiddlemanという静的サイト構築ツールを使うことにしました。

Middlemanすげー!

 何が凄いって、インストールするだけでテンプレートエンジンも、SASSも、CoffeeScriptもJS/CSSのMinifierも全部使えちゃいます。しかもサーバーを起動しておけばMarkdownやHaml、ERb等を編集すると即座にビルドして反映してくれます。ココらへんに関してはjekyllも同じですけどね。

本題

 さて、早速カスタムなレンダラを実装しましょう。なんでこんなものを使うかというと、Markdownに独自のタグっぽいものを実装して表現力をちょっと上げたかったからです。Markdownの変換の前に独自タグをHTMLに変換してしまおう、って寸法です。

redcarpet編

 MiddlemanでMarkdownを使う場合は、このRedcarpetか、標準で入っているkramdownを使うことになるかと思います。Redcarpetのインストール方法に関しては今回は説明しませんので、各自ググってください。インストール方法と言ってもGemfileにちょろっと書くだけですが。

 Redcarpetでカスタムレンダラを追加するのはとても簡単です。

class CustomRenderer < Redcarpet::Render::HTML
  def preprocess(full_document)
    full_document.gsub(/\[box\](.+?)\[\/box\]/m, '<div class="box">\1</div>')
  end
end

set :markdown_engine, :redcarpet
set :markdown, renderer: CustomRenderer   # ここで設定

 こんな感じです。Redcarpet::Render::HTMLというクラスを継承して、preprocessというメソッドをオーバーライドします。preprocessメソッドの引数は変換対象となるテキストが入っていますので、こいつを加工して返してやればおーけーです。他にもpostprocess(変換後に呼ばれるメソッド)などもあります。詳しくはドキュメントを参照してください。
 拡張が終わったら、Middlemanにこのレンダラを使うように指定してやります。これでおk。コイツをConfig.rbに記述するか、別のファイルに書いてそのファイルをConfig.rbでrequireしてください。いずれにしろConfig.rbからこのクラスが見える必要があります。

kramdown編

 今度はMiddlemanに標準で入っているMarkdownレンダラ、kramdownでカスタムレンダラを作る方法です。スマートに書く方法がなかったので、ちょっと強引な感じになっています。

class CustomRenderer < Middleman::Extension
  def after_configuration
    Kramdown::Parser::Kramdown.class_eval do
      def adapt_source(source)
        "OK\n"
      end
    end
  end
end

::Middleman::Extensions.register(:custom_renderer, CustomRenderer)
activate :custom_renderer

 Middlemanの拡張を作成します。after_configuration、つまりMiddlemanの設定が終わったタイミングでkramdownのパーサーをいじります。
 adapt_sourceというのは、どうやらコードがパースされる前に呼び出されるメソッドのようで、コイツを書き換えてやることで変換前にテキストを弄ることが出来ます。ここでハマりかけるポイントが一つ。返却する文字列の最後に改行がないと死にます。上記例のように、必ず改行を付加しましょう。あとは普通にCustomRendererを拡張として読み込み、activateで有効化します。これらのコードもRedcarpet同様Config.rbに記述してください。

なぜRedcarpetじゃないの?

 両エンジンを触った感じ、Redcarpetのほうが扱いやすいかな―、と感じました。しかし一点だけ、どうしてもkramdownでなければならないポイントがありました。それはブロック要素でMarkdownを囲うと、その要素の中身はMarkdownとして認識されない、という点です。これはMarkdownの仕様として定められているので認識されないのが正しい挙動ではありますが、先述の通り独自タグをHTMLのタグに置き換えるような使い方をすると、Markdownが使えなくなってしまいます。
 kramdownではこの仕様を無視して、ブロック要素中もMarkdownとして認識させることの出来るオプションがあります。素晴らしい!

set :markdown, :parse_block_html => true

 この一行をConfig.rbに記述します。これでブロック要素中のMarkdownも正しく変換されます。

 もちろん、Redcarpetでpreprocessではなくpostprocessを使って、変換後に独自タグをHTMLタグに変換してもうまくいきます。が、この場合独自タグがテキスト(段落)としてみなされるため、pタグが挿入されます。つまり、pタグが挿入されることを考慮して置換処理を行わなくてはなりません。このため、今回はRedcarpetではなくkramdownを採用しました。

おまけ

 kramdownでコードブロックを作る際は、タブではなく4スペースでインデントしないとうまく認識されませんでした。また、kramdownではRedcarpetで採用されているTriple-Backtick(```←これで囲うやつ)は使えないはずですが、何故か使えました。逆にTriple-Tilda(~~~←これ)で囲う方法は使えませんでした。謎。バージョンは1.3.3でした。設定が悪いのかな。

 以上、カスタムレンダラを作る方法でした。Middleman、便利だよ!

0 件のコメント :

コメントを投稿