#272 Markdown with Redcarpet
- Download:
- source codeProject Files in Zip (204 KB)
- mp4Full Size H.264 Video (12.4 MB)
- m4vSmaller H.264 Video (7.75 MB)
- webmFull Size VP8 Video (8.32 MB)
- ogvFull Size Theora Video (19.8 MB)
3ヶ月ほど前にGithubがRedcarpetというRuby gemを導入しました。このgemはMarkdownコードを解釈するのに使われ、Githubではドキュメントをマークアップする簡単な方法としてUpskirtライブラリと共に内部的に採用されています。操作方法も簡単なこのRedcarpetを使って、今回のエピソードではRailsアプリケーションに追加する方法およびカスタマイズの方法、またコードブロックをシンタックスハイライトする方法を紹介します。
下の簡単なブログアプリケーションを使って作業を行っていきます。下の記事に入力した内容はHTMLとして直接出力されるので、2つのパラグラフに分けて入力したにもかかわらず一つのブロックとして表示されています。
現在、内容はいかなる方法でも解釈されていません。このアプリケーションをMarkdownを介するように修正していきます。これによって記事を作成したり編集する人が、柔軟にフォーマットを制御できるようになります。
Redcarpetをインストールする
まず最初に、もうお分かりだと思いますが、Redcarpet gemをインストールします。これには通常どおりGemfileにgemへの参照情報を追記して、bundleコマンドを実行してインストールします。
source 'http://rubygems.org' gem 'rails', '3.0.9' gem 'sqlite3' gem 'nifty-generators' gem 'redcarpet'
次にArticleController
のshow
ページで、記事の内容を表示するコード行にRedcarpetを追加します。そのためにRedcarpet
オブジェクトを新規に作成し、マークアップしたい内容を渡して、返された文字列に対して to_html
を呼び出します。
<% title @article.name %> <%= Redcarpet.new(@article.content).to_html %> <p> <%= link_to "Edit", edit_article_path(@article) %> | <%= link_to "Destroy", @article, :confirm => 'Are you sure?', :method => :delete %> | <%= link_to "View All", articles_path %> </p>
しかしページを再度読み込んでみても、求める結果にはなっていません。
Rails 3がHTMLを自動的にエスケープしたので、Redcarpetが追加したタグがそのままページに表示されています。これを直す簡単な方法は、出力をraw
で囲む方法です。
<% title @article.name %> <%= raw Redcarpet.new(@article.content).to_html %> <p> <%= link_to "Edit", edit_article_path(@article) %> | <%= link_to "Destroy", @article, :confirm => 'Are you sure?', :method => :delete %> | <%= link_to "View All", articles_path %> </p>
ページを再度読み込むと、今度は期待通りに表示されました。
Markdownコードを表示させたいときに毎回この操作を行うのも手間なので、markdown
というヘルパーへソッドを作成してこの作業を簡略化します。
module ApplicationHelper def markdown(text) Redcarpet.new(text).to_html.html_safe end end
ヘルパーメソッドから内容を返すときに、raw
の代わりにhtml_safe
を呼び出します。これで記事のページで新しく作成したヘルパーメソッドを使えるようになりました。
<% title @article.name %> <%= markdown @article.content %> <p> <%= link_to "Edit", edit_article_path(@article) %> | <%= link_to "Destroy", @article, :confirm => 'Are you sure?', :method => :delete %> | <%= link_to "View All", articles_path %> </p>
Redcarpetをカスタマイズする
次にRedcarpetのカスタマイズ方法を見ていきます。デフォルト設定では、単一の改行(line break)を処理しません。新しいパラグラフの始まりを定義するには空白行が必要です。例えば下のような単一の改行を含んだ文を入力したとします。
give me a break
これは、出力では1行の文として表示されます。
このような文を入力した時には、HTMLの該当する場所に改行タグが挿入されたほうがいいでしょう。Redcarpetのドキュメントを見ると、オプションのリストがありその中のhard_wrapがまさにこの目的で使えそうです。
以下のように、Redcarpetのコンストラクタ(constructor)にオプションをシンボルとして追加します。
module ApplicationHelper def markdown(text) Redcarpet.new(text, :hard_wrap).to_html.html_safe end end
記事のページを再度読み込むと「Give me a break」の行は希望どおり2行で表示されます。
他にも役に立つオプションを追加することができます。filter_html
オプションを指定すると、すべてのHTMLタグを出力しないようにします。autolink
を追加すると、コード中で検知されたURLをリンクに変更します。記事中にコードサンプルを含む予定なので、no_intraemphasis
オプションも使用します。これによって、単語と単語の間のアンダースコアを強調の開始や終了と解釈しないようにすることで、Rubyのメソッドや変数名に用いられるアンダースコアによって文字が強調されないようにします。最後に、ブログの記事中のコードサンプルを読みやすく表示するためにさらに2つのオプションを指定します。fenced_code
(すぐ後で実際の使用例を示します)とgh_blockcode
です。
オプション指定が多くなりすぎたので、ヘルパー内で配列として抽出します。
module ApplicationHelper def markdown(text) options = [:hard_wrap, :filter_html, :autolink, ↵ :no_intraemphasis, :fenced_code, :gh_blockcode] Redcarpet.new(text, *options).to_html.html_safe end end
これらのオプションが設定されたところで、Githubでと同じように記事中にコードブロックを追加できるようになりました。ブロックは最初と最後を3つのバッククォートを含む行で囲み、かつ開始行では言語を指定します。記事を編集してRubyコードのブロックを追加してみます。
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ``` ruby puts "Hello, world!"
<p>これが次のように表示されます。コードサンプルは、<code>lang</code>要素で言語を定義した<code>pre</code>タグで囲まれています。</p> <div class="imageWrapper"> <img src="http://railscasts.com/static/episodes/asciicasts/E272I06.png" width="802" height="428" alt="コードブロックが表示された"/> </div> <p>従来の方式のFenced Codeで、バッククォート(`)の代わりにチルダ(~)を使ってコードブロックを定義することもできます。</p> ``` html Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ~~~ruby puts "Hello, world!" ~~~
これはバッククォートを使用したときとまったく同じように表示されます。
シンタックスハイライト
記事にコードブロックを追加できるようになりましたが、次にシンタックスハイライトを行うにはどうすればいいでしょうか。GithubはこれをPythonのライブラリのPygmentsとAlbino gemの組み合わせで処理していて、今回のアプリケーションでも同じように設定してみます。
Gemfile
にAlbinoを追加し、合わせてNokogiriも加えてRedcarpetが生成するHTMLを解釈して分解できるようにします。
source 'http://rubygems.org' gem 'rails', '3.0.9' gem 'sqlite3' gem 'nifty-generators' gem 'redcarpet' gem 'albino' gem 'nokogiri'
いつものようにbundleを実行して、すべてがインストールされたことを確認します。
Pygmentsもインストールする必要があります。マシンにはPythonがインストールされているので、以下のコマンドを実行してインストールを行います。
$ sudo easy_install pygments
ApplicationHelper
で、RedcarpetコードをHTMLに変換する辺りにsyntax_highlighter
というラッパーメソッドを追加します。このメソッドはNokogiriを用いてドキュメント内のlang
属性のついたpre
要素(Redcarpet がコードブロックとして定義している部分)を探します。そしてこれらの要素の内容を、シンタックスハイライトされたコードに置き換えます。
module ApplicationHelper def markdown(text) options = [:hard_wrap, :filter_html, :autolink, ↵ :no_intraemphasis, :fenced_code, :gh_blockcode] syntax_highlighter(Redcarpet.new(text, ↵ *options).to_html).html_safe end def syntax_highlighter(html) doc = Nokogiri::HTML(html) doc.search("//pre[@lang]").each do |pre| pre.replace Albino.colorize(pre.text.rstrip, pre[:lang]) end doc.to_s end end
このコードはrack-pygmentize gemを元にしているので、仕組みを知るために一度見てみることをお勧めします。この方法によって、もしCoderayのような違うシンタックスハイライト用ライブラリを使いたい場合にも簡単に交換できるようになります。
シンタックスハイライトを目立たせるためにスタイルシートを追加しました(Githubで参照できます)。ページを再度読み込むとコードサンプルがハイライトされています。
Redcarpetの紹介は以上です。さらに詳しい情報はプロジェクトのGithubページを参照してください。