Ruby 3.0.0-devでのActive Record Session Storeのエラー回避

Rails アプリケーションの Ruby 3.0 対応を裏ではじめている頃合いだと思うのですが、Ruby 3.0.0-dev (2020-10-21T09:10:14Z を使っていたけれど preview1 でも同様かも?) を使った Active Record Session Store で以下のエラーが起きた際のメモを書いておきます。

Failure/Error: super

RuntimeError:
  class variable @@silencer of ActiveSupport::Logger is overtaken by Logger

これは ActiveRecord::SessionStore::Session.logger.silence_logger を使っている場合に起きるエラーです。

本対応としては Active Record Session Store 本体への PR が既に取り込まれていましたが、このエントリを書いている時点ではまだリリースまではされていません。

github.com

未リリースのパッチを適用するひとつの方法としては Gemfile で以下のような対応をしておきます。

-gem 'activerecord-session_store'
+# Use `github` option until the following patch is released.
+# https://github.com/rails/activerecord-session_store/pull/159
+gem 'activerecord-session_store', github: 'rails/activerecord-session_store', ref: 'f188efbc'

より堅実に fork 指定をしておきたい場合は、以下のエントリで引用している sue445 さんのコメントを参考にしてください。

koic.hatenablog.com

あとはソースコードsilence_logger となっている箇所を silence にすれば Ruby 2.7 系以前の Rails アプリケーション互換で解決すると思います。

-        ActiveRecord::SessionStore::Session.logger.silence_logger do
+        ActiveRecord::SessionStore::Session.logger.silencer do

冒頭のエラーログによって Google 検索でシュッと見つけられると便利な人がいるかもしれないので書き残しておきました。

2020年11月11日追記

調査できていませんが、ActiveRecord::SessionStore::Session.logger.silencer のブロックが期待通り処理されていない可能性があるので、おかしそうな動きがあれば upstream のリポジトリへのレポートやパッチなどしましょう。

RuboCop 1.0 がリリースされた

RuboCop 1.0 がリリースされた。

github.com

自分が最初にリリースを行った 0.93.1 が RuboCop 1.0 より前の最後のリリースになったというのも感慨深いものがあるけれど、さておきついに 1.0 になった。

よい節目なので RuboCop 1.0 と周辺に関するエントリを書いておこうと思う。

0.93.1 までアップグレードしている人への主な変更

ここまでアップグレードしている人は、1.0 に向けたマイルストーンを達成したひととおりの機能を手に入れています。0.93.1 から 1.0 での主な変更点は以下です。

  • デフォルトで pending だったコアの Cop がすべてデフォルトで有効になった。DidabledByDefault: true などしているのでなければ、これが一番ユーザーインパクトのある変更だと思う。なお、今後 1 系で追加される Cop はデフォルトで pending となり 2.0 で一括でデフォルト有効になります。Performance や Rails などの拡張 Cop はコアと独立しているので pending のままです。
  • rubocop -V で使っている拡張 Cop の gem のバージョンも表示するようにしました。問題調査やバグレポートのときなどにご活用ください。
  • 拡張 Cop 開発者向けですが、on_xxx (例えば on_send) に加えて after_xxx (after_send) といったイベントフックが用意されました。イベントサイクルの前後関係を活用した実装が可能になります。
  • ネストした部署名への破壊的変更になるのですが、RuboCop::Cop::Foo::Bar::Baz といった Cop があったとき Bar が部署名だったものを Foo/Bar が部署名になるように部署の決定ルールが変わりました。おそらく最初に RSpec/FactoryBot といった部署あたりが対象になるでしょう。

新たな Cop の追加は 0.93.1 から pending にする間がないためしていません。

0.93.1 までアップグレードしていない人への主な変更

RubyKaigi Takeout 2020 や 銀座Rails などで追加機能や主だった変更について話しているので、そちらを参照してください。

RubyKaigi Takeout 2020 の発表では 1.0 までの変遷をアーキテクチャベースで話しています。

www.youtube.com

銀座 Rails では設定ファイルの解説をベースに、発表当時に新たに追加された機能など紹介を含めています。

時系列としては銀座 Rails 、RubyKaigi Takeout 2020 の順に作ったものですが、独立しているのでどちらからでも参照できると思います。

Bozhidar の最初のコミットからちょうど 7 年半の 21日にリリースが決まったのは先週。私は関わるようになってからコントリビューター時代を含めて4年くらいでの 1.0 リリースとなりました。

RuboCop 1.0 時点でのコアチームは以下のとおりで、それぞれかなりの時間を開発に費やしていると思うので GitHub Sponsors をしている開発者への応援などしてもらえるとモチベーションがあがると思います。

(卒業生に Yuji Nakayama, Evgeni Dzhelyov がいます。)

2.0 に向けてもいろいろと見ていくと思うので、リリースされた最初のメジャーバージョン 1.0 を楽しんでください。

Bozhidar の素晴らしいエントリを言及して今日は締め括ります。素晴らしいチームとたくさんのコントリビューターへ。 Congratulations!

metaredux.com

Nokogiriが1.11.0からプリコンパイル済みで配布される

Nokogiri が 1.11.0 からプリコンパイル済みで配布される (らしい) 。

このエントリを書いている時点での Nokogiri のプレリリースバージョンは 1.11.0.rc3 なので、大きな問題がなければ近日リリースの Nokogiri からという少し先取りの話になる。

おや?となったツイートは以下。

後述するイシューに詳しくは記載されていますが、Linux だけではなく macOS にも対応しているらしい。

早速手元の macOS で見てみることにする。

% time gem install --prerelease nokogiri
Building native extensions. This could take a while...
    exists /Users/koic/src/github.com/sparklemotion/nokogiri
Successfully installed nokogiri-1.11.0.rc3
1 gem installed
gem install --prerelease nokogiri  47.72s user 26.48s system 89% cpu 1:22.86 total

おや?Building native extensions という見慣れた文字がと思ったら、どうも現時点だと macOS については darwin-19 のみが対象らしい

手元は darwin-17 だったので、まだネイティブビルドが必要なのだった。

% ruby -e 'puts Gem::Platform.local.to_s'
x86_64-darwin-17

いずれにせよ新しめのアーキテクチャmacOSLinux なんかを使っているのであれば、インストール時のネイティブビルディングの待ち時間や、ビルドエラーでハマる時間が改善されると思う。

もしプリコンパイルのものを使いたくないのであれば、Bundler 経由の場合だと bundle config set force_ruby_platform true なんかで設定できるらしい。詳しくは以下のイシューを参照してください。

github.com

リリースが楽しみですね。

Parser gem のバージョニングと RuboCop の TargetRubyVersion

Ruby 2.7.2 にあわせた Parser 2.7.2.0 がリリースされた。

Parser と RuboCop の繋がりは大きいので、この機会に RuboCop で解析する Ruby のバージョン指定との関係も書いておく。

Parser のバージョニング

Parser のバージョニングは、最新の安定版 Ruby のパッチバージョンに Parser としてのリリースバージョンを加えたものになる。例えば現在の最新の安定版 Ruby は 2.7 系であり、今回リリースされた Parser 2.7.2.0 は 2.7.2 に対応した最初のリリース (0オリジン) という意味。

Ruby では安定版のパッチリリースで構文へのバックポートが入る場合があるため、安定版のパッチ版リリースごとに Parser のバージョンを揃えておくと丁寧。参考としては Ruby 2.5.5 から 2.5.6 への変更で Parser への構文バックポートを入れていることがある。

github.com

この Parser のバージョニングから察せられると思うが、Parser 2.7.1.5 から Parser 2.7.2.0 にすると Ruby 2.7.1 から Ruby 2.7.2 への構文サポートに切り替わる。つまり単一の Parser のバージョンで複数の Ruby のパッチバージョン (2.7.0, 2.7.1 と 2.7.2 など) はサポート (梱包) していない。単一の Parser のバージョンがサポートしているのは複数のパッチバージョン (2.7 系, 2.6 系, 2.5 系, 2.4 系, 2.3 系...) までとなる。

RuboCop の TargetRubyVersion との関係

RuboCop との関係だが、.rubocop.yml で構文解析する Ruby のバージョンを指定する TargetRubyVersion に指定するバージョンはマイナーバージョンまでが意味を持つ。

TargetRubyVersion: 2.7

たまにパッチバージョンまで指定されているのを見るが意味がない (警告を出すようにしても良いかもしれない) 。

どういうことかというと、パッチバージョンの指定は依存している Parser のバージョンに依存する。いまどきなら Gemfile (Gemfile.lock) から依存している Parser のバージョンが 2.7.1.5 なら Ruby 2.7.1 での構文解析をし、2.7.2.0 なら Ruby 2.7.2 系での構文解析をする。

つまり RuboCop が解析する Ruby のパッチバージョンは .rubocop.yml ではなく、Gemfile (Gemfile.lock もっというとランタイムの Parser のバージョン) からのバージョンが意味を持つ。

最新でない過去の Ruby バージョンとの関係

Parser 自体は Ruby 1.8 からの 2.7 (experimental を含めると 3.0) までの構文を梱包している。つまり Parser 2.7.2.0 をインストールすると Ruby 2.7.2, 2.6.6, 2.5.8, 2.4.10, 2.3.10, さらに 2.2 以下の構文サポートが入る (実装的には MacRuby や RubyMotion の構文解析も入るようだがよく知らない) 。

執筆時点での次の Ruby 2.6 系となる Ruby 2.6.7 が将来リリースされたらリリースされる Parser はどうなるかというと、その時の最新安定版 Ruby が 2.7.2 であれば Parser 2.7.2.x となる (x の部分が繰り上がり) 。リリースされるのは Parser 2.6 系ではない点に注意。このようにちょっと複雑だったりするので、面倒だなと思う人は常に Ruby と Parser のバージョンを上げておくと概ね足並みがそろっていくと思う。

この記事を書きつつ Parser のリリースバージョンがサポートしている Rubyマトリックスのドキュメントがあると便利だと思う人がいるかもしれないと思ったけれど、自分の時間を使うには他にやることが無限にあるので、過去を紐解く時間を捻出したドキュメント化とその提案なんかが好きな人がいればお任せしたい。

ちなみに以下の PR に書いたように、今回の Ruby 2.7.1 から 2.7.2 への Parser gem としての構文解析ポーティングはないが、Ruby ランタイムバージョンと Parser の解析バージョンの不一致警告を防ぐ意味合いでもバージョンを揃えておくと気持ち的に平穏だと思う。

github.com

Kaigi on Rails STAY HOME Edition に登壇した

Kaigi on Rails STAY HOME Edition に『TDD with git. Long live engineering』というタイトルで登壇した。

kaigionrails.org

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

Zoom での発表だったけれど、ふだん使いは Google Meets や Slack Call がメインだったりするので、メジャーなツールと知っているのとは裏腹に Zoom は個人的には不慣れなツールというあたりにバタついてしまっていた。それもあって当日、発表者のリアルタイム配信もしようとしていたけれど、そこは断念した (たぶん自分の発表風景は表示されていなかったと思う) 。Kaigi on Rails チームからは事前の接続リハーサルが別日に用意されていたので、そこで接続試験しておくべきだったようだ。オンライン時代登壇への準備不足よ、、、このあたりはいろいろと反省。

発表中はデスクトップに出ている Zoom でスタッフの yuki3738 が相槌をうってくれたりしていて、オンライン発表に付いてくる寂寞感みたいなものを払拭してくれていてありがたかったです。どうもありがとう。

本編ですが、タイトルは DHH の "TDD is dead. Long live testing." が元ネタ。

yattom.hatenablog.com

発表ネタのあたためについて少し触れると、私はふだんメモレベルでのパッチネタや CFP ネタなんかを Basecamp で管理しているのですが、どうも今年の7月2日にはストックしていたネタだったので3ヶ月越しで世に出せたようです (当時はちょっと違ったタイトルで考えていたらしい) 。世に出す良い機会をありがとうございました。

f:id:koic:20201005140817p:plain

本編裏のチャットで流れていたコメントについて、割と重要な点をスキップしてしまっていた部分をこちらの日記の方でひとつ補足。この講演で話していた git のコミット編集とは Pull Request 中のもので、master (main) に対しては編集しないのは前提。特にあとでスライドの編集などは予定していないので、本コメントでパッチに代えます。

また、本編中で「良ければご参照ください」としていた以下のページについては画像として貼っておきます。あくまで一例なのでご参考程度に。

f:id:koic:20201005132442j:plain

スケジュールの関係で懇親会への参加はできなかったのですが、kakutani さんからは以下のフィードバックをもらってました。ありがとうございます。

Kaigi on Rails はまた来年も開催されるそうで楽しみにしています。ありがとうございました。

Kaigi on Rails STAY HOME Edition に登壇します

今週末 2020年10月3日(土) に開催される Kaigi on Rails STAY HOME Edition に『TDD with git.
Long live engineering.』というタイトルで登壇します。

kaigionrails.org

私の登壇は 16:20-16:40 です。

概ね話はできていて、今回はアジャイルソフトウェア開発に寄った話です。

また、今回は同僚の yucao24hours9sako6 もそれぞれのセッションで登壇します (9sako6 は今年の新卒氏です) 。登壇3人分について詳しくは yucao24hours が勤務先のブログの方の方に書いてくれているのでどうぞ。

blog.agile.esm.co.jp

私の登壇はライブ配信を予定しています。オンラインでお会いしましょう。

RubyKaigi Takeout 2020 に登壇した

RubyKaigi Takeout 2020 に登壇した。

本編登壇は RubyKaigi 2018 以来 2 回目。去年 RubyKaigi 2019 では LT 登壇で TracePoint を使うことによって仕込んでしまったバグの話をしていた

今回のスライドは以下です。

RuboCop 1.0 に向けた話

本編から RuboCop 1.0 に向けた話をいくつかピックアップしておきます。後述の収録への問題があったため、特に終盤テンポが上がっていることからテキストとして少し補足しておきます。

拡張 API の変更

拡張 Cop の開発者向けの API は以下のように変更になります。

これまでの API:

class CustomCop < Cop
  def on_send(node)
  end

  def autocorrect(node)
  end
end

これからの API:

class CustomCop < Base
  extend AutoCorrector

  def on_send(node)
    add_offense(node) do |corrector|
    end
  end
end

従来の RuboCop::Cop::Cop はすぐに削除されるものではない soft deprecated なものですが、今後は RuboCop::Cop::Base を使うことをすすめます。RuboCop 自体もまだ移行中です。依存している Parser 実装から変わっているため、まだ予期していない不具合を持っている可能性があります。何か問題を見つけたら rubocop-hq までご連絡ください。

Safe / SafeAutoCorrect

RuboCop には SafeSafeAutoCorrect という概念があります。

Safe偽陽性を起こすことなく検出できるかどうかによって、避けることが難しい偽陽性が目立ったものは安全でない Safe: false がマークされています。

SafeAutoCorrect は振る舞いの互換性を維持できるかどうかです。こちらも RuboCop の提案による変更で非互換を起こすものは SafeAutoCorrect: false が設定されています。また偽陽性を持ったままの変更したことで壊れることを防ぐため Safe: false とされているものは SafeAutoCorrect: false です。

とりわけ最近の重要な変更点として、rubocop -a オプションの挙動変更と rubocop -A オプションの導入です。前者は安全な Cop でのみ自動修正を行うもので、後者は安全でない Cop も含めて自動修正を行うものです。

RuboCop 0.86 以前では、frozen stirng magic comment を追加する場合は rubocop -a でしたが、frozen string になったことで破壊的メソッドへの呼び出しがあるような場合など非互換の変更になる可能性があります。このような安全でない Cop での自動修正を実行したい場合は rubocop -A コマンドを使うことになります。

以下のエントリも参考にしてください。

koic.hatenablog.com

pending ステータス

もしいま使っている RuboCop がちょっと古くてアップグレードしづらいといった問題は、Cop への pending ステータスの導入での軽減を図っています。

ここ何バージョンかで RuboCop に追加された新しい Cop は 1.0 までは pending というステータスで、デフォルトでは有効になっていません。ユーザーが明示的に .rubocop.yml への Enabled: trueEnabled: false を指定してどうするかを決めるようになっています。

pending ステータスのものは RuboCop 1.0 で有効になりますので、もしそれまでにデフォルトで有効になるべきでない Cop を見かけたら RuboCop HQ までお知らせください。

本編収録にあたって

音楽 CD でライブ盤とスタジオ盤があるように、これまでふだんをライブだったとすると、今回はじめてのスタジオ盤の収録となってなかなか思うように行かなかった。その点を書き残しておく。

スライドと録音を同期させる方法として RubyKaigi チームから紹介があり、普段使いのプレゼンテーションツールが Keynote なので、Keynote での録音が手速かろうと進めていた。

実はそこでもいろいろとあって、手元の環境が古すぎて録音機能がない Keynote 6.0 (2013年) からのアップグレードというのが最初に必要だった。そしてそもそもの macOS が古いため OS のアップグレードも必要でとなり、そのあたりのセットアップに時間が使われたりした。

そのあとはスライドへの音録りになるけれど Keynote の録音機能の使い方を調べつつ覚えていった。ページ切り替えのタイミングによってやや音質が変わるのは、うまく録音できていた箇所まで戻してやり直しかひと呼吸取るためのものだった。

そしてインターネットの向こうにも人がいない PC に向かって話すということができなくて、全編テキスト起こしをするということをしていた。筋肉少女帯の『詩人オウムの世界』のギターソロはふだんアドリブで弾いているところ、譜面に起こしたという逸話をもとにしたものだったけれど、以下のようにうまくいった部分とそうでない部分があった。

  • 話し始めるにあたり、何から話していくかの流れができているのは良い
  • 急いでテキスト起こしをしたツケで、バグっているテキストをそのまま読み上げてしまっている箇所がある (Opal への説明ミスとか)

はじめての試みっぽいバグとしてテキストを読みつつもライブ感を作りたくて、流れに乗ったタイミングでテキストから外れるというものだった。どっちつかずの弊害でした。

こういったことをやっていると、タイムキーピングが疎かになって終盤、畳み掛けるようなスピードで収めに行くことになった。結果として 25分程度というオファーのところ 25分54秒だったので、最後は編集で少しカットできないかやってみようとしたけれど、ソフトの選定や使い方を覚えるまで辿り着けず断念。これは時間の中でこなせるスキルを予め獲得しているかという経験がものをいう世界線だった (そしてそのスキルは現状持ちあわせていなかった) 。 いちおう覚えたこととして、あとで編集しやすいようにまとまりのあるスライドごとに音声を収めておくと良いという知識を得ることができた。ページ切り替えのタイミングで話しているとそのパートだけを削除したいという編集ができなくなるのが理由。

あと MacBook Pro の内蔵マイクで収録していたけれど、途中音質がガラッと変わる部分があった。機材自体は変えていないので姿勢やマイク距離が変わったなどで、音の当て方が変わっていたのではと思う。このあたり音質を担保するのであればそれようの機材を使った方が良さそう (今現在は持っていないのですが) 。

以上のような失敗経験やノウハウを得る良い機会にもなったのが今回のスライド作成だった。次回はもう少しうまくできるんじゃないかなと思っている。

RubyKaigi Takeout 自体は、リモートながら RubyKaigi 感があって良かった。収録済みの配信であるもののタイムテーブルがあったのが良い舞台装置になっていたのではと思っている。勤務先ではタイムテーブルを見る会を実施したり、当日はどちらのトラックを聞こうか悩んだりできていつもながらのアクティビティを起こすきっかけにできたのは大きかったと思う。コンテンツとしては Parser を使ったトランスパイルの話がいくつかあったり、それに伴って見慣れた S 式が出てきたりと、普段の OSS 活動で備えている特異技術とリンクしたトピックなんかはとりわけ興味深く聞いていました。

楽しかったです。RubyKaigi Takeout 2020 の関係者のみなさん、ありがとうございました。

f:id:koic:20200908145328j:plain