LoginSignup
10
2

More than 5 years have passed since last update.

ERBのtrim_modeの違い

Posted at

この記事ではRuby 2.4.1に同梱されているERBについて書く。読んだ過程をメモしているだけなので多分あまり要約されてないので、読みにくかったらるびまの記事を参照して欲しい。

ERBのオプション

ERB#initializeの引数は以下のように定義されている。

def initialize(str, safe_level=nil, trim_mode=nil, eoutvar='_erbout')

eoutvarは単に内部の変数名が変えられるだけなので普通は変える必要ないし、safe_levelで調整できる$SAFEも普通は使わない機能だと思うが、trim_modeは出力結果に改行を入れるかどうかを細かくコントロールできるのでまあ使う機能なのではないだろうか。

というわけでこの記事では最も重要なERBのコンパイルオプションであろうtrim_modeについて書く。

trim_mode オプション

ERB#initializeに渡したtrim_modeなる引数は、ERB::Compiler#prepare_trim_modeというメソッドで内部的なオプション@percent,@trim_modeに変換される。元の引数はそれ以降使われないので、@percent@trim_modeが事実上のコンパイルオプションとなる。

@trim_modeに関しては同じ名前なのに中身が違うので少々紛らわしいが、この記事ではtrim_modeERB#initializeに与えられる引数、@trim_modeERB::Compilerで使われるインスタンス変数の値ということにする。

内部的にはtrim_mode1,2,0,Stringおよびそれ以外というように分岐しているが、最終的にセットされうる値から逆算してまとめると以下のようになる。

@percent @trim_mode trim_mode
代表的な値
ソースコード上のコメント
false nil 0, "", nil
false ">" 1, ">" %>で終わる行の改行を削除する
false "<>" 2, "<>" <%で始まり%>で終わる行の改行を削除する
false "-" "-" -%>で終わる空行を削除する
true nil "%" %で始まる行でRubyのコード実行を有効にする
true ">" "%>" %で始まる行でRubyのコード実行を有効にする,%>で終わる行の改行を削除する
true "<>" "%<>" %で始まる行でRubyのコード実行を有効にする,<%で始まり%>で終わる行の改行を削除する
true "-" "%-" %で始まる行でRubyのコード実行を有効にする,-%>で終わる空行を削除する

ERBのScannerの決定

ではそれらのコンパイルオプションは何に使われるのか。実はこれらは一部@percentがマジックコメントの判定に使われているくらいで、コンパイラ本体では使われていない。
では何に使われているかというと、コンパイラが使用するScannerの決定とその内部で使われる。なので、ほとんどスキャナオプションといっても良い。

ERBでは4つのスキャナ(うち1つは使われる条件が重複しており使われなさそう)が定義されており、これは@percent@trim_modeに応じて使われるものが以下のように決定される。

@percent @trim_mode trim_mode
代表的な値
Scanner ソースコード上のコメント
false nil 0, "", nil SimpleScanner2
false ">" 1, ">" TrimScanner %>で終わる行の改行を削除する
false "<>" 2, "<>" TrimScanner <%で始まり%>で終わる行の改行を削除する
false "-" "-" ExplicitScanner -%>で終わる空行を削除する
true nil "%" TrimScanner %で始まる行でRubyのコード実行を有効にする
true ">" "%>" TrimScanner %で始まる行でRubyのコード実行を有効にする,%>で終わる行の改行を削除する
true "<>" "%<>" TrimScanner %で始まる行でRubyのコード実行を有効にする,<%で始まり%>で終わる行の改行を削除する
true "-" "%-" TrimScanner %で始まる行でRubyのコード実行を有効にする,-%>で終わる空行を削除する

Scannerごとの挙動の違い

選択されうるこれらのScannerのそれぞれがどのような挙動をするか見ていく。

SimpleScanner2

デフォルトの挙動。「<%, <%=, <%#といったトークンを読んだあとそれにマッチする%>を既に読んでいるか」でスキャンに使う正規表現を2パターン切り替える。TrimScannerは両方にマッチする正規表現を使うので、SimpleScanner2の方がスキャンが速そうに見える(試してない)。

開始タグとしてスキャンされるのは<%%,<%=,<%#,<%であり、終了タグとしてスキャンされるのは%%>, %>である。<%%%%>はテキストとして<%%>を打ちこむためのエスケープ用で、それらは通常のテキストとしてスキャンされる。

なおコンパイラはそれらの特別なタグがスキャンされる際は、そのタグの部分のみをスキャンされることを想定しており、改行とかがついた状態でスキャンされると壊れるようになっているため("%>"じゃなくて"%>\n"だとダメ)、正規表現もそれに対応して作られている。

改行を無視するとかそういうことはやらないので、行末に%>とかがある場合でも、その後の改行が生成コードに付加される。

ExplicitScanner

%開始行をRubyとして評価する機能を使わず、-%>で終わる行の空行を削除する場合に使われるスキャナ。
これもSimpleScanner2と同じく、スキャナがERBタグの途中かに応じて正規表現をスイッチするので多分TrimScannerより速い。機能自体はTrimScannerのサブセットに過ぎないと思う。

(全然関係ないけど、Itamaeとかだとこれが採用されているので-%>が使える)

TrimScanner

ここまで書いてたら僕はもう理解できてしまったのでもう解説しなくていい気がしてきた。

唯一%開始行をRubyとして評価するのに対応しているスキャナであり、その分毎回スキャンが遅くなるはず。また"<>"">"といったコンパイルオプションもこのスキャナでしか動作しない。

このスキャナを読む上で難しいところは@trim_modeに応じて行を読み込むためのメソッドが変わるところで、以下のように分岐している。

@trim_mode @scan_line用メソッド
">" :trim_line1
"<>" :trim_line2
"-" :explicit_trim_line
nil :scan_line

まとめ

全ての情報をまとめると以下のようになる。

@percent @trim_mode trim_mode
代表的な値
Scanner 行スキャンメソッド ソースコード上のコメント
false nil 0, "", nil SimpleScanner2 #scan
false ">" 1, ">" TrimScanner #trim_line1 %>で終わる行の改行を削除する
false "<>" 2, "<>" TrimScanner #trim_line2 <%で始まり%>で終わる行の改行を削除する
false "-" "-" ExplicitScanner #explicit_trim_line -%>で終わる空行を削除する
true nil "%" TrimScanner #scan_line %で始まる行でRubyのコード実行を有効にする
true ">" "%>" TrimScanner #trim_line1 %で始まる行でRubyのコード実行を有効にする,%>で終わる行の改行を削除する
true "<>" "%<>" TrimScanner #trim_line2 %で始まる行でRubyのコード実行を有効にする,<%で始まり%>で終わる行の改行を削除する
true "-" "%-" TrimScanner #explicit_trim_line %で始まる行でRubyのコード実行を有効にする,-%>で終わる空行を削除する
10
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
2