くりにっき

フルスタックキュアエンジニアです

自作gemでモンキーパッチrbsを利用する

コンテキスト

僕が直近2〜3年以内に新しく作ったgemでは全てrbsとsteepを導入してガッツリ型を書いています。

しかし自分のgemのrbsは書けても、自分のgemが依存しているrubyの標準ライブラリのメソッドや依存している別のgemの方で型定義がなかったり足りていなかったりして steep check が通らなくて困ることがよくあります。

こういう時には https://github.com/ruby/gem_rbs_collection や https://github.com/ruby/rbs にパッチを送ればいいんですが、とはいえ本家に取り込まれるまで自分のgemの開発が止まるのはつらいのでfork版を使うことが多いでしょう。

しかし足りない型定義が多かった時にfork版をメンテするのもつらい気がしているので*1僕はモンキーパッチとしてrbsを入れるようにしています。

こういうことは多分自分しかやっていないだろうなあと思いつつ、他の人がどうやってるか知りたいので自分が普段やってることを紹介してみます。

構成

下記のように sig/non-gemify/ のようなディレクトリを作って、ここにモンキーパッチrbsを入れています。

$ tree sig/
sig/
├── non-gemify
│   ├── io.rbs
│   └── kernel.rbs
├── ruby_header_parser
│   ├── argument_definition.rbs
│   ├── config.rbs
│   ├── enum_definition.rbs
│   ├── function_definition.rbs
│   ├── parser.rbs
│   ├── struct_definition.rbs
│   ├── type_definition.rbs
│   ├── typeref_definition.rbs
│   └── util.rbs
└── ruby_header_parser.rbs

3 directories, 12 files

sig-non-gemify/ のようにしてもよさそうですが好みの問題かと思います。

エディタやIDEによっては sig/ 以外のディレクトリを認識しなくて困ることをなんとなくエスパーして sig/ の中にサブディレクトリを作る方法にしました。(杞憂かも)

手順

モンキーパッチrbsをgemファイルに含めて https://rubygems.org/ で配布を行うと、そのgemをインストールしたユーザの開発環境に悪影響が起きるかもしれません。

そのため、gemspecで下記のように除外設定を入れた方がいいです。

   spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls|
     ls.readlines("\x0", chomp: true).reject do |f|
       (f == gemspec) ||
-        f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
+        f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile sig/non-gemify])
     end
   end

あとはsig-non-gemify/ のように sig/ ディレクトリの外に置くようにした場合には下記のように Steepfile に設定追加も必要だと思います。

 target :lib do
   signature "sig"
+  signature "sig-non-gemify"

モンキーパッチrbsが完全になくなった時に Steepfile に存在しないディレクトリを書いた時にエラーにならないかも気になったんですが、今のsteepの最新版(1.8.3)だと問題なかったです。

モンキーパッチrbsを使うメリット

  • 本家のgem_rbs_collectionを併用できるので常に最新を使い続けることができる
  • 本家に投げたパッチが取り込まれた時に自分のgemではそのモンキーパッチrbsだけを消すだけでいい
  • 一気に取り込まれるとは限らないのでマージされたものから段階的にモンキーパッチrbsを消すことができる

最後に

みんなのgem開発時のこの手の困りごとの解決方法を知りたいので教えてください!!!

2024/12/05 20:27追記

ruby-jp slackで聞いたら

sig/gems/ ってディレクトリにいろいろ放り込んでますね。取り込まれた後にお掃除してます、

とのことだった。そうなりますよね...

*1:実際にそういう運用はしたことないのであくまで想像