homeASCIIcasts

272: RedcarpetでMarkdown 

(view original Railscast)

Other translations: En Es Fr

Other formats:

Written by Naomi Fujimoto

3ヶ月ほど前にGithubがRedcarpetというRuby gemを導入しました。このgemはMarkdownコードを解釈するのに使われ、Githubではドキュメントをマークアップする簡単な方法としてUpskirtライブラリと共に内部的に採用されています。操作方法も簡単なこのRedcarpetを使って、今回のエピソードではRailsアプリケーションに追加する方法およびカスタマイズの方法、またコードブロックをシンタックスハイライトする方法を紹介します。

下の簡単なブログアプリケーションを使って作業を行っていきます。下の記事に入力した内容はHTMLとして直接出力されるので、2つのパラグラフに分けて入力したにもかかわらず一つのブロックとして表示されています。

ブログアプリケーション

現在、内容はいかなる方法でも解釈されていません。このアプリケーションをMarkdownを介するように修正していきます。これによって記事を作成したり編集する人が、柔軟にフォーマットを制御できるようになります。

Redcarpetをインストールする

まず最初に、もうお分かりだと思いますが、Redcarpet gemをインストールします。これには通常どおりGemfileにgemへの参照情報を追記して、bundleコマンドを実行してインストールします。

/Gemfile

source 'http://rubygems.org'

gem 'rails', '3.0.9'
gem 'sqlite3'
gem 'nifty-generators'

gem 'redcarpet'

次にArticleControllershowページで、記事の内容を表示するコード行にRedcarpetを追加します。そのためにRedcarpetオブジェクトを新規に作成し、マークアップしたい内容を渡して、返された文字列に対して to_htmlを呼び出します。

/app/views/articles/show.html.erb

<% 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で囲む方法です。

/app/views/articles/show.html.erb

<% 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>

ページを再度読み込むと、今度は期待通りに表示されました。

今度は期待通りに2つのパラグラフになった

Markdownコードを表示させたいときに毎回この操作を行うのも手間なので、markdownというヘルパーへソッドを作成してこの作業を簡略化します。

/app/helpers/application_helper.rb

module ApplicationHelper
  def markdown(text)
    Redcarpet.new(text).to_html.html_safe
  end
end

ヘルパーメソッドから内容を返すときに、rawの代わりにhtml_safeを呼び出します。これで記事のページで新しく作成したヘルパーメソッドを使えるようになりました。

/app/views/articles/show.html.erb

<% 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)にオプションをシンボルとして追加します。

/app/helpers/application_helper.rb

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です。

オプション指定が多くなりすぎたので、ヘルパー内で配列として抽出します。

/app/helpers/applciation_helper.rb

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!"
```

これが次のように表示されます。コードサンプルは、lang要素で言語を定義したpreタグで囲まれています。

コードブロックが表示された

従来の方式のFenced Codeで、バッククォート(`)の代わりにチルダ(~)を使ってコードブロックを定義することもできます。

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のライブラリのPygmentsAlbino gemの組み合わせで処理していて、今回のアプリケーションでも同じように設定してみます。

GemfileにAlbinoを追加し、合わせてNokogiriも加えてRedcarpetが生成するHTMLを解釈して分解できるようにします。

/Gemfile

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 がコードブロックとして定義している部分)を探します。そしてこれらの要素の内容を、シンタックスハイライトされたコードに置き換えます。

/app/helpers/application_helper.rb

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ページを参照してください。