はじめに
この記事は、rbs-inlineを触ってみた際のメモ的な記事です。rbs-inlineのREADMEを参照しながら動かしてみました
検証しているバージョン
- Ruby 3.3.4
- rbs-inline 0.6.0
- steep 1.7.1
導入
RubyやBundlerは導入ずみであるとします。
1. bundle install
bundle init
でGemfileを作成し、その中にrbs-inlineを追加します。
# frozen_string_literal: true
source "https://rubygems.org"
gem 'rbs-inline', require: false
追記したら、bundle install
。
$ bundle install
Fetching gem metadata from https://rubygems.org/...
Resolving dependencies...
Fetching rbs 3.5.3
Installing rbs 3.5.3 with native extensions
Fetching rbs-inline 0.6.0
Installing rbs-inline 0.6.0
Bundle complete! 1 Gemfile dependency, 5 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
2. Rubyを書く
README.mdにサンプルが書かれているので、それを参考にコードを書きます。
$ mkdir lib
$ touch lib/person.rb
# rbs_inline: enabled
class Person
attr_reader :name #: String
attr_reader :addresses #: Array[String]
# @rbs name: String
# @rbs addresses: Array[String]
# @rbs return: void
def initialize(name:, addresses:)
@name = name
@addresses = addresses
end
def to_s #: String
"Person(name = #{name}, addresses = #{addresses.join(", ")})"
end
# @rbs &block: (String) -> void
def each_address(&block) #:: void
addresses.each(&block)
end
end
3. RBS fileの生成をCLI上で確認する
bundle exec rbs-inline [ディレクトリ/ファイル名]
とすると、CLI上にRBSの生成結果が出力されます。
$ bundle exec rbs-inline lib/person.rb
# Generated from lib/person.rb with RBS::Inline
class Person
attr_reader name: String
attr_reader addresses: Array[String]
# @rbs name: String
# @rbs addresses: Array[String]
# @rbs return: void
def initialize: (name: String, addresses: Array[String]) -> void
def to_s: () -> String
# @rbs &block: (String) -> void
def each_address: () { (String) -> void } -> untyped
end
🎉 Generated 1 RBS files under
4. 自動生成したRBSをファイルに保存する
bundle exec rbs-inline --output [ディレクトリ/ファイル名]
を実行すると、sig/generated
配下にrbsファイルが保存されます。
$ bundle exec rbs-inline --output lib/person.rb
🎉 Generated 1 RBS files under sig/generated
5. steepで型チェック
$ bundle add steep
でsteepを導入します。
% bundle add steep
Fetching gem metadata from https://rubygems.org/.............
(省略)
Fetching steep 1.7.1
Installing steep 1.7.1
Steepfileを$ bundle exec steep init
で生成します。
$ bundle exec steep init
Writing Steepfile...
$ tree .
.
├── Gemfile
├── Gemfile.lock
├── Steepfile
├── lib
│ └── person.rb
└── sig
└── generated
└── person.rbs
4 directories, 5 files
Steepのgetting startedを参考に、Steepfileに以下を追記します
target :app do
check "lib"
signature "sig"
end
bundle exec steep check
で型チェックをします。
$ bundle exec steep check
# Type checking files:
.....................................................................................
No type error detected. 🧋
6. わざとエラーが発生することを体験する
5までの手順で、型チェックができるようになりました。しかし、エラーが出る場合を体験していないので、わざとエラーが出るようにしてみます。
6-1. sig/generated/person.rbs
を編集する
今回は、rbs-inlineを書かずに手動でRBSファイルを変更します。
以下のように、def age: () -> String
を追記します。
(省略)
def age: () -> String
end
6-2. Person#age
を追加
Personクラスに、Integerを返すageインスタンスメソッドを追記します。
# rbs_inline: enabled
class Person
# 省略
def age
20
end
# 省略
6-3. 型チェックを実行する
bundle exec steep check
を実行すると、エラーが出力されました。RBSではStringを期待していたのに、Person#age
ではIntegerを返していることから、MethodBodyTypeMismatch
と判定されているようです。
$ bundle exec steep check
# Type checking files:
....................................................................................F
lib/person.rb:19:6: [error] Cannot allow method body have type `::Integer` because declared as type `::String`
│ ::Integer <: ::String
│ ::Numeric <: ::String
│ ::Object <: ::String
│ ::BasicObject <: ::String
│
│ Diagnostic ID: Ruby::MethodBodyTypeMismatch
│
└ def age
~~~
Detected 1 problem from 1 file
6-4. 修正する
rbs-inlineを用いて、RBSを修正してみましょう。
まずは、Person#age
に戻り値の型定義を追加します。
def age #: Integer
20
end
つぎに、`bundle exec rbs-inline --output lib/person.rb`でRBSファイルを自動生成します。
$ bundle exec rbs-inline --output lib/person.rb
🎉 Generated 1 RBS files under sig/generated
6-5. 修正後の型チェックを実行
bundle exec steep check
を再実行すると、今度は問題なく型チェックが通りました。
$ bundle exec steep check
# Type checking files:
.....................................................................................
No type error detected. 🍵
さいごに
初めてrbs-inlineに触れてみました。Rubyのコードの中に型も一緒に書ける体験は非常にいいものだと感じました。まだ入門したばかりなので、もっと触れていこうと思います。