[Rails] RBSを導入してみるぞ

2024/02/11

Rails

Railsで爆速開発をしているみなさん、こんにちは。

Railsを書いていると、やれ型がないからクソだの、ダックタイピングが気持ち悪いだの色々言われますよね。

個人的に爆速開発するならRails一択だと思っており、それらの言葉に負けずRailsを書いているのですが、そんなに言われるなら型というのはすごくいいものなんだろうということで、RBSを導入してみました。

RBSとは

RBSとは Ruby 3.0からRubyに標準添付されている型システムの総称であり、ライブラリ名でもあります。

RBSファイルは、Rubyファイルとは別のファイル・別の言語で記述することが特徴です。

class/module名とメソッド名に引数の型と返り値の型を記述します。

TypeScriptでいう.d.tsファイルのようなイメージですね。

なので、これがあってもなくても動作には関係なく、あくまで開発者のためのドキュメントという位置づけです。

導入

さて、導入ですが、Ruby3.0からはデフォルトでRBSが導入されているので、特に何もしなくても使えるようになっています。

ただ、ActiveRecordで継承されるメソッドをmodelに対して個別に記述するのは面倒なので、rbs_railsというgemを使って自動生成するようにしました。

Gemfile
gem 'rbs_rails' # Rails用のRBSファイルを自動生成するgem
gem 'steep' # RBSファイルをチェックするためのgem
bundle install

また、steepというgemも導入していますが、これはRBSファイルをチェックするためのgemです。

VSCodeの設定

VSCode上でRBSファイルを使うために、VSCodeの設定を変更します。

ローカルではdevContainerを利用しているので、.devcontainer/devcontainer.jsonに以下の設定を追加しました。

.devcontainer/devcontainer.json
{
  // ...
  "customizations": {
    "extensions": [
      "soutaro.rbs-syntax", // https://github.com/soutaro/vscode-rbs-syntax
      "soutaro.steep-vscode" // https://github.com/soutaro/steep-vscode
    ]
  }
}

これでVSCode上で.rbsのファイルのシンタックスハイライトがつくようになり、型定義のエラーやワーニングが表示されるようになります。

使ってみる

さて、使っていきましょう。

まずは簡単な初期設定ファイルを作成していきます。

Gemの型定義ファイルを作成する

bundle exec rbs collection init
bundle exec rbs collection install

gitignoreにも追加しておきましょう。

.gitignore
/.gem_rbs_collection

RBSファイルを生成する

steep init

これで、Steepfileというファイルが生成されます。

以下のように書き換えましょう。

Steepfile
target :app do
  signature 'sig'

  check 'app'
end

target :lib do
  signature 'sig'

  check 'lib'
end

rbs_railsでRBSファイルを生成する

bin/rails rbs_rails:all

これで、sigディレクトリにRBSファイルが生成されます。

sig/rbs_railsディレクトリが作成されていることを確認してください。

この時点で、一度steep checkを実行してみましょう。

steep check

たくさん怒られましたか?

未定義ファイルがたくさんあると思うので、それを一つずつ解消していきましょう。

RBSファイルを作成する

sigディレクトリ配下の構成は、以下のYouTubeを参考にしています。

https://youtu.be/VbXDuYA1sXE?si=zCcDy_jxhCyIXSwm

sig
├── app # Railsのappディレクトリ配下の自作RBSファイル
├── gems # collectionで対応できなかったgemのRBSファイル
├── rbs_rails # rbs_railsで生成されたRBSファイル
└── standard_lib # 標準ライブラリで不足しているRBSファイル

それでは、appディレクトリ配下のRBSファイルを作成していきましょう。

app/models/cat.rb
class Cat < ApplicationRecord
  validates :name, presence: true
  validates :age, presence: true

  def self.ransackable_attributes(_auth_object = nil)
    %w[
      age
      created_at
      id
      name
      updated_at
    ]
  end

  def sample_method
    name
  end
end
sig/app/models/cat.rbs
class Cat < ::ApplicationRecord
  def self.ransackable_attributes: (Object _auth_object) -> Array[String]
  def sample_method: () -> String
end

これで、steep checkを実行してみましょう。

無事通りましたか?

それでは、sample_methodageに変更してみましょう。

そうするとエラーが出るはずです。

Cannot allow method body have type `::Integer` because declared as type `::String`
  ::Integer <: ::String
    ::Numeric <: ::String
      ::Object <: ::String
        ::BasicObject <: ::String

↑上記のようなものがVSCode上のProblemsに表示されるはずです。

これで、RBSファイルの型チェックができるようになりました。

やってみての所感

RBSファイルを書くことで、型チェックができるようになり、型のないRubyに型を導入できるようになりました。

面白いなとは思いつつ、これくらいの簡単なものではまだメリットを感じきれないですね…

もうちょっと大きなプロジェクトで使ってみたいと思います。

また、controllerやworkerなどに型を持たせるメリットがあるのか?というところが現状把握しきれていない感じがするので、そこも含めて使っていきたいと思います。

個人的には、ViewComponentのgemがRBS Collectionに含まれていなかったのが意外でした。

まだまだ対応していないgemが多いので、そこも含めて使っていく上での課題になりそうです。

今回はこのあたりで。