コンテンツにスキップ

rigor-activerecord

ActiveRecordのファインダー呼び出しとリレーション呼び出しを、プロジェクトのdb/schema.rbおよび発見したモデルクラスに対して型付けします。これによりUser.find(1)Userに、User.where(emial: …)は未知のカラムとしてフラグが立ち、user.postsはチェーンを通じてその要素型を保持します。このプラグインはソースのみを読みます。active_recordを決してロードしないため、RigorはRailsから切り離されたままです。

このプラグインはrigortypeにバンドルされて提供され、別途のインストールは不要です。設定ファイルのplugins:の下で有効化します:

plugins:
- rigor-activerecord
demo.rb:20:1: info: `User.find` returns User (table: `users`) [plugin.activerecord.model-call]
demo.rb:23:1: info: `User.where` (:admin) on table `users` [plugin.activerecord.model-call]
errors_demo.rb:13:1: error: `User.where(emial: ...)` references unknown column `emial` on table `users` (did you mean `:email`?) [plugin.activerecord.unknown-column]
errors_demo.rb:25:1: error: `User.find` expects at least 1 argument, got 0 [plugin.activerecord.wrong-arity]
診断重大度ルール
認識されたModel.findModel.find_byModel.whereの呼び出し:infoplugin.activerecord.model-call
Model.find_by(unknown: ...)Model.where(unknown: ...):errorplugin.activerecord.unknown-column
引数0個のModel.find:errorplugin.activerecord.wrong-arity
db/schema.rbが読み取れない:warningplugin.activerecord.load-error

「もしかして」候補は、解決されたテーブルのカラム名に対するレーベンシュタイン距離≤3を用います。

plugins:
- gem: rigor-activerecord
config:
schema_file: "db/schema.rb" # default
model_search_paths: ["app/models"] # default
model_base_classes: ["ApplicationRecord", "ActiveRecord::Base"] # default

3つのキーはすべて任意です。次のような場合に調整します:

  • スキーマが別の場所にある(schema_file: "shared/db/schema.rb")。
  • モデルが標準外のディレクトリにある(model_search_paths: ["domain/models", "engines/billing/app/models"])。
  • ベースクラスがカスタムである(model_base_classes: ["DbRecord", "ApplicationRecord"])。

このプラグインは診断に加えて、呼び出し箇所の型も提供します。クラス側: User.find(1)UserUser.find_by(...)User | nilUser.find_by!(...) → 非nullableのUser。インスタンス側: カラムの読み取り(user.name)はそのカラムの値型にナローイングされ、user.admin?boolに、単数の関連(post.user)はターゲットモデルにナローイングされます。

リレーションを返す呼び出し箇所 ── User.where(...)User.allUser.order(...)has_manyhas_and_belongs_to_manyのアクセサ(user.posts)、ユーザー宣言のscopePost.published)── はActiveRecord::Relation[Model]にナローイングされます。チェーンされたクエリメソッドは要素型を保持し、イテレーション(user.posts.each { |p| ... })はモデルを生み出します。型付きリレーションに対して呼び出されたユーザー定義のスコープ(User.where(...).published)が、誤ったcall.undefined-methodを表面化させることはありません。

  • 直接のスーパークラスのみマッチUser < ApplicationRecordである状況下でのclass Admin < Userは発見されません。Usermodel_base_classesに追加するか、すべての具体的なモデルを明示的に列挙してください。
  • db/schema.rbのみdb/structure.sql(生のSQLダンプ)はこのイテレーションではサポートされていません。
  • カラムの読み取りであり、セッターではない。このプラグインはインスタンス側のカラムの読み取りuser.nameuser.admin?)と単数の関連を型付けしますが、name=セッターやダーティトラッキング系(name_changed?name_was、…)は型付けしません。
  • プロジェクト独自のインフレクションはまだ読み取られない。モデル↔テーブルの複数形化は本物のActiveSupportインフレクターを通ります(そのためPerson → peopleMouse → miceは解決されます)が、config/initializers/inflections.rbで宣言したルールはまだ取り込まれません ── それに依存するモデルにはself.table_nameが必要です(ADR-39スライス3)。

アーキテクチャ(キャッシュされたスキーマパーサ → モデルインデックス → アナライザーのチェーン)、ソースのレイアウト、デモの実行方法、そしてこのプラグインが行使するプラグインの契約(contract)サーフェスは、プラグインのREADMEに記載されています。自分自身のプラグインを書くには、examples/のウォークスルーとrigor-plugin-authorスキルを参照してください。

© 2026 TypedDuck. Licensed under CC BY-SA 4.0.