Rails 6.1で生成コードをRuboCop適用済みにする

Rails 6.1 がリリースされたので、y-yagi さんによる待望のパッチが使えるようになった。

PR にあるように使い方はシンプルで、Rails 6.1 での config/environments/development.rb に以下の設定を足す。

config.generators.after_generate do |files|
  parsable_files = files.filter { |file| file.end_with?('.rb') }
  unless parsable_files.empty?
    system("bundle exec rubocop -A --fail-level=E #{parsable_files.shelljoin}", exception: true)
  end
end

RuboCop で Style/FrozenStringLiteralComment cop を有効にしているので、--auto-correct-all あるいは -A を指定している (RuboCop 0.87 以上は -ASafeAutoCorrect: false な cop への自動修正もされる) 。

bin/rails g を実行すると、コード生成の流れで rubocop コマンドが実行される。

% bin/rails g migration create_articles
Running via Spring preloader in process 80818
      invoke  active_record
      create    db/migrate/20201214064506_create_articles.rb
Inspecting 1 file
C

Offenses:

db/migrate/20201214064506_create_articles.rb:1:1: C: [Corrected] Style/FrozenStringLiteralComment: Missing frozen string literal comment.
class CreateArticles < ActiveRecord::Migration[6.1]
^
db/migrate/20201214064506_create_articles.rb:2:1: C: [Corrected] Layout/EmptyLineAfterMagicComment: Add an empty line after magic comments.
class CreateArticles < ActiveRecord::Migration[6.1]
^
db/migrate/20201214064506_create_articles.rb:4:1: C: [Corrected] Layout/EmptyLinesAroundBlockBody: Extra empty line detected at block body beginning.

1 file inspected, 3 offenses detected, 3 offenses corrected

すると、以下のように自動修正が適用されたコードが生成される。

% cat db/migrate/20201214064506_create_articles.rb
# frozen_string_literal: true

class CreateArticles < ActiveRecord::Migration[6.1]
  def change
    create_table :articles do |t|
      t.timestamps
    end
  end
end

自分はモデルやコントローラなどはコード生成しないが、マイグレーションファイルだけは bin/rails g を使っており、たまに fstring マジックコメントを書き忘れたまま CI に push してしまうので、この機能は待望していたものだった。

余談だけれど、かつて bin/rails g で生成するコードにデフォルトで fstring マジックコメントを足す PR を出して通せなかったことがあったけれど、これはアプリケーションレイヤーで (フックという抽象化によって) 問題解決をコントロールできる柔軟なアプローチで感動しました。

y-yagi さんありがとう!

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

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