Railsアプリを型チェックできるようにSteepに入門してみた
今年も残り少なくなってきて、そういえばRailsアプリに型チェックを導入してみたいなーと思っていたのを思い出したのでSteepに入門してみた。
RailsアプリへのSteep導入は神速さんのブログがすごく丁寧だったのでそれを参考にしてやった sinsoku.hatenablog.com
差分としては
- lib/tasks/rbs.rake を作成する
- 現在は
bin/rails g rbs_rails:install
するとlib/tasks/rbs.rakeが生成される
- 現在は
- 不足してるrbsを用意する
- 自分は
ActionCable::Channel::Base
とActionCable::Connection::Base
だけでよかった
- 自分は
# sig/patch.rbs module ActionCable module Channel class Base end end module Connection class Base end end end
あと、神速さんのブログのscaffold を試すのところにあるようにコントローラーのエラーも確かに発生していて、自分は scaffold userをしたんだけどそのエラーが下記。
# Type checking files: ..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................F..... app/controllers/users_controller.rb:7:4: [error] Cannot find the declaration of instance variable: `@users` │ Diagnostic ID: Ruby::UnknownInstanceVariable │ └ @users = User.all ~~~~~~ app/controllers/users_controller.rb:17:4: [error] Cannot find the declaration of instance variable: `@user` │ Diagnostic ID: Ruby::UnknownInstanceVariable │ └ @user = User.new ~~~~~ app/controllers/users_controller.rb:27:4: [error] Cannot find the declaration of instance variable: `@user` │ Diagnostic ID: Ruby::UnknownInstanceVariable │ └ @user = User.new(user_params) ~~~~~ app/controllers/users_controller.rb:31:34: [error] Type `::UsersController` does not have method `user_url` │ Diagnostic ID: Ruby::NoMethod │ └ format.html { redirect_to user_url(@user), notice: "User was successfully created." } ~~~~~~~~ app/controllers/users_controller.rb:45:34: [error] Type `::UsersController` does not have method `user_url` │ Diagnostic ID: Ruby::NoMethod │ └ format.html { redirect_to user_url(@user), notice: "User was successfully updated." } ~~~~~~~~ app/controllers/users_controller.rb:60:32: [error] Type `::UsersController` does not have method `users_url` │ Diagnostic ID: Ruby::NoMethod │ └ format.html { redirect_to users_url, notice: "User was successfully destroyed." } ~~~~~~~~~ app/controllers/users_controller.rb:69:4: [error] Cannot find the declaration of instance variable: `@user` │ Diagnostic ID: Ruby::UnknownInstanceVariable │ └ @user = User.find(params[:id]) ~~~~~ Detected 7 problems from 1 file
path helperのやつとインスタンス変数の定義がないと怒られたので解決してみた。
path helper関連
これはrbs_rails:allをしたときにsig/rbs_rails/path_helpers.rbsが生成されているのになんでそれ見てくれないのかなと疑問だったんだけど、
# sig/rbs_rails/path_helpers.rbs interface _RbsRailsPathHelpers ・・・ def users_url: (*untyped) -> String def new_user_url: (*untyped) -> String def edit_user_url: (*untyped) -> String def user_url: (*untyped) -> String ・・・ end
このようにinterfaceで定義されているだけなので、これをコントローラーのrbsファイルでincludeしてやればよさそう。
(まだRBSが全く分かってないんだけど、RBS基礎文法最速マスター - pockestrap見つつこう使うのかなーみたいな感じでやってみたらエラーはでなくなった)
# sig/app/controllers/users_controller.rbs class UsersController < ApplicationController include _RbsRailsPathHelpers # GET /users or /users.json def index: () -> untyped # GET /users/1 or /users/1.json def show: () -> nil # GET /users/new def new: () -> untyped ・・・ end
インスタンス変数
Cannot find the declaration of instance variable: `@user` Cannot find the declaration of instance variable: `@users`
@userと@usersのRBS定義がないのでこれは手動で定義した。
# sig/app/controllers/users_controller.rbs class UsersController < ApplicationController @user: User @users: User::ActiveRecord_Relation include _RbsRailsPathHelpers # GET /users or /users.json def index: () -> untyped # GET /users/1 or /users/1.json def show: () -> nil # GET /users/new def new: () -> untyped ・・・ end
あと注意点としては rbs_rails:allしたときにmodelの定義も生成してくれるけど、それはdb:migrateを実行しておかないと生成されない。
rbs_rails/rake_task.rb at 227285bd04d526525331a9b05fe9ce5e78038946 · pocke/rbs_rails · GitHub
::ActiveRecord::Base.descendants.each do |klass| next unless RbsRails::ActiveRecord.generatable?(klass)
ここの RbsRails::ActiveRecord.generatable?(klass)
が
rbs_rails/active_record.rb at 227285bd04d526525331a9b05fe9ce5e78038946 · pocke/rbs_rails · GitHub
def self.generatable?(klass) return false if klass.abstract_class? klass.connection.table_exists?(klass.table_name) end
とあるようにDBにテーブルが存在しているかを見てるので。
これでとりあえずはsteep checkが通るようになったので、なんとか入門までは行けた感じがする。
$ bundle exec steep check # Type checking files: ........................................................................................................................................................................................................................................ No type error detected. 🫖