[Steep]Railsの本番環境にruby3.0の型定義を入れていく
やったこと
自分の持ち手の中に以下の三拍子が揃ったプロダクトがあったのでruby 3.0まであげて強い気持ちで型を入れていくことにしました。
- 立ち上げ間もない(利用者も限定的)
- 各種ライブラリが最新(変な古いバージョンに影響されることが少ない)
- 今後そこそこの機能拡張が見込まれる
うちのチームでは初めてのRuby 3.0&型付だったけどやっていき
結論成果物
- tool
- RBS
- Steep
- API周りのモデル層に型定義を導入
- circleciによる自動テストの追加
Rubyの型とは
- RBS
- Steep
Rubyの静的解析はv3.0で導入された新機能になります。
型定義はrubyのコードの外側にRBSファイルとして定義していきます。
2010年代は静的型言語の時代でした。Rubyは抽象解釈を武器に、型宣言なしで静的型チェックする未来を目指します。RBSとTypeProfはその第一歩です。Rubyがもたらす誰も見たことがない静的型の世界を見守ってください — Matz
引用: https://www.ruby-lang.org/ja/news/2020/12/25/ruby-3-0-0-released/
Rubyの型定義周りでいくつかのツールが出てきてこんがらがりやすいのでさっくりまとめます。
ここら辺は クックパッドさんの開発者ブログ が詳しく書かれていました。
- RBS
- Rubyの型定義を行うための言語。Ruby 3 にバンドルされています
- TypeProf
- Rubyのコードから型を解析してRBSファイルを出力するためのツール。Ruby 3 にバンドルされる。
- Steep/Sorbet
- Rubyの型チェックの実施やIDEで型の表示やリアルタイムで型の確認などをしてくれるツール。
さらにRailsや本番環境に型を導入するにはここら辺も必要になってきました。
-
gem_rbs_collection
- 各種gemのrbsファイルをよしなに集約してくれるgem
-
rbs_rails
- Railsの各種機能のrbsを提供してくれたり、ActiveRecordやurl_helper周りの定義ファイルの作成タスクを提供してくれている
rspecで書いたテストからTypeProf通して自動生成とかしてくれないかなぁ。。。という希望(見つからなかった)
導入していく
必要なライブラリの導入
一旦各種ライブラリを導入
# Gemfile
group :development, :test do
gem 'rbs_rails', require: false
gem 'steep', require: false
gem 'rbs', require: false
end
gem_rbs_collection導入
どこかにおいてプロダクト間で同じものを利用してもいいのですが
- 各々のPCでgem_rbs_collectionを置く場所を強制する
- 別プロダクトで参照するgem_rbs_collectionの場所を強制する
のようなお気持ちがなかったのでサブモジュールとしてプロダクト配下に追加
$ git submodule add https://github.com/ruby/gem_rbs_collection.git gem_rbs/gems
Steepfileの用意
Steepfileを一旦作成(余計なものも入っているかもしれないですが一旦導入することを優先)
target :app do
signature 'sig' # => 型定義ファイルをおくディレクトリ
check 'app' # => チェック対象ディレクトリ(最終的にはmodelsの特定のディレクトリ(API関連のロジック)だけに絞りました...)
repo_path "gem_rbs/gems" # => submoduleで追加したディレクトリ
library 'pathname'
library 'logger'
library 'mutex_m'
library 'date'
library 'monitor'
library 'singleton'
library 'tsort'
library 'activesupport'
library 'actionpack'
library 'activejob'
library 'activemodel'
library 'actionview'
library 'activerecord'
library 'railties'
end
rbs_railsのセットアップ
こちらの通りに進めていく
https://github.com/pocke/rbs_rails#installation
# lib/tasks/rbs.rake
require 'rbs_rails/rake_task'
RbsRails::RakeTask.new
タスク実行!
$ bundle exec rails rbs_rails:all
ActiveRecordやら各種url_helper周りの型定義ファイルが出てきた。。。。つよぃ。。。
circleciによる自動テスト
実際にrspecでテストしているところに入れていくので細かいところははしょります
git submoduleで追加したgem_rbs_collectionを更新する
commands:
...
install_submodule:
description: install submodule
steps:
- run:
name: git submodule init
command: git submodule init
- run:
name: git submodule update
command: git submodule update
job用意
jobs:
steep:
executor:
name: default
steps:
- checkout
- setup_something # bundle install etc
- install_submodule
- run:
name: run steep
command: bundle exec steep check
workflowに追加
workflows:
version: 2
build-and-deploy:
jobs:
- steep
- rspec # (既存のもの)
実際のプロダクトに入れていく
実際のRBSの書き方に関しては pockestrap - RBS基礎文法最速マスター や githubのドキュメント が参考になります。
またローカルで小さなrubyファイルを作成してTypeProfを実行してどんなRBSが出力されるかをみると参考になります。
まずスコープを絞った
出てきたエラー件数を見て一旦対象を外部露出しているAPIのロジック部分に絞りました。
target :app do
...
check 'app/models/api'
...
end
ARを拡張している層の型定義はgeneratorに寄せて自動生成
このプロダクトではARをラッピングした層を用意しており、基本的なメソッドはARにdelegateして特定のメソッドを拡張できるようにしていました。
この層ではmethod_missingをフックに自前定義していないものはARにdelegateする機構を組んでいて、ARで提供しているメソッドも提供しています。
こちらに対してissueがありましたがどうにも上手いやり方はない様子...
https://github.com/ruby/rbs/issues/422
最初はよく使われているものだけ共通層に定義すればいいかなと思ったのですが、個別モデルの事情(特にRelation周り)によったものを都度定義するのはだいぶしんどかったので最終的にgeneratorを自分で作ることにしました。
幸い rbs_rails でActiveRecordに対する型定義を自動生成していたので多分に参考にさせてもらいながらシンプルを保てる範囲で自動型定義ファイルの出力をするようにしました。
一つのclassに対して複数のrbsファイルで分けても大丈夫と言うことなのでrbs_railsと同様自前のディレクトリを用意してそこにrbsファイルを出力するようにしました。
これによって実際の自前定義したビジネスロジック部分だけを型付けしていけばよくなったのでだいぶやる気が上がりました
Steepのバグ?に当たってciが通らない
継承元のメソッドに対してsuper(**args, &block)と渡した時にblock optionalで宣言した型定義が通らない問題に直面し
悩んだ挙句ruby-jpにお尋ねした。
https://ruby-jp.slack.com/archives/CM3PA3DAB/p1618535504180600
結果Steepのバグの可能性が出てきた。
そこでどうしてもクリアできないところへの対処を教えてもらいました。
__skip__ = begin
dosomething # この中ではSteepによる検査がスキップされる
end
結果
参考
ruby3.0 release note
クックパッド開発者ブログ - Ruby 3の静的解析機能のRBS、TypeProf、Steep、Sorbetの関係についてのノート
pockestrap - RBS Railsを使ってRailsアプリケーションにSteepを導入する
Author And Source
この問題について([Steep]Railsの本番環境にruby3.0の型定義を入れていく), 我々は、より多くの情報をここで見つけました https://qiita.com/tatematsu-k/items/a0e8bf3a244a6e6b95f5著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .