はじめに
この記事はMastodon
にRBS
を導入したときの導入背景や手順などをまとめた記事になります。
主に僕個人の備忘録としてまとめています。
導入背景
2017年5月から6年ほどCreatodon
というMastodon
のサーバーを個人的に運営しています。
直近で開発環境周りの体験向上としてCIの修正とかE2Eテストの追加とか色々やっていたんですが、型関係はまだ手を入れていませんでした。
また独自機能の実装やそのメンテナンスなども行っており、既存のModel
やService
などの型情報が簡単に確認できる状況が欲しくもなっていました。
そこで
TypeProf
を使い、既存のModel
やService
などのRBS
を自動生成- 既存のライブラリの型情報などは
gem_rbs_collection
を使って導入 Steep
で生成したRBS
の型情報をVS Code
で表示&型情報のチェック
までをMastodon
に導入してみました
やったこと
TypeProfを使い、既存のModelやServiceなどのRBSを自動生成
まずはMastodon
にある既存のModel
やService
、Contoroller
などの型をTypeProf
を使って自動生成しました。
コードとしては以下のような型の自動生成用タスクを追加して対応しました。
# frozen_string_literal: true require 'fileutils' top_level = self using(Module.new do refine(top_level.singleton_class) do def generate_type_signature(dir_name) files = Dir.glob("app/#{dir_name}/**/*") files.each do |file| next unless file.include?('.rb') result = `bundle exec typeprof #{file}` dir_name = File.dirname(file) FileUtils.mkdir_p("sig/#{dir_name}") File.write("sig/#{file}s", result) end end end end) namespace :type do namespace :prof do task models: :environment do generate_type_signature('models') end task controllers: :environment do generate_type_signature('controllers') end task helpers: :environment do generate_type_signature('helpers') end task lib: :environment do generate_type_signature('lib') end task presenters: :environment do generate_type_signature('presenters') end task serializers: :environment do generate_type_signature('serializers') end task services: :environment do generate_type_signature('services') end task validators: :environment do generate_type_signature('validators') end task workers: :environment do generate_type_signature('workers') end end task prof: ['prof:models', 'prof:controllers', 'prof:helpers', 'prof:lib', 'prof:presenters', 'prof:serializers', 'prof:services', 'prof:validators', 'prof:workers'] end
あとは欲しい場所(Model
ならbundle exec rake type:prof:models
のような感じで)の型情報をタスクとして実行すればOKです。
既存のライブラリの型情報などはgem_rbs_collectionを使って導入
次に既存のライブラリの型情報をgem_rbs_collection
を使って導入しました。
こちらのドキュメントを参考に以下のコマンドを実行。
rbs collection init
実行するとrbs_collection.yaml
が生成されます。これを以下のように変更しています。
# Download sources sources: - type: git name: ruby/gem_rbs_collection remote: https://github.com/ruby/gem_rbs_collection.git revision: main repo_dir: gems # You can specify local directories as sources also. # - type: local # path: path/to/your/local/repository # A directory to install the downloaded RBSs path: .gem_rbs_collection gems: # Skip loading rbs gem's RBS. # It's unnecessary if you don't use rbs as a library. - name: rbs ignore: true - name: steep ignore: true - name: ipaddr
変更内容としては後述するSteep
での型チェック時にパスするために必要なライブラリの設定だけ追加してる感じです。
あとは.gem_rbs_collection
を.gitignore
に追加して、以下のコマンドを実行するだけです。
rbs collection install
これで既存のライブラリの型情報が導入できました。
Steepで生成したRBSの型情報をVS Codeで表示
次にSteep
を導入し、TypeProf
とgem_rbs_collection
で導入した型情報をVS Code
上に表示させます。
まずはGemfile
にSteep
を追加してbundle install
を実行。
gem 'steep'
その後bundle exec steep init
を実行して、設定ファイルを生成します。
bundle exec steep init
生成されたSteepfile
を以下のように変更します。
target :app do check 'app' signature 'sig' end
check
には型情報をチェックしたい場所のディレクトリを指定し、signature
にRBS
があるディレクトリを指定している感じです。
あとは以下のVS Code
のExtension
をインストールし、VS Code
を再起動すればOKです。
再起動すると以下の画像のように型情報が表示されているかと思います。
型チェックがパスするように修正
次にbundle exec steep check
がパスするように修正しました。
やったこととしては
- デフォルトの設定だと大量のエラーが出るので一時的にIgnore
TypeProf
で生成された一部のRBS
での型情報が正しくないと表示されたのを修正app/lib
配下の一部のコードで重複エラーが返されたので修正
という感じです。
エラーを一時的にIgnoreするのは以下のようにSteepfile
を修正しました。
ignores = [ Steep::Diagnostic::Ruby::MethodDefinitionMissing, Steep::Diagnostic::Ruby::UnknownConstant, Steep::Diagnostic::Ruby::NoMethod, Steep::Diagnostic::Ruby::UnexpectedBlockGiven, Steep::Diagnostic::Ruby::IncompatibleAssignment, Steep::Diagnostic::Ruby::UnknownInstanceVariable, Steep::Diagnostic::Ruby::UnexpectedPositionalArgument, Steep::Diagnostic::Ruby::UnresolvedOverloading, Steep::Diagnostic::Ruby::InsufficientPositionalArguments, Steep::Diagnostic::Ruby::UnknownGlobalVariable, Steep::Diagnostic::Ruby::UnexpectedError, Steep::Diagnostic::Ruby::MethodBodyTypeMismatch, Steep::Diagnostic::Ruby::ArgumentTypeMismatch, Steep::Diagnostic::Ruby::UnsupportedSyntax, Steep::Diagnostic::Ruby::UnsupportedSyntax, Steep::Diagnostic::Ruby::UnexpectedSuper, Steep::Diagnostic::Ruby::UnexpectedYield, Steep::Diagnostic::Ruby::BreakTypeMismatch, Steep::Diagnostic::Ruby::RequiredBlockMissing, Steep::Diagnostic::Ruby::ReturnTypeMismatch, Steep::Diagnostic::Ruby::ImplicitBreakValueMismatch, ] target :app do check 'app' signature 'sig' configure_code_diagnostics do |hash| ignores.each do |ignore| hash[ignore] = :information end end end
あとTypeProf
で生成されたRBSの型情報が正しくない件については、以下のようなRange
が返ってくると期待されているものでエラーが返ってきました。
def show: -> Range
現状では泣く泣くuntyped
に置き換えて、一旦パスするようにしています......。
def show: -> untyped
app/lib
配下のコードの重複エラーについては以下のように対応しました。
def current_time: -> Float | ...
今後
おおよその型情報についてはTypeProf
とgem_rbs_collection
のおかげでなんとかなっていますが、untyped
なままのものも多い状況です。
なので、この辺を少しづつ片付けていきたいですねー。