himanushi/music-server

ruby / rbs で型検証追加する

Closed this issue · 62 comments

TypeScriptを実装しているとやはり型チェックはあったほうが何かと便利。
Ruby 3.0 で導入予定な静的型検証である rbs を使ってみる。

流れ
rbs ファイル作りまくる(rails も)
steep で検証しまくる
以上

steep は vscode のプラギンもある模様

一旦保留
(gem がないとか色々と辛い

gem "rbs" で使えるようになってる!ほんと神!

未来の自分のために、これかなり優先度あげて確認してみる

gem "rbs"
導入した

rbsファイル作ってく

日本語でおk

rbs の内容自動生成コマンド
rbs prototype rb app/models/album.rb

rails どうすんねんと思っていたらすでに神gemが
https://github.com/pocke/rbs_rails

rake copy_signature_files
rake generate_rbs_for_model
するだけでsigディレクトリにファイルがたっくさん追加される

rake generate_rbs_for_model 実行時に
Mysql2::Error: Table '***********_db.action_text_rich_texts' doesn't exist
というエラーがでる。

action_text スキップしないとダメか

自動生成ほんと神
明日 rbs validate やる
https://github.com/himanushi/music-server/compare/18/rbs?expand=1

検証方法がようわからない

mode: :extension がなくなっていた

rbs ファイルの定義が壊れていないかを検証する
rbs -I sig/ validate

https://pocke.hatenablog.com/entry/2020/06/15/081130

これフルでモジュール書かないとエラーにならないか?

やっと全てのファイルを修正できた

rbs の情報少なすぎてツライ

rake generate_rbs_for_model した時に rails メソッド以外のメソッドも出力しないとか

  loader = RBS::EnvironmentLoader.new()
  loader.add(path: Pathname("sig"))
  environment = RBS::Environment.from_loader(loader).resolve_type_names
  album = RBS::TypeName.new(name: :Album, namespace: RBS::Namespace.root)
  decl = environment.class_decls[album]
  builder = RBS::DefinitionBuilder.new(env: environment)
  instance = builder.build_instance(album)
  instance.methods[:artists].method_types.join("\n")

このへん使えば行けそう

だめだ
どうすれば

まぁ基礎はできたので検証やってくか
https://github.com/soutaro/steep

steep神だった

あとはどうやって運用していくのかか

むずいのでライブラリの簡単な奴からrbsしっかり作ってく

ruby 標準ライブラリの型がところどころないので untyped にしておく

追加してみた
検証エラー出まくってるけど進めたいのでテストと合わせて都度追加してく感じで
#32

今後求めるもの

  • Rails model に RDoc で書いたものがそのまま rbs で出力される
  • VScode で steep の型補完をする(すでにプロジェクトはある)

全てのクラスが終わるまで rbs の issue をクローズさせないッ!!

型検証だいぶいいね
全体の見直しになってとても素晴らしい

型設定するの楽しくなってきた
わかってくると楽しい

model 全てやったのだが、sig の extend 専用をどうやって実装するのかわからん。
具体的には extend ActiveSupport::Concern の module って include 専用なので基本的に NoMethodError になるんよ。

やっと書き方わかった
module ClassMethods : _ActiveRecord_Relation[untyped] こうだよ!

やっと進んだ

ハマってた
なんで NoMethodError なるんじゃああああああ
ってなってたら singleton method だった

rbs -I sig method Album where
↑これじゃなく↓これね
rbs -I sig method --singleton Album where

rbs やる人のハマりポイント全てハマるマン

インターフェースの立ち位置についての考え
https://twitter.com/soutaro/status/1285225653059506176?s=20

Github issue を Twitter みたいに使うマン

カンマ区切りできるんかーい

_module-self-types_ ::= _class-name_ _type-arguments_ `,` _module-self-types_            (Class instance)
                      | _interface-name_ _type-arguments_ `,` _module-self-types_        (Interface)

一生懸命 interface 作ってたわ

lib rbs 化やってく

型安全最高!

vscode のプラグインとして steep vscode 導入した。最近追加されたっぽい

最高なんだが?Ruby 最強なんだが?
スクリーンショット 2020-08-27 5 51 23

これはほんとに最高だわ
sig追加捗る

まだ不安定なので目印的な感じかな
でもこれ高速起動してたらかなり使える

知らないうちに rbs のリポジトリ fork してた

型書くの最高じゃんと思ってた。
けど今日の RubyKaigi の Matz の話聞いてて、確かに型検証をリアルタイムで行われ続ければ型を書く必要ないなぁと。
遠い未来かもしれないがPCの性能とか型検証が優れれば逆に型を書くこと自体がボトルネックになるのか。
とても面白いカンファレンスだった。

改めて rbs のリファレンス読んだけど独自の型宣言できたのか。。。。
Hash とか繰り返し使うとき便利じゃん。
https://github.com/ruby/rbs/blob/master/docs/syntax.md#type-alias-declaration

これ gem 側が sig 提供進まないとかなり追加辛い感じがする
**はとても良いけど全員が追加するかというとそうじゃないので難しい

gem 側の sig は基本的にデフォルトで untyped で宣言されておいてくれるといいのかなー。
def foo: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped みたいな。

type profiler の出番か

最強のガバ型(警告が大量に出るのでオススメしない)
(*untyped, **untyped) ?{ (*untyped, **untyped) -> untyped } -> untyped
ガバ型
(*untyped) ?{ (untyped) -> untyped } -> untyped

gem の型はまぁしっかり実装した方がいいけどそれはこちらの作業ではないので基本的にはガバ型で実装してく。
めんどかったら最強ガバ型で実装。

アプリ側の型はしっかり書いてgemは今後実装者が追加するはずなのでそんな感じの方針で書いてく。

型キツくなってきた

compact で nil が除外されないのがもっとも辛い

随時やってく感じなのでクローズ