Nanoc のカスタマイズ - 年月毎の記事のアーカイブを用意する

Written by at 2010/07/25 16:12:45 [, ]

Nanoc で年月毎の記事のアーカイブを表示できるようにする。

年月毎のページ(Item)を取得し表示する

まずは、年月毎のページ(Item)を取得するためのヘルパーを用意する。

lib/helper.rb に以下のヘルパーを追加する。

lib/helper.rb:

def articles_by_year_month
  result = {}
  current_year = current_month = hash_of_year = items_of_the_month = nil

  sorted_articles.each do |article|
    d = Date.parse(article[:created_at])

    if current_year != d.year
      current_month = nil
      current_year = d.year
      hash_of_year = result[current_year] = {}
    end

    if current_month != d.month
      current_month = d.month
      items_of_the_month = hash_of_year[current_month] = []
    end

    items_of_the_month << article
  end
  result
end

def mm_str_month(month)
  sprintf("%02d", month)
end

articles_by_year_month により、{ 年 => { 月 => [Item の配列] } } というデータ構造の Hash が取得できるようになる。 より具体的には、{ 2010 => { 5 => [item1, item2], 6 => [item3, item4] } } となる。

上記で取得されるデータから、全ての記事の中から年月毎の記事の一覧を表示する部分を追加していく。

部分テンプレートとして、layouts/_articles_by_year_month.html ファイルを作成し、以下のコードを記述する。

layouts/_articles_by_year_month.html

<% articles_by_y_m = articles_by_year_month %>

<ul>
<% articles_by_y_m.keys.sort_by{|e| -e}.each do |year| %>
  <% articles_by_y_m[year].keys.sort_by{|e| -e}.each do |month| %>
    <li>
      <%= link_to("#{year}/#{mm_str_month(month)}", "/archives/#{year}/#{mm_str_month(month)}/") %>
      (<%= articles_by_y_m[year][month].size %>)
    </li>
  <% end %>
<% end %>
</ul>

上記の部分テンプレートを表示するために、layouts/default.html に記載されているサイドバー部分に表示部分を追加しておく。

<div id="sidebar-wrapper">
  <h2>Archives</h2>
    <%= render('_articles_by_year_month') %>
  ...

これで、記事の年月とその件数、年月毎の記事一覧を表示するためのリンク先(e.g. /archives/2010/06/)までは表示できるようになった。このサイトでいうと、以下の部分。

Nanoc Archives By Year Month

次に、記事一覧を表示するためのページを用意する。

年月毎の記事一覧を表示するためのページ(Item)を動的に生成する

同じタグを持つページの一覧を表示する際に実施した方法(Nanoc のカスタマイズ - タグを管理する)で年月毎の記事一覧も用意することにする。

RulespreprocessItem を動的に生成することにする。

ロジック自体は、helper に切り出しておく。

lib/helper.rb:

def create_year_month_archive_pages
  articles_by_year_month.each do |year, month_hash|
    month_hash.each do |month, articles|
      month_str = sprintf("%02d", month)
      items << ::Nanoc3::Item.new(
        "<%= render('_archive_by_year_month', :year => #{year}, :month => #{month}) %>",
        { :title => "Archives: #{year}/#{month_str}", :is_hidden => true },
        "/archives/#{year}/#{month_str}/",
        :binary => false
      )
    end
  end
end

上記の Item インスタンス生成の際に使用している部分テンプレート _archive_by_year_month は、以下の内容となる。

layouts/_articles_by_year_month.html:

<h2>Archive: <%= year %>/<%= mm_str_month(month) %></h2>

<ul>
<% articles_by_year_month[year][month].each do |article| %>
  <li>
    <%= link_to(article[:title], article.path) %><br />
    <span class="meta">
          <%= render('_summary_meta_info', :item => article) %>
    </span>
  </li>
<% end %>
</ul>

Rulespreprocess メソッドのブロックに以下の記載を追加し、年月毎のページの一覧を表示するための Item を動的に生成する。

preprocess do
  ...
  create_year_month_archive_pages
end

以上で完了となる! このサイトでいうと、以下の部分。

Nanoc Arhive Page

参考サイト

関連記事

  1. 静的な Web サイト生成ツール Nanoc 基本編 - まずは試してみる
  2. Nanoc のカスタマイズ - 最近の記事一覧の表示
  3. Nanoc のカスタマイズ - タグを管理する
  4. Nanoc のカスタマイズ - 年月毎の記事のアーカイブを用意する

Nanoc のカスタマイズ - タグを管理する

Written by Taka at 2010/07/25 15:32:45 [, ]

Nanoc でページに付加したタグを管理できるようにする。

ページをタグで管理する

ページをタグ付けして管理する。

やろうとしていることは以下の通り。

  1. 個別のページ(article)で、タグ付けされているタグの名称を表示する
  2. タグの一覧を表示する
    • タグ名とそのタグ付けがされているページの件数を表示
    • タグ名は、同じタグを持つページ一覧へのリンクを持つ
  3. 同じタグを持つページ一覧を表示する
    • 一覧にはページのタイトルが表示される

タグ自体は、各ページ(item)のメタデータで定義をしている。

 --- 
 title: Nanoc のカスタマイズメモ
 tags: [nanoc, ruby]
 created_at: 2010/07/25 15:32:45
 kind: article
 excerpt: Nanoc のカスタマイズメモ
 publish: true
 ---

定義しているタグを表示させるには、標準で用意されているヘルパー Nanoc3::Helpers::Tagging を使う。

以下のメソッドが利用できるようになる。

  • (Array) items_with_tag(tag)
    引数で与えられたタグ名でタグ付けされている全ての item を返す
  • (String) link_for_tag(tag, base_url)
    引数で与えられたタグのリンクを返す。
    例えば、link_for_tag("ruby", "/tags/")という呼び出しを行うと、 <a rel="tag" href="/tags/ruby">ruby</a> という html を返す。
  • (String) tags_for(item, params = {})
    引数で与えられた item にタグ付けされたタグを特定の書式のリストで返す。
    params = {} に指定するオプションとしては、base_url(default: “http://technorati.com/tag/”)、 none_text(default: “(none)”)、separator(default: “, “) がある。

また、過去触れていたが、Nanoc にはタグ関連だけでなく、他にもいろいろな便利な helper メソッドが用意されており、 ここでは、以下の3つのヘルパーが include されていることを前提とする。

include Nanoc3::Helpers::Blogging
include Nanoc3::Helpers::Tagging
include Nanoc3::Helpers::Rendering
include Nanoc3::Helpers::LinkTo

ただ、これからやろうとしていることには、標準の helper メソッドだけでは不足するものあるので、必要なものは随時追加していく。

1. 個別のページで、タグ付けされているタグの名称を表示する

これは、既存の Nanoc3::Helpers::Tagging.tags_for(item, params = {}) を使うことで実現できる。

<%= tags_for(item, { :base_url => '/tags/', :separator => ', ' }) %>

上記は、

<a rel="tag" href="/tags/nanoc">nanoc</a>
,
<a rel="tag" href="/tags/ruby">ruby</a>

と展開される。このサイトでいうと、以下の部分。

Nanoc tags_for

2. タグの一覧を表示する

これを実現するためのユーティリティは標準で用意してくれても・・・と思ったが、無さ気なので用意する。

サイト内のページで定義しているタグ名と、そのタグ付けがされているページの件数をハッシュで返すメソッドを用意する。

lib ディレクトリ配下に、tag_util.rb を用意し、以下の記述を行う。

lib/tag_util.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

上記で記載した TagUtil.count_by_tag と、Nanoc3::Helpers::Tagging.link_for_tag を使って、表示部分を記述する。

表示部分は、部分テンプレートで用意する。layouts ディレクトリに、_tag_summary.html を用意し、以下の記述を行なう。

layouts/_tag_summary.html:

<% tags = count_by_tag(@items) %>

<% if tags %>
  <ul>
  <% tags.sort_by{|e| e[0]}.each do |k, v| %>
    <li><%= link_for_tag(k, "/tags/") %> x <%= v %></li>
  <% end %>
  </ul>
<% else %>
  <p>(none)</p>
<% end %>

上記の部分テンプレートを表示するために、layouts/default.html に記載されているサイドバー部分に表示部分を追加しておく。

<div id="sidebar-wrapper">
  <h2>Tags</h2>
    <%= render('_tag_summary') %>
  ...

これで、「タグ名とそのタグ付けがされているページの件数を表示」「タグ名は、同じタグを持つページ一覧へのリンクを持つ」ことができた。このサイトでいうと、以下の部分。

Tag Summary

次に同じタグを持つページ一覧を表示するページを用意する。

3. 同じタグを持つページの一覧を表示する

タグは、ページを作成する際に、メタデータとして

tags: [nanoc]

という風に書いていく。新なタグを追加する度にそのタグが付けられているページの一覧を表示するためのページをマニュアル(手動)で作成していくのは現実的ではない。

ここでは、動的に on memory でタグ付けされたページの一覧を表示するためのページ(Item)を作成するようにする。

Rules には、preprocess というメソッドに、 全てのデータがロードされた後で、かつ、サイト(Site)がコンパイルされる前に実行されるブロックを記述することができる。

ここで、タグ付けされたページの一覧を表示する Item を動的に作成するようにする。

まず、別出しでメソッドを用意しておく。lib/helper.rb に以下のコードを追加する。

lib/helper.rb:

def create_tag_pages
  tag_set(items).each do |tag|
    items << ::Nanoc3::Item.new(
      "<%= render('_tag_page', :tag => '#{tag}') %>",
      { :title => "Tags: #{tag}", :is_hidden => true },
      "/tags/#{tag}/",
      :binary => false
    )
  end
end

上記の tag_set というのは、全ての Item から使用されているタグの名称を配列で取得するヘルパーメソッドである。これは、lib/tag_util.rb に追加している。

lib/tag_util.rb:

module TagUtil
  def tag_set(items = nil)
    require 'set'

    items = @items if items.nil?
    tags = Set.new
    items.each do |item|
      next if item[:tags].nil?
      item[:tags].each { |tag| tags << tag }
    end
    tags.to_a
  end
  ...

render で使用しているパーシャルページは以下の内容になる。

layouts/_tag_page.html:

<h2>Contents tagged with "<%= tag %>"</h2>

<% if taged_items = items_with_tag(tag) %>
  <ul>
    <% taged_items.sort_by{|e| e[:created_at]}.reverse.each do |item| %>
      <li>
        <%= link_to(item[:title], item.path) %><br />
        <span class="summary-meta-info">
          <%= render('_summary_meta_info', :item => item) %>
        </span>
      </li>
    <% end %>
  </ul>
<% else %>
  <p>none</p>
<% end %>

Rulespreprocess メソッドのブロックに以下の記載を追加し、タグ付けされたページの一覧を表示するための Item を動的に生成する。

Rules:

preprocess do
  ...
  create_tag_pages
end

以上で完了となる!

このサイトでいうと、以下の部分。

Nanoc Tag Page

参考サイト

関連記事

  1. 静的な Web サイト生成ツール Nanoc 基本編 - まずは試してみる
  2. Nanoc のカスタマイズ - 最近の記事一覧の表示
  3. Nanoc のカスタマイズ - タグを管理する
  4. Nanoc のカスタマイズ - 年月毎の記事のアーカイブを用意する

Nanoc のカスタマイズ - 最近の記事一覧の表示

Written by Taka at 2010/07/25 13:01:31 [, ]

Nanoc で最近の記事一覧を表示できるようにしてみる。このサイトでいうと、以下の部分。

Recent Articles

標準で用意されているヘルパーを活用する

Nanoc には、標準で幾つかのヘルパーが用意されており、これからやろうとしていることは、そのヘルパーを Mix-In することで容易に実現できる。

上記のヘルパーを利用するためには、事前にヘルパーを include しておく必要がある。

lib 配下の Ruby スクリプトはコンパイル時に全てロードされるので、適当なファイルで必要な Module を include しておけばよい。
ここでは、lib/default.rb にこれから使用する3つのヘルパーを include しておく。

lib/default.rb:

include Nanoc3::Helpers::Blogging
include Nanoc3::Helpers::Rendering
include Nanoc3::Helpers::LinkTo

それぞれ以下の役目を持つヘルパーとなる。

  • Blogging Helper (Module: Nanoc3::Helpers::Blogging)
    • ブログを構築する際に役立つ機能を集めたもの
    • このヘルパーで扱われるページの前提
      • ページ(Item) のメタデータ kind"article" が設定されていること
      • ページ(Item) のメタデータ created_at に発行日となるタイムスタンプを持っていること。 created_at には、Time.parse で処理できるフォーマットで記載を行う。
  • Rendering Helper (Module: Nanoc3::Helpers::Rendering)
    • 部分テンプレートとしてレイアウトを描画する。
    • Rails を扱ったことのある方であれば、Rails のテンプレートを表示する際の render メソッド、と言えばピンとくるかもしれない。
  • LinkTo Helper (Module: Nanoc3::Helpers::LinkTo)
    • リンクの生成のためのヘルパー

Blogging Helper を利用するためのページのメタデータの例は以下のようなもの。

--- 
title: Nanoc Customize
tags: [nanoc]
created_at: 2010/06/24 21:30:31
kind: article
publish: true
---

サイドバーに直近10件の記事一覧を表示させる

まずは、記事一覧用の部分テンプレートとして、layouts/_article_list.html を用意して、以下を記述しておく。

<ul>
<% sorted_articles[0, 10].each do |article| %>
  <li>
    <%= link_to(article[:title], article.path) %>
    <span class="meta">(<%= article[:created_at] %>)</span>
  </li>
<% end %>
</ul>

上記は、表示させたい記事の配列を取得するために、Nanoc3::Helpers::Blogging#sorted_articles を利用し、ページのメタデータの kind“article” となっているページを created_at の降順にソートされたリストを10件表示する。

それぞれのリストの要素は、ページのタイトルを表示し、そのページへのリンクを持つ。

layouts/default.html でサイドバーを表示しているスペースに上記の部分テンプレートの表示部分を記載する。

<div id="sidebar-wrapper">
  <h2>Recent Entries</h2>
    <%= render('_article_list') %>
  ...

以上で、本サイトのサイドバーの Recent Articles のような記事一覧の表示が追加される。

関連記事

  1. 静的な Web サイト生成ツール Nanoc 基本編 - まずは試してみる
  2. Nanoc のカスタマイズ - 最近の記事一覧の表示
  3. Nanoc のカスタマイズ - タグを管理する
  4. Nanoc のカスタマイズ - 年月毎の記事のアーカイブを用意する

静的な Web サイト生成ツール Nanoc 基本編 - まずは試してみる

Written by Taka at 2010/07/19 12:55:25 [, ]

Nanoc とは?

Nanoc とは、静的な Web サイトを生成するための Ruby で書かれたコンテンツビルドツールである。

自身の好みのフォーマットで記載した文書を他のフォーマット(標準では HTML)に変換して出力してくれる。

それだけではない。

作成した文書群を1つの文書の集合体(所謂サイト)として管理することができる。 結果として作成されるコンテンツは静的だが、その静的なコンテンツを作成する段階で、最近の文書の一覧、タグ付けされたページの一覧など、文書の属性に合わせたページを動的に作成することができる

コンテンツマネージメントシステム(CMS)のようだが、一般的に言われる CMS がサーバ側で動作するのに対し、Nanoc はローカルの普通のパソコン上で動作する。
Ruby を入れて、Nanoc をインストールしてしまえば、Apache や Web Application サーバなど特別なものをインストールする必要はない。 (Nanoc で使用するプレビュー用の簡易 Web サーバは別途ライブラリとしてインストールする。)

サーバで公開しようと思えば、生成された静的コンテンツをまとめてサーバにアップロードするだけでよい。静的なコンテンツなので、サーバ側に特別な Web Application サーバなどは必要ない。普通のレンタルサーバなどで公開することができる。

さらにうれしいこととしては、Ruby を知っている人であれば、コンテンツの生成時に利用する filter、helper などを必要な要件に合わせて自作し、拡張していくことができる。
また、汎用的によく使われるであろう filter、helper の機能は既に用意されていたりするので、out-of-the-box で大概のことはできるようになっている。

このサイトも元々 Mephisto という Ruby On Rails ベースの CMS で管理していたが、Nanoc に切り替えてみた。

Nanoc を使ってみる

インストール

Ruby が入っていない場合は、Ruby をインストールしておく。また、Nanoc は RubyGems というパッケージ管理ツールでインストールすることになるので、RubyGems も入れておく必要がある。
ここでは、Ruby、RubyGems のインスール方法についての記載は割愛する。詳細は以下のサイトなどを参照。

上記が既にインストールされていることを前提で、gem コマンドを使って Nanoc をインストールする。

$ sudo gem install nanoc

Nanoc には、作成されたサイトを確認するためのプレビュー機能がある。これを利用するためには、adsf というライブラリを必要とするので、これも gem でインストールしておく。

$ sudo gem install adsf

この文書を書いている環境は以下の通り。

$ nanoc -v
nanoc 3.1.3 (c) 2007-2010 Denis Defreyne.
Ruby 1.8.6 (2010-01-11) running on i686-darwin9.8.0 with RubyGems 1.3.7

新規サイトの作成

適当なディレクトリで、nanoc コマンドの create_site コマンドを使って新規のサイトを作成する。

$ nanoc create_site dr_blog
      create  config.yaml
      create  Rakefile
      create  Rules
      create  content/index.html
      create  content/stylesheet.css
      create  layouts/default.html
Created a blank nanoc site at 'dr_blog'. Enjoy!
$ cd dr_blog
$ ll
total 24
-rw-r--r--  1 taka  staff    22  6 15 11:22 Rakefile
-rw-r--r--  1 taka  staff   692  6 15 11:22 Rules
-rw-r--r--  1 taka  staff  2082  6 15 11:22 config.yaml
drwxr-xr-x  4 taka  staff   136  6 15 11:22 content
drwxr-xr-x  3 taka  staff   102  6 15 11:22 layouts
drwxr-xr-x  3 taka  staff   102  6 15 11:22 lib
drwxr-xr-x  2 taka  staff    68  6 15 11:22 output

上記は、dr_blog という名前のサイトを作成している。コマンドを発行したディレクトリ配下にコンテンツが作成されている。

content ディレクトリ配下に文書を作成していくことになる。作成したコンテンツから静的サイトを作成するには、nanoc compile コマンドを使用する。
先程のコマンド実行時に既に雛形的なコンテンツが用意されているので、コンパイルして現在の状況をプレビューで確認してみる。

$ nanoc compile
Loading site data...
Compiling site...
      create  [0.00s]  output/style.css
      create  [0.03s]  output/index.html

Site compiled in 0.05s.

content ディレクトリ存在するコンテンツがコンパイルされて、output ディレクトリに出力される。

プレビューを確認するためのサーバを起動する。

$ nanoc view

デフォルトでは、localhost の 3000 ポートで待ち受けているので、ブラウザの URL 欄に、http://localhost:3000/ と入力する。

ブラウザで以下の画面が確認できたら、ここまでの作業は順調に進んでいることになる。

Nanoc Preview

コンテンツを作成していく作業は、

  • コンテンツを書く
  • コンパイルする (nanoc compile)
  • 生成されたコンテンツを確認する (nanoc view)

という流れになる。

コンテンツの作成

試しに、1コンテンツ作成してみる。

先程のプレビュー画面で見た画面のコンテンツ(Nanoc では、Item と呼んでいる)は、content/index.html に記載されている。

--- 
title: Home
---
<h1>A Brand New nanoc Site</h1>

<p>You've just created a new nanoc site. The page you are looking at right now is the home page for your site. To get started, consider replacing this default homepage with your own customized homepage. Some pointers on how to do so:</p>

<ul>
  <li><p><strong>Change this page's content</strong> by editing the "index.h%tml" file in the "content" directory. This is the actual page content, and therefore doesn't include the header, sidebar or style information (those are part of the layout).</p></li>
  <li><p><strong>Change the layout</strong>, which is the "default.html" file in the "layouts" directory, and create something unique (and hopefully less bland).</p></li>
</ul>

<p>If you need any help with customizing your nanoc web site, be sure to check out the documentation (see sidebar), and be sure to subscribe to the discussion group (also see sidebar). Enjoy!</p>

------ で囲まれている領域は、コンテンツ(Item)のメタデータ(もしくは、属性)と呼ばれるものである。記法は YAML のフォーマットとなっている。このセクションは生成されるコンテンツにはそのままは表示されない部分となる。

content ディレクトリ配下にファイルを作成していけば、それがそのままコンテンツになるのだが、ここでは、Nanoc の用意している create_item コマンドを使う方法を試してみる。

ちなみに、先程から「コンテンツ」という言葉に Item という表記を入れていたが、Nanoc では1つのコンテンツは、1つの Item として管理されている。

hello-world というコンテンツを作成する。

$ nanoc create_item hello-wold
      create  content/hello-wold.html
An item has been created at /hello-wold/.

content ディレクトリ配下に作成された hello-wold.html の内容は以下の通り。

--- 
title: A New Item
---
Hi, I'm a new item!

以下のように書き換えて保存する。

--- 
title: Hello World!!
---
<h2>This is ths first page. "Hello World"</h2>

コンパイルする。

$ nanoc compile
Loading site data...
Compiling site...
      create  [0.02s]  output/hello-wold/index.html
        skip  [0.00s]  output/style.css
        skip  [0.00s]  output/index.html

Site compiled in 0.03s.

新たに output/hello-wold/index.html が生成されている。URL に http://localhost:3000/hello-world/ と入力し、ブラウザで表示を確認する。

Nanoc Hello World

ほんとの触りの部分だけの紹介だが、Nanoc は拡張性が高く便利なツールだ。

関連記事

  1. 静的な Web サイト生成ツール Nanoc 基本編 - まずは試してみる
  2. Nanoc のカスタマイズ - 最近の記事一覧の表示
  3. Nanoc のカスタマイズ - タグを管理する
  4. Nanoc のカスタマイズ - 年月毎の記事のアーカイブを用意する

Rails のコンローラのフィルタのようなものを実装してみる

Written by Taka at 2009/12/31 19:42 []

Rails のコントローラで利用できるフィルターのような機能を Rails ではないアプリに組み込みたく、実装してみることにした。

ちなみに、Rails のコントローラ内で利用できるフィルタには、以下のものがある。

  • before_filter: アクションの前に実行
  • after_filter : アクションの後に実行
  • around_filter: アクションの前後で実行

あまりよい例を出せないのだが、現在以下のクラスが既に実装されているものとする。

class Greeting
  def initialize(out = STDOUT)
    @out = out
  end

  def hello
    @out.puts 'hello!'
  end
end

Rails のフィルターのように上記の hello メソッドの前後で実行するための機能をClass Macroで追加できるようにしたい。 実装イメージとしては、以下のような感じ。

class Greeting
  ...
  before_filter :before_greeting
  after_filter :after_greeting

  ...

  private
  def before_greeting
    @out.puts 'Ya!'
  end

  def after_greeting
    @out.puts 'bye!'
  end
end

Greeting.new.hello
# >> Ya!
# >> hello!
# >> bye!

通したいテストは以下のような感じ。

require 'test/unit'

class GreetingTest < Test::Unit::TestCase
  def setup
    @out = StringIO.new
    @greeting = Greeting.new(@out)
  end

  def test_running_filters
    @greeting.hello
    assert_equal ['Ya!', 'hello!', 'bye!'], @out.string.split("\n")
  end
end

やることは、2つ。

  • 既に実装されているGreeting#helloメソッドには手を入れないで機能を追加する。
  • 前後に呼び出す機能をコールバックとして定義し、実行できるようにする。

全て1から実装するのはシンドイので。。なにかと便利な ActiveSupport を使う。

ActiveSupport には、ActiveSupport::Callbacks というモジュールがあり、Rails のコントローラのフィルタでもこのモジュールが利用されている。拝借する。

まずは、前後に挟む機能を定義するためのフィルタの準備。モジュールで準備する。

require 'rubygems'
require 'active_support'

module Filters
  def self.included(base)
    base.class_eval do
      include ActiveSupport::Callbacks

      define_callbacks :before_filter, :after_filter
    end
  end
end

このモジュールは、利用するクラスでインクルードされると、

  • ActiveSupport::Callbacks モジュールをインクルードする。
  • ActiveSupport::Callbacks モジュールで定義されている define_callbacks メソッドで :before_filter:after_filter をコールバックメソッドとして定義する。

ということを行う。

これで、Greeting クラスで Filters モジュールをインクルードすれば、before_filterafter_filter でコールバックメソッドが定義できるようになる。

後は、Greeting クラスの中で、それぞれのコールバックされるメソッドを記述し、コールバックメソッドの呼び出しを行なわないといけない。

Open ClassGreeting クラスを再定義し、Around Alias で既にある hello メソッドに機能を追加する。

class Greeting
  include Filters

  before_filter :before_greeting
  after_filter :after_greeting

  def hello_with_filters
    run_callbacks(:before_filter)
    hello_without_filters
    run_callbacks(:after_filter)
  end

  alias_method :hello_without_filters, :hello
  alias_method :hello, :hello_with_filters

  private
  def before_greeting
    @out.puts 'Ya!'
  end

  def after_greeting
    @out.puts 'bye!'
  end
end

最終的に1つのファイルに以下のように記述している。

filter_example1.rb:

class Greeting
  def initialize(out = STDOUT)
    @out = out
  end
  
  def hello
    @out.puts 'hello!'
  end
end

require 'rubygems'
require 'active_support'

module Filters
  def self.included(base)
    base.class_eval do
      include ActiveSupport::Callbacks

      define_callbacks :before_filter, :after_filter
    end
  end
end

# Open Greeting class
class Greeting
  include Filters

  before_filter :before_greeting
  after_filter :after_greeting

  def hello_with_filters
    run_callbacks(:before_filter)
    hello_without_filters
    run_callbacks(:after_filter)
  end

  alias_method :hello_without_filters, :hello
  alias_method :hello, :hello_with_filters

  private
  def before_greeting
    @out.puts 'Ya!'
  end

  def after_greeting
    @out.puts 'bye!'
  end
end

# test
require 'test/unit'

class GreetingTest < Test::Unit::TestCase
  def setup
    @out = StringIO.new
    @greeting = Greeting.new(@out)
  end

  def test_running_filters
    @greeting.hello
    assert_equal ['Ya!', 'hello!', 'bye!'], @out.string.split("\n")
  end
end

テストを実行。

$ ruby filter_example1.rb
Loaded suite filter_example1
Started
.
Finished in 0.000389 seconds.

1 tests, 1 assertions, 0 failures, 0 errors

OK だ。

このくらいのことであれば、AspectR を使うこともできるが、より自由な組み込みができそう。