既存rails projectにrbs/steepを導入する
gemの追加
bundle add --group development --require false steep rbs_rails
rbs collection のインストール
bundle exec rbs collection init
bundle exec rbs collection install
標準で .gem_rbs_collection
ディレクトリにインストールされるので
.gitignore に .gem_rbs_collection
を追加しておくと良い
rbs_rails の rake task 作成
lib/tasks/rbs.rake ファイルを下記内容で作成
require 'rbs_rails/rake_task'
RbsRails::RakeTask.new
rbs_rails の rake task 実行(これはmodelが更新されるたびに実行すると良い)
bin/rails rbs_rails:all
sig/rbs_rails/ に rbs ファイルが生成されるので、覗いてみる。
rake task は定期的に実行されるので、 sig/rbs_rails/ 以下のファイルには手動修正は行わない。
Steepfile の作成
下記コマンドでひな形を生成(ドキュメント目的)
bundle exec steep init
Steepfile に下記内容を追加する
target :app do
signature "sig" # rbs file directory
check "app" # target diregtory
collection_config "rbs_collection.yaml" # rbs collection のインストールで作成されたファイル
end
これで
bundle exec steep check
でチェックできるようになった。
VSCodeに steep extension をインストールすると、VSCode でも利用できる。
ここまでは簡単。
既存コードのプロトタイプ一括生成
sig/rbs_rails に rbs_rails の生成したrbsファイルがあるので、ディレクトリを分ける。
find app -type d | xargs -S1000 -I{} bundle exec rbs prototype rb {}/*.rb -o sig/app/
sig/local/app
みたいにもう1階層掘るのも良いかも
日本語が混じっているとrbs prototypeがそのまま出して エラーになるので
生成されたrbsファイルに日本語が混じっていれば修正する。
例えば
alias 日本語 aliased_method
みたいなのがあれば
alias `日本語` aliased_method
のように書き換える
同様に
def method: (日本語: untyped) -> untyped
も
def method: (`日本語`: untyped) -> untyped
のように置き換える
この挙動はbugの様な気もしたので issue をたてた。https://github.com/ruby/rbs/issues/1194
修正箇所は
このあたりか?
あとは、定義がなくてエラーの出る個所をひたすら型定義していく。gemファイルのものは
sig/gems/...
のようなディレクトリに置いたりすると、自分たちのコードや自動生成されたものとも分けられてよい。
他所のgemのrbsファイルが上手くつくれたらrbs_collectionにPRを出して貢献していくのがいいと思う
ActiveRecord::TimeWithZone型(ActiveRecordのupdated_atなど)にDateTimeやTimeを入れていると型違いでエラーになるので
in_time_zone() をつけて TimeWithZone に明示的に変換してまわる。
たまに刺さるファイルがあるので --verbose
とかでファイルを特定して
Steepfileに
ignore(
"該当ファイル1",
"該当ファイル2",
)
なとどしていったん除外する。
VSCode Steep で余計な警告を出さないようにignoreしたものたち
configure_code_diagnostics do |hash|
hash[D::Ruby::NoMethod] = nil
hash[D::Ruby::FallbackAny] = nil
hash[D::Ruby::UnknownInstanceVariable] = nil
hash[D::Ruby::MethodDefinitionMissing] = nil
hash[D::Ruby::UnknownConstant] = nil
hash[D::Ruby::UnexpectedError] = nil
hash[D::Ruby::UnexpectedSuper] = nil
hash[D::Ruby::IncompatibleAssignment] = nil
hash[D::Ruby::UnexpectedBlockGiven] = nil
hash[D::Ruby::UnexpectedKeywordArgument] = nil
hash[D::Ruby::UnexpectedPositionalArgument] = nil
hash[D::Ruby::InsufficientKeywordArguments] = nil
hash[D::Ruby::UnresolvedOverloading] = nil
hash[D::Ruby::UnexpectedJump] = nil
end