2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

いにしえのテンプレートエンジンを処した話

Last updated at Posted at 2023-05-25

はじめに

弊社のとあるシステムでは(超)マイナーテンプレートエンジンを使って開発を行っていたのだが、

  • 機能がとにかく少ない
  • ドキュメントがほぼ無い
  • バグがそこかしこに埋まっている
  • 拡張性に乏しい

などの様々な問題を抱えていたため、数か月間かけてTwigへのリプレイスを行った。

特殊過ぎてどこにも需要は無さそうだけど、万一こんな謎作業に従事することになった人がいたら少しでも参考になればと思う。

元のテンプレートエンジンはDjangoのTemplateを参考に作られていたので、本記事では勝手にTELD(Template engine like Django's one)と呼称する。1

実際にやったこと

一度のリリースで完全に移行するのは非常にデメリットが大きかった2ため、
以下のように手順を細かく分けて作業を行った。

  1. TELDとTwigの相違点調査
  2. TELDの書式をTwigに寄せる
  3. 【TELDにあってTwigに無い機能】をTELDから削減
  4. 【両方にあるけど微妙に挙動が違う機能】についてTELDを修正
  5. 【TELDにあってTwigに無い機能】をTwig拡張として実装
  6. TELDとTwigをプロダクト環境で同時に動かして差分チェック
  7. プロダクト環境をTwigへ完全に切り替え

TELDを修正する場合、万全を期すため以下のようにさらに細かく段階を踏んで作業を行った。

  1. TELDをTwigと同じ書き方で動くようにする
  2. TELD本来の書き方を非推奨化(ログを出力させる)してリリース
  3. Twigの書き方でのみ動くように修正して再度リリース

ゆえにリプレイス完了までに実際は10回以上リリースを行っている。

1. TELDとTwigの相違点調査

DjangoのTemplateとTwigが微妙に似ているおかげで、タグや変数の基本的な書き方については問題なかった。3

ただやはり完全に一緒ということは無く、以下のように細かい部分で違いが多数あった。

対象項目 TELD Twig
フィルタ引数の書式 {{ my_date|date:"Y-m-d" }} {{ my_date|date("Y-m-d") }}
不正なフィルタの扱い
例) {{ var||escape }}
なぜか動く(無視される) エラーになる
変数名として使える文字 -~などの記号も使える -~は演算子として解釈される
フィルタ名称
例) URLエンコード
urlencode url_encode
フィルタ挙動
例) {{ 20230102|date("Y/m/d") }}
人間的な解釈(2023年1月2日 UNIXタイムスタンプとして解釈
if系タグの種類 if, ifequal, ifnotequal等 ifのみ
forタグ内のループ変数 forloop.counter等 loop.index等
コメントアウト方法 {# ~ #}{% comment %}~{% endcomment %} {# ~ #}のみ
タグ直後の改行文字の扱い 削除されない 削除される4
includeタグの参照パス includeタグが記載されたテンプレートからの相対パス ルートパスを基準とした絶対パス

2. TELDの書式をTwigに寄せる

フィルタ引数の書式変数名として使える文字等の差異はTwigの拡張で対応できないため、
Twig同様の書式でも動くようにTELDを修正し、テンプレート内の該当箇所は正規表現で一括置換した。

ようするに力業で解決。

3. 【TELDにあってTwigに無い機能】をTELDから削減

【TELDにあってTwigに無い機能】は、テンプレート内での使用頻度によって対応を分けた。

  • テンプレート内で全く使われていないタグ、フィルタ、関数
    →TELDから削除し、新規利用を抑止

  • テンプレート内で少しだけ使われているタグ、フィルタ、関数
    →もし本当に必要ならTwigと同じ書き方ができるようにTELDを修正。無くても何とかなるなら削除
    例)ifforwardmatchとかいう謎タグをif ~ starts withに置き換えた、等

  • テンプレート内で多数使われているタグ、フィルタ、関数
    →Twigの拡張で対応することにしたので後述
    例)ifequalタグ、commentタグ等

4. 【両方にあるけど微妙に挙動が違う機能】についてTELDを修正

  • forタグ内のループ変数/フィルタ名称の差異
    →Twigと同じ書き方ができるようTELDを修正し、テンプレート内の該当箇所を一括置換

  • フィルタ挙動の差異
    →問題となるような使い方をしていたらログを出すように修正。該当箇所が見つかったら適宜修正

  • includeタグの参照パスの差異
    →こちらもTwigの拡張で対応することにしたので後述

5. 【TELDにあってTwigに無い機能】をTwig拡張として実装

  • ifequalタグ、ifnotequalタグ等
    →組み込みのIfTokenParserをほぼコピペして専用のTokenParserを実装

  • commentタグ
    →本来ならLexerで何とかすべきなのかもしれないが、TokenParserとして無理やり実装

  • 相対パス対応のincludeタグ
    →専用のTokenParser、Node、Loaderを実装して無理やり実現

6. TELDとTwigをプロダクト環境で同時に動かして差分チェック

TELDの裏側でTwigによるレンダリングも行えるように魔改造を施し、

  • レンダリング結果に差分があったらログ出力
  • いずれかがエラーになった場合はもう片方のレンダリング結果を表示

とすることで、普通の調査では分からなかった細かい違いをあぶり出した。

TELDのforタグにarray以外を渡すと勝手にarrayでラップしてくれる(文字列とかもループできる)仕様になってたけど、こんなの実際に差分取らなきゃ気付けなるわけないよね。

7. プロダクト環境をTwigへ完全に切り替え

TELDの実行関数をTwigの呼び出しにそっくり挿げ替えて終わり!
今のところ特にエラーもお問い合わせも来ていないのでセーフ!!

おわりに

Twigになってコーディングが格段にやりやすくなったと思うので、今はがんばった自分をほめてあげたい。

  1. OSSではあるが、こんな記事なので名前を出す勇気が出なかった。

  2. 移行完了まで他メンバーが一切リリースできなくなる、万一なにか対応漏れがあったとしても気づくことが困難。等

  3. というか一番差が少ないであろうTwigをあえて選んだというのが正確。

  4. scriptタグ(JavaScript)内の改行って大事だよね……。

2
0
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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?