💁

Rails 部分テンプレートで defined? を避けて、local_assigns や locals マジックコメントを使おう

に公開

はじめに

Ruby on Rails には、部分テンプレート(パーシャル)という機能があり、View を分割して再利用できます。
その部分テンプレートで、以下のようなコードを見たことはありませんか?

<div>
  <h1>
    <% if defined?(title) %>
      <%= title %>
    <% else %>
      未設定
    <% end %>
  </h1>
  <% if defined?(body) %>
    <p><%= body %></p>
  <% end %>
  <% if defined?(comments) && comments.present? %>
    <ul>
      <% comments.each do |comment| %>
        <li><%= comment %></li>
      <% end %>
    </ul>
  <% end %>
</div>

このように defined? を使って、部分テンプレートに変数が渡されたかどうかで表示を切り替える方法があります。
私自身、昔から Rails を使っていてよく見かけていたので、とくに気にしていませんでした。
ただし、この方法は現状ではあまり良い方法とは言えません。
では、なぜ良くないのか、どのように改善できるのかを考えてみます。

defined? がなぜ良くないのか

defined? を使うことで、変数が存在するかどうかを簡単に判定できますが、いくつか問題点があります。

  • コードの可読性や保守性が下がる
  • 意図せず変数が生成されて挙動が変わる
  • タイプミスした変数名でも defined? だと false になり、意図しない動作になる

改善方法

local_assigns を使う

Rails の部分テンプレートでは、local_assigns というハッシュでローカル変数の有無を判定できます。

<div>
  <h1>
    <%= local_assigns[:title] || "未設定" %>
  </h1>
  <% if local_assigns[:body].present? %>
    <p><%= body %></p>
  <% end %>
  <% if local_assigns[:comments].present? %>
    <ul>
      <% local_assigns[:comments].each do |comment| %>
        <li><%= comment %></li>
      <% end %>
    </ul>
  <% end %>
</div>

この方法なら、親ビューから明示的に渡された変数だけを判定できます。

参考

locals マジックコメントを使う

Rails 7.1 以降では、部分テンプレートの先頭に # locals: コメントを書くことで、必要なローカル変数を明示できます。

<%# locals: (title:, body:, comments: []) %>
<div>
  <h1><%= title || "未設定" %></h1>
  <% if body.present? %>
    <p><%= body %></p>
  <% end %>
  <% if comments.present? %>
    <ul>
      <% comments.each do |comment| %>
        <li><%= comment %></li>
      <% end %>
    </ul>
  <% end %>
</div>

この方法を使うと、変数が渡されていない場合にエラーとなり、意図しない挙動を防げます。

参考

まとめ

部分テンプレートで変数の有無を判定する際は、defined? ではなく local_assignslocals マジックコメントを使うことで、より安全で可読性の高いコードになります。

最後まで読んでいただきありがとうございます。この記事が少しでも役に立ったと思ったら、Like♥ を押していただけると励みになります。

Discussion