Railsでcontent_tagメソッドの代わりにtagメソッドに使う

Rails で content_tag メソッドの代わりに tag メソッドに使うように促す cop を次の RuboCop Rails 2.6.0 で導入する予定です。

github.com

以下、bad ケースと good ケースをサンプルから抜粋します。

# bad
content_tag(:p, 'Hello world!')
content_tag(:br)

# good
tag.p('Hello world!')
tag.br

2020年5月14日追記

第一引数に変数をとるケースについては、public_send を使うくらいなら content_tag を使う方が良さそうです。

以下は元記事として残していますが、これらの方法はとらない方が良いです。


PR の説明にあるように content_tag はレガシーな API ということで、デフォルトで有効で良いと思っているものの、現状だと第一引数に変数をとる以下のケースに問題があります。

オートコレクトでコードが壊れることを防ぐため、以下の PR を問題解決のため開いていますが public_send を使っているのが気にかかっている点です。

github.com

Case 1: NoMethodError を防ぐ

元コード:

content_tag(name, 'foo', class: 'bar')

現状の auto-correct:

tag(name, 'foo', class: 'bar')
#=> NoMethodError (undefined method `each_pair' for "foo":String)

今後:

tag.public_send(name, 'foo', class: 'bar')

Case 2: symbolize_keys を使って ArgumentError を防ぐ

元コード:

content_tag(name, 'foo', {'class' => 'bar'})

現状の auto-correct:

tag.public_send(name, 'foo', {'class' => 'bar'})
#=> `ArgumentError (wrong number of arguments (given 3, expected 1..2))`

今後:

symbolize_keys を使って ArgumentError を防ぐハックをしている。 引き渡されたハッシュのキーがすべてシンボルかどうか静的解析で検知がつらいための苦肉の策。

既存アプリケーションが壊れるのを防ぐためとはいえ、この解決はつらい。

tag.public_send(name, 'foo', {'class' => 'bar'}.symbolize_keys)

Case 3: ArgumentError を防ぐため三項演算子を使っている

元コード:

content_tag(name, 'foo', options)

現状の auto-correct:

options = nil
tag.public_send(name, 'foo', options)
#=> `ArgumentError (wrong number of arguments (given 3, expected 1..2))`

optionsnil のときに ArgumentError です。

今後:

tag.public_send(name, 'foo', options ? options.symbolize_keys : {})

なかなか厳しいものがあるので、content_tag の第一引数が変数の場合は無視するという対応も考えられるものの、もっと良い tag への変換があれば教えてもらえると嬉しいです (知らないだけでもっと良い tag メソッドへの置換方法がある?) 。