Offers MagazineにOSS開発に関する記事を寄稿した

少し前に Offers さんより、RuboCop コミッターとしての OSS 開発に関する話というオファーをいただいて Offers Magazine に執筆した。文筆物では12年ぶりの新作。

offers.jp

オファーいただいていた文字数に対して分量割増しで書いてたものの、自分の OSS 開発に関する火付けで重要な松田さんとのエピソードや、OSS 開発者としての人生観的な影響を受けている kamipo さんとのエピソードなど、収まらずに割愛せざるを得なかったものもたくさんあるので、機会があればまたどこかで。

さて、今回は OSS を続けることへのモチベーションの維持や、パッチそのものの質といった部分に焦点をあてた話にした。続けることへのロールモデルとして、分かりやすいであろう GitHub における草というものを持ってきている。書くべきことは本編に書いたので、良ければご一読ください。

また、執筆にあたり編集の峪さんには、執筆のオファーからの打ち合わせ、アウトライン作成、その他もろもろ編集多岐に渡るやりとりでお世話になりました (時代背景から推測できるとおり、すべてオンラインで行っていました) 。ありがとうございました!

RuboCop 1.5で導入された`SuggestExtensions`パラメータ

RuboCop 1.5で導入されたSuggestExtensionsパラメータについて記しておきます。

導入されたパッチは以下です。

github.com

たとえば Bundler を使って RSpec を依存指定している一方で、RuboCop RSpec を使っていない場合は、以下のように RSpec に対応した拡張 RuboCop が存在することを rubocop コマンド実行のメッセージでサジェスチョンします。不必要な場合はメッセージ中に含まれている方法でオプトアウトできます。

% bundle exec rubocop --parallel
(snip)

Tip: Based on detected gems, the following RuboCop extension libraries
might be helpful:
  * rubocop-rspec (http://github.com/rubocop-hq/rubocop-rspec)

You can opt out of this message by adding the following to your
config (see
https://docs.rubocop.org/rubocop/extensions.html#extension-suggestions
for more options):
  AllCops:
    SuggestExtensions: false

現在デフォルトでサジェスチョンするのは、RuboCop HQ で管理されている以下の拡張 RuboCop です。

新たに拡張 RuboCop を足すこともなく、こういったサジェスチョンが不要といった場合は、メッセージにあるように SuggestExtensions: false を .rubocop.yml に指定してください。サジェスチョンメッセージが消えます。

AllCops:
  SuggestExtensions: false

また、先ほど RuboCop 1.5.2 がリリースされました。

rubygems.org

RuboCop 1.5.0, 1.5.1 は、依存している regexp_parser について 2.0 以上を要求するようにしていましたが、Capybara などは現状 1 系のサポートのみとなっていたため、RuboCop 1.5 系にアップグレードできないというフィードバックがありました。

そのため、RuboCop 1.5.2 では regexp_parser 1 系と 2 系をサポートするようにしました。

注意点としては、regexp_parser 1 系を使っている場合は knu さんが直してくれたマルチバイト文字の正規表現に関する既知のバグが残っているので、ご注意ください (regexp_parser 2 系に上がると直ります) 。両系をサポートしたパッチはこちら。

github.com

bundle update で RuboCop 1.5 系へのアップグレードを試みた際に、唐突に 1.4.2 から 1.5.2 に上がった場合はこういった事情になります。

インタラクティブ プレゼンテーション パターンズを再読した

Kaigi on Rails STAY HOME Edition で登壇した動画が公開された。公開作業ありがとうございます。

www.youtube.com

Kaigi on Rails への登壇自体の話は以前書いたエントリに譲り、今回はプレゼンに関する『インタラクティブ プレゼンテーション パターンズ (2004年) 』 (通称イプパタ) という文献を思い出したので、良い機会なので少し書き残しておきます。Web 資料としては以下から辿れます。

objectclub.jp

紹介されているパターンのうち、自分のプレゼンスタイルに取り入れているものもあれば取り入れていないものもあるけれど、全体的にプレゼンへの考え方として結構な影響を受けていたりする。

インタラクティブ プレゼンテーション パターンズ』にある『その場修正』といったパターンに対しては『直前までの作り込み』といった派生系を、イプパタ以降のテックカンファレンスで発見 (ハッチング) している。『その場修正』が JIT 的な最適化だとすれば『直前までの作り込み』は AOT 的な最適化といったところか。今回の Kaigi on Rails でも自分より前にされていたプレゼンを自分の話の流れに取り入れたりしていた。

イプパタについてプレゼンに対するパターンという視点はいま見ても面白いと思うのと、オンラインカンファレンスならではのプレゼンテーションパターンというのはありそうなので、そういった視点で社会を観察してみても面白いかもしれない。

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