標準添付ライブラリ紹介 【第 10 回】 ERB

書いた人:西山

はじめに

Ruby には便利な標準添付ライブラリがたくさんありますが、なかなか知られていないのが現状です。そこで、この連載では Ruby の標準添付ライブラリを紹介していきます。

今回は、ERB について紹介します。

eRuby, eruby, ERb, ERB

まず用語について説明します。

厳密には以下のような使い分けが可能ですが、(Perl と perl の使い分けと違って) 大文字で始まる Ruby と小文字で始まる ruby が使い分けられていないのと同じように、使い分けにはあまりこだわらなくても構いません。

eRuby
任意のテキストファイルに Ruby スクリプトを埋め込む書式の仕様
eruby
前田修吾さん作の eRuby の C による実装
ERB
関将俊さん作の eRuby の Ruby による実装
ERb
ERB が標準添付になる前のバージョン (ERb と ERbLight があった)

eRuby

eRuby では任意のテキストファイルに Ruby スクリプトを埋め込めます。 基本的には、次のマークアップ (eRuby タグ) を使って Ruby スクリプトを埋め込みます。

<% … %>
Ruby スクリプト片をその場で実行
<%= … %>
式を評価した結果をその場に挿入
<%# … %>
eRuby のコメント (ERB#src (後述) にも埋め込まれない)
<%%
「<%」をその場に挿入 (「<% … %>」の中ではそのまま)
%%>
「<% … %>」などの中で「%>」になる (「<% … %>」の外では「%%>」のまま)
% で始まる行
ERB で trim_mode (後述) が ‘%’ の時、また、eruby ではいつでも、Ruby スクリプト片をその場で実行
%% で始まる行
ERB で trim_mode (後述) が ‘%’ の時、また、eruby ではいつでも、% で始まる行になる

その他の部分 (地の文) はそのまま出力されます。

eRuby は HTML や XML に限らず、任意のテキストファイルの出力に使用できます。

ERB

では、eRuby を実際に使うためのライブラリである ERB について解説します。

基本的な使い方

ERB の一番基本的な使い方では、eRuby スクリプトを ERB.new の第一引数に指定して ERB オブジェクトを生成して、result メソッドで結果を文字列で取得します。

例:

require 'erb'
str = "hoge"
erb = ERB.new("value = <%= str %>")
puts erb.result(binding)

出力例:

value = hoge

あまり良い例ではありませんが、ここでは例としてかけ算の九九の表を生成するスクリプトを作ってみました。

require 'erb'
puts ERB.new(DATA.read).result(binding)
__END__
 <% (1..9).each do |y| %>  <%= y %><% end %>
<%
(1..9).each do |x|
%><%= x %><%
  (1..9).each do |y|
%><%= sprintf '%3d', x*y %><%
  end
%>
<% end %>

出力例:

   1  2  3  4  5  6  7  8  9
1  1  2  3  4  5  6  7  8  9
2  2  4  6  8 10 12 14 16 18
3  3  6  9 12 15 18 21 24 27
4  4  8 12 16 20 24 28 32 36
5  5 10 15 20 25 30 35 40 45
6  6 12 18 24 30 36 42 48 54
7  7 14 21 28 35 42 49 56 63
8  8 16 24 32 40 48 56 64 72
9  9 18 27 36 45 54 63 72 81

まず 1 行目で ERB を使うために erb ライブラリを require しています。

次に DATA.read で END 以降の内容を読み込んでいます。 ここでは例を簡単にするために Ruby スクリプトの末尾に END を使って eRuby スクリプトを埋め込んでいますが、普通は別ファイルから読み込みます。

そして読み込んだ eRuby スクリプトを使って ERB オブジェクトを生成して、result メソッドを使って結果の文字列を取得しています。 result メソッドの引数に組み込み関数 binding を使って取得した Binding オブジェクトを指定しています。

最後に結果の文字列を puts を使って出力しています。

ERB#run

ERB#run は ERB#result の結果をそのまま標準出力に出力します。 結果の文字列を加工したり、他の処理に利用したりする場合は ERB#result を使う必要がありますが、出力するだけなら ERB#run が使えます。

先ほどの例ではそのまま出力するだけだったので、ERB#result の値を puts する代わりに ERB#run を使っても同じ出力が得られます。

END 以降と出力例は同じなので省略します。

require 'erb'
ERB.new(DATA.read).run(binding)
__END__

ERB#src

ERB#src は eRuby を Ruby スクリプトに変換したソースを返します。 Ruby スクリプトに変換した結果をキャッシュしたり、eval で実行したりすることが出来ます。

上記の例のように Ruby スクリプトに直接 eRuby を埋め込んだ場合、eRuby 部分で例外が発生すると実際のファイル名と行番号とは違っていて、エラーの場所を特定しにくくなります。 そういう場合は ERB#src と eval を使って、ファイル名と行番号を指定することによってエラーの場所がわかりやすくなります。

たとえば、

#!/usr/bin/ruby -Ke
require 'erb'
now = Time.now
puts eval(ERB.new(DATA.read).src, binding, __FILE__, __LINE__+2)
__END__
今日は<%= now.stftime("%Y-%m-%d") %>です。

というスクリプトなら

e.rb:6: undefined method `stftime' for Sat Nov 11 12:09:15 JST 2006:Time (NoMethodError)
        from e.rb:4

のようになります。

ERB#filename, ERB#filename=

attr_accessor で定義されている ERB#filename, ERB#filename= を使って、エラーメッセージ用のファイル名の取得や設定が出来ます。

ファイル名を変更するだけなら ERB#src で取得した Ruby スクリプトを eval する代わりに ERB#filename= が使えます。

使用例:

#!/usr/bin/ruby -Ke
require 'erb'
fname = ARGV.shift
erb = ERB.new(File.read(fname))
erb.filename = fname
puts erb.result(binding)

binding 引数

ローカル変数やインスタンス変数を eRuby スクリプトの中で使いたい場合は、適切な場所の Binding オブジェクトを ERB#result や ERB#run に指定する必要があります。

どこで実行しても同じ結果になるスクリプトの場合は result メソッドの引数の binding を省略してデフォルトの TOPLEVEL_BINDING でも構いません。 上記の九九の例の場合は x と y というローカル変数を使っているので、eRuby スクリプトの実行前にローカル変数 x または y を使っていると、どちらも 9 に変わってしまいます。

END 以降は同じなので省略します。

require 'erb'
x = y = nil
ERB.new(DATA.read).result(binding)
p x, y
__END__

出力例:

9
9

trim_mode

先ほどの例の eRuby スクリプトでは余分な改行や空白が入らないようにするために「<%」や「%>」などの位置がわかりにくくなっています。

HTML の場合は改行の位置や空白などは気にせずに書いても、ブラウザで表示させたときに連続する空白は単独の空白と同じ扱いになるので気にならないのですが、HTML を直接見た場合には気になってしまいます。

そこで ERB には trim_mode という機能がついています。

trim_mode = ‘-‘

’-‘ という trim_mode では、以下の違いがあります。

<%-
(「<%-」のインデントに使われている) 行頭の空白文字を削除
-%>
直後の改行を出力しない

「<%-」と行頭の間に空白以外の文字があったり、「-%>」と改行の間に (空白も含めて) 他の文字があったりすると、「<%」や「%>」と同じ挙動になります。

trim_mode の使用例

先ほどの九九の例を、ここでは Rails でデフォルトとして使われている ‘-‘ という trim_mode を使って書き直してみます。

出力例は同じなので省略します。

require 'erb'
puts ERB.new(DATA.read, nil, '-').result(binding)
__END__
 <% (1..9).each do |y| %>  <%= y %><% end %>
<%- (1..9).each do |x| -%>
<%= x -%>
  <%- (1..9).each do |y| -%>
<%= sprintf '%3d', x*y -%>
  <%- end %>
<%- end -%>

「<%」(または「「<%=」) と「%>」の対応がそれぞれの行に収まっていたり、 内側のループがインデント出来たりして、最初の例より読みやすく出来ています。

読みやすく出来るようになったところで HTML (の一部) を出力するように変更してみましょう。

require 'erb'
puts ERB.new(DATA.read, nil, '-').result(binding)
__END__
<table>
  <tr>
  <%- (1..9).each do |y| -%>
    <th><%= y %></th>
  <%- end -%>
  </tr>
<%- (1..9).each do |x| -%>
  <tr>
    <th><%= x %></th>
  <%- (1..9).each do |y| -%>
    <td><%= x*y %></td>
  <%- end -%>
  </tr>
<%- end -%>
</table>

出力例:

<table>
  <tr>
    <th>1</th>
    <th>2</th>
    <th>3</th>
    <th>4</th>
    <th>5</th>
    <th>6</th>
    <th>7</th>
    <th>8</th>
    <th>9</th>
  </tr>
  <tr>
    <th>1</th>
    <td>1</td>
    <td>2</td>
    <td>3</td>
    <td>4</td>
    <td>5</td>
    <td>6</td>
    <td>7</td>
    <td>8</td>
    <td>9</td>
  </tr>
  <tr>
    <th>2</th>
    <td>2</td>
    <td>4</td>
    <td>6</td>
    <td>8</td>
    <td>10</td>
    <td>12</td>
    <td>14</td>
    <td>16</td>
    <td>18</td>
  </tr>
(中略)
  <tr>
    <th>9</th>
    <td>9</td>
    <td>18</td>
    <td>27</td>
    <td>36</td>
    <td>45</td>
    <td>54</td>
    <td>63</td>
    <td>72</td>
    <td>81</td>
  </tr>
</table>

長いので途中を省略しましたが、eRuby スクリプトも出力結果も読みやすく出来るようになりました。

trim_mode = ‘%’

trim_mode に ‘%’ を指定すると「%」で始まる行を「<% … %>」と同じように Ruby スクリプトとして実行します。その行の改行は出力しません。

require 'erb'
puts ERB.new(DATA.read, nil, '%').result(binding)
__END__
% require 'mathn'
<%= 1/2 + 1/3 %>

出力例:

5/6

その他の trim_mode

’-‘ と ‘%’ の他に改行の扱いを変える ‘>’ と ‘<>’ があります。

‘>’ は行末が「%>」の時に改行を出力しません。 すべての「%>」が trim_mode が ‘-‘ の「-%>」相当になるようなものです。 「%>」と改行 (“\n”) の間に空白を含めて他の文字が入っていると改行が出力されます。

‘<>’ は行頭が「<%」かつ行末が「%>」の行の改行を出力しません。 「<% end %>」とそれに対応する「<% if … %>」や「<% ….each do %>」などの改行を出力したくない時に使えます。 行頭と行末の両方を見て改行を出力するかどうかを決めているので、trim_mode が ‘-‘ の時の「<%- … -%>」と違って、行頭がインデントされていると行頭の空白も改行も出力されてしまいます。

trim_mode のまとめ

まとめると以下のようになります。

nil または ‘’ または 0
(デフォルト)
‘>’ または 1
行末が「%>」の時に改行を出力しない
‘<>’ または 2
行頭が「<%」で行末が「%>」の時に改行を出力しない

’-‘:行頭が「<%-」の時に (インデントに使われている) 行頭の空白文字を削除、行末が「-%>」の時に直後の改行を出力しない ‘%’:「%」で始まる行を「<% … %>」と同じように Ruby スクリプトとして実行して改行は出力しない ‘%>’ または ‘>%’:’>’ と ‘%’ の両方 ‘%<>’ または ‘<>%’:’<>’ と ‘%’ の両方 ‘%-‘:’-‘ と ‘%’ の両方

改行文字は “\n” のみを想定しているため、行末を見て改行を出力しないようになっているものは、改行が “\n” ではなく “\r\n” になっている場合も改行が出力されてしまいます。

具体的には、’>’ または ‘<>’ の時に “<% … %>\r\n” となっている、または ‘-‘ の時に “… -%>\r\n” になっていると、行末に “\r” が入っていることになってしまって、改行が出力されてしまいます。

ERB.new のその他の引数

safe_level

ERB.new の第 2 引数で eRuby スクリプトが実行されるときのセーフレベルを指定することが出来ます。CGI の中で使われる時は 1 が指定されることが多いようです。

safe_level を気にしない場合は nil を指定します。

間違えて 0 や今のセーフレベルを指定すると、ERB#result が呼ばれたときのセーフレベルで実行可能な内容だったとしても、実行前にセーフレベルを変更しようとして SecurityError になってしまいます。

require 'erb'
erb = ERB.new('', $SAFE)
$SAFE = 1
erb.run

実行例:

/usr/lib/ruby/1.8/erb.rb:736:in `result': tried to downgrade safe level from 1 to 0 (SecurityError)
        from /usr/lib/ruby/1.8/erb.rb:739:in `value'
        from /usr/lib/ruby/1.8/erb.rb:739:in `result'
        from /usr/lib/ruby/1.8/erb.rb:722:in `run'
        from e.rb:4

eoutvar

ERB.new の第 4 引数で eRuby スクリプトの中で出力をためていく変数を変更することが出来ます。 eRuby スクリプトの中で eRuby スクリプトを使う場合は変更する必要がありますが、普通は変更する必要はありません。

変更する必要がある場合というのは、たとえば以下のようなスクリプトの場合です。

require 'erb'
erb1 = ERB.new("erb1")
erb2 = ERB.new("erb2: <%= erb1.result(binding) %>")
puts "erb2 without eoutvar:"
puts erb2.result
erb2 = ERB.new("erb2: <%= erb1.result(binding) %>", nil, nil, '_erbout2')
puts "erb2 with eoutvar:"
puts erb2.result

出力例:

erb2 without eoutvar:
erb1
erb2 with eoutvar:
erb2: erb1

この例では erb2 の方で変更しましたが、erb1 の方を変更しても良いでしょう。

メソッドとして使う

今までの例では ERB オブジェクトを直接扱っていましたが、メソッドとして定義することによって、binding などを気にせずに普通のメソッドとして扱えるようになります。

eRuby をメソッドとして使う例としては、header や footer のように HTML の断片を返すといった使い方があります。

ERB#def_method

ERB#def_method を使うことで eRuby をメソッドとして使うことが出来ます。

メソッドということで、ここではメソッドの引数やインスタンス変数を使う例を作ってみました。

age.rb:

require 'erb'
class Hoge
  def initialize
    @year = 1993
  end
end
erb = File.open('age.erb') {|f| ERB.new(f.read)}
erb.def_method(Hoge, 'age(this_year)', 'age.erb')
puts Hoge.new.age(Time.now.year)

age.erb:

生まれた年: <%= @year %>
数え年: <%= this_year - @year + 1%>

出力例:

生まれた年: 1993
数え年: 14

ERB#def_method(mod, methodname, fname=’(ERB)’) の引数は以下の通りです。

mod
メソッドを定義するモジュール (またはクラス)
methodname
メソッド定義の def の右の部分
fname
エラー表示用のファイル名

methodname は def の右の部分なので、例のように引数を付けたり、「foo(bar=’bar’)」のようにデフォルト引数をつけたりすることも出来ます。

ERB::DefMethod.def_erb_method

def_erb_method(methodname, erb) を使って、もっと簡単にファイル名だけでメソッド定義ができるようになります。

使い方は、ERbMethod を extend して、def_erb_method の引数にメソッド名とファイル名を指定します。

例:

require 'erb'
class Hoge
  extend ERB::DefMethod
  def_erb_method('to_html', 'hoge.rhtml')
end
puts Hoge.new.to_html

def_erb_method(methodname, erb) の引数は以下の通りです。

methodname
ERB#def_method の methodname と同じ
erb
文字列ならファイル名として読み込んで ERB に変換してメソッドとして定義

erb には直接 ERB オブジェクトを渡すことも出来ます。 そのときは「erb.def_method(self, methodname)」と同じ意味になります。

先ほどの例を ERB::DefMethod を使って書き直すと、以下のようになります。

age.rb:

require 'erb'
class Hoge
  extend ERB::DefMethod

  def initialize
    @year = 1993
  end

  def_erb_method('age(this_year)', 'age.erb')
end
puts Hoge.new.age(Time.now.year)

ERB#def_module, ERB#def_class

ERB#def_module(methodname=’erb’) と ERB#def_class(suplerklass=Object, methodname=’erb’) というメソッドもあります。 これはそれぞれメソッドを定義した無名のモジュール、無名のクラスを返すメソッドです。

ERB::Util

ERB::Util を include することで、HTML などを生成するときに便利な

  • HTML の「&“<>」のエスケープをする html_escape, h
  • URL エンコードする url_encode, u

という 4 つのメソッドが使えるようになります。

h と u は eRuby の中で「<a href=”<%=u url %>“><%=h str %></a>」のように使うことで、「<%=h … %>」や「<%=u … %>」という見やすい書き方が出来るようになっています。

標準出力

元々の eRuby では標準出力への出力もその場所に埋め込まれるようになっていたので、eRuby 内の Ruby スクリプト片で標準出力を行った場合、eruby や (旧) ERb では出力内容がその場所に埋め込まれます。 一方、ERB とその元になった ERbLight では「<%= … %>」を使わないと、そのまま標準出力に出力されてしまいます。

hello.erb:

Hello, <% print "World" %>.

という eRuby で、 eruby と ERB では次のような違いが出てきます。

eruby:

Hello, World.

ERB:

WorldHello, .

その場所に出力を埋め込みたい場合は、普通は print などは使わず「<%= … %>」だけを使うようにすれば良いと思います。

おわりに

今回は ERB を中心に eRuby について紹介しました。

ruby 1.8.5 の ERB では、ERB, quote の脚注に書かかれているように、ruby-list:41960 で紹介されている <%= … %> の出力内容を自動的にエスケープに対応するためのインターフェイスが入っています。興味があれば試してみてください。

関連リンク

著者について

西山和広。 Ruby hotlinks 五月雨版や 現在の Ruby リファレンスマニュアルのメンテナをやっています。 Ruby リファレンスマニュアルはいつでも執筆者募集中です。 何かあれば、マニュアル執筆編集に関する議論をするためのメーリングリスト rubyist@freeml.com (参加方法) へどうぞ。

Ruby リファレンスマニュアルは現在青木さんによる新システムに移行準備中です。 手伝っていただける方は ruby-reference-manual ML に入ってください。

標準添付ライブラリ紹介 連載一覧