rails new
してから steep check
が通るまでにやってみた作業をまとめておく。
GitHub
コードはGitHubで公開しているので、詳細な手順を知りたい方は参照してください。
手順
gemを入れる
# Gemfile group :development do gem 'rbs_rails', require: false gem 'steep', require: false end
bundle install
を実行する。
$ bundle install
rbs_rails のREADMEの手順に従って lib/tasks/rbs.rake
を作成する。
# lib/tasks/rbs.rake require 'rbs_rails/rake_task' RbsRails::RakeTask.new
gemのrbsを取得する
いくつかのgemの型定義はgem_rbs_collectionから取得して利用できる。
$ bundle exec rbs collection init $ bundle exec rbs collection install
取得したrbsファイルはGitで管理する必要はないので .gitignore に追加しておく。
/.gem_rbs_collection
rbs_railsでrbsを生成する
Railsの提供するメソッド定義をいくつか自動生成してくれる。
bin/rails rbs_rails:all
自動生成されるファイルなので .gitattributes に追加しておくと良い。*1
sig/rbs_rails/** linguist-generated
Steepfileを作る
bundle exec steep init
で雛形が作成されます。最小の設定だと以下の通り。
target :app do signature "sig" check "app" end
既存ファイルのrbsを用意する
ディレクトリ構造にあわせてrbsのプロトタイプを生成するシェル - アジャイルSEの憂鬱 で紹介したワンライナーを使う。
$ find app/ -name '*.rb' | xargs -I{} bash -c 'mkdir -p sig/$(dirname {}); bundle exec rbs prototype rb {} > sig/{}s;'
app/models/application_record.rb
のように最初から存在するファイルに対して、rbsのプロトタイプを生成する。
不足してるrbsを用意する
gem_rbs_collectionにないgemのrbsは自分で書く必要があるので書く。
rails new
した直後の状態で不足していた型定義は以下の通り。(オプションによって変わるかもしれない)
# sig/patch.rbs module ActiveRecord class Base def self.primary_abstract_class: () -> void end end module ActionMailer class Base def self.default: (untyped) -> void def self.layout: (untyped) -> void end end module ActionCable module Channel class Base end end module Connection class Base end end end
型チェックを実行する
ここまで設定がおわれば、 steep check
を実行できる。
$ bundle exec steep check # Type checking files: ..................................................................................................................................................... No type error detected. 🫖
ジェネレーター後にrbsを自動生成する
ジェネレーターを使ったときにrbsのプロトタイプを作成したり、 rbs_rails:all
を自動で実行するようにしておく。
少し楽になる。
# config/environments/development.rb Rails.application.configure do config.generators.after_generate do |files| files.each do |f| next unless f.match?(%r{^app/.+\.rb$}) rb_path = Rails.root.join(f) rbs_path = Rails.root.join('sig', f.sub(/\.rb$/, '.rbs')) rbs_path.dirname.mkpath unless rbs_path.dirname.exist? system("bundle exec rbs prototype rb #{rb_path} > #{rbs_path}", exception: true) end if files.any? { |f| f.match?(%r{^app/.+\.rb$}) } system("bin/rails rbs_rails:all", exception: true) end end end
scaffold を試す
ここまでの設定を済ませた状態で、scaffoldを試してみる。
$ bin/rails g scaffold book title author
型チェックを実行する。
$ bundle exec steep check # Type checking files: ......................................................................................................................................F...................... app/controllers/books_controller.rb:28:34: [error] Type `::BooksController` does not have method `book_url` │ Diagnostic ID: Ruby::NoMethod │ └ format.html { redirect_to book_url(@book), notice: "Book was successfully created." } ~~~~~~~~ app/controllers/books_controller.rb:41:34: [error] Type `::BooksController` does not have method `book_url` │ Diagnostic ID: Ruby::NoMethod │ └ format.html { redirect_to book_url(@book), notice: "Book was successfully updated." } ~~~~~~~~ app/controllers/books_controller.rb:55:32: [error] Type `::BooksController` does not have method `books_url` │ Diagnostic ID: Ruby::NoMethod │ └ format.html { redirect_to books_url, notice: "Book was successfully destroyed." } ~~~~~~~~~ Detected 3 problems from 1 file
コントローラで呼び出しているメソッドで、エラーが出る。
エラーについて
web上の記事だと check "app/models"
だけ型チェックしてる記事が多いので、現状だとコントローラの型チェックは難しいのかも?
直し方が分かったら、またブログに書くかもしれない。
おまけ
GitHub Actionsで型チェックを動かす
# .github/workflows/check.yml name: check on: push: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: run: runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@v3 - uses: ruby/setup-ruby@v1 with: bundler-cache: true - run: bundle exec rbs collection install - run: bundle exec steep check
Steep VSCode Integration
steep-vscode を使うと、VSCode上で型チェックの結果を見ることができる。
注意: devcontainerだとrbsの変更が反映されない?
devcontainerでsteep-vscodeを使ってもrbsのファイル変更が検知されない問題が起きた。
Command + Shift + P
=> Steep: Restart all
で再起動すれば直る。
原因はよく分からないですが、ファイルIO関連なのでDocker for Mac限定の問題かもしれません。
*1:プルリクのdiffが閉じた状態になる