こんにちは。 id:Pocke です。マネーフォワードでは、クラウド会計Plus の開発と、今回お話する RBS のメンテナンスを行っています。 最近、福井県は若狭にある年縞博物館に行って楽しんできました。1
さてこの記事では rbs collection の基本と構成要素を解説します。 この記事を読むことで rbs collection がどのようなもので何をしているのかが理解できるようになるでしょう。
この記事では記事執筆時点での最新の、rbs v3.2.1 を対象としています。
rbs collection の基本的な使い方
rbs collection の基本的な使い方は簡単です。
設定ファイルの生成
まず、rbs collection init
コマンドで設定ファイルを生成します。この生成は rbs collection を使い始める初回にのみ行います。
$ rbs collection init
このコマンドは rbs_collection.yaml
を生成します。この内容は後ほど機能ごとに説明するので、ひとまず次に進みましょう。
RBS ファイルのインストール
次にrbs collection install
コマンドで gem の RBS ファイルをインストールします。
$ rbs collection install
このコマンドはアプリケーションに必要な gem をGemfile.lock
から判断し、それらの gem の RBS ファイルをダウンロードします。
rbs collection install
までを行うと、rbs collection の基本的なセットアップは完了です。
rbs validate
などのrbs
コマンドやsteep
、typeprof
といったコマンドから、インストールしたRBSが使えるようになります。
依存の解決
先程、rbs collection
はGemfile.lock
から必要な gem を判断すると説明しました。これの詳細を見ていきましょう。
アプリケーションの型定義に必要となる gem は、そのアプリケーションの開発に使っている gem と基本的に等しくなるという仮定をrbs collection
は置いています。
つまりGemfile.lock
に書かれている gem (== そのアプリケーションの開発に使っている gem)の RBS ファイルが揃っていれば、そのアプリケーションの型付けができるはずです。
この仮定の元、rbs collection
では依存の解決を Bundler に任せています。
Bundler はアプリケーションが依存する gem をすべてGemfile.lock
に書き出すため、rbs collection
ではGemfile.lock
に書かれている gem の RBS ファイルをダウンロードします。
とはいえこの仮定が常に成り立つわけではありません。例えばGemfile.lock
に書かれていない gem の RBS が必要になることもありますし、その逆にGemfile.lock
に書かれている gem でも RBS が必要ないこともあります。
rbs collection
は、この問題に対して3つの解決策を提供しています。Gemfile
、rbs_collection.yaml
、そしてmanifest.yaml
の3つのファイルで設定することで、この問題を解決することができます。
これらのファイルはそれぞれ違った役割を持っています。以下でこれらのファイルについて解説します。
Gemfile
で不要な gem を指定する
1つ目はGemfile
を利用する方法です。
アプリケーションの開発には必要だけれども、型定義は必要ない gem とはどのような gem でしょうか? これにはいくつかのケースが考えられますが、その中でも最も一般的なのはアプリケーションの開発に使うツール群でしょう。
rubocop
やsteep
といった gem がこれに当たります。たとえばsteep
gemには Steep 自体を実装するコードのための型定義が含まれていますが、これがアプリケーションの開発時に参照されることは非常に稀です。2 これらのツールの RBS はアプリケーションの型検査には必要ありません。
rbs collection
では、これらのツールにrequired: false
がよく指定されていることに着目します。例えばsteep
gem を利用する場合はGemfile
に次のように書くでしょう。
gem "steep", require: false
このようにrequire: false
が指定されている gem (とその gem が依存する gem)の RBS ファイルは、rbs collection install
コマンドでダウンロードされません。
この例ではsteep
gem の RBS はダウンロードされませんし、steep
gem が依存する activesupport
gem の RBS も(ほかに activesupport
gem への依存がなければ)ダウンロードされません。
とはいえアプリケーション都合でGemfile
にrequire: false
は指定したいが RBS ファイルはほしいケースも存在するでしょう。そのような場合は次のセクションで説明するファイルのrbs_collection.yaml
で、ignore: false
を指定するとよいでしょう。
rbs_collection.yaml
で gem の過不足を設定する
2つ目はrbs_collection.yaml
を用いて設定する方法です。
rbs_collection.yaml
のgems
セクションに過不足を調整したい gem を書くことで、不要な gem を無視したり、不足している gem を追加したりできます。
不要な gem を無視する場合、rbs_collection.yaml
に次のように書きます(なおこのファイルにはgems
以外のセクションもありますが、この例では省略しています)。
gems: - name: nokogiri ignore: true
この例ではnokogiri
gem (とnokogiri
gem が依存する gem)の RBS をダウンロードしません。型定義が必要ないがrequire: false
を書きたくないような gem に対してこれを指定すると良いでしょう。
次に、不足している gem を追加する場合はrbs_collection.yaml
に次のように gem の名前のみを書きます。
gems: - name: pathname
この例では pathname
が Gemfile.lock
に含まれていなくとも、pathname
gem の RBS がダウンロードされます。
pathname
のような Default gem や標準ライブラリはGemfile.lock
に含まれないことが多いため、それらライブラリの型定義を使いたい場合に、ここに書き足せます。
最後にrequire: false
が指定された gem を読み込む方法を見てみましょう。この場合は、その gem の名前とignore: false
を指定します。
gems: - name: steep ignore: false
この例では、たとえsteep
gem がGemfile
でrequire: false
を指定されていたとしても、steep
gem の RBS がダウンロードされます。
manifest.yaml
でGemfile
に表れない依存を指定する
3つ目の方法は、manifest.yaml
というファイルを使って設定をする方法です。
このファイルはアプリケーションの開発向けではなく、gem の型定義を書くための設定ファイルです。
gem の型定義はpathhname
などの Default gem や標準ライブラリに依存することがあります。
これらのライブラリは多くの場合gemspec
に含まれないため、Gemfile.lock
にも含まれません。
manifest.yaml
は、そのようなGemfile.lock
に表れない依存を指定するためのファイルです。gem の開発者が RBS ファイルを書く場合や、ruby/gem_rbs_collectionに gem の型定義を追加する際に使います。
例えばactivesupport
gem のmanifest.yaml
は次のようになっています。
https://github.com/ruby/gem_rbs_collection/blob/248499a924c3cb331d40ca667d40528814043788/gems/activesupport/6.0/manifest.yaml
dependencies: - name: monitor - name: date - name: singleton - name: logger - name: mutex_m - name: time - name: pathname - name: securerandom - name: erb
manifest.yaml
はアプリケーション開発をする上では気にすることはありませんが、gem の RBS を書く際には使うことがあるかもしれません。
source
ここまでで、rbs collection
がダウンロードする gem をどうやって指定するのかが分かりました。次に、rbs collection
がどこから gem の RBS ファイルをダウンロードするのかを説明します。
rbs collection
では、RBS ファイルをどこからダウンロードするかを source という概念で管理しています。
rbs collection install
は依存関係の解決をGemfile.lock
を用いて行い、その結果の gem がどれかの source に含まれていれば、その source から RBS ファイルをダウンロードします。
現在はgit
, local
, stdlib
, そしてrubygems
の4つの type の source が定義されています。これらについて見ていきましょう。
git
source
一番よく使われるのはgit
source でしょう。git
source は、git リポジトリから RBS ファイルをダウンロードするための source です。主にruby/gem_rbs_collectionリポジトリのために使われます。
git
source を使う場合、どのgit
リポジトリを参照するかをrbs_collection.yaml
に指定する必要があります。ただしrbs collection init
コマンドがruby/gem_rbs_collectionを参照する設定を生成するため、このリポジトリのみを使う場合は特に設定する必要はありません。
rbs collection init
が生成するrbs_collection.yaml
のsources
は次のようになっています。
sources: - type: git name: ruby/gem_rbs_collection remote: https://github.com/ruby/gem_rbs_collection.git revision: main repo_dir: gems
各設定の意味は以下のとおりです。
type
は固定でgit
を指定します。name
にはrbs_collection.yaml
内で一意になる名前を設定します。remote
にはgit clone
に指定するアドレスを指定します。revision
にはダウンロードしたい RBS がコミットされているブランチ名やコミットハッシュを指定します。repo_dir
には RBS ファイルがルールに則って3配置されているディレクトリを指定します。
rbs_collection.yaml
には複数の source を記述できます。例えば一般的な gem の RBS はruby/gem_rbs_collectionから、社内向けの gem は社内で管理している別のリポジトリからダウンロードするように設定することなどが可能です。
local
source
local
source は、ローカルのディレクトリに存在する RBS ファイルを直接参照するための source です。
git
source では扱えない、git リポジトリとしては管理されていないディレクトリを source として扱いたい場合に使います。たとえばアプリケーションの git リポジトリにライブラリの RBS をコミットしている場合などが考えられるでしょう。
local
source を使う場合、rbs_collection.yaml
に次のように記述します。
sources: - type: local path: path/to/local/dir
local
source を使う場合、type
には固定でlocal
を指定します。
そしてライブラリごとの RBS ファイルが置かれているディレクトリを path
に指定します。path
の中のディレクトリ構造は、git
source と同様のルールに則っている必要があります。
local
source を使う場合、local
source で管理されている RBS を編集したあとにrbs collection install
コマンドを実行せずともその変更は反映されます。local
source は編集する機会が多くなることが予想されるため、このような挙動になっています。Bundler のそれと似たような挙動ですね。
なお、これはgit
source とは異なる挙動です。git
source では更新後の git revision が参照されるように、rbs collection update
を実行する必要があります。
stdlib
source
今までの2つの source は明示的にrbs_collection.yaml
への記載が必要でしたが、ここから説明する2つの source には記載が必要なく、デフォルトで利用されます。そのためrbs collection
を使っている上ではあまり意識することは無いかもしれません。
stdlib
source は、標準ライブラリの RBS ファイルを利用するための source です。
標準ライブラリの RBS は、ruby/rbsに含まれており、rbs
gem に同梱されて配布されています。アプリケーションの依存に標準ライブラリが含まれている場合、この source を通してrbs
gem のパッケージ内の RBS ファイルを参照します。
存在する標準ライブラリの一覧は、次のリンクから確認できます。 https://github.com/ruby/rbs/tree/v3.2.1/stdlib
rubygems
source
rubygems
source は、インストールされた gem のパッケージに含まれる RBS ファイルを参照するための source です。rubygems
source も設定の明示は必要のない source です。
gem の型定義を配布するには大きく2つの方法があります。1つはruby/gem_rbs_collectionリポジトリを使う方法で、この場合にはgit
source を使います。もう1つは gem のパッケージのsig
ディレクトリに RBS ファイルを含める方法で、rubygems
source はこの場合に使われます。
例えばrbs
gem 自体の型定義ファイルは、この方法で配布されています。rbs
gem のsig/
ディレクトリを見てみると、rbs
gem 自体の型定義ファイルが含まれていることが分かります。
https://github.com/ruby/rbs/tree/v3.2.1/sig
この方法で配布されている gem はまだ多くありませんが、将来的にはこの方法で配布される gem が増えていくことを期待しています。
最後に
rbs collection
の基本的な使い方と、その構成要素について解説をしました。require: false
や source に関しては今まであまり解説されてこなかったところであり、あまり知られていなかった情報もあったかと思います。
これを機に RBS を使う方が増えたら幸いです。
- 年縞というのは湖などの底に土などが縞模様を描いて堆積したものです。この縞模様は1年に1層ずつ作られるという特徴があり、これを調べることによって過去を1年刻みで知ることができます。この博物館の近くにある水月湖は年縞がきれいに7万年分も堆積している世界でも特異な場所です。年縞博物館ではこの水月湖の年縞を中心に展示がされており、控えめに言ってめちゃくちゃ面白かったのでぜひ行ってみてください。水月湖の研究者が出版している本も面白かったのでおすすめです。 https://www.amazon.co.jp/dp/B01N59630B https://www.amazon.co.jp/dp/B06X6H7RPS↩
-
アプリケーションで
steep
gem のコードを読み込んで利用していれば型定義が必要ですが、基本的にはまずありません。↩ - ディレクトリ構造の詳細は、次のドキュメントを参照してください。 https://github.com/ruby/rbs/blob/v3.2.1/docs/repo.md#directory-structure↩