Rails の link_to_if にブロックを渡したときの挙動が分かりづらいので、使う場合の注意点をまとめてみました。
TL;DR
ブロックの内容を条件によってリンクにしたい場合は capture を使うのがシンプルで良いです。
<% body = capture do %> <%= image_tag 'icon.png' %> <span><%= user.name %></span> <% end %> <%= link_to_if condition, body, root_path %>
link_to_unless や link_to_unless_current の場合も同じ。
link_to_if にブロックを渡す例
ユーザーのアイコンと名前を条件によってリンクにする場合を考えます。*1
<% if condition %> <% link_to root_path do %> <img href="icon.png"></img> <span><%= user.name %></span> <% end %> <% else %> <img href="icon.png"></img> <span><%= user.name %></span> <% end %>
これを DRY にするために link_to_if
を使おうとしても、うまく動きません。
<% link_to_if condition, root_path do %> <img href="icon.png"></img> <span><%= user.name %></span> <% end %> # If the condition is true # => <a href="/">/</a> # else # => <img href="icon.png"><span>name</span>
条件が true の場合、ブロックが 評価されない からです。
ブロックを必ず評価するメソッド
条件が true
の時にaタグで囲むメソッドを作ってみました。
def link_to_block_if(condition, options = {}, html_options = {}, &block) if condition link_to(options, html_options, &block) else capture(&block) end end
使い方はこんな感じ。
<% link_to_block_if condition, root_path do %> <img href="icon.png"></img> <span><%= user.name %></span> <% end %>
Rails へプルリクを送ることを検討
rails/rails で検索してみると、 link_to_if
でブロックを必ず評価するプルリクが出て、1度マージされた後 revert されていました。
- Update url_helper.rb by stevenspiel · Pull Request #19844 · rails/rails
- Fixed behaviour, when block wasn't passed to link_to from link_to_if(condition = true) method by avdept · Pull Request #19936 · rails/rails
また、 Qiita にも同じような記事がありました。
似たようなことで困っている人がいることは分かったので、 Rails にプルリク送ってみても良いかもしれないと考えた。
メソッド名の検討
link_to_block_if
は微妙なので、他のメソッド名を考えてみましたが、どれも微妙・・・。
- link_to_content_if - link_or_content_tag - wrap_link_to_if
link_to_if
や cache_if のように _if
のメソッドは条件が true
の時にブロックが評価されない。
また、 find_or_create_by のように _or_
のメソッドは前半が true
の時にブロックが評価されない。
Rails の他メソッドと一貫性の無いメソッド名になるのはダメそう。
命名に時間がかかるメソッド
そもそも、命名に時間がかかるメソッドは何かがダメです。 そこで、1つのメソッドにする事を止めて、下記のように2つのメソッドで書いてみました。
<% body = capture do %> <%= image_tag 'icon.png' %> <span><%= user.name %></span> <% end %> <%= link_to_if condition, body, root_path %>
あれ、これで良いのでは・・・。Rails の標準メソッドの組み合せなので、後から読んでも分かりやすいし。
まとめ
link_to_if
にブロックを渡した時の挙動は少し分かりづらい- ただ、 Rails の
_if
のメソッドとしては一貫性がある - 命名に悩むような難しいメソッドは、そもそも設計がダメ
Rails にプルリクを投げる前に気づくことができて良かった。
RubyKaigi 2016 で色んな Rubyist に link_to_if
について話を聞いて、助言を頂いたお陰です。感謝。