`Layout/LineLength` のデフォルトを 120 にした

RuboCop の LineLength のデフォルトが 80 ではつらいという声はずっと聞いていて、そもそも RuboCop にパッチを送り始めた頃は、いつかこの変更をするのが目標のひとつだった。

今回 3年半越しに実現することができた。現在の最新版の RuboCop 0.84.0 に適用済み。

github.com

OSS で年月をかけた粘り強い振る舞いは kamipo さんから学んだことで、RangeError にまつわる rails/rails#30000 もそこに到るまでを含めてコントリビュータ時代から何年か掛けていると聞いている。

RuboCop リポジトリ自体の方では、その後にコアメンバーからエディタを縦分割した際に 120 だと厳しいという声があり、RuboCop リポジトリ自体の設定は 100 になったというエピソードがある (デフォルトの設定は 120 で維持されています) 。RuboCop 自体も無数にあるリポジトリのひとつなので、アクティブメンテナーが過ごしやすい設定に歩み寄った形。

実際のところ 80 から 120 になると、本来名付けたかった名前への省略や、本質的に不必要な行の折り返しなんかを考える yak shaving な手間が減るという効能を実感しているので、80 では厳しいと思っていた人には良いのではと思う。従来の 80 がお気に入りの場合は Layout/LineLength を 80 にセットしてください。

Rails 6.0で不要なArel.sqlを減らす

Rails 5.2 で以下のように Arel.sql を使ってセキュアな文字列であることを明示するという警告が出るようになっていたのは、多くの Rails アプリケーション開発者が通ってきているように思える。

DEPRECATION WARNING: Dangerous query method (method whose arguments are used as
raw SQL) called with non-attribute argument(s): "対象のSQL文字列".
Non-attribute arguments will be disallowed in Rails 6.0. This method should not
be called with user-provided values, such as request parameters or model
attributes. Known-safe values can be passed by wrapping them in Arel.sql().

新しい Rails であれば、不必要な Arel.sql (いわゆる false alarm) を抑制できるという話を思い出したので、rails/rails のコミットを調べて Oracle も同様の対応をした。 余談だが OracleNULLS FIRST, NULLS LAST をサポートしているのでその対応も入っている。

github.com

新しい Rails と言っているのは、この Oracle 向けパッチを書いた時点では Arel.sql の false alarm への抑制に次の Rails 6.1 が必要だと思っていたところ kamipo さんから Rails 6.0 にも入っているというコメントをもらった (なんといういい仕事!) 。

なので Rails 6.0 にして不要な Arel.sql を除去すれば、いきなり Arel という文字列を見てなんぞという初見でちょっとビビらせる機会が減るのではと思う。

Oracle enhanced adapter の 6.0 ブランチの方にもバックポートしておいたので、そちらは次のリリースで対応される見込みです。

Rails 5.2が出る頃に Arel.sql について日記を書いていたので、今回 2年半越しに良い話を書けたのだった。

koic.hatenablog.com

RuboCopドキュメントのAsciiDoc化

今日は代休をとっていた。RuboCop の方で公式サイトというかランディングページが刷新された。

rubocop.org

サイトといえばドキュメントいうわけで、そちらは Markdown から AsciiDoc に置き換えようという動きがあって、ドキュメント生成タスクまわりを AsciiDoc 対応したりしていた。

github.com

バックエンドとしては Antora を使うよう Read The Docs から移管が進められる。

antora.org

将来的に CIDER のように検索窓がつく予定なので、ユーザーとしてはドキュメントへの検索の使い勝手が良くなるんじゃないかなと思う。

docs.cider.mx

このあたりコンテンツ同期やデプロイまわりの整備などもあって今後整っていくと思う。

Ruby Banitsa THURSDAY, 21ST OF MAY

昨晩というか今日の JST 1:30 くらいから Bozhidar が RuboCop 1.0 の話をするというのでインターネット講演を視聴していた。

rubybanitsa.com

たぶんブルガリア方面のローカル Ruby コミュニティのようで、ツールは Zoom で 34 人くらい参加していたと思う。 ブルガリア語 (?) の講演だったっぽいところ、Genadi から英語に切り替えようか?とチャットが来たけれど、スライドは英語だったのでそのまま続けてもらって視聴していた。あと講演が終わった後のチャットがブルガリア語っぽくて、それはそれで新鮮な世界だった。もちろん読めたりはしないけれど。

他にも Palkan とか参加していたように思えたけれど、みんな元気そうでよかった。

話としては RubyKaigi 2018 で Bozhidar が来日講演した内容に RuboCop 1.0 で実現されていることの話がミックスされていて、自分が RubyKaigi 2020 で話そうとしていたこととオーバーラップしている部分もあった。これはどこかでまとめて話すと思うので割愛。

ともあれ地球の裏側のテックトークに瞬時に参加できて、インターネットは本当にえらいと思った。

あと、RuboCop 1.0 は来週、再来週にはリリースされるんじゃないかなと思う。

自分としてはもういくつかやりたいことがあったけれど、時間は有限なので RuboCop 1.0 以降に持ち越しです。とりあえずコントリビュータ時代から足掛け 3年半越しでの LineLength のデフォルト値を 80 から拡張できたので、1.0 の目玉になるもののひとつはできたんじゃないかなと。

このあたりの話に興味があれば、来月に勤務先の Rails/OSS パッチ会をオンラインで再開するのでよければどうぞ。

blog.agile.esm.co.jp

Ruby 2.7 と Rails 6.0 / Rails 5.2 への展望と懸念

Ruby 3.0 に向けた Ruby 2.7 と Rails の状況がめまぐるしく変わっているようなので、現在持っている知識のスナップショットを書き残しておきます。

Rails 6.0 系と Ruby 2.7 系

昨日、Matz によって今後リリースされる Ruby 2.7.2 でキーワード引数の分離への警告出力が Ruby 本体側で取りやめになるだろう旨の投稿が Rails の Discuss フォーラムにありました。

discuss.rubyonrails.org

これにより以下の組み合わせでは、キーワード引数の分離への警告が出なくなる見通しが考えられます。

というか警告が抑制される場合の Ruby 2.7.2 はジョーカーなので、Rails 6 系での Ruby 2.7 普及バージョンになる気がしています。

Rails 5.2 系と Ruby 2.7 系

さて、Ruby 2.7.2 でキーワード引数の分離への警告が出なくなったので、Rails 5.2 系ユーザーについても Ruby 2.7 に気兼ねなくアップグレードできるかというと、Ruby 2.7 で入った Monitor の非互換が Rails 5.2 系にはいまのところバックポートされていないようで、これがどのようなことを招くのかが (私は) よく分かっていません。

いちおうこのことについては、勤務先の Idobata をとおして amatsuda 顧問に伝えたので、何かしらの動きがあるかもしれません。

既存の Rails 5.2 系ユーザーが取れる手順としては、Rails 6.0 にアップグレードしてから Ruby 2.7 系にアップグレードするというのが、現状のリスクを減らした移行手順になりそうです。

Rails 5.1 系以下

Rails 5.1 以下はすでにサポートされていないので、Rails 5.2 系以上にアップグレードしましょう。

guides.rubyonrails.org

(Ruby 2.7 系と Rails 5.2 系の組み合わせへの懸念については、yahonda さんに教えて頂きました。)

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 メソッドへの置換方法がある?) 。

Faker のコミット権をもらった

昨年くらいに Faker の org メンバーになっていたけれど、リポジトリの PR がたまっていている状態をどうにかしたかったので、そこのチームにコミット権を付与してもらった。

github.com

アクティブなチームメンバーがマージできるようになったのではと思う。