hitode909の日記

趣味はマリンスポーツですの日記です

PerlのテンプレートエンジンText::Xslate用のリファクタリングツールXRTをRubyで作った

PerlのテンプレートエンジンであるXslateのテンプレートを静的解析してリファクタリングするためのツール,XRT(Xslate Refactoring Tool)というのをRubyで作った.

背景

仕事でWebアプリケーションのテンプレートをText::XslateのTTerseで書いているのだけど,長期間開発していると,テンプレートがだんだん複雑になってくる.

長くなったり,ネストが大きくなったりしたテンプレートは分割したいのだけど,壊さないよう注意深くエディタで切り取って,新たなファイルを作って,貼り付け,という作業が発生して,緊張感が高く,[% END %]が足りないとコンパイルエラーになったり,中途半端に失敗するとdivを閉じ忘れていたりする.

コードの一部を別の関数に切り出す機能はIDEなどに搭載されていて,人間がやるよりは機械のほうが得意そうなので,ツールを作ることにした.

インストール

Rubyで書いていて,Rubygemsに上がっているので,gem install xrtでインストールできる.

gem install xrt

いまのところ,2つのコマンドがある.

制御構造のネスト数を表示する

たとえば,以下のようなテンプレートがあるとして,

<html>
  <body>
    [% FOR item IN items %]
      [% IF item.title %]
        <h1>[% title %]</h1>
      [% END %]
    [% END %]
  </body>
</html>

xrt dumpコマンドを実行すると,制御構造のネスト数をあわせて表示してくれる.最初のFORでネスト数が1になり,その後のIFで2になっていること分かる.

最後のENDで0に戻っていて,コンパイルできるテンプレートは必ずネストが0で終わるので,このテンプレートは動きそうとういことが分かる.

% xrt dump templates/sample.html
Dumping templates/sample.html
<html>
  <body>
    0[% FOR item IN items %]1
      1[% IF item.title %]2
        <h1>2[% title %]2</h1>
      2[% END %]1
    1[% END %]0
  </body>
</html>
0

テンプレートの一部を別のテンプレートに切り出す

xrt extractでテンプレートの一部を別の新しいテンプレートに切り出すことができる.

さきほどのテンプレートのうち,FOR文を別のテンプレートに切り出したいときは,以下のように実行する.

% xrt extract templates/sample.html '[% FOR item IN items %]' templates/ _items.tt

結果は以下で,FOR文ごとtemplates/_items.ttに移動している.最初は4つほどあったインデントも,ファイルが移動したので0からのスタートになっていて便利.

# tempaltes/sample.html
<html>
  <body>
    [% INCLUDE "_items.tt" %]
  </body>
</html>
# templates/_items.tt
[% FOR item IN items %]
  [% IF item.title %]
    <h1>[% title %]</h1>
  [% END %]
[% END %]

このように,xrt dumpで切り出す候補を眺めて,xrt extractで分割,を繰り返すことで,安全にテンプレートを小分けにすることができる.

ご利用ください

きのうの晩から作り始めて,夕方ごろには動くようになったので良いペースで作れた.

Text::XslateはPerlのライブラリなのだけど,リファクタリングツールがXslateのAPIに直接依存すると,Xslate本体の開発にあわせて壊れたりしそうなので,実装は共通化せず,Rubyで書くことにした.

内部的には,リファクタリングをするのに必要なくらいの粒度でテンプレートをパースして,構文木を作って,置き換える操作を定義していくことで実装していて,IFやWRAPPERやFORはブロック,とか,ブロックはENDで終わる,とかそういう感じの素朴なパーサーを書けば完成したので,うまくいったと思う.

どうぞご利用ください.

今後

きのうから作り始めたので,いろいろやりたいことはあって,

  • 余計なINCLUDEをその場に展開したい
  • テンプレートを2つ渡すと共通部分を括り出してほしい
  • エディタから呼べるようにしたい
  • Linterを作って,許容するネスト数の上限を超えてたらテストが落ちるようにしたい
  • TTerseにしか対応していないのでKolonも対応したい
    • 僕はKolon使ってないのでモチベーションは低い

など,いろいろやりたいことはあるけど,とりあえず仕事のリファクタリングでは使えそうなくらいにはなったので使っていきたい.