`render.view_component` ペイロードにビューテンプレートパスを追加

viewcomponent/view_component

render.view_component イベントのペイロードに view_identifier フィールドが追加され、レンダリングされたテンプレートファイルのパスを取得できるようになりました。これにより、Coverband のようなコードカバレッジツールがビューテンプレートの使用状況を追跡できるようになります。

背景

これまで render.view_component イベントのペイロードには、コンポーネントの .rb ファイルパスを示す identifier のみが含まれていました。コンポーネント自体がレンダリングされたことはわかっても、どのテンプレートファイルが実際に使われたかは追跡できない状態でした。

Coverband のようなツールは、ActiveSupport::Notifications の計装イベントを利用してコードカバレッジを計測します。しかしテンプレートファイルのパス情報がペイロードに含まれないため、.html.erb ファイルが実際に使われているかどうかを検出できないという問題がありました。

この制約を解消するために、テンプレートファイルのパスを保持する view_identifier フィールドがペイロードに追加されました。

技術的な変更

lib/view_component/instrumentation.rb に2つのメソッドが変更・追加され、テンプレートパスをペイロードへ安全に渡す仕組みが実現されました。

変更前:

def render_in(view_context, &block)
  return super if !Rails.application.config.view_component.instrumentation_enabled.present?

  ActiveSupport::Notifications.instrument(
    "render.view_component",
    {
      name: self.class.name,
      identifier: self.class.identifier
    }
  ) do
    super
  end
end

変更後:

def render_in(view_context, &block)
  return super if !Rails.application.config.view_component.instrumentation_enabled.present?

  payload = {
    name: self.class.name,
    identifier: self.class.identifier,
    view_identifier: nil
  }

  ActiveSupport::Notifications.instrument(
    "render.view_component",
    payload
  ) do
    result = super
    payload[:view_identifier] = @__vc_instrumentation_view_identifier
    result
  end
ensure
  @__vc_instrumentation_view_identifier = nil
end

def around_render
  result = super
  @__vc_instrumentation_view_identifier = current_template&.path
  result
end

ポイントは around_render のオーバーライドにあります。Base#render_inensure ブロックが @current_template を元に戻す前のタイミングで current_template&.path を取得し、インスタンス変数 @__vc_instrumentation_view_identifier に退避させています。その後、render_ininstrument ブロック内で payload[:view_identifier] にセットすることで、イベントリスナーに渡るペイロードへ確実に書き込まれます。

インライン call メソッドを使うコンポーネント(テンプレートファイルを持たない)では current_template が存在しないため、view_identifiernil になります。また、render_inensure 節で @__vc_instrumentation_view_identifiernil にリセットすることで、再利用時の状態汚染を防いでいます。

更新後のペイロードは次のようになります:

ActiveSupport::Notifications.subscribe("render.view_component") do |event|
  event.payload
  # => {
  #   name: "MyComponent",
  #   identifier: "/app/components/my_component.rb",
  #   view_identifier: "/app/components/my_component.html.erb"  # インライン call コンポーネントでは nil
  # }
end

テスト側では test/sandbox/test/instrumentation_test.rb に1行が追加され、view_identifier が正しいテンプレートパスを返すことが検証されています。

assert_match("app/components/instrumentation_component.html.erb", events[0].payload[:view_identifier])

設計判断

around_render フックを経由してテンプレートパスを取得する設計が採用されました。

current_templateBase#render_inensure ブロックでリセットされるため、render_in のブロック内から直接参照しようとしても取得できません。around_rendersuper(テンプレートのレンダリング)完了直後・ensure 発動前に処理を挿入できる唯一のタイミングです。この位置でパスを中間変数に退避させ、render_in からそれを拾う2段構えの構造が、既存のライフサイクルを壊さずに情報を引き渡す最小限の方法として選ばれています。

また、ペイロードオブジェクトを事前に payload 変数として取り出し、ブロック内で直接ミューテートする実装も注目に値します。ActiveSupport::Notifications.instrument のブロック外でペイロードを変更してもイベントリスナーには反映されませんが、同一オブジェクトへの参照を保持することでブロック内の後段処理として書き込み可能にしています。

まとめ

view_identifier の追加は、render.view_component ペイロードに対する小さな変更ですが、計装の情報密度を高める実用的な改善です。ViewComponent のレンダリングライフサイクルを深く理解したうえで、既存の around_render フックと ensure ブロックの実行順序を巧みに活用した設計は、同様のフック機構を扱う際の参考になります。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
6b5cfdb2

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

「総論→各論→結論」の3部構成が明確です。リード文、背景、技術的な変更、設計判断、まとめの各要素がすべて含まれており、理想的な構成です。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```言語:ファイルパス)およびPR番号のリンク記法が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

ViewComponentのライフサイクルやActiveSupport::Notificationsに関する知識を前提としており、専門知識を持つエンジニアという対象読者に完全に適合しています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論から各論へ展開され、各段落はトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られており、非常に読みやすいです。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内で引用されているコードブロック(変更前・変更後)は、提供されたDiffの内容と完全に一致しています。ファイル名も正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「instrumentation」、「payload」、「around_render」、「ensure」などの技術用語が、文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

「around_render」と「render_in」内の「ensure」ブロックの実行順序を利用してテンプレートパスを取得する仕組みの説明が、技術的に正確かつ論理的です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張(Coverbandへの影響、従来の課題、解決策など)は、PRのDescriptionやDiff内容によって裏付けられており、ハルシネーションは検出されませんでした。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#2572)やファイルパスなどの固有名詞はすべて正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトルはPRのタイトルと内容を的確に反映しており、主題の乖離はありません。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

PR情報に含まれないバージョン情報やリリース予定などの外部知識の追記はなく、事実に基づいた記述が徹底されています。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「これまで」や「更新後」といった時間表現が、PRによる変更の前後関係を正しく示しており、歪曲はありません。