Sansan Tech Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

HTML用ERBファイルのフォーマットを統一した話

こんにちは。 名刺アプリ「Eight」でエンジニアをしている鳥山(@pvcresin)です。

最近、ミスタードーナツのミニオンコラボの商品を食べたのですが、 どれも美味しくて見た目もかわいいので最高でした。 特にポン・デ・リングベースのものは、表面のキャンディが口の中でパチパチと弾けて楽しいのでオススメです。

さて今回は、RailsのViewで使う、HTML用ERBファイルのフォーマットを統一した話をします。

ERBとは

ERB(eRuby、embedded Ruby)はテキストにRubyのコードを埋め込むための仕様です。 Railsでは特にViewの部分のHTML生成によく利用されます(拡張子は.erb)。 ERBでは、以下のような記法でRubyのコードを埋め込めます。

<ul>
  <% @features.each do |f| %>
    <li><%= f %></li>
  <% end %>
</ul>

ERBの実装としては、標準ライブラリのERBやRails 5.1以降で採用されているErubiなどがあります。

Eightでは、一部のWebページとHTMLメールにERBファイルを利用しています。
前者は、かつてRailsのみで画面を作っていた時代のなごりです。 現在はReactで画面を作っているので、新規作成することは基本的にありません。
後者は、GmailやOutlookなどのメールクライアントで表示されるHTMLメールです。 最新のWeb技術が使えない1ため、古のtableレイアウトを使う必要があるなど、いろいろと制約が多いのが難点です。

ERBファイルのフォーマットを統一するメリット

この記事での「フォーマット統一」とは、インデントなどを適切に設定するシンプルな整形に加えて、静的解析ツールによる指摘の修正も含めることとします。
フォーマット統一によるメリットは、主に以下の3点が挙げられます。

可読性の向上

適切なインデントや改行は、コードの構造を明確にし、他の開発者がコードを読みやすくなります。 特にHTMLはタグのネストが深くなりやすいため、インデントが揃っていることで、どのタグがどのタグに対応しているのかが一目でわかります。 また、開始タグと終了タグのインデントが揃っていると、エディタやIDEによっては折りたたむことができ、見通しを良くできます。

品質の向上

フォーマットをチェックする仕組みを導入することで、終了タグの書き忘れや安全でないコードの発見などが容易になります。 これにより、バグを未然に防ぐことができ、コードの品質を向上させられます。

レビューの効率化

コードのフォーマットが統一されていることで、変更時の差分が少なくなるため、不要なレビュー指摘を減らせます。 これにより、開発者はコードのより本質的な部分のレビューに集中できるようになります。

以上を踏まえると、フォーマットは単にコードをきれいにするだけでなく、チーム全体の開発者体験とコードの品質を向上させるための重要なステップといえます。

フォーマット統一に使用するツール

今回は、HTML BeautifierとERB Lintの2つのツールを使って、ERBファイルのフォーマットを統一することにしました。
HTML Beautifierは、HTMLを整形するためのツールで、ERBにも対応しています。 主にインデントや改行、空白などを整理するために利用します。
ERB Lintは、ERB用の静的解析ツールです。 約20個のルールが用意されているほか、RuboCopとも連携も可能で、エラーの自動修正(AutoCorrect)に対応しています。 configファイルでは、以下のような形でERB向けにRuboCopの設定を上書きできます。

---
EnableDefaultLinters: true
linters:
  ErbSafety:
    enabled: true
  Rubocop:
    enabled: true
    rubocop_config:
      inherit_from:
        - .rubocop.yml
      Layout/InitialIndentation:
        Enabled: false

こちらは主にコードの品質を向上させるために利用します。

それぞれの強みを活かし、HTML Beautifierで大まかにコードを整えた後に、ERB Lint(+ RuboCop)で自動修正を行っていきます。 ちなみに、どちらのツールにも有志の方が作成したVSCode用の拡張機能があり、それらを使うと、より便利に開発を行えます。

作業手順

大まかな作業の方針としては、範囲を限定してフォーマットを統一したPRを出し、その範囲を徐々に広げていくことにしました。
これは、フォーマットが変更されることでコードの挙動が変わってしまう可能性があるため、段階的に進めることで、変更範囲を把握しやすくするためです。
また、他の開発と並行してサイドタスク的に進めるため、コンフリクトを少なくしたいという意図もありました。

1. 「特定の範囲」でフォーマットを統一する

まず「特定の範囲」を決め、以下のようなスクリプトを使って、その範囲内のファイルをフォーマットします。

#!/bin/sh
set -eo pipefail
files="$*"

echo "Running HTML Beautifier..."
bundle exec htmlbeautifier $files

echo "Running ERB Lint..."
bundle exec erblint --autocorrect $files

例えば「特定の範囲」として、app/views下のprofileとteamディレクトリのみを対象にする場合は、以下のように実行します。

./script/format_erb.sh app/views/{profile,team}/**/*.html.erb

ただし、元コードの形によってはうまくフォーマットされないことがあり、必要に応じて差分を確認しながら元ファイルのフォーマットを手動で修正する必要があります。

2. 「特定の範囲」にpre-commitフックを設定する

次に、新しく変更が加えられた場合に対処します。 EightではLefthookを使ってGitフックを管理しているので、コミット前にフォーマットを統一するために、pre-commitフックを設定しました。

---
pre-commit:
  parallel: true
  commands:
    backend-erb-linter:
      glob: "app/views/{profile,team}/**/*.html.erb"
      run: |
        script/format_erb.sh {staged_files}
        LINT_EXIT_CODE=$?
        exit $LINT_EXIT_CODE
      stage_fixed: true

先ほどと同じ範囲でglobを設定し、ステージングされたファイルに対して同じスクリプトを使ってフォーマットの統一を行います。 これにより、「特定の範囲」でフォーマットが統一された状態を今後も担保します。

3. 「特定の範囲」を広げる

その後は、「特定の範囲」を徐々に広げながらPRを出していきます。 最終的にapp/views/**/*.html.erbまで広げたら完了としました。
また、どうしてもフォーマットを変更すると壊れてしまう、いくつかのファイルに関しては、Lefthookの除外設定を行って無視するようにしました。 例えば、CSSのwhite-space: pre;などの設定により空白が重要な意味を持つファイルがそれに該当します。

余談

これは余談ですが、当初Lefthookの除外設定が一つの正規表現しか受け付けなかったため、階層が異なる複数のファイルを除外したい場合に指定が面倒という問題がありました。 これについて機能リクエストのIssueにコメントしたところ、1時間もしないうちに複数のパスを指定できるようにする機能追加PRが作成され、次の日にはリリースされていました! 爆速の対応に驚きました。

まとめ

HTML用のERBファイルのフォーマットを統一したことで、コードの可読性と品質を向上できました。 実際に作業を進める中で、インデントのズレによって終了タグがないことに気づけたり、開始タグと終了タグが揃っていないことに気づけたりと、品質向上につながることが多かったです。
また、全体で約400のファイルを対応したのですが、作業は1カ月ほどで終わり、障害もなく作業を終えられた点も良かったです。
一部のコードではフォーマットをかけると崩れる場合があり、Parserの気持ちを汲みつつ、改行位置などを調整するのが地味に大変でした。
これからも開発者体験向上のため、地道に改善を続けていきたいと思います!

media.sansan-engineering.com


  1. Can I emailで機能のサポート状況を確認できます。

© Sansan, Inc.