eRuby のファイルから Ruby の文と式だけを抜き出す

eRuby ファイルに Syntax error があると、該当箇所を見つけるのはかなり困難である。理由は簡単で、HTML と Ruby コードとが混じっているから。
たとえば <% for item in list %> と <% end %> のあいだにたくさんの HTML タグが入っていると、それだけで for と end の対応を確認するのが難しくなる。

たとえばこんな hoge.rhtml があったとする。

<html>
  <body>
    <h1><%=h @title %></h1>
    <% if !@list || @list.empty? %>
    <p>not found.</p>
    <% else %>
    <table>
      <thead>
        <tr>
          <th>#</th>
          <th>Name</th>
          <th>Site</th>
        </tr>
      </thead>
      <tbody>
        <% i = 0 %>
        <% for item in @list %>
        <%   i += 1 %>
        <%   color = i % 2 == 1 ? '#FFCCCC' : '#CCCCFF' %>
        <tr bgcolor="<%= color %>">
          <td><%= i %></td>
          <td><%=h item.name %></td>
          <% if item.url %>
          <td><a href="<%= item.url %>"><%=h item.url %></a></td>
          <% else %>
          <td>-</td>
          <% end %>
        </tr>
      </tbody>
    </table>
    <% end %>
  </body>
</html>


この hoge.rhtml には Syntax error がある。それは次のようにして確かめることができる。

$ erb -x hoge.rhtml | ruby -wc
-:34: syntax error, unexpected $end, expecting kEND


しかし、実際にどこが原因で Syntax error になっているのかは分からない。hoge.rhtml をみても、HTML と Ruby コードが入り交じっていて、見つけるのが困難である。

こんなときは、Erubis に -x -E NoText をつけると、HTML を取り去って Ruby コードだけを抜き出してくれる (逆に HTML だけを抜き出したいときは -x -E NoCode を指定する)。

$ erubis -x -E NoText hoge.rhtml
_buf = '';

         _buf << (h @title ).to_s;
     if !@list || @list.empty? 

     else 









         i = 0 
         for item in @list 
           i += 1 
           color = i % 2 == 1 ? '#FFCCCC' : '#CCCCFF' 
                      _buf << ( color ).to_s;
               _buf << ( i ).to_s;
               _buf << (h item.name ).to_s;
           if item.url 
                        _buf << ( item.url ).to_s;   _buf << (h item.url ).to_s;
           else 

           end 



     end 


_buf.to_s


この機能は非常に便利なので、-x -E NoText のかわりに -X というショートカットが用意されている。

また -U をつけると連続する空行を圧縮し (uniq)、-C をつけると空行を取り除き (compact)、-N をつけると行番号が表示される (number)。

$ erubis -XNC hoge.rhtml
    1:  _buf = '';
    3:           _buf << ( @title ).to_s;
    4:       if !@list || @list.empty? 
    6:       else 
   16:           i = 0 
   17:           for item in @list 
   18:             i += 1 
   19:             color = i % 2 == 1 ? '#FFCCCC' : '#CCCCFF' 
   20:                        _buf << ( color ).to_s;
   21:                 _buf << (h i ).to_s;
   22:                 _buf << (h item.name ).to_s;
   23:             if item.url 
   24:                          _buf << ( item.url ).to_s;   _buf << (h item.url ).to_s;
   25:             else 
   27:             end 
   31:       end 
   34:  _buf.to_s


これを見れば一目瞭然。17 行目の for 文に対応する end が抜けていることがわかる。

このように、Erubis の -E NoText を使うと Ruby のコードだけを抜き出してくれるので、チェックが簡単になる。

また Erubis は PHP にも対応していて、PHP ファイルから PHP のコードのみを抜き出すことができる (なんというムダ機能!)。

$ erubis -l php --pi=php -XNC --trim=false hoge.php
    3:          <?php echo $title; ?>
    4:      <?php if (! $list) { ?>
    6:      <?php } else { ?>
   16:          <?php $i = 0; ?>
   17:          <?php foreach ($list as $item) { ?>
   18:          <?php   $i += 1; ?>
   19:          <?php   $color = i % 2 == 1 ? '#FFCCCC' : '#CCCCFF'; ?>
   20:                       <?php echo $color; ?>
   21:                <?php echo $i; ?>
   22:                <?php echo $item->name; ?>
   23:            <?php if ($item->url) { ?>
   24:                         <?php echo $item->url; ?>  <?php $item->url; ?>
   25:            <?php } else { ?>
   27:            <?php } ?>
   31:      <?php } ?>


これらに限らず、Erubis は拡張性が非常に高い。Erubis は高速性が注目されているけど、それに比べると拡張性の高さはあまり知られていない。たとえば -E NoText の機能は、たったこれだけで実現されている。

module Erubis
  module NoTextEnhancer
    def add_text(src, text)
      src << ("\n" * text.count("\n")) ## テキスト行数分の改行を追加
      if text[-1] != ?\n     ## テキストが改行で終わってない場合は、
        count = text.length - text.rindex(?\n) - 1
	src << (' ' * count) ## 最後の行の文字数分だけ半角空白を追加
      end
    end
  end
end

class MyEruby < Erubis::Eruby
  include Erubis::NoTextEnhancer
end


Erubis は他にも機能てんこもりなので、知りたい人はユーザーズガイドChapter3. Enhancerとかをどうぞ。

なお Erubis では -z を指定すると Syntax error をチェックしてくれる。これは erb -x hoge.rhtml | ruby -wc と比べると、複数ファイルを一度にチェックできるという利点がある。

$ erubis -z views/**/*.rhtml                    ## zsh の場合
$ find views -name '*.rhtml' | xargs erubis -z  ## zsh 以外の場合