Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails: ビューのパーシャルではローカル変数だけを使うこと(翻訳)

概要

元サイトの許諾を得て翻訳・公開いたします。

なお、Railsのmainブランチにマージ済みの#45602では、ビューテンプレートに渡してよいローカル変数をマジックコメントで制限できるようになります。おそらくRails 7.1から利用可能になると思われます。

参考: Allow templates to define which locals they accept by joelhawksley · Pull Request #45602 · rails/rails
参考: 週刊Railsウォッチ20220822 テンプレートが受け取れるローカル変数をマジックコメントで定義可能になった

Rails: ビューのパーシャルではローカル変数だけを使うこと(翻訳)

Railsのビュー層は、ユーザーや顧客がサービスにアクセスする主要な手段です。ビュー層は非常に柔軟性が高いのですが、その分管理が難しくなりがちです。

複雑なRailsアプリケーションでビューやパーシャルを多用していると、バグやスコープの問題が発生しやすくなります。

以下のように書くのではなく

パーシャル内でインスタンス変数に直接アクセスする。

<!-- パーシャルを呼び出す -->
<%= render "user_email" %>
<!-- _user_email.html.erbパーシャルの内部 -->
<%= @user.email %>

以下のように書くこと

コンテキストをローカル変数で渡す。

<!-- パーシャルを呼び出す -->
<%= render partial: "user_email", locals: { user: @user } %>
<!-- _user_email.html.erbパーシャルの内部 -->
<%= user.email %>

そうする理由

これは主に、複雑なプロジェクトでメンテナンス性とコード編成をより良好に保つための優れた方法です。Railsのビュー環境は非常に多くのグローバルスコープを明示的に容認していますが、私を含むベテラン開発者の多くが、パーシャルの外にある変数を触って痛い目にあった経験があります。

さらに、パーシャル内でインスタンス変数やヘルパーメソッド(Railsの CurrentAttributesが提供するcurrent_userなど)を利用しないようにすることで、キャッシュがより確実に効くようになります。理由は、キャッシュキー生成で必要なコンテキストが、パーシャルに渡す値だけで済むようになるからです。このキャッシュキーがないと、無効なデータを持つキャッシュされたパーシャルを配信してしまう可能性が生じます。

以下の例を見てみましょう。

<!-- パーシャルを呼び出す -->
<%= render partial: 'things/thing', collection: @things, cached: true %>
<!-- things/_thing.html.erbパーシャル -->
<%= thing.name %>
<%= link_to "Edit", edit_thing_path(thing) if @user.admin? %>

ここで問題となるのは、パーシャル内にある@userのユーザーです。

複数のユーザーがキャッシュから同じパーシャルを取り出すとき、どのユーザーが最初にページをレンダリングしたかに応じて、"Edit"へのリンクが表示されたりされなかったりすることになります。しかも、このリンクが非adminユーザーに誤って表示されてしまうとセキュリティホールになる可能性があります。

そうしない理由があるとすれば

強力なグローバルスコープに万全の注意を払えるのであれば、使っても「害」にはなりません。Railsの並外れた柔軟性と「何でもできる」能力は、Railsを使う大きなメリットの1つであることを最初に学びます。しかし、プロジェクトの規模が大きくなり続けると、この柔軟性が思わぬバグの原因になる可能性もあります。この柔軟性は、The Rails Doctorineでも"切れ味の鋭い刃物"と呼ばれているものの1つです。便利ですが、油断するとケガをするかもしれません。

あえて柔軟性を重んじる道を選ぶのであれば、十分練り上げられた網羅的なテストによって上述のような問題から保護できるかもしれません。

推奨事項

ビューのパーシャルは、極めて良好な形でマーシャリング1されていない限り、たちまちカオスに陥る可能性があります。グローバルステートを想定せずに使ったとしたら、なおさらです。

大規模プロジェクトでは、何らかのコンポーネントシステムの導入を検討するとよいでしょう。Rails世界で主要なソリューションは、GitHubでも使われているViewComponentです。他にはPhlexというライブラリもあり、非常に活発に開発されています。

viewcomponent/view_component - GitHub
phlex-ruby/phlex - GitHub

関連記事

実践ViewComponent(1): 現代的なRailsフロントエンド構築の心得(翻訳)

実践ViewComponent(2): コンポーネントを徹底的に強化する(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。