今年も残り少なくなってきて、そういえば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. 🫖