Kaigi on Rails 2025 ActionViewからReActionViewへ – HTML構造を理解するERBエンジン

ogp

こんにちは、株式会社Techouseでインターンとして働いているWaki Yuukuです。
本記事では、Kaigi on Rails 2025で紹介された次世代のERBエンジン「ReActionView」の技術についてまとめます。
普段何気なく使っているERBの裏側で何が起きているのか、そしてReActionViewのすごさや面白さを伝えられるようにまとめました。

Techouseでは、若手のエンジニアが積極的に技術カンファレンスに参加し、最新の技術動向を学び、共有する文化を大切にしています。
私もその一環として、2025年9月に開催されたKaigi on Rails 2025に参加しました。

二日目の発表では、HotwireのコントリビューターであるMarco Roth氏が、次世代のERB開発エコシステムである「HERB」を用いてRailsのViewを強化する新しいERBエンジン「ReActionView」について講演されました。
この発表開発は、Railsのフロントエンド開発における未来を感じさせる内容で、とてもワクワクしました。

本記事では、そのワクワクを皆さんと共有できればと考えています。

Kaigi on Rails 2025: Marco Roth氏のセッション

ReActionView(Reactive ActionView)は、RailsのActionViewと互換性を保ちながら、現代のフロントエンド開発の要求に応える次世代のERBエンジンです。Marco Roth氏は、2025年を通じてRubyKaigi、RailsConf、Rails Worldと段階的にこのビジョンを発表してきました1

Hotwire、Stimulus、Turboなどの登場により、HTMLテンプレートの重要性が再び高まっています。ReActionViewは、従来のActionViewが抱える課題を解決し、Railsのビューレイヤーに新しい可能性をもたらそうとする取り組みです。

Rails View Layerの課題

RailsのView層では長年、Erubiが利用されてきました。しかし、近年のWeb開発の進化に伴い、いくつかの課題が見えてきています。

1. ERBを文字列として扱う仕組み

現在のActionViewは、ERBテンプレートを文字列テンプレートエンジンとして処理しています。ERBタグの外側にあるHTMLも単なる文字列として扱われるため、HTMLの構造を認識できず、タグの閉じ忘れなどの無効なHTMLもコンパイル時に検出できません。

例えば、以下のコードはエラーにならず、無効なHTMLが生成されます:

div class="container">
  
    h1>Welcome,  user.name %>
  

2. 開発ツールの不足

近年、RubyやJavaScriptの開発環境は大きく進化しました。LSPによる補完機能、リアルタイム構文チェック、自動フォーマッタなどが一般的になっています。
しかし、HTML+ERBファイルに対しては基本的な開発支援が不足していました2

  • リアルタイムの構文チェックがない
  • HTMLとRubyが混在するため自動フォーマットが困難
  • ベストプラクティスやアクセシビリティの自動チェックができない

3. 複雑なUI要求への対応

現代のWebアプリケーションでは、リアルタイムバリデーション、動的フォーム、大規模コードベースの管理など、従来のActionViewが設計時に想定していなかった要求が増えています3

ReActionViewが目指すもの

ERB開発エコシステム「Herb」

ReActionViewの核となるのが、Herb(HTML-aware ERB Parser)です4。Herbの中核となるのはC言語で実装された高速パーサーで、ERBファイルを文字列ではなくHTML構造(HTMLの要素やタグの階層関係など)として理解します。
詳しくはIntroducing Herb: A new HTML-Aware ERB Parserをご覧ください。

充実した開発者ツール

ReActionViewは、Herbを活用して以下のツールを提供します:

エディタ統合

  • リアルタイムHTML検証とエラー警告
  • 自動フォーマッタ
  • リンター
  • Language Server

ブラウザツール

  • View/Partial/Component Outlinesでビューの境界を可視化
  • Jump to Sourceでブラウザからエディタへジャンプ
  • ERB Output Hoverで生成元のERBコードを表示
  • 検証オーバーレイでHTML検証エラーを表示

恥ずかしながら私はRailsのView層に関して「ERBをよしなにActionViewが変換してるんでしょ」くらいの認識しかありませんでした。その裏側でどのような処理が行われているのかをほとんど意識していませんでした。
ReActionViewの革新性を理解するために、まず現在のActionViewがERBテンプレートをどのように処理しているのかを見ていきます。
そんなの知ってるよという人は読み飛ばしていただいて構いません。

ERBテンプレートの変換フロー

RailsがERBテンプレートをHTMLに変換するプロセスは、以下のシーケンス図で表現できます:

ActionView処理フロー

特に面白いのは、ActionViewがERBテンプレートをHTMLに変換するプロセスでErubi::EngineがERBテンプレートを一度中間表現のRubyコードに変換する部分です。
中間のRubyコードは変数を展開した文字列(HTML)を生成する役割があります。それについては次の「動作原理」のセクションで詳しく見ていきます。

Erubiエンジンの動作原理

テンプレートの分割処理

Erubiは、正規表現を使ってERBテンプレートを静的部分(HTMLなど)と動的部分(Rubyコード)に分割します。この変換は非常にシンプルで、Erubi::Engineクラスのinitializeメソッドにほとんどの処理が収まっており、比較的理解しやすい実装になっています5

簡単に説明すると、Erubiは_bufというバッファ変数を使い、HTML文字列を順次連結していきます。この時、HTML要素は構造に関係なくテキスト要素として_bufに追加されます。タグ内のRubyコードはそのまま実行され、タグ内のRubyコードは評価された結果が文字列化されて_bufに追加されます。

例えば、以下のERBテンプレート:

Admin

これは、Erubiによって以下のようなRubyコードに変換されます(簡略化):

_buf = ::String.new
_buf '

'.freeze _buf @user.name ).to_s _buf '

'.freeze if @user.admin? _buf 'Admin'.freeze end _buf '

'.freeze _buf.to_s

現在の課題点

現状のActionViewとErubiの組み合わせには、いくつかの課題があることがわかりました。

1. HTML構造の非認識

Erubiは正規表現でERBタグを見つけ、それ以外の部分は単なる文字列として扱います。そのため、以下のような問題が発生します:



このコードは正常にコンパイルされ、実行されますが、生成されるHTMLは無効です。

2. エラーメッセージの限界

文字列として処理されるため、エラーが発生した際の情報が限定的です:
以下のような分かりづらいエラーメッセージを何度も見たことがあるのではないでしょうか?

Erubiのエラーメッセージ例

このエラーメッセージだけでは、どのHTML要素の中で問題が発生したのか、ERBファイルのどの文脈なのかがわかりにくい場合があります。

3. ツールサポートの困難さ

文字列ベースのアプローチでは、以下のようなツールを提供するのが困難です:

  • リアルタイム検証:タグの対応関係やHTML仕様への適合性をチェックできない
  • 補完:現在のHTML要素のコンテキストに応じた補完ができない

これらの課題は、Erubiの設計そのものに起因しています。Erubiは「文字列テンプレートエンジン」として設計されているため、HTMLの構造を理解していません[^2]。

ここから、Kaigi on Rails 2025で紹介されたReActionViewが、ActionViewに比べてどのように変わるのかをみていきます。

ReActionViewの全体構成

ReActionViewは、既存のActionViewの仕組みを大きく変更するものではありません。実際に置き換わるのはHandler部分のみで、他の部分は既存のActionViewの仕組みをそのまま利用します。

Handlerは、受け取ったERBテンプレートをRubyコードに変換する役割を担う部分です。ReActionViewでは、この変換処理にHerbパーサーを利用することで、HTML構造を理解した上でコード生成を行います。

以下のシーケンス図は、ReActionViewの処理フローのを示しています。先ほどのシーケンス図と比較してみください。

ActionView処理フロー

図からわかるように、最終的にHTMLレスポンスを返す流れは同じです。変わるのは、ERBテンプレートからRubyコードを生成するHandler部分だけです。この設計により、既存のRailsアプリケーションへの影響を最小限に抑えながら、HTML構造を理解する機能を導入できます。

Herb Parserの導入

ReActionViewで使われるHerbパーサーは、C言語で実装された高速なERBパーサーです。最大の特徴は、ERBファイルを文字列としてではなく、HTML構造として理解しながら解析することです。

Erubiが正規表現でERBタグを探して文字列として処理していたのに対し、HerbはHTML要素、タグの階層関係、属性、そしてERBコードの位置を正しく認識します。このアプローチにより、パース段階でHTML構造の妥当性を検証できるようになります。

Herbの詳細な仕組みや設計思想については、Marco Roth氏がRubyKaigi 2025で発表したEmpowering Developers with HTML-Aware ERB Toolingや、Introducing Herb: A new HTML-Aware ERB Parserをご覧ください。

Herb::Engineの仕組み

構文木(AST)ベースのアプローチ

Herb::Engineは、Herbパーサーが生成する抽象構文木(AST: Abstract Syntax Tree)を利用してレンダリングを行います。

Erubiが_bufに文字列を順次連結していくのに対し、Herb::EngineはまずERBファイル全体をASTとして解析します。これにより、HTML構造を理解した上でレンダリングが可能になります。

実際にどのような構文木が生成されるのか、簡単な例を見てみましょう。以下のERBテンプレートを考えます:



re Herbパーサーは、このテンプレートを以下のようなAST構造として解析します:

@ DocumentNode (location: (1:0)-(1:24))
├── errors: []
└── children: (1 item)
    └── @ HTMLElementNode (location: (1:0)-(1:24))
        ├── errors: []
        ├── open_tag: 
        │   └── @ HTMLOpenTagNode (location: (1:0)-(1:3))
        │       ├── errors: []
        │       ├── tag_opening: "" (location: (1:2)-(1:3))
        │       ├── children: []
        │       └── is_void: false
        │       
        ├── tag_name: "p" (location: (1:1)-(1:2))
        ├── body: (1 item)
        │   └── @ ERBContentNode (location: (1:3)-(1:20))
        │       ├── errors: []
        │       ├── tag_opening: "" (location: (1:18)-(1:20))
        │       ├── parsed: true
        │       └── valid: true
        │       
        │   
        ├── close_tag: 
        │   └── @ HTMLCloseTagNode (location: (1:20)-(1:24))
        │       ├── errors: []
        │       ├── tag_opening: "" (location: (1:20)-(1:22))
        │       ├── tag_name: "p" (location: (1:22)-(1:23))
        │       ├── children: []
        │       └── tag_closing: ">" (location: (1:23)-(1:24))
        │       
        ├── is_void: false
        └── source: "HTML"

ASTの各ノードは、以下のような情報を持っています:
HTMLElementNode
タグはHTMLElementNodeとして表現され、開始タグと終了タグが正しく対応していることがわかります。

ERBContentNode
はERBContentNodeとして表現され、HTMLとは明確に区別されています。

位置情報とエラー検出
各ノードはerrors配列を持ち、構文エラーを検出できるようになっています。

Erubiでは、これらの情報はすべて「文字列の連結順序」としてしか扱われていませんでした。しかし、Herb::Engineは構造を理解しているため、実行前にHTML妥当性を検証できます。
例えば、以下のようなhtmlの閉じタグ忘れのあるコードの場合:



ASTは以下のように閉じタグの不足をエラーとして、MissingClosingTagErrorノードとして表現するようになります。

@ DocumentNode (location: (1:0)-(1:20))
├── errors: []
└── children: (2 items)
    ├── @ HTMLOpenTagNode (location: (1:0)-(1:3))
    │   ├── errors: (1 item)
    │   │   └── @ MissingClosingTagError (location: (1:0)-(1:3))
    │   │       ├── message: "Opening tag `

` at (1:1) doesn't have a matching closing tag `

` in the same scope." │ │ └── opening_tag: "p" (location: (1:1)-(1:2)) │ │ │ │ │ ├── tag_opening: "" (location: (1:2)-(1:3)) │ ├── children: [] │ └── is_void: false │ └── @ ERBContentNode (location: (1:3)-(1:20)) ├── errors: [] ├── tag_opening: "" (location: (1:18)-(1:20)) ├── parsed: true └── valid: true

以上により、ReActionViewはHerbパーサを利用することにより、HTML構造を理解した上でレンダリングを行い、パース時にHTML妥当性を検証できるようになります。

ここまで、ReActionViewの技術的な仕組みを見てきました。このセクションでは、実際にReActionViewを試してみることで、どのような開発者体験の改善があるのかを確認していきます。

ReActionViewを試してみる

ReActionViewは既存の.html.erbと完全互換でありながら、HTML検証やデバッグモードなどの開発者体験を向上させる機能を提供します。セットアップは非常に簡単です。

セットアップ手順

1. Gemfileに追加

gem "reactionview", "~> 0.1.4"

2. インストールと設定

bundle install
rails generate reactionview:install

3. 設定を有効化

ReActionView.configure do |config|
  config.intercept_erb = true
  config.debug_mode = Rails.env.development?
end

これだけで、既存の.html.erbファイルが自動的にHerb::Engineで処理されるようになります。

改善されたエラーメッセージ

ReActionViewを使うと、HTML構造を理解した上でエラーメッセージが生成されるため、問題の特定が容易になります。

例えば、以下のようにタグを閉じ忘れたコードがあるとします:

  • HTML検証

    テンプレートのHTMLを自動的に検証します。

  • このページにアクセスすると、以下のような詳細なエラーメッセージが表示されます:

    ReActionViewのエラー画面

    このエラーメッセージを見ると、

    タグの閉じ忘れであることを一目で確認することができます。従来のActionViewでは実行時にしかわからなかった問題が、ReActionViewではパース時に正確に検出され、わかりやすくフィードバックされます。

    デバッグツール(View Annotations、ERB Output Hover)

    ReActionViewは、開発者がビューの構造を理解しやすくするためのデバッグツールも提供しています(開発環境のみ)。

    View Annotations

    config.debug_mode = trueに設定すると、ブラウザでレンダリングされたHTMLに、どのView/Partial/Componentから生成されたかを示す境界線が表示されます。複雑なビュー構成でも、どのPartialがどの部分を担当しているのかが一目瞭然です。

    ERB Output Hover

    ブラウザの開発者ツールでHTML要素にホバーすると、その要素を生成した元のERBコードが表示されます。最終的なHTMLから、どのようなERBテンプレートで生成されたのかを逆引きできるため、デバッグ効率が大きく向上します。

    これらのツールは、大規模なRailsアプリケーションでビューのデバッグをする際に、特に威力を発揮するのではないでしょうか。

    埋め込みRubyのホバー表示例

    HTML検証とリアルタイムフィードバック

    ReActionViewのもう一つの大きな特徴は、リアルタイムでHTML検証を行う点です。

    先ほどの「改善されたエラーメッセージ」で見たように、タグの閉じ忘れなどはサーバー起動時やアクセス時に検出されます。しかし、将来的にはエディタ統合により、コードを書いている最中にリアルタイムで検証できるようになることが期待されています。

    Marco Roth氏は、Herbを活用した以下のツールも開発しています:

    • Herb Language Server:エディタでのリアルタイム構文チェック
    • Herb Formatter:ERBファイルの自動フォーマット
    • Herb Linter:ベストプラクティスやアクセシビリティのチェック

    これらのツールを組み合わせることで、TypeScriptやRustのような強力な開発体験を、ERBテンプレートでも実現できる未来が見えてきます。従来は「書いて実行してみないとわからない」だった問題が、「書いた瞬間にわかる」に変わるのです。

    本記事では、Kaigi on Rails 2025で紹介されたReActionViewについて、その技術的な仕組みから実際の開発者体験の改善まで見てきました。

    従来のActionViewとErubiは、ERBテンプレートを文字列として処理していたため、HTML構造の検証やリアルタイムフィードバックが困難でした。ReActionViewは、Herbパーサーによるアプローチで、この長年の課題を解決しようとしています。
    抽象構文木(AST)を活用することで、パース時にHTML妥当性を検証し、エラーメッセージを改善し、デバッグツールを提供できるようになりました。


    Techouseでは、社会課題の解決に一緒に取り組むエンジニアを募集しております。
    ご応募お待ちしております。

    jp.techouse.com




    元の記事を確認する

    関連記事