nanoc導入メモ 4/5 「Markdown独自拡張」編

nanocの醍醐味は、なんといってもRubyによるカスタマイズで記事の作成を効率化したり、挙動を細かく調整できることです。

ここでは番外編として、記事の作成に深く関わるMarkdown周辺のカスタマイズを紹介します。

日本語Markdown

Markdownの見出しは通常#の連続やアンダーラインで表しますが、当サイトでは日本語のテキストでよく使われる「■」と「●」で代用しています。

実装は「lib/default.rb」で文字列を置き換えるフィルタを定義し、「Rules」でMarkdownのフィルタより先に適用するだけです。

class JpMarkdownFilter < Nanoc::Filter
  identifier :jp_markdown_filter

  def run(content, params={})
    mod_content = content.gsub(/^■/, '### ')
    mod_content = mod_content.gsub(/^●/, '#### ')
  end
end

ちなみに、「■」は「s」、「●」は「m」でIMEに辞書登録しておけば簡単に入力できます。

リンクを新規ウィンドウで開く

Redcarpetにはリンクを「target ="_blank"」で新しいウィンドウで開くための機能はありません。以下のようにして独自のレンダを用意することで常に新規ウィンドウで開くように変更できます。(参考:Issue #85: :auto_link => target=_blank ・ tanoku/redcarpet ・ GitHub)

require "redcarpet"
require "cgi"

class MyRedcarpetRenderer < Redcarpet::Render::XHTML
  def link(link, title, alt_text)
    "<a href=\"#{CGI::escapeHTML(link)}\" target=\"_blank\">#{alt_text}</a>"
  end
end

Redcarpetのレンダは、フィルタ適用時の「:renderer」オプションで指定します。

compile '*' do
  if item.binary?
    # don't filter binary items
  else
    case item[:extension]
      when 'mkd'
        filter :jp_markdown_filter
        filter :redcarpet, :options => {:fenced_code_blocks => true},
                           :renderer => MyRedcarpetRenderer
        layout 'default'
        # ~~~中略~~~
    end
  end
end

コードハイライトを行う

Redcarpetの独自レンダに以下を追加することでCodeRayを使った「:fenced_code_blocks」のコードハイライトが可能になります。(参考:Redcarpet のシンタックスハイライトに CodeRay を使う - すぱぶらの日記)

require "redcarpet"
require "coderay"

class MyRedcarpetRenderer < Redcarpet::Render::XHTML
  # ~~~中略~~~
  def block_code(code, language)
    if language then
      CodeRay.scan(code, language).div
    else
      "\n<pre><code>#{code}</code></pre>\n"
    end
  end
end

CodeRayはgemでインストールしておく必要があります。

> gem install coderay

画像のサイズを取得し、Highslide JSで表示する

Highslide JSは画像をスムーズに拡大表示することのできるJavaScirptライブラリです。当サイトでは記事本文に挿入する画像の縦横サイズを自動的に取得し、このHighslide JSで表示するようにしています。

sample picture sample picture

Rmagickのインストール

まず、画像のサイズを取得するためにRmagickインストールをインストールします。Rmagickのインストールには、先にImageMagickのインストールが必要です。(参考:Ruby 1.9.2 how to install RMagick on Windows? - Stack Overflow)

ImageMagick: Install from Binary DistributionからWindows用のバイナリをダウンロードし、インストールオプションの「development headers and libraries」にチェックして空白を含まないパスにインストールします。

ユーザ環境変数に以下を追加します。

CPATH=C:\path\to\ImageMagick\include
LIBRARY_PATH=path\to\ImageMagick\lib

gemでrmagickをインストールします。

> gem install rmagick

私の環境では使用時に「CORE_RL_Magick_.dllが見つからない」旨のエラーが出ましたがvcredist_x86.exeをインストールして再起動したら使用可能になりました。(参考:RMagickをインストールした。 | aruy.net)

Highslide JSの導入

Highslide JSの使い方はダウンロードしたファイルに含まれるサンプルを見ればだいたい分かると思います。

「graphics」内の画像をnanocの「content/images/highslide」に、「highslide.js」を「content/javascripts」に、「highslide.css」と「highslide-ie6.css」を「content/stylesheets」にそれぞれコピーします。

なお、「loader.gif」の画像は使用しないため削除します。(削除しないと「loader.white.gif」と名前が被るためコンパイル時にエラーになります)

以下の内容で「layouts/partial_htmls/highslide.erb」を作成し、「layouts/default.haml」のhead内からrenderで読み込みます。

<script type="text/javascript" src="/javascripts/highslide.js"></script>
<link rel="stylesheet" type="text/css" href="/stylesheets/highslide.css" />

<script type="text/javascript">
    hs.graphicsDir = '/images/highslide/';
    hs.outlineType = 'rounded-white';
</script>

「Rules」のrouteとlayoutを以下の内容に変更します。

route '*' do
  if item.binary?
    item.identifier.chop + '.' + item[:extension]
  else
    case item[:extension]
      when 'sass'
        item.identifier.chop + '.css'
      when 'css'
        item.identifier.chop + '.css'
      when 'js'
        item.identifier.chop + '.js'
      else
        item.identifier + 'index.html'
    end
  end
end

layout '/partial_htmls/*/', :erb
layout '*', :haml, :ugly => true

画像タグ生成方法の変更

画像サイズの自動取得とタグの生成は、こちらもRedcarpetの独自レンダの中で実装します。以下の内容で、画像の横幅が300px以上の場合、自動的に300pxまで縮小した状態で表示されるようになります。

require 'rmagick'

class MyRedcarpetRenderer < Redcarpet::Render::XHTML
  #~~~中略~~~
  def image(link, title, alt_text)
    img = Magick::Image.read(File.expand_path(File.join(File.dirname(__FILE__), '..')) + '/content' + link).first
    max_width = 300.0
    width = img.columns
    height = img.rows

    if width > max_width then
      height = (height * (max_width / width)).round
      width = max_width.round
    end

    s =  <<"EOS"
<a href="#{link}" class="highslide" onclick="return hs.expand(this)">
<img src="#{link}" alt="#{alt_text}" title="Click to enlarge" width="#{width}" height="#{height}" />
</a>
<span class="highslide-caption">#{alt_text}</span>
EOS
  end
end

なお、それほど大きな画像は使用しないと思われるので、クリック時のローディング時間短縮のためにもサムネイルの生成はしていません。

おわりに

サイト公開までに行ったnanocのカスタマイズについては、これで大体書き出すことができました。

次回「運用効率化バッチ」編では、繰り返し必要になるデプロイ作業などをバッチ化して簡単に実行する方法を紹介します。

連載一覧

blog comments powered by Disqus