Money Forward Developers Blog

株式会社マネーフォワード公式開発者向けブログです。技術や開発手法、イベント登壇などを発信します。サービスに関するご質問は、各サービス窓口までご連絡ください。

20230215130734

rbs collectionの基本と構成要素

こんにちは。 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コマンドやsteeptypeprofといったコマンドから、インストールしたRBSが使えるようになります。

依存の解決

先程、rbs collectionGemfile.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つの解決策を提供しています。Gemfilerbs_collection.yaml、そしてmanifest.yamlの3つのファイルで設定することで、この問題を解決することができます。

これらのファイルはそれぞれ違った役割を持っています。以下でこれらのファイルについて解説します。

Gemfileで不要な gem を指定する

1つ目はGemfileを利用する方法です。

アプリケーションの開発には必要だけれども、型定義は必要ない gem とはどのような gem でしょうか? これにはいくつかのケースが考えられますが、その中でも最も一般的なのはアプリケーションの開発に使うツール群でしょう。

rubocopsteepといった 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 への依存がなければ)ダウンロードされません。

とはいえアプリケーション都合でGemfilerequire: falseは指定したいが RBS ファイルはほしいケースも存在するでしょう。そのような場合は次のセクションで説明するファイルのrbs_collection.yamlで、ignore: falseを指定するとよいでしょう。

rbs_collection.yamlで gem の過不足を設定する

2つ目はrbs_collection.yamlを用いて設定する方法です。

rbs_collection.yamlgemsセクションに過不足を調整したい 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

この例では pathnameGemfile.lock に含まれていなくとも、pathname gem の RBS がダウンロードされます。 pathnameのような Default gem や標準ライブラリはGemfile.lockに含まれないことが多いため、それらライブラリの型定義を使いたい場合に、ここに書き足せます。

最後にrequire: falseが指定された gem を読み込む方法を見てみましょう。この場合は、その gem の名前とignore: falseを指定します。

gems:
  - name: steep
    ignore: false

この例では、たとえsteep gem がGemfilerequire: falseを指定されていたとしても、steep gem の RBS がダウンロードされます。

manifest.yamlGemfileに表れない依存を指定する

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.yamlsourcesは次のようになっています。

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層ずつ作られるという特徴があり、これを調べることによって過去を1年刻みで知ることができます。この博物館の近くにある水月湖は年縞がきれいに7万年分も堆積している世界でも特異な場所です。年縞博物館ではこの水月湖の年縞を中心に展示がされており、控えめに言ってめちゃくちゃ面白かったのでぜひ行ってみてください。水月湖の研究者が出版している本も面白かったのでおすすめです。 https://www.amazon.co.jp/dp/B01N59630B https://www.amazon.co.jp/dp/B06X6H7RPS
  2. アプリケーションでsteep gem のコードを読み込んで利用していれば型定義が必要ですが、基本的にはまずありません。
  3. ディレクトリ構造の詳細は、次のドキュメントを参照してください。 https://github.com/ruby/rbs/blob/v3.2.1/docs/repo.md#directory-structure