RubyKaigi 2022 に登壇した

RubyKaigi 2022 に登壇した。

rubykaigi.org

当日の発表スライドは以下です。

今回はリアルタイムでのリモート登壇でした。

本編

概ねサーバーモードなしの RuboCop のブートと、サーバーモードありの RuboCop のブートの違いと中身の概略について話せたかと思います。

今回話した高速化は、rubocop のブートに関わるモジュールローディング部分であり、解析自体が速くなったわけではないですが、起動時間の高速化ということで繰り返し起動することの多いツール特性に対しては有用な高速化だったかなと思います。

ふつうに使っている分には rubocop --server としてオプションありで使って問題ないと思いますが、RuboCop Performance や RuboCop Rails といったサードパーティー gem への bundle update があると、現状ではそれらを検知できないので rubocop --restart-server が必要になります (RuboCop 本体のアップデートは検知して、自動再起動するようにしています) 。Spring のように起動しているとハマったときに面倒みたいなことを減らせるよう、このあたりは早めに対応しようかなという気持ち。

ちなみに .rubocop.yml の構成を変えた場合は rubocop --restart-server は不要です。これは現状だとサーバー側で実行のたび設定読み込まれ、構成変更が都度反映されるためです。ただ、これは予期せず実装がそうなっていたというもので (!) 、将来的にこのあたりへのキャッシュ導入 (と変更検知導入) とのベンチマーク次第で少し動きが変わるかもしれません。

サーバーモードの実装で一番どうするか悩んだのはコマンドラインインタフェースをどうするかでした。そのあたりの話はコードとしては残っていませんが、決定を下した経緯として話しておこうと思って私の頭の中にしか残っていないことと、プルリクエストで行った議論をベースにしてコンテンツに含んでおきました。完全にうなすけくんの言ってくれているこれ。

また、本編で話していないプロセスの基礎的な話は『なるほどUnixプロセス ― Rubyで学ぶUnixの基礎』がおすすめなので、参考図書として記しておきます。

tatsu-zine.com

サーバーモードについては公式ドキュメントにも記載していますので、どうぞお試しください。

docs.rubocop.org

(おまけ) 放送事故

バーチャル会場の音声をつけっぱなし (というのにあとで気づいた) のままヘッドホンをしていたので、話すのに集中するためヘッドホンを外したら同期されていたらしいマイクも切れて、無音放送になるという放送事故の登壇実績を解除してしまいました。読唇術の技術を求めてしまってすみません。

タイムキープ用に手元に置いていたスマホに、勤務先の事業部長の @m_pixy から電話があって気づきました。おかげで本題では音声復旧できました (無音になった導入部分は時間の都合であきらめて先に進みました) 。音声トラブルで電話をくれた @m_pixy は m_pix "y" だったので、まさか matz のキーノートが自分にとってこういった伏線になるとは。@m_pixy ends with "y".

放送事故中、メンションしてくれた方々ありがとうございました。のちのリアルタイムリモートスピーカーに伝えられる教訓としては、ヘッドホン、イヤホンを外すときは連動してマイクオフにならないか気をつけましょう (気を付けます) 。

最後に、RubyKaigi チームのみなさん、通訳のみなさんありがとうございました。また、@yahonda さんに、昨年に引き続き事前に英文や構成レビューをしていただきました。いつもありがとうございます。

そして、miwa さんのツイートによると、だいたい満席だったようでありがとうございました!RubyKaigi はドラマがあって良いですね。

2022年9月12日(月) 追記

同僚で登壇者でもある @fugakkbn が、Togetter でこの登壇のまとめを作ってくれていました。ありがとうございます!

togetter.com

2022年9月13日(火) 追記

さっそく VS CodeServer モードの連携を試してくれた結果を文書にまとめていただけたようです。cumet04 さん、ありがとうございます!

zenn.dev

RubyKaigi 2022 に登壇します

RubyKaigi 2022 に『Make RuboCop super fast』というタイトルで登壇します。

私の登壇は2日目である 2022年9月9日(金) の 11:30-12:00 です (昨年と同じ日時だった) 。

rubykaigi.org

最近 RuboCop に追加した server mode の話です。server mode 導入のはじまりの PR は以下。

github.com

server mode の導入に至った課題点とその解決の仕組み。現状での弱点や今後の展望といった利用者として知っておくと良さそうな話なんかも含めて話す予定です。server mode は絶賛開発途上でもあるので、割りと鮮度高めの話になるのではと思っています。

また、今年の RubyKaigi には勤務先の永和システムマネジメントから同僚の @ima1zumi@fugakkbn も登壇します。お楽しみに。

test-queue 0.5.0 をリリースした

前回の v0.4.2 のリリースが 2017年1月らしいので、5年越しのリリースになった。

rubygems.org

自分が欲しかったのは以下の警告を抑制するパッチだったけれど、前回リリースからの差分が他にもあったので、ざっと見ておいたことも書き残しておく。

rspec-queue spec
Starting test-queue master (/tmp/test_queue_69954_4740.sock)
/Users/koic/.rbenv/versions/2.7.6/lib/ruby/gems/2.7.0/gems/test-queue-0.4.2/lib/test_queue/runner.rb:299: warning: instance variable @early_failure_limit not initialized

今回 v0.5.0 リリース前に確認したところ、TEST_QUEUE_SLAVE_MESSAGE という環境変数設定の名前が、非推奨警告も何もなくカジュアルに TEST_QUEUE_REMOTE_MASTER_MESSAGE に変更されていてユーザーフレンドリーじゃないだろうと、既存の環境変数は非推奨警告付きで利用可能な形にしておいた。

`TEST_QUEUE_SLAVE_MESSAGE` is deprecated. Use `TEST_QUEUE_REMOTE_MASTER_MESSAGE` instead.

いちおう slave という言葉は控えましょうという世論が背景に強かった頃の変更らしいけれど、変更先に master が入っているのはいいのか?という疑問は残りつつ、私自身はベターネームを付けたいための時間も強い意思もないので、そこはそっとしておいた。

ということで、v0.5.0 についての変更要点は以下を参照してください。

github.com

今後、test-queue 開発の内部改善としては CI 環境が travis.org のままで動いていないので、travis.com 移行をするかどうするかというのがあり、そのあたりは GitHub Actions か何かに引越しすれば良いだろうかというお気持ち。

ユーザー向けの機能的には RSpec 4 (beta) に向けた PR が来ていて、そのレビューとかあるけれど、RuboCop で試してみたらどうも RSpec 4 対応のそのパッチ自体不正確なようで期待どおりの動きをしていないのだった (端的にいうとパスすべきテストが落ちている) 。とりわけ RSpec 4 (beta) の API がどの程度安定しているのかも分かっていないし、急ぐ話でもないので追い追い無理ないくらいに。

とりあえず、RSpec 4 が出る頃には次のバージョンを出そうかなという気持ちはあるものの、他とのトリアージなので、もし急ぎの何かがあればメンションなりしてください。

test-queue のコミッターになった

Ruby の parallel test runner である、test-queue のコミッターになった。

github.com

数年前に送った自分のパッチのリリースが欲しくて、RubyKaigi 2014 のクロージングキーノートスピーカーでもある @tmm1 とやりとりしたのがきっかけ。@tmm1 は現在使っていないとのことで、私の方でいくらかのメンテナンスを巻き取る予定でリリース権ももらっています。

現状でサッとリリースできないか最新安定版 v0.4.2 からの差分を眺めたところ、緩和すべき Breaking Change があったりしたので、もろもろ調整した後にリリースする予定です。

RuboCop 1.30 で非推奨になった自動修正オプション名

RuboCop 1.30 で、自動修正する際のオプションである --auto-correct--auto-correct-all は非推奨になり、それぞれ --autocorrect--autocorrect-all になりました。

変更対象のオプション名は long name のみで、short name である -a-A に変更はありません。

まとめると、自動修正に関するオプションは今後以下が正です。

安全な自動修正

  • rubocop -a
  • rubocop --autocorrect

安全でない自動修正

  • rubocop -A
  • rubocop --autocorrect-all

今回の変更は自動修正に関する用語として混在していた auto-correct と autocorrect を、autocorrect の方に統一することを目的としたものです。

なお、この変更が行われた PR は以下です。

github.com

おそらく RuboCop 2.0 あたりで非推奨になったオプションは削除されます。

long name のオプション名を組み込んでいるプログラムはもちろんのこと、書籍やインターネット文献では非推奨になったオプション名を見かけることになると思うため、書き残しておきます。

RuboCopの解析バージョンサポートを見直した

RuboCop は Ruby の EOL バージョンについて、EOL の 1年後までサポートしています。

RuboCop の Ruby バージョンサポートといった時に「ランタイムバージョン」と「解析バージョン」の 2 つがあり、このエントリを書いている時点の最新 RuboCop 1.30 リリースまで、このふたつのバージョンが混同された状態でライフサイクルを共にしていました。

ここでそれぞれのバージョンをおさらい。

  • ランタイムバージョン ... RuboCop を実行する Ruby のバージョン。rubocop コマンドを実行する ruby 処理系のバージョンです。
  • 解析バージョン ... RuboCop が解析する Ruby コードのバージョン。.rubocop.yml の TargetRubyVersion などで指定しているバージョンです。詳しくはこちらの過去記事をどうぞ。

例えば Ruby 2.5 は RuboCop 1.28 でサポート打ち切り、Ruby 2.4 は RuboCop 1.12 でサポート打ち切りとなっていますが、これは 「ランタイムバージョン」と「解析バージョン」の両方を意味していました。

RuboCop 1.30 では、Ruby 2.5 以下をランタイムバージョンは使うことはできませんが、TargetRubyVersionRuby 2.0 以上を指定することができるようになっています。

例えば、Gem 開発で古い Ruby バージョンから新しい Ruby バージョンまでをサポートしたいというニーズや、古い Ruby バージョンのアプリケーションだけど RuboCop はアップグレードして新しい機能を使いたいといったことに対応できます。

特に前者は「RuboCop がサポートバージョンを打ち切ったため、Gem の Ruby 最小要求バージョンが繰り上げる」という事例をいくつか見てきており、RuboCop が Gem のメンテナンスサイクルに作用するのがエンドユーザーにとって幸せかという疑問を解決する形になります (TargetRubyVersionRuby 2.0 まで解析可能になったので) 。

GitHub Actions などで RuboCop をリンターとしている場合は、Ruby 2.6 以上をワークフローのランタイム Ruby バージョンに指定して、.rubocop.yml の TargetRubyVersion に最小要求の Ruby バージョンを指定すると良いでしょう。

ちなみに、Ruby 1.9Ruby 2.0 以降との非互換による Cop のメンテナンスコストの方が高くつきそうなので、いずれもサポートしません。

github.com

先日公開された『2022 Ruby on Rails Community Survey Results』の Ruby バージョンの利用率調査だと、EOL の利用は Ruby 2.6 (15%) 、Ruby 2.5 (8%) 、Ruby 2.4 (4%) 、Ruby 2.3 (3%) 、Ruby 2.2 (1%) 、Ruby 2.1 (1%) 、Ruby 2.0 (0%) 、Ruby 1.9 (2%) 、Ruby 1.8 (1%) ということで、全体の 35% くらいあるようです。そのうち Ruby 2.0 ~ 2.6 までの 32% くらいは救えるだろうか?といったところ。

rails-hosting.com

とはいえ、アプリケーションであればはやめに Ruby バージョンのアップグレードをしていけると良いですね。

Ruby 3.2.0dev にマージされた Rust YJIT をビルドする

YJIT の Rust 実装がマージされました。いまのところ今年の Shopify からの代表作ではと見ています (YJIT 自体は Ruby 3.1 で C 実装導入されている機能です) 。

github.com

そういうわけで、現在の Ruby 3.2.0dev で YJIT を有効にする場合は、Rust の処理系 (1.60.0 1.58.1 以上) が必要になります。Rust のインストールに使う rustup については以下の公式ページなどを参照してください。

www.rust-lang.org

YJIT はデフォルトではビルドされません (なので YJIT を使わないビルドであれば Rust 処理系は不要です) 。

デフォルトの YJIT なしの Ruby をビルドしている場合は --yjit オプションを渡しても、ruby: warning: Ruby was built without YJIT support と以下のように警告が出力されます。YJIT の API は存在しないので、使おうとするとエラーになります。

% ruby --yjit -e 'p RubyVM::YJIT.enabled?'
ruby: warning: Ruby was built without YJIT support
-e:1:in `<main>': uninitialized constant RubyVM::YJIT (NameError)

p RubyVM::YJIT.enabled?
        ^^^^^^

このあたり Ruby 3.1 と異なるデフォルト (のビルド) 挙動なので、YJIT を有効にしているユーザーは気に留めておくと良さそうです。

% ruby --yjit -ve 'p RubyVM::YJIT.enabled?'
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) +YJIT [x86_64-darwin19]
true

Ruby 3.2.0dev において、YJIT 込みで Ruby 本体をビルドする場合は以下のように ./configre にオプションを渡します。

% ./autogen.sh
% ./configure --enable-yjit=dev ...
% make -j install
building Rust YJIT (dev mode)
% ruby --yjit -ve 'p RubyVM::YJIT.enabled?'
ruby 3.2.0dev (2022-04-27T15:00:54Z master b43eb54a0c) +YJIT [x86_64-darwin19]
true

リリースモード --enable-yjitデバッグ開発モード --enable-yjit=dev でオプションが異なります。詳しくはドキュメントを参照してください。

github.com

rbenv を使っているようであれば、環境変数 RUBY_CONFIGURE_OPTS を使ったオプション渡しが手早いです。

% RUBY_CONFIGURE_OPTS=--enable-yjit rbenv install 3.2.0-dev
% ruby --yjit -ve 'p RubyVM::YJIT.enabled?'
ruby 3.2.0dev (2022-04-27T15:00:54Z master b43eb54a0c) +YJIT [x86_64-darwin19]
true

さらに Ruby のランタイムでも YJIT はデフォルト無効なので、YJIT を有効化する場合は ruby コマンドのオプションに --yjit を渡すか、環境変数 RUBY_YJIT_ENABLE=1 なんかを渡します。直接 ruby コマンドを使わずに rake 経由なんかで実行する場合は環境変数の指定方法を使うことになると思います。

あと当然ですが、Ruby のビルド環境に Rust がインストールされていなければ、YJIT 有効のビルド時に以下のようなエラーになります。

% RUBY_CONFIGURE_OPTS=--enable-yjit rbenv install 3.2.0-dev
(snip)

Last 10 log lines:
checking whether -pie is accepted as LDFLAGS... yes
checking whether wrapper for LD_LIBRARY_PATH is needed... no
checking whether dtrace USDT is available... no
checking for __builtin_setjmp... yes with cast ()
checking for _setjmpex as a macro or function... no
checking for _setjmp as a macro or function... yes
checking for sigsetjmp as a macro or function... no
checking for setjmp type... __builtin_setjmp
checking for rustc... no
configure: error: rustc is required. Installation instructions available at https://www.rust-lang.org/tools/install

Rust 処理系をインストールしましょう。

コード的には ruby/yjit ディレクトリ以下が、Rust の世界になっているようで、興味のある人は見てみると発見があるかもしれません。

github.com

RubyGems にも Rust 拡張に向けた Cargo ビルダーがマージされたこともあり、Ruby エコシステムと Rust との関わりが今後の関心どころです。

github.com

そういった流れもあるので、個人的には The Rust Programming Language (日本語訳) の後に『プログラミング Rust 第2版』を読んでいるところですが、痒い所に手が届く感じでおすすめです (先に The Rust Programming Language で全体の雰囲気を掴んでおくと理解がスムースかも) 。

🦀