ククログ

株式会社クリアコード > ククログ > Red Data Tools:RDocとRubyGemsを疎結合にしたい!

Red Data Tools:RDocとRubyGemsを疎結合にしたい!

Ruby用のデータ処理ツールを提供するプロジェクトRed Data Toolsをやっている須藤です。そんなにデータ処理に関係はないのですが、1年ちょい前からRDocとRubyGemsを疎結合にする改良をRed Data Toolsメンバー(@mterada1228@ericgpksと私)で取り組んでいました。先日、プルリクエストにするところまで仕上がったので、なにをやっていたのか、どうしてやっていたのかなどをここにまとめておきます。

背景

最初に背景を説明します。私は数年単位でえっちらおっちら取り組むことが多いのですが、これも発端は年単位で前の話から始まります。。。

私はRubyのバインディングを開発していることもあって、15年くらい前のRuby 1.8.7/1.9.1どちらでも使えるWindows用バイナリ入りgemをDebian GNU/Linux上で作る方法やら5年くらい前の2019年、fat gemをやめるやらで、バインディングを配布するいい感じの仕組みを模索していました。過去記事にも書いてありますが、fat gemをやめるための仕組みの1つとしてnative-package-installerというライブラリーも開発しています。今でもこれを使ってはいますが、いくつか解決したい課題があります。

  1. 依存するシステムライブラリーをインストールするために設定ではなくRubyコードを書かないといけないのが面倒
  2. 自動で依存するシステムライブラリーをインストールされるのがイヤなユーザーがいる(参考: https://github.com/apache/arrow/issues/34457

このあたりを解決するためにはRubyGemsともっと仲良くした仕組みが必要そうです。ということで、rubygems/rubygems#1296 Standardize +requirements+ field from the specificationDraft of the upcoming RubyGems external_depenencies RFCでゴニョゴニョしていました。

結局まだrubygems/rubygems#1296に提案をまとめられていないのですが、各パッケージマネージャーへの対応をRubyGems本体でやるのは違うんじゃないかとは思っています。というのは、世の中にはたくさんのパッケージマネージャーがあるからです。新しいパッケージマネージャーに対応させるためにRubyGems本体を更新する、既存のパッケージマネージャーの挙動が変わったのでRubyGems本体を更新するというのはキビシイ気がしています。(更新してもらえなそう。)

別の方向として、RubyGemsと仲良くしつつもRubyGems本体より更新しやすいなにかで実現できないかと考えていました。そこで目をつけたのがRubyGemsのプラグイン機能です。RubyGemsのプラグイン機能を使うとインストールプロセスの途中に処理を挟むことができますし、その処理は別gemとして管理できます。

プラグイン機能でいけるかなーと調べていたのですが気になるところがありました。RubyGemsプラグインであるgemをインストールしたとき、そのときに一緒にインストールしている別のgemのインストール処理ではインストール直後のRubyGemsプラグインは有効にならないのです。なにを言っているのかわからないと思うので具体例を示します。

gem-srcというRubyGemsプラグインとしても動くgemがあります。このプラグインが有効になっている場合、gem installするとインストールしたgemのソースを自動で取得してくれます。次のようにするとkaminari gemのソースを自動で取得してくれるというわけです。

$ gem install gem-src
$ gem install kaminari

しかし、次のようにgem-srcとkaminariを同時にインストールするとkaminariのソースは取得されません。インストールされたRubyGemsプラグインは次のgem installからしか有効にならないからです。

$ gem install gem-src kaminari

ということで、RubyGemsプラグインをインストールしたらすぐにプラグインを有効にするようにしました。それがrubygems/rubygems#6673 Load plugin immediatelyです。ここでもgem-srcとkaminariを例として使いました。本当は依存しているシステムライブラリーを自動でインストールする仕組みのために必要だったのですが、まだ机上の空論で現実世界での問題ではないので、具体例として紹介するには説得力に欠けるからです。

そうそう、どうしてこの挙動が問題になりそうかを説明していませんでした。バインディングの依存gemとして依存しているシステムライブラリーを自動でインストールしてくれるRubyGemsプラグインを指定しようと思っていたのです。その場合、gem install red-arrowとかしたら自動でRubyGemsプラグインもインストールされますが、そのときにはそのRubyGemsプラグインは動かないので依存しているシステムライブラリーが自動でインストールされないのです。

それはそれとして、このインストール直後からすぐにRubyGemsプラグインが有効になる機能の別のユースケースとしてRubyGemsにあるRDocの機能を呼び出している処理を削除できるんじゃない?ということを考えていました。おそらく、歴史的な経緯でこのRubyGemsとRDocの統合処理がRubyGemsに入っているのだとは思います。しかし、YARDなど別のドキュメントツールを使っているときなどはこの機能は不要なので、RubyGems側ではなくRDoc側で実現できた方がスッキリしそうです。

ついに最初のところまで話が戻ってきました!fat gemをいい感じにやめるための仕組みづくりの途中でRubyGemsを改良したけど、それを使えばRubyGemsとRDocもいい感じにできそうだからついでにやっちゃうか!が背景です。こういう寄り道みたいなことをしているので年単位で進めることになってしまうのですが、そっちのほうがいい世界になりそうだからそういう人がいてもいいんじゃないかな。

はじまり

ということで、「ついでに」RubyGemsとRDocを疎結合にするか!となりました。が、私が一人でやるのもアレな気がしたので、Red Data Toolsのチャットやらなにやらで@mterada1228@ericgpksを誘いました。このあたりかな。@mterada1228はOSSにプルリクエストを送ったことがないということだったので、それを経験してもらえるといいなぁという思惑もありました。

進め方

2人ともRubyGemsにもRDocにも詳しくないので、実現したいことを説明してあとはがんばってみましょう!では完了する気はしません。ということで、3人で一緒に進めることにしました。2人がわからないところは私が随時解説することで前に進められるのでは?というアプローチです。Speeeさん向けにやっていたサポートプログラミングというスタイルです。

ただ、せっかくやるなら再利用できるといいなぁと思って、3人で一緒にやっているところをライブ配信しました。アーカイブにもして↓のプレイリストにまとめているので今でも見れます。

RubyGemsとRDocの密結合を解消する

私は少し脱線しながら関連することを説明しがちなので、RubyGemsとRDocに関係すること以外にも「メンテナーの気持ちに寄り添ったプルリクエストの説明の書き方」とかいろいろお役立ち情報が入っているはずです。1回約30分で全38回なのでたくさんありますが、OSSにプルリクエストを送ってみたい人は見てみてください。(動画編集が得意で大事なところをピックアップしたダイジェスト版を作れる人がいたら連絡をお待ちしています。そういうのは面倒なので、ライブ配信の動画そのままのやつしかありません。。。)

「まとまった時間をとって一気にやってしまう」という時間の確保の仕方は難しいので、毎週水曜日の12:15-12:45(お昼休み時間)に開催していました。1回約30分になっているのはお昼休み時間に終わらせるためです。ライブ配信前にXで宣伝していたこともありましたが、私が面倒でやらなくなってしまったので、この取り組みを知っている人はすくなかったと思います。。。(Red Data Toolsの活動を宣伝してくれる人からの連絡をお待ちしています。)

実現方法

詳細はできあがったプルリクエストや動画を見てもらうといいのですが、一応、ここでもざっくりとどうやって実現したのかを紹介しておきます。

従来はRubyGemsがRDocのコードを呼び出してドキュメントを生成していました。しかし、これではRDocを変更したらRubyGemsも変更しなくてはいけなくなるかもしれません。

そこで、ドキュメントを生成する処理をRDoc側に持っていくことにしました。そのために利用したのがRubyGemsのプラグイン機能です。RubyGemsのプラグイン機能は、RubyGemsが特定のタイミングで登録された処理を呼び出すことで実現されています。どういうタイミングかとか、どういう引数で呼び出すかとかは仕様として決まっているのでそうそう変わることがありません。RDocにRubyGemsプラグイン機能を実装し、「インストール後」などのタイミングで適切な処理(ドキュメントを生成とか)をRubyGemsから呼び出してもらうようにしました。

これにより、RubyGemsがRDocの機能を呼び出さなくてもドキュメントを生成できるようになりました。ただ、RubyGemsがRDocを呼び出してドキュメントを生成しているコードはまだ残しています。このタイミングでそのコードを削除してしまうと「新しいRubyGems」+「古いRDoc」の組み合わせでドキュメントが生成されなくなってしまうからです。互換性の維持は今回の改良で難しかったことの一つです。

RubyGemsにある該当処理はこの改良が入ったRDocが普及したあとに安全に削除できます。早くても数年後でしょうけど。。。

まとめ

この1年強Red Data Toolsメンバーの@mterada1228@ericgpksと一緒に取り組んでいたRDocとRubyGemsを疎結合にする改良が一段落したので宣伝しました。背景を書いたら疲れてしまったのでそれ以降の話はざっくりになってしまいました。

興味がある人はプルリクエストをレビューしたり、動画を見たりしてください。

一緒に開発するとかもやっているRed Data Toolsに興味がある人はRed Data Toolsのチャットに来てください。一緒にRuby用のデータ処理ツールを整備しましょう!

Rubyでデータ処理できるように開発をお手伝いして欲しい!とか、サポートプログラミングなどで「組織のOSS開発支援をして欲しい!」とかであればお問い合わせからどうぞ。