【Ruby3】rbs コマンド使い方【RBS】

# はじめに

RBS は Ruby3 で追加された、Ruby の型を宣言する言語です。
この記事では、その RBS 言語で書かれた型定義を処理するためのライブラリ `rbs` gem の `rbs` コマンドの使い方についてまとめてみました。

以下の環境で動作を確認しました。

```
$ ruby -v
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
$ rbs -v
rbs 2.8.2
```

# `rbs` コマンドの使い方

以下コマンドでインストールできます。

```
$ gem install rbs
(省略)
$ rbs -v
rbs 2.8.2
```

`rbs` または `rbs help` で ヘルプが出ます。

```
$ rbs help
Usage: rbs [options...] [command...]

Available commands: ast, annotate, list, ancestors, methods, method, validate, constant, paths, prototype, vendor, parse, test, collection, version, help.

Options:
    -r LIBRARY                       Load RBS files of the library
    -I DIR                           Load RBS files from the directory
        --no-stdlib                  Skip loading standard library signatures
        --collection PATH            File path of collection configuration (default: rbs_collection.yaml)
        --no-collection              Ignore collection configuration
        --repo DIR                   Add RBS repository
        --log-level LEVEL            Specify log level (defaults to `warn`)
        --log-output OUTPUT          Specify the file to output log (defaults to stderr)
```

- オプション
    - `-r` でライブラリを、 `-I` でディレクトリ読み込むことができます。
- サブコマンド
    - 以下のサブコマンドが使用可能です。

`ast`, `annotate`, `list`, `ancestors`, `methods`, `method`, `validate`, `constant`, `paths`, `prototype`, `vendor`, `parse`, `test`, `collection`, `version`, `help`


## 各サブコマンドの使用方法

サブコマンドの種類ごとに分け、それぞれ簡単に使い方まとめました。

- `rbs` コマンドに関する情報を表示するコマンド
    - `help`, `version`, `paths`
- `*.rb` ファイルから型定義の雛形を生成するコマンド
    - `prototype`
- 読み込まれた `*.rbs` ファイルに関する情報を表示するコマンド
    - `ast`, `list`, `ancestors`, `methods`, `method`, `constant`
- `*.rbs` ファイルのチェックを行うコマンド
    - `validate`, `parse`
- その他コマンド
    - `annotate`, `test`, `collection`, `vendor`

## `rbs` コマンドに関する情報を表示するコマンド

- `help`: ヘルプを表示する

上に書いたので、省略します。

- `version`: バージョンを表示する

```
$ rbs version
rbs 2.8.4
```

- `paths`: 読み込まれた `*.rbs` ファイルのパスが表示されます。

```
$ rbs paths
/var/lib/gems/2.7.0/gems/rbs-2.8.4/core (dir, core)
```

```
$ rbs -I types paths
/var/lib/gems/2.7.0/gems/rbs-2.8.4/core (dir, core)
types (dir)
```

## `*.rb` ファイルから型定義の雛形を生成するコマンド

- `prototype`

指定した `*.rb` ファイルから、RBSのひな形を出力します。

```
$ rbs prototype --help
Usage: rbs prototype [generator...] [args...]

Generate prototype of RBS files.
Supported generators are rb, rbi, runtime.

Examples:

  $ rbs prototype rb foo.rb
  $ rbs prototype rbi foo.rbi
  $ rbs prototype runtime String
```

試しに以下のファイルを用意します。

```rb:sample.rb
class Sample
  def self.sample
    puts 'sample class'
  end
end
```

`rbs prototype` コマンドで、型定義のプロトタイプが出力されました。

```rb
$ rbs prototype rb sample.rb 
class Sample
  def self.sample: () -> untyped
end
```

## 読み込まれた `*.rbs` ファイルに関する情報を表示するコマンド

- `list`: クラス・モジュール・インターフェースが表示されます。

```
$ rbs list class
::Array (class)
::BasicObject (class)
::Binding (class)
::Class (class)
::Comparable (module)
::Complex (class)
::Dir (class)
::Encoding (class)
```

- `ancestors`: 指定したクラスのスーパークラスが表示されます。

```
$ rbs ancestors Array
::Array[Elem]
::Enumerable[Elem]
::Object
::Kernel
::BasicObject
```

- `methods`: クラスに定義されているメソッドが表示されます。クラスを指定する必要があります。

```
$ rbs methods Array
! (public)
!= (public)
!~ (public)
& (public)
* (public)
+ (public)
- (public)
<< (public)
```

- `method`: メソッドの型が表示されます。クラスとメソッドを指定する必要があります。

```
$ rbs method Array reverse
::Array#reverse
  defined_in: ::Array
  implementation: ::Array
  accessibility: public
  types:
      () -> ::Array[Elem]
```

- `constant`: 指定した定数の型が表示されます。

```
$  rbs constant ::Object
Context: ::
Constant name: ::Object
 => ::Object: singleton(::Object)
```

-  `ast`: 読み込まれている型定義情報のASTをJSONで表示するコマンドです。

```json
$ rbs ast | jq . | less
[
  {
    "declaration": "class",
    "name": "::Array",
    "type_params": [
      {
        "name": "Elem",
        "variance": "covariant",
        "unchecked": true,
        "location": {
          "start": {
            "line": 566,
            "column": 12
          },
          "end": {
            "line": 566,
            "column": 30
          },
          "buffer": {
            "name": "/var/lib/gems/2.7.0/gems/rbs-2.8.4/core/array.rbs"
          }
        },
        "upper_bound": null
      }
    ],
(省略)
```

## `*.rbs` ファイルのチェックを行うコマンド

- `parse`: 指定したRBSファイルをパースし、syntax error があったら表示されます。

```
$ rbs parse types/syntax_error.rbs 
types/syntax_error.rbs:3:0...3:1: Syntax error: unexpected token for class/module declaration member, token=`` (pEOF)
```

- `validate`: RBSファイルを validate します。(`parse` よりも詳しい？)

```
$ rbs -I types validate types/syntax_error.rbs 
Traceback (most recent call last):
        20: from /usr/local/bin/rbs:23:in `<main>'
        19: from /usr/local/bin/rbs:23:in `load'
        18: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/exe/rbs:7:in `<top (required)>'
        17: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/cli.rb:130:in `run'
        16: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/cli.rb:433:in `run_validate'
        15: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/environment.rb:149:in `from_loader'
        14: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/environment.rb:149:in `tap'
        13: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/environment.rb:150:in `block in from_loader'
        12: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/environment_loader.rb:97:in `load'
        11: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/environment_loader.rb:153:in `each_decl'
        10: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/environment_loader.rb:123:in `each_dir'
         9: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/environment_loader.rb:123:in `each'
         8: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/environment_loader.rb:124:in `block in each_dir'
         7: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/environment_loader.rb:156:in `block in each_decl'
         6: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/environment_loader.rb:144:in `each_file'
         5: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/environment_loader.rb:144:in `each'
         4: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/environment_loader.rb:145:in `block in each_file'
         3: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/environment_loader.rb:132:in `each_file'
         2: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/environment_loader.rb:162:in `block (2 levels) in each_decl'
         1: from /var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/parser_aux.rb:17:in `parse_signature'
/var/lib/gems/2.7.0/gems/rbs-2.8.4/lib/rbs/parser_aux.rb:17:in `_parse_signature': types/syntax_error.rbs:3:0...3:1: Syntax error: unexpected token for class/module declaration member, token=`` (pEOF) (RBS::ParsingError)
```

## その他コマンド

- `vendor`

`vendor/sigs`ディレクトリを作成し、読み込まれている型情報から rbs ファイルが作成されます。

```
$ rbs vendor
Vendoring signatures to vendor/sigs...
  Copying RBS files...
```

- `collection`: RBS collection のコマンドです。 (RBS ファイルの依存関係の解決等を行うコマンド)
    - rbs collection init
        - rbs_collection.yaml ファイルが作成される
    - rbs collection install
        - rbs_collection.yaml ファイルに指定された gem の RBS をインストールする
    - rbs collection update
        - rbs_collection.yaml ファイルに指定された gem の RBS を更新する

RBS collection については、以下に記載されています。ここでは割愛します。

https://github.com/ruby/rbs/blob/master/docs/collection.md

- `annotate`: RDoc から RBSファイルを更新してくれるコマンドらしいです。
    - (RDoc使ったことがないのでヘルプのみ記載します。追加の情報があれば後日追記します。)

```
$ rbs annotate --help
Usage: rbs annotate [options...] [files...]

Import documents from RDoc and update RBS files.

Examples:

  $ rbs annotate stdlib/pathname/**/*.rbs

Options:
        --[no-]system                Load RDoc from system (defaults to true)
        --[no-]gems                  Load RDoc from gems (defaults to false)
        --[no-]site                  Load RDoc from site directory (defaults to false)
        --[no-]home                  Load RDoc from home directory (defaults to false)
    -d, --dir DIRNAME                Load RDoc from DIRNAME
        --[no-]arglists              Generate arglists section (defaults to true)
        --[no-]filename              Include source file name in the documentation (defaults to true)
        --[no-]preserve              Try preserve the format of the original file (defaults to true)
```

- `test`
    - (使い方がわからなかったので、一旦ヘルプのみ記載します。追加の情報があれば後日追記します。)

```
$ rbs test --help
Usage: rbs [rbs options...] test [test options...] COMMAND

Examples:

  $ rbs test rake test
  $ rbs --log-level=debug test --target SomeModule::* rspec
  $ rbs test --target SomeModule::* --target AnotherModule::* --target SomeClass rake test

Options:
        --target TARGET              Sets the runtime test target
        --sample-size SAMPLE_SIZE    Sets the sample size
        --unchecked-class UNCHECKED_CLASS
                                     Sets the class that would not be checked
        --double-suite DOUBLE_SUITE  Sets the double suite in use (currently supported: rspec | minitest)
```

# おわりに

`rbs` コマンドには様々なサブコマンドが用意されていることがわかりました。サブコマンドの多くは `rbs サブコマンド --help` で使用方法が出てくるので、簡単に様々なコマンドを試すことができました。

これから RBS が普及していけば、業務でも `rbs` コマンドを利用する機会などが出てくるかもしれないので、今回調べきれなかったサブコマンドも含め、使いこなせるようになりたいです！

# 参考文献

- [Rubyで型チェック！動かして理解するRBS入門 〜サンプルコードでわかる！Ruby 3.0の主な新機能と変更点 Part 1〜](https://qiita.com/jnchito/items/bf8c6c2e1dd6cff05f4e)
- [rbs cli - pockestrap](https://pocke.hatenablog.com/entry/2020/06/15/081130)
- [RBS CollectionをRailsアプリで試してみよう](https://zenn.dev/leaner_dev/articles/20210915-rubykaigi-2021-rbs-collection)
- github
    - https://github.com/ruby/rbs
    - https://github.com/ruby/gem_rbs_collection
    - https://github.com/pocke/rbs_rails