🎃

[Sorbet] テストを実行するだけで型(sig)を書いてくれるツール「gelauto」の紹介

に公開

背景

RubyのプロジェクトにSorbetで型をあてているとき、メソッドの型を何も書かないとuntypedになってしまいます。
とはいえ、すべてのメソッドにsigで書いていくのは結構大変ですよね。

実は、Sorbetにはsigを提案してくれる機能があります。
しかし、個人的な印象として、型がある程度あたったコードでないとuntypedと提案されることが多いように思います😇

紹介

https://github.com/camertron/gelauto

今回紹介するgelautoは、テストを実行するだけで型を書いてくれるツールとなっています。内部的にはTracePointを使っています。
テストはあるけど、まだあまり型があたっていないプロジェクトには特に便利なツールと言えそうです。

Sorbet(ソルベ)に合わせて、gelato(ジェラート)をもじった名前にしてあるのもいいですね。
gelautoの読み方もジェラートでいいのでしょうか。

類似のツールとして、テストを実行するとRBSで型を書いてくれるRBS::Traceがあります。
最近、SorbetがRBS Commentに対応し始めたので、これらを組み合わせて使うのもありかと思います。
今年のRubyKaigiでは、RBS::TraceとSorbetのRBS Comment対応の両方の話が聞けそうですね👀

使い方

ではミニマルな環境を作り、実際にgelautoを使ってみます。(gelauto v2.1.0)
コードはこちらに: https://github.com/pvcresin/minimal-gelauto-sample

今回はBundlerを使ったサンプルを作りました。
Gemfileは以下のようになっています。

# frozen_string_literal: true

source "https://rubygems.org"

gem "gelauto"
gem "racc"
gem "rspec"
gem "sorbet-static-and-runtime"

Sorbetで型チェックを行い、RSpecでテストを書いています。gelautoの実行にraccがいるようだったので、加えています。(追記: raccの追加はgelauto v2.2.0でいらなくなりました。)
gelautoはいろいろな使い方ができるのですが、このサンプルではRSpecを実行したときにテスト対象のコードに型(sig)を追記する形にしています。

例えば、以下のようなlib/sample.rbに対し、

# typed: true
require 'sorbet-runtime'

class A
  extend T::Sig

  def foo(x)
    x.to_s
  end
end

RSpecで書かれたテスト、spec/sample_spec.rbがあるとします。

require_relative '../lib/sample'

describe 'A' do
  describe "#foo" do
    it "returns the string" do
      expect(A.new.foo(1)).to eq('1')
    end
  end
end

ここでgelautoのREADME.mdに従って以下のコマンドを実行します。

bundle exec gelauto run --annotate $(find . -name '*.rb') -- bundle exec rspec spec/

するとlib/sample.rbにsigが追加されました🎉

# typed: true

require 'sorbet-runtime'

class A
  extend T::Sig

+ sig { params(x: Integer).returns(String) }
  def foo(x)
    x.to_s
  end
end

手軽で良いですね。

気になる点

最強かと思いきや気になる点もあります。

  • 開発はあまり活発ではない😇
  • 最後のリリースではparserが2系なのでdef a = 1などが使えない
  • 末尾の改行が消される

期待と不安を同時に味わえるgelauto。
どんどんユーザが増えることによって、盛り上がっていくといいですね。

追記

2025/04/04

gelauto v2.2.0がリリースされ、以下の点が解消されました🎉

  • Bundlerでgelautoを実行する際に別途raccのインストールが必要
  • 最後のリリースではparserが2系なのでdef a = 1などが使えない

Sampleのバージョンも更新してあります。

Discussion