Rails Ecosystem Plugins — Roadmap
ステータス: 計画中、2026-05-08。このドキュメントはRailsアプリ向けのrigor-*プラグインファミリーの計画をキャプチャしています。情報提供目的であり、個々のプラグイン契約の拘束力のあるソースは各プラグインのディレクトリ下のREADME.mdと統合仕様に残ります。
覆された前提(2026-06-02): サブツリー分割の配布モデルは廃止されました。このロードマップは、各プラグインが最終的に
git subtree splitで独自の個別公開gemへ抽出されることを前提に書かれました。そのモデルは取り下げられました — バンドルされたプラグインは現在、単一のrigortypegemの中で出荷され(プラグインごとのgemspecはコミット9769f5faで削除)、ADR-31はサードパーティプラグインを作者自身のリポジトリに置くものとし(WD4)、サブツリーマージは稀な予約済みインポートオプション(WD5)としてのみ残し、計画された送出フローとしては残しませんでした。以下の「作業原則」のサブツリー分割に関する記述、「サブツリー分割の準備チェックリスト」、そしてプラグインごとの「抽出」ステップは、現在の計画ではなく歴史的文脈として読んでください。ティアテーブル、依存グラフ、プラグインごとの動作スケッチは正確で有用なままです;変わったのは配布の前提だけです。
このファミリーの最初のプラグイン——rigor-activerecord——はmaster(コミットe8fda84)に着地し、skills/rigor-plugin-author/SKILL.mdの「モノリポでスタート」規律に従ってモノリポにステージされています。(元の「契約が安定したらgit subtree splitで抽出」という規律の後半は廃止されました — 上記の覆された前提の注記を参照してください。)
- 各プラグインはサブツリー分割されます — 実際のRailsコンシューマーに対してその契約が安定したら、独自のリポジトリ(
rigortype/rigor-<id>)に。モノリポはインキュベーター;最終的な置き場は独立したgemです。 - プラグインごとの
demo/ディレクトリがプラグインとともにリリースされます。プラグイン間で共有されるRailsアプリスケルトンなし——サブツリー分割後、各demo/はプラグインとともに移動し、自己完結していなければなりません。Railsシェイプのディレクトリツリー(例:app/models/application_record.rb)の重複は、クリーンな抽出と引き換えに許容されます。 - Railsへの実際のアラインメントは目標であり、ランタイム依存関係ではありません。プラグインのソースコードは
require "rails"/require "active_record"をしません。プロジェクトのソースファイルを解析します。しかし、プラグインの動作(生成されるパスヘルパー、受け入れられるカラム型、認識されるフィルタチェーン)は、同じ入力に対してRailsが実際に生成 / 受け入れるものとMATCHしなければなりません。同じconfig/routes.rbに対して小さなRailsアプリのrails routes -E/ スキーマダンプ出力とプラグイン出力を比較する統合仕様を推奨します。 - クロスプラグインファクトは共有APIを通じて流れます。
rigor-actionpackのストロングパラメータコンシューマーはrigor-activerecordが構築するモデルインデックスを必要とします。そのクロスプラグインハンドオフはv0.1.xのクロスプラグインAPI(ADR-9)を通じて行われ、重複した読み取りや共有キャッシュプロデューサーIDを通じてではありません。
プラグインティアテーブル
Section titled “プラグインティアテーブル”Tier 1プラグインは最高のユーザー価値を持ち、かつ解析器側の新しいAPIを必要としないため最初に着地します。Tier 2は既存プラグインを拡張するかADR-9がリリースするクロスプラグインAPIを必要とします。Tier 3は特化型——具体的なユーザー需要があれば作成します。
| ティア | プラグイン | スコープ | API要件 |
|---|---|---|---|
| 1A | rigor-rails-routes | 実際のconfig/routes.rb DSL → *_path / *_urlバリデーション | 現行API |
| 1B | rigor-rails-i18n | config/locales/*.yml → t('key.path')バリデーション | 現行API |
| 1C | rigor-actionmailer | メーラーメソッド + ビューテンプレート存在確認 | 現行API |
| 1D | rigor-activejob | ジョブperformの引数アリティ | 現行API |
| 2A | rigor-activerecord拡張 | アソシエーション、enum、スコープ、バリデーション、コールバック | 現行API;既存gemの0.2.0+として着地 |
| 2B | rigor-actionpack Phase 1 | ストロングパラメータ → ARカラムバリデーション | クロスプラグインAPI(ADR-9) |
| 2C | rigor-factorybot | ファクトリー属性 → ARカラムバリデーション | クロスプラグインAPI |
| 2D | rigor-actionpack Phase 2-4 | フィルタチェーン、レンダーターゲット、ルートヘルパー消費 | クロスプラグインAPI |
| 3A | rigor-rspec | let / subject / モックターゲットバリデーション | 現行API |
| 3B | rigor-pundit | ポリシーメソッド存在 + authorize引数バリデーション | 現行API |
| 3C | rigor-sidekiq | ワーカーperformのアリティ、キュー設定 | 現行API |
| 3D | rigor-graphql | スキーマ → リゾルバ引数型 | 現行API |
| 3E | rigor-activestorage | has_one_attachedマクロ + 生成メソッド | クロスプラグインAPI |
| 3F | rigor-actioncable | チャンネルメソッド + ブロードキャスト名 | 現行API |
Tier 1+2が着地した後、rigor-railsはこれらの依存関係をgemspecで宣言し、ユーザーがGemfileに1行追加するだけでスタック全体にオプトインできるメタgemになります。
プラグインスケッチ
Section titled “プラグインスケッチ”rigor-rails-routes
Section titled “rigor-rails-routes”Tier 1A — 現行API。実際のconfig/routes.rbを解析します(教育目的でplugins/rigor-routes/が使用するYAML簡略化ではなく)。
プラグインのv0.1.0向けDSLサーフェス:
Rails.application.routes.draw do ... endブロックresources :name [, only: [...] | except: [...]]resource :nameget/post/patch/put/delete "/path", to: "controller#action", as: :nameroot to: "controller#action"- ネストされた
resources(1レベル深さ) member do ... end/collection do ... endnamespace :admin do ... end(パス + ヘルパー名にプレフィックスを付ける)
v0.1.0のスコープ外:
scope :module:/scope :path:/scope :as:- 制約(
constraints: { id: /\d+/ }) - カスタム
direct(:name) { |obj| ... } - マウント可能エンジン(
mount Sidekiq::Web => "/sidekiq") - フォーマット制限
診断:
controllers/users_controller.rb:42:7: info: `user_post_path` → GET /users/:user_id/posts/:idcontrollers/users_controller.rb:50:1: error: no route helper `widgts_path` (did you mean `widgets_path`?)controllers/users_controller.rb:51:1: error: `user_path` expects 1 argument (:id), got 0アーキテクチャ: rigor-activerecordのPrism上のSchemaParser再帰下降とrigor-routesのヘルパー名テーブルを組み合わせます。ヘルパー生成ルールは実際のRailsによる慎重な検証が必要——下記「Railsへの実際のアラインメント」を参照。
Railsへの実際のアラインメント:統合仕様は同じconfig/routes.rbに対してプラグインのHelperTableをrails routes -Eの出力と比較します。demo/下の小さなRailsアプリがリファレンスを提供します。
rigor-rails-i18n
Section titled “rigor-rails-i18n”Tier 1B — 現行API。config/locales/*.ymlに対してt('key.path')を検証します。
サーフェス:
t('key.path')/I18n.t('key.path')/I18n.translate('key.path')t('key.path', interpolation_var: value)— ロケール値内の%{var}プレースホルダーに対して補間キーを検証しますl(time, format: :short)— ロケールの日付フォーマットキーに対して:shortを検証します
v0.1.0のスコープ外:
- 遅延ルックアップ(レンダリングされたコントローラー / ビューパスに対して解決される
t('.title')——rigor-actionpackが必要) - ロケールフォールバックチェーン
- 複数形ルール
診断:
view.html.erb:5:1: info: `t('users.welcome')` resolves in en, jaview.html.erb:8:1: error: missing key `users.welcom` in en (did you mean `users.welcome`?)view.html.erb:12:1: error: `users.welcome` expects interpolation `name`, got `username`アーキテクチャ: rigor-routes(YAML読み取り) + rigor-pattern(t(literal_key)のリテラル文字列ゲーティング)。IoBoundaryを通じてロケールパスをグロブループします。
rigor-actionmailer
Section titled “rigor-actionmailer”Tier 1C — 現行API。メーラーコールの形状とビューパスの存在を検証します。
サーフェス:
class UserMailer < ApplicationMailer def welcome(user) @user = user mail(to: user.email) endend
UserMailer.welcom(user).deliver_now # error: undefined methodUserMailer.welcome.deliver_now # error: missing required argUserMailer.welcome(user, foo: 1) # error: wrong arityさらにapp/views/<mailer_underscore>/<method_name>.{html,text}.erbの存在チェック。
アーキテクチャ: rigor-activerecordのModelDiscovererパターンをメーラークラス(ApplicationMailer / ActionMailer::Baseのサブクラス)に適用。ビューパスはIoBoundary経由でチェック。
rigor-activejob
Section titled “rigor-activejob”Tier 1D — 現行API。ジョブクラスの#perform定義に対してJob.perform_laterの引数アリティを検証します。
サーフェス:
class WelcomeEmailJob < ApplicationJob def perform(user_id, locale = "en") ... endend
WelcomeEmailJob.perform_later(123) # infoWelcomeEmailJob.perform_later # error: missing user_idWelcomeEmailJob.perform_later(123, "ja", :foo) # error: wrong arityアーキテクチャ:小規模——クラス発見 + コールごとのアリティチェック。rigor-actionmailerと同じパターン。
rigor-actionpack
Section titled “rigor-actionpack”Tier 2B+2D — クロスプラグインAPI(ADR-9)が必要。「Railsアプリがこれを望む」旗艦プラグインですが、主な価値はrigor-activerecordのモデルインデックスとのクロスチェックから生まれます。段階的なロールアウト:
Phase 1 — ストロングパラメータ
Section titled “Phase 1 — ストロングパラメータ”def user_params params.require(:user).permit(:name, :emial) # error: column `:emial` not on table `users` (did you mean `:email`?)endADR-9のservices.fact_storeからrigor-activerecordの:model_indexファクトを読みます。requireのSymbol引数:userをUserモデルに解決し、permitキーをテーブルに対して検証します。
Phase 2 — フィルタチェーン
Section titled “Phase 2 — フィルタチェーン”class UsersController < ApplicationController before_action :authenticate, only: [:create, :update] # validates :authenticate exists as an instance method # validates :create, :update exist as actionsendコントローラークラス内での2パス: アクションメソッド宣言を収集し、フィルタ:method_nameとonly: / except: Symbolリストを検証します。
Phase 3 — レンダーターゲット
Section titled “Phase 3 — レンダーターゲット”def show render partial: "users/profile", locals: { user: @user } # validates app/views/users/_profile.html.erb existsendIoBoundaryでパーシャルファイルの存在を確認します。
Phase 4 — ルートヘルパー消費
Section titled “Phase 4 — ルートヘルパー消費”def show redirect_to user_path(@user)endADR-9を通じてrigor-rails-routesの:helper_tableファクトを消費します。コールサイトでヘルパー名 + アリティを検証します(コントローラー定義ファイルではなく——コントローラーはどこからでも呼び出される可能性があります)。
rigor-factorybot
Section titled “rigor-factorybot”Tier 2C — クロスプラグインAPIが必要。ファクトリー属性のARカラムに対するバリデーション。
FactoryBot.define do factory :user do name { "Alice" } invlid_attribute { "x" } # error if rigor-activerecord is loaded endend
create(:usre) # error: factory undefined (did you mean :user?)build(:user, emial: "x") # error: column mismatch2フェーズ: ファクトリー定義を発見(rigor-statesmanに類似)し、使用サイトを検証。ADR-9のfact_storeを通じてrigor-activerecordのモデルインデックスを消費します。
rigor-rspec
Section titled “rigor-rspec”Tier 3A — 現行API。テストDSLのフロー追跡。
RSpec.describe User do let(:user) { User.new(name: "Alice") } subject(:greeting) { "Hello, #{user.name}" }
it "greets" do expect(greeting).to eq("Hello, Alice") expect(user).to receive(:nme).and_return("X") # error: no method :nme on User endend実装が重い(RSpec DSLは幅広い)。予想サイズ: 600行超。テスト側のバリデーションが明確な優先事項になったら作成——Railsアプリはコントローラー / モデル / ビューのバリデーションからより多くの恩恵を受けるため、おそらくTier 3。
rigor-pundit
Section titled “rigor-pundit”Tier 3B — 現行API。ポリシーメソッドの存在 + authorize引数バリデーション。
authorize @user, :update?authorize @user, :destory? # error: undefined policy method (did you mean :destroy?)ポリシークラス発見器 + コールごとのバリデーション。慣習的なマッピング: User → UserPolicy、アクションメソッド:update? → UserPolicy#update?。cancancanは類似した形状だが異なる慣習を持つ別のプラグイン。
プラグイン依存関係グラフ
Section titled “プラグイン依存関係グラフ” ┌────────────────────────┐ │ rigor-activerecord │ │ (already landed) │ └──┬─────────────────────┘ │ publishes :model_index via fact_store ┌─────────────────────┼─────────────────────┐ ▼ ▼ ▼ ┌────────────────────┐ ┌──────────────────────┐ ┌─────────────────┐ │ rigor-actionpack │ │ rigor-factorybot │ │ rigor-active- │ │ Phase 1 (params) │ │ │ │ storage │ └────────────────────┘ └──────────────────────┘ └─────────────────┘ ▲ │ consumes :helper_table │ ┌──────────┴───────────┐ │ rigor-rails-routes │ │ publishes :helper_ │ │ table │ └──────────────────────┘
┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────┐ │ rigor-rails-i18n │ │ rigor-actionmailer │ │ rigor-activejob │ │ (independent) │ │ (independent) │ │ (independent) │ └──────────────────────┘ └──────────────────────┘ └──────────────────┘
┌──────────────────────┐ ┌──────────────────────┐ │ rigor-rspec │ │ rigor-pundit │ │ (independent) │ │ (independent) │ └──────────────────────┘ └──────────────────────┘rigor-rails(メタgem)はこれらすべての上位に位置し、gem依存関係を通じてすべてを取り込みます。スタック全体を欲しいユーザーには: gem "rigor-rails"。
デモ / テストアプリ戦略
Section titled “デモ / テストアプリ戦略”プラグインごとの自己完結型デモ。各plugins/rigor-<id>/demo/はプラグインのスコープに適した小さなRailsシェイプのディレクトリツリーをリリースします。git subtree split後、デモは手動での修正なしにプラグインとともに移動します。
クロスカットなRailsアプリ(ストロングパラメータ + AR + ルート)を必要とするTier 1+2プラグインの場合でも、デモはプラグインごとです——各プラグインのデモはそのプラグインが必要とするRailsサーフェスのみを含みます。rigor-actionpackのデモはコントローラーファイルとモデルフィクスチャのapplication_record.rbを持ちますが、フルRailsディレクトリツリーはありません。
Railsへの実際の検証: 統合仕様はtmpdirに小さなrails newスケルトンをexecし、プラグイン出力をrails routes -E / db:schema:dump等と比較することがありますが、それはTESTツールであり、デモ時のフィクスチャではありません。
サブツリー分割準備チェックリスト
Section titled “サブツリー分割準備チェックリスト”プラグインごとに、分割前に確認:
-
plugins/rigor-<id>/ディレクトリが自己完結している(外部を指すrequire_relativeがない)。 -
plugins/rigor-<id>/demo/がRUBYLIB=$PWD/../lib bundle exec rigor checkでクリーンに実行される。 -
spec/integration/plugins/<id>_plugin_spec.rbの統合仕様が実際のPlugin::Loader.loadコンシューマーとしてプラグインがロードされた状態でパスする。 - [ ]プラグインの
gemspecがrigortypeに対する正しいsemver範囲を宣言している(例:>= 0.1.0, < 0.2.0)。 - [ ]クロスプラグインファイル参照なし——クロスプラグインデータは
services.fact_store(ADR-9以降)または重複した読み取り(ADR-9以前)のみを通じて流れる。 - READMEに抽出後にキューに入れられているものを説明する「将来の方向性」セクションがある。
すべてチェックが付いたら実行:
git subtree split --prefix=plugins/rigor-<id> -b rigor-<id>-extractedgit remote add rigor-<id> git@github.com:rigortype/rigor-<id>.gitgit push rigor-<id> rigor-<id>-extracted:masterその後モノリポで: plugins/rigor-<id>/を削除し、対応するspec/integration/plugins/<id>_plugin_spec.rbを削除し、examples/README.mdの比較表からその行を削除し、README.mdのプラグインリストを更新します。
- 計画を文書化する — このファイル + ADR-9。(現在のコミット。)
- Tier 1プラグインを実装する(現行API) —
rigor-rails-routes、rigor-rails-i18n、rigor-actionmailer、rigor-activejob。各々を独自のコミットとして、サブツリー分割の準備を念頭に置いて。 - クロスプラグインAPIを実装する(ADR-9) —
Plugin::FactStore+prepare(services)フック +consumes:マニフェストフィールド +Plugin::Loaderでのトポロジカルソート。パブリックAPIドリフトスナップショットを更新。SKILLセクションを追加。 - Tier 2プラグインを実装する(クロスプラグイン) —
rigor-actionpackPhase 1(ストロングパラメータ)、次にrigor-factorybot。これらは実際のコンシューマーに対してADR-9を実証します。 - 安定化 + 抽出 — 各プラグインの
examples/ディレクトリが2リリース以上安定したら、サブツリー分割フローを実行し、独立したリポジトリに移行します。 - Tier 3 + メタgem — ユーザー需要が表面化したらTier 3プラグインを作成。Tier 1+2が抽出されたら、依存関係の集約とともに
rigor-railsメタgemを公開します。
Tier 1プラグイン(現行API)は作成時間のみのブロッカーであり、契約設計のブロッカーではありません。必要に応じて独立した実装者が並行して着地させることができます。Tier 2はADR-9の実装をブロッカーとします。
© 2026 TypedDuck. Licensed under CC BY-SA 4.0.