Railsでカスタムmarkdownを実装する

Railsでブログ実装したくなることないですか?
その際に記法の選択肢の一つにmarkdownが出てくると思います。
redcarpetを使うと簡単にmarkdownを実装できるので、その方法をまとめました。

Redcarpetでmarkdownを実装する

Gemfileにredcarpetを追加

gem 'redcarpet'

helperにメソッドを作る

app/helpers/markdown_helper.rb など作って、

module MarkdownHelper
  def markdown(text)
    unless @markdown
      renderer = Redcarpet::Render::HTML.new
      @markdown = Redcarpet::Markdown.new(renderer)
    end

    @markdown.render(text).html_safe
  end
end

とします。

完成

あとはビューとかでmarkdownを呼ぶだけです。簡単ですね。

markdownをカスタマイズする

とりあえず重要っぽいものだけ。

htmlを無効にする

レンダラにオプションを渡すといけます。

renderer = Redcarpet::Render::HTML.new(filter_html: true)

これで<script>alert('hoge');</script>とかできなくなります。

改行を楽にする

renderer = Redcarpet::Render::HTML.new(hard_wrap: true)

行末にスペース2ついれなくても改行できるようになります

テーブルを使用可能にする

これはMarkdownにオプションを渡します。

@markdown = Redcarpet::Markdown.new(renderer, tables: true)

独自の記法を実装する

これは若干大変です。

Redcarpet::Render::HTMLのサブクラスを作る

lib/redcarpet/render/original_html.rb など作って、

class Redcarpet::Render::OriginalHTML < Redcarpet::Render::HTML
end

とする。

オリジナルレンダラを読み込む

config/application.rbのclass Application < Rails::Application下に以下を追加

config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]

起動時にlib以下が読み込まれるようになるので、都度rails sし直しましょう

markdown_helper.rbは以下のように変える。

renderer = Redcarpet::Render::OriginalHTML.new

見出しをh2からにする

lib/redcarpet/render/original_html.rbを、

class Redcarpet::Render::OriginalHTML < Redcarpet::Render::HTML
  def header(text, level)
    level += 1
    "<h#{level}>#{text}</h#{level}>"
  end
end

にする。
このように、このへんに書いてあるコールバックをオーバーライドして独自のカスタマイズを加えます。
ここでsuperを呼ぶとうまくいかなくて、それはredcarpetがCで実装されたライブラリだからみたいです。

独自の記法を追加する

ためしに、はてなブログが採用しているgist記法を実装してみましょう。
[gist:数字]とすると数字のidを持つgistを表示できるやつです。

postprocessという、redcarpetの処理後に呼ばれるコールバックがあり、ここで変更を加えると、filter_htmlオプションの影響を受けず修正を加えることができます。

lib/redcarpet/render/original_html.rb を、

class Redcarpet::Render::OriginalHTML < Redcarpet::Render::HTML
  def postprocess(full_document)
    full_document.gsub(/\[gist:(.+)\]/, '<script src="https://gist.github.com/\1.js"></script>')
  end
end

にしてみましょう。
…もうできました。
要するに、postprocessの引数に修正を加えて返してやればいいわけです。

まとめ

redcarpetを使うとrailsでmarkdownを手軽に実装できます。是非お試しください。