nanoc導入メモ 3/5 「カスタマイズ」編
nanocはデフォルトでは極めてシンプルな静的Webサイトを生成するだけですが、 付属のヘルパを利用することで新着記事表示やタグ分類機能などが使用できるようになります。
また、hamlやsassといったDSLを利用することでサイトの構築・メンテナンスを効率化したり、 Markdownおよび独自のフィルタによって記事作成時の手間を軽減することができます。
今回は、このサイトを構築するにあたり実施したカスタマイズの一部を紹介します。
Markdown, Haml, Sassのフィルタを適用する
まずはサイト構築にあたり面倒なHTMLとCSSのコーディングを避けるためにMarkdown, Haml, Sassを使えるようにします。
Markdownパーサのインストール
Markdownパーサの実装はたくさんあるので迷うところですが、私は多機能かつ処理の早そうなRedcarpetを選びました。Markdownパーサの種類については以下のページが参考になります。
RedcarpetはC実装を含むためインストールにはDevKitが必要です。RubyInstaller - Downloadsからダウンロードして以下のコマンドでインストールします。
> ruby dk.rb init
config.ymlが生成されるので末尾にRubyのインストールディレクトリを追加
- C:/path/to/ruby-install-dir
> ruby dk.rb install
> gem install redcarpet
Haml, Sassのインストール
HamlとSassはインデントでタグの入れ子構造を表すテンプレート言語です。SassにはSCSSというCSSの文法に近づけた派生版がありますが、Sassの方が記述量が少なくて済むのでおすすめです。
> gem install haml
> gem isstall sass
フィルタの適用
「Rules」ファイルを以下のように編集することで、コンテンツの拡張子を基に各フィルタを適用することができます。
compile '*' do
if item.binary?
# don't filter binary items
else
case item[:extension]
when 'md'
filter :redcarpet, :options => {:fenced_code_blocks => true}
layout 'default'
when 'haml'
filter :haml
layout 'default'
when 'sass'
filter :sass
end
end
end
route '*' do
if item.binary?
item.identifier.chop + '.' + item[:extension]
else
case item[:extension]
when 'sass'
item.identifier.chop + '.css'
else
item.identifier + 'index.html'
end
end
end
layout '*', :haml, :ugly => true
最後のlayoutルールでhamlのフィルタに設定している「:ugly => true」オプションは、出力するHTMLのインデントを取り除くもので、preタグに余計な空白が入ることを防ぐために必要になります。(参考:Markdown and code block indentation - nanoc | Google グループ)
CSSフレームワークのCompassとsusyを利用する
グリッドレイアウトのCSSフレームワークを使用することでクロスブラウザ対応のnカラムレイアウトを簡単に実現することができます。
グリッドレイアウトのフレームワークとしてはBlueprintが有名ですが、今回はよりシンプル?なsusyを利用してみました。(なおBlueprintが固定幅なのに対し、susyは可変幅という特徴があるようです)
Compass, susyのインストール
susyはCompassのプラグインとして実装されているため、先にCompassをインストールしておきます。 Compassではsassで利用できる便利なMix-inが多数定義されています。
> gem install compass
> gem install compass-susy-plugin
以下のコマンドでsusyを使ったCompassプロジェクトのひな形を生成しておきます。
> compass create myproject -r susy -u susy -x sass
Compass, susyの適用
nanocのサイトフォルダに「.compass」フォルダを作成し、上記の「compass create」で生成された「config.rb」をコピーし、設定をnanocに合わせ変更します。
# Require any additional compass plugins here.
require 'susy'
# Set this to the root of your project when deployed:
http_path = "/"
project_path = File.expand_path(File.join(File.dirname(__FILE__), '..'))
css_dir = "output/stylesheets"
sass_dir = "content/stylesheets"
images_dir = "content/images"
# You can select your preferred output style here (can be overridden via the command line):
output_style = :compressed
# To enable relative paths to assets via compass helper functions. Uncomment:
# relative_assets = true
# To disable debugging comments that display the original location of your selectors. Uncomment:
line_comments = false
preferred_syntax = :sass
また、「sass」フォルダにある「screen.sass」と「_base.sass」を「content/stylesheets」配下にコピーします。以降、スタイルは「screen.sass」に記述します。
Rulesに以下を追加することでCompassとsusyが利用可能になります。
require 'compass'
Compass.add_configuration "#{File.dirname(__FILE__)}/.compass/config.rb"
sass_options = Compass.sass_engine_options
compile '*' do
if item.binary?
# don't filter binary items
else
case item[:extension]
# ~~~中略~~~
when 'sass'
filter :sass, sass_options.merge(:syntax => item[:extension].to_sym)
end
end
end
susyのグリッドレイアウトは各要素に「full」「columns」「alpha(左寄せ)」「omega(右寄せ)」といったMix-inを加えることで定義します。
参考までに、当サイトのレイアウトは以下のようになっています。
#page
+container
#header
+full
#nav
+full
#section
+columns(8)
+alpha
#sidebar
+columns(4)
+omega
#side_left_nav
+columns(2,4)
+alpha(4)
#side_right_ad
+columns(2,4)
+omega(4)
#footer
+full
Bloggingヘルパ、Taggingヘルパを利用する
ブログに必要な「トップページ・サイドバーへの新着記事表示」「RSS配信」「アーカイブページ」「タグ機能」などを実装していきます。
「lib/default.rb」で以下のヘルパをincludeしておきます。なお、下記の一行目はRuby1.9のマジックコメント、末尾はraise_encoding_error対策です。
# coding: utf-8
include Nanoc::Helpers::Blogging
include Nanoc::Helpers::LinkTo
include Nanoc::Helpers::Tagging
Encoding.default_external = 'UTF-8'
Nanoc::Helpers::Blogging
Bloggingヘルパでは記事となるコンテンツに「kind: article」と「created_at: <タイムスタンプ>」の属性を加えることになっています。
各記事で毎回これらの属性を指定するのは面倒なので、「Rules」に以下を追加して「articles」フォルダ配下のコンテツに自動的に属性を設定するようにしました。(ファイル名の先頭を「YYYY-MM-DD」とする前提です)
preprocess do
articles = items.select {|item| item.identifier =~ %r|^/articles/.*/|}
articles.each do |item|
item.attributes[:kind] ||= "article"
item.attributes[:created_at] ||= item.identifier.match(/\d\d\d\d-\d\d-\d\d/).to_s
end
end
新着記事表示
トップページを表示する「content/index.haml」は以下のように記述します。当サイトでは記事本文のh3以降を「続きを読む」で表示するようにしています。
- sorted_articles[0, 6].each do |article|
%h2 #{link_to(article[:title], article.path)}
- if article_desc_match_data = article.compiled_content.match(/^.*?(?=<h3>)/m)
= article_desc_match_data.to_s
%p.readmore
= link_to("記事の続きを読む »", article.path)
- else
= article.compiled_content
サイドバーに新着記事のリストを表示するには「layouts/default.haml」で以下のように記述します。
%h2 Recent posts
%ul.recent_posts
- sorted_articles[0, 6].each do |article|
%li #{link_to(article[:title], article.path)}
RSS配信
RSS配信(Atom feedの生成)を行うためには追加でbuilderのgemが必要です。
> gem install builder
まず、「config.yaml」でAtom feedの生成に使われる属性を設定します。
base_url: http://n.blueblack.net
title: ナレッジエース
author_name: nase
author_uri: http://n.blueblack.net
次に「content/atom_feed.erb」を以下の内容で作成します。
<%= atom_feed :limit => 6 %>
また、「Rules」に以下のルールを追加します。
compile '/atom_feed' do
filter :erb
end
route '/atom_feed' do
'/atom_feed.xml'
end
最後に「layouts/default.haml」のhead内に以下を追加して完了です。
%link{:rel => "alternate", :type => "application/atom+xml", :title => "Atom feed", :href => "/atom_feed.xml"}/
アーカイブページ
過去記事を月毎の一覧で出力する方法を紹介します。(参考:Using nanoc to create a list of blog articles, sorted by month and year)
「lib/default.rb」に以下のメソッドを追加します
def grouped_articles
sorted_articles.group_by do |a|
[ Time.parse(a[:created_at]).year, Time.parse(a[:created_at]).month ]
end.sort.reverse
end
以下の内容で「content/archives.haml」を作成すれば月毎に記事一覧が表示されます。
%h2 記事一覧
- grouped_articles.each do |yearmonth, articles_this_month|
%h3 #{yearmonth.first}年#{yearmonth.last}月
%ul
- articles_this_month.each do |article|
%li
%span #{Time.parse(article[:created_at]).strftime("%d日")}
= link_to(article[:title], article.path)
Nanoc::Helpers::Tagging
Taggingヘルパの基本的な利用方法は「Getting Started」編の「Writing some Custom Code」で見たとおりです。
当サイトではNanoc のカスタマイズ - タグを管理するで公開されているタグ毎の記事数をカウントするメソッドを使わせていただいています。(「lib/default.rb」に以下を追記)
module TagUtil
def count_by_tag(items = nil)
items = @items if items.nil?
count_by_tag = Hash.new(0)
items.each do |item|
if item[:tags]
item[:tags].each do |tag|
count_by_tag[tag] += 1
end
end
end
count_by_tag
end
end
include TagUtil
「layouts/default.haml」は以下のようになります。
#article
- if @item[:kind] == 'article'
%h2 #{link_to(@item[:title], @item.path)}
%p.article_meta Created at: #{@item[:created_at]} Tags: #{tags_for(@item, {:base_url => "/tags/index.html#"})}
= yield
-#~~~中略~~~
#sidebar
-#~~~中略~~~
%h2 Tags
- tags = count_by_tag(@items)
- if tags
%ul
- tags.sort_by{|e| e[0]}.each do |k, v|
%li #{link_for_tag(k, "/tags/index.html#")} x #{v}
- else
%p (none)
生成するページ数を削減するためタグ毎の記事一覧はまとめて一つのページに出力し、ページ内のアンカーにリンクするようにしています。タグ毎の記事一覧となる「content/tags.haml」の内容は以下のとおりです。
%h2 タグ一覧
- tags = count_by_tag(@items)
- if tags
- tags.sort_by{|e| e[0]}.each do |k, v|
%h3{'id' => "#{k}"} #{link_for_tag(k, "/tags/index.html#")} x #{v}
%ul
- items_with_tag(k).sort_by{|e| e[:created_at]}.reverse.each do |item|
%li= link_to(item[:title], item.path)
- else
%p (none)
おわりに
予想以上に長くなってきたので「カスタマイズ」編は以上とします。書ききれなかった部分について、「Markdown独自拡張」編と「運用効率化バッチ」編として別途掲載します。
連載一覧
- nanoc導入メモ 1/5 「Getting Started」編
- nanoc導入メモ 2/5 「Basic Concepts」編
- nanoc導入メモ 3/5 「カスタマイズ」編
- nanoc導入メモ 4/5 「Markdown独自拡張」編
- nanoc導入メモ 5/5 「運用効率化バッチ」編