社内向けに「ソフトウェアの契約と見積り勉強会」を行った

5年くらい前に XP 祭りで登壇した内容を、当時と組織メンバーの顔ぶれも変わっていることもあり社内向けに話した。

www.slideshare.net

参加メンバーからは「3秒見積りは良くないのを知れた」「ハイブリッド契約という手法を知った」など、見積りや契約まわりに関する知見を伝えることができて良かったのではと思う。あと『誰と重要』

スライド中にも引用しているスティーブ・マコネルの『ソフトウェア見積り』は、後半はともかく前半は「見積りとは、何であり、何ではないのか?」を知る意味でも読んでおいた方が良い。これは受託開発とかサービス開発とか一切関係なくオススメどころか、ソフトウェアの開発に掛かる見積りの基礎的な考え方を語る上で必読だと思っている。

時間がない人は、最初は要所要所にある箱書きのポイントだけ流してみても関心を引く構成になっていると思う。

f:id:koic:20210304200807p:plain

『達人プログラマー』然り、『時を超えた建設への道』然りの良書構成、おすすめ。

RuboCop Performance 1.10 をリリースした

RuboCop Performance 1.10 をリリースした。

JST で 3月1日に 1.10.0 をリリースして、3月2日に 1.10.1 をバグフィックスリリースした。以下、2つの新 Cop が目玉です。

Performance/RedundantSplitRegexpArgument cop

機能リクエストがあがっていた Cop をコントリビュータが実装してくれました。split メソッドの引数について正規表現ではなく文字列で十分というケースを検出する Cop です。

# bad
'a,b,c'.split(/,/)

# good
'a,b,c'.split(',')

1.10.0 の時点だと 'oneSplittwo'.split(/split/i) といった ignore case オプションも警告する偽陽性があったため、1.10.1 で修正リリースしています。

Performance/RedundantEqualityComparisonBlock cop

こちらは Rails コミッターの kamipo さんからのフィードバック。

Enumerable#all?, Enumerable#any?, Enumerable#one?, Enumerable#none? に関して、ブロックで ===, ==, is_a?, kind_of? を使っている以下のようなケースを、Ruby 2.5 で追加された引数渡しにする Cop です。

# bad
items.all? { |item| pattern === item }
items.all? { |item| item == other }
items.all? { |item| item.is_a?(Klass) }
items.all? { |item| item.kind_of?(Klass) }

# good
items.all?(pattern)

いちおう引数渡しにした際に内部で呼び出される ===== と同じ振る舞いなケースのこともあれば、 組み込みの Regexp のようにサブクラスで再定義されている可能性があることから安全でない Cop としてマークしています。 (より安全側に倒して、=== の場合は LHS でブロック引数と異なる値が使われているときに限定するか悩んだ気がする。)

どちらの Cop も good ケースのコードとしてもシンプルになって良いのではと思います。それでは。

RuboCopのGitHubオーガニゼーションがrubocopになった

「RuboCopのGitHubオーガニゼーションがrubocopになった」とか、どういうタイトルだと思うけれど、表題のまま。

歴史的には、bbatsov/rubocop だったものが、RubyKaigi 2018 のときに rubocop-hq/rubocop になって、今回念願の rubocop/rubocop に至ったという流れ。

お察しのとおり、長らく rubocop オーガニゼーションが他に取得されていたため rubocop-hq になっていたわけです。その後、rubocop というオーガニゼーションを、去年の 10 月くらいに譲渡してもらっていたところ、以下の GitHub Discussions を皮切りに移行に踏み切られました。

github.com

従来の rubocop-hq オーガニゼーション経由でのアクセスでも、GitHub の方で良きにリダイレクトしてくれるのでユーザー影響はあまりないと思いますが、無駄なリダイレクトを挟むこともないので rubocop-hq になっている箇所があれば rubocop に指定し直すと良いです。

また、オーガニゼーションのリネームではなくリポジトリ個々の引越しだったため、このエントリを書いている時点ではいくつかのリポジトリは旧来の rubocop-hq に配置されたままのものがあります (だいたい移行されていてメジャーなところだと rubocop-rspec が残っているくらいだと思う) 。

github.com

遠くないうちに rubocop オーガニゼーションに引越しすると思いますが、念のため。

社内向けに「スローテスト勉強会」を行った

スローテスト対策への熱が再燃してきていたので、Ruby関西 勉強会で以前に使ったスライドをベースにオンライン社内勉強会を行った。

www.slideshare.net

やっぱりどのプロジェクトでも Feature テストが Rails アプリケーション開発でのスローテストの要因になっていることが多いようで、このあたりミドルウェアレベルでスピードアップするようなエポックメイキングなソリューションが誕生することを願っている (他力本願) 。

最後は「スローテスト対策への銀の弾丸はないけれど、金の弾丸はある」というので締め括った🍵

irbでオートハイライトやオートインデントがされないときに見直す設定

Ruby 2.7 で大幅に強化された irb でオートハイライトやオートインデント、マルチライン編集ができていない場合、~/.irbrc に以下のような設定が残っている可能性があります。

IRB.conf[:USE_READLINE] = true

IRB.conf[:USE_READLINE]true が設定されている場合、この設定自体を消すか false に設定すると Reline が使われて、Ruby 2.7 以降の強力な機能群が使えるようになります。

たぶん Readline まわりのビルドに苦しんだ頃の名残りが ~/.irbrc に残っていたんじゃないかなと思っています。

余談ですが、つい先日に Nokogiri 1.11.0 がリリースされてプリコンパイル済みのパッケージ配布になったので、Readline と Nokogiri のビルドという Ruby アプリケーション開発でのインストールの鬼門が減っていっているのは、いい話ですね。

RuboCop の Ruby 3.0 対応

構文解析まわりは Ruby 3.0 リリース以前に概ね対応されていると思うので、うまく解析できなかったらバグの可能性があります。その場合の初手としては Parser gem を 3.0.0.0 以上にすると解決するかもしれないので、bundle update parser をしても再現するか見てみてください。

ここでは Ruby 3.0 リリース前後のクリスマスあたりから年末年始にかけて実装していた既存 Cop への対応と、新たな Cop について書いておきます。RuboCop 1.7 に入っているものもあれば、今後のリリースに含まれていくものも併せて記しておきます。

Frozen Regexp, Range リテラル対応

Ruby 3.0 では Regexp リテラルならびに Range リテラルについて、デフォルトで frozen になりました。それにともない定数代入に対する Cop である Style/RedundantFreeze cop と Style/MutableConstant cop について、Ruby 3.0 でのそれらリテラルの定数代入で freeze が不要なことを検知できるようにしました。

github.com

これは早めに入れておきたかったことから最優先で対応していたもので、RuboCop 1.7 に含まれています。

Hash#except 対応

Hash#except ならびに ENV#except に置換可能なケースを検出する Style/HashExcept cop を追加しました。Ruby 3.0 以上のみ有効な Cop ですが、Hash#except は Active Support からインポートされたメソッドなので、Active Support を使っている Ruby 3.0 の Rails プロジェクトなんかで、より効果を発揮するかもしれません。

github.com

Ruby 3.0 構文解析を正式サポート

Ruby 3.0 に正式対応した Parser 3.0.0.0 以上を要求するようにしました。

github.com

以下のような警告が出るひとは Parser のバージョンを上げてみてください。

% ruby -v
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
% rubocop
warning: parser/current is loading parser/ruby30, which recognizes
warning: 3.0.0-dev-compliant syntax, but you are running 3.0.0.
warning: please see
https://github.com/whitequark/parser#compatibility-with-ruby-mri.

Endless Method Definition のスタイル cop

本エントリ中でこの Cop だけは自分の実装ではないもの (ざっとレビューはしている) 。これはコントリビューターのダニエルが提案している Cop で、Endless Method Definition に対するスタイルをチェックする Cop です。Ruby 3.0 リリースイベントでも Endless Method Definition でメソッド本体を複数行で書いていた ko1 さんに、mame さんが1行で書くことを想定していたというやりとりがありましたが、この Cop はデフォルトで1行定義であれば許可するといった振る舞いになる方向です。

github.com

Dir.glob の結果がソート済みになる対応

Ruby 3.0 から Dir.glob の結果がファイルシステムに依存せずソート済みになることに対応しました。Ruby 3.0 だと Dir.glob のあとに sort を加える警告をしません。将来 RuboCop が Ruby 3.0 以上のみのサポートになったら、この Lint/NonDeterministicRequireOrder cop は非推奨警告して削除する予定です。

github.com

上記と対になる新規 Cop で、Dir.globに続いて sort がされている場合に冗長な sort を消します 。

github.com

元々 Lint/NonDeterministicRequireOrder cop の提案がインターネットの海の向こうから来たときに、Dir.glob の結果がソートされないことで困るユースケースがあることを、amatsuda さんにパッチ会で相談したのを覚えていたので、本家がデフォルトでソート済みになったのを受けて対応しようと思っていたのを行った流れです。

lambda の引数にブロックリテラルを使わず Proc 引数を渡す警告

Ruby 3.0 で lambda(&proc { do_something }) のように、lambda の引数にブロックリテラルを使わず Proc 引数を渡している場合の警告をエミューレーションする Cop を追加しています。オートコレクトについて少し悩んだのですが、既存の振る舞いを維持する形にしました。

github.com

余談ですが、このパッチが 2021 年の書き初めです。

NIL, TRUE, FALSE, Ramdom::DEFAULT定数の削除や非推奨への対応

NIL, TRUE, FALSE, Ramdom::DEFAULT といった定数が削除、非推奨されることに対応しておこうと思っていたのですが、まとめて非推奨定数を検知できる Cop として追加の提案をしておきました。

github.com

Numbered Parameter への代入がエラーになることへの対応

Numbered Parameter への代入は Ruby 2.7 では警告、Ruby 3.0 でエラーになるようになりました。Ruby 2.7 の警告をエミュレーションした Cop を追加しました。ただのエミュレーションではない拡張ポイントとして、Numbered Parameter は _1 から _9 までで、_0_10 以上はただのローカル変数のようでしたが、Naming 部署の Cop として一律警告するようにしました。この拡張があるため Lint ではなく Naming 部署に置いたという流れです。

github.com


RuboCop の Ruby 3.0 サポートについて、引き続き対応していく予定です。

また、OSS 活動を支援してくれるスポンサーを GitHub Sponsors で募っていますので、こちらもどうぞよろしく。

github.com

それではハックを続けましょう。

`&& return` より `and return` を好む

かつての RuboCop は condition and return というコードに対して、condition && return にするようデフォルトでサジェスチョンしていた。

このサジェスチョンはおかしく見えたので、結構前に構成を変えてデフォルトで condition and return を受け入れるようにしている。

このあたり Ruby での演算子の結合順位をおさらいすると、以下のとおり。

  1. &&
  2. ||
  3. and, or

docs.ruby-lang.org

サジェスチョンされるがまま演算子を変えると、結果が変わるサンプルです。

% ruby -e 'p false || true and return' # p(false || true) and return と等価
true

% ruby -e 'p false || true && return' # p(false || (true && return)) と等価
# noop

このように結合順位の関係から、条件式と制御フローをつなげる時は && より and を一貫して使う方が原則好ましいと思っています。

参考までに上述している RuboCop Rails の PR にも書いていますが、RailsDoubleRenderError が起きた時のメッセージで示されるのは render :action and return です。

github.com

auto-correct されたから常に正しいわけではないので、おかしいと思うケースと遭遇したときは立ち止まってもう一度レビューしてみましょう。

あとこういったおかしいところを地道になおして行っていたりもしているので、RuboCop のバージョンが古い場合はアップデートしてみてください。