コンテンツにスキップ

Rails Ecosystem Plugins — Roadmap

ステータス: 計画中、2026-05-08。このドキュメントはRailsアプリ向けのrigor-*プラグインファミリーの計画をキャプチャしています。情報提供目的であり、個々のプラグイン契約の拘束力のあるソースは各プラグインのディレクトリ下のREADME.mdと統合仕様に残ります。

覆された前提(2026-06-02): サブツリー分割の配布モデルは廃止されました。このロードマップは、各プラグインが最終的にgit subtree splitで独自の個別公開gemへ抽出されることを前提に書かれました。そのモデルは取り下げられました — バンドルされたプラグインは現在、単一のrigortype gemの中で出荷され(プラグインごとのgemspecはコミット9769f5faで削除)、ADR-31はサードパーティプラグインを作者自身のリポジトリに置くものとし(WD4)、サブツリーマージは稀な予約済みインポートオプション(WD5)としてのみ残し、計画された送出フローとしては残しませんでした。以下の「作業原則」のサブツリー分割に関する記述、「サブツリー分割の準備チェックリスト」、そしてプラグインごとの「抽出」ステップは、現在の計画ではなく歴史的文脈として読んでください。ティアテーブル、依存グラフ、プラグインごとの動作スケッチは正確で有用なままです;変わったのは配布の前提だけです。

このファミリーの最初のプラグイン——rigor-activerecord——はmaster(コミットe8fda84)に着地し、skills/rigor-plugin-author/SKILL.mdの「モノリポでスタート」規律に従ってモノリポにステージされています。(元の「契約が安定したらgit subtree splitで抽出」という規律の後半は廃止されました — 上記の覆された前提の注記を参照してください。)

  1. 各プラグインはサブツリー分割されます — 実際のRailsコンシューマーに対してその契約が安定したら、独自のリポジトリ(rigortype/rigor-<id>)に。モノリポはインキュベーター;最終的な置き場は独立したgemです。
  2. プラグインごとのdemo/ディレクトリがプラグインとともにリリースされます。プラグイン間で共有されるRailsアプリスケルトンなし——サブツリー分割後、各demo/はプラグインとともに移動し、自己完結していなければなりません。Railsシェイプのディレクトリツリー(例: app/models/application_record.rb)の重複は、クリーンな抽出と引き換えに許容されます。
  3. Railsへの実際のアラインメントは目標であり、ランタイム依存関係ではありません。プラグインのソースコードはrequire "rails" / require "active_record"しません。プロジェクトのソースファイルを解析します。しかし、プラグインの動作(生成されるパスヘルパー、受け入れられるカラム型、認識されるフィルタチェーン)は、同じ入力に対してRailsが実際に生成 / 受け入れるものとMATCHしなければなりません。同じconfig/routes.rbに対して小さなRailsアプリのrails routes -E / スキーマダンプ出力とプラグイン出力を比較する統合仕様を推奨します。
  4. クロスプラグインファクトは共有APIを通じて流れますrigor-actionpackのストロングパラメータコンシューマーはrigor-activerecordが構築するモデルインデックスを必要とします。そのクロスプラグインハンドオフはv0.1.xのクロスプラグインAPI(ADR-9)を通じて行われ、重複した読み取りや共有キャッシュプロデューサーIDを通じてではありません。

Tier 1プラグインは最高のユーザー価値を持ち、かつ解析器側の新しいAPIを必要としないため最初に着地します。Tier 2は既存プラグインを拡張するかADR-9がリリースするクロスプラグインAPIを必要とします。Tier 3は特化型——具体的なユーザー需要があれば作成します。

ティアプラグインスコープAPI要件
1Arigor-rails-routes実際のconfig/routes.rb DSL → *_path / *_urlバリデーション現行API
1Brigor-rails-i18nconfig/locales/*.ymlt('key.path')バリデーション現行API
1Crigor-actionmailerメーラーメソッド + ビューテンプレート存在確認現行API
1Drigor-activejobジョブperformの引数アリティ現行API
2Arigor-activerecord拡張アソシエーション、enum、スコープ、バリデーション、コールバック現行API;既存gemの0.2.0+として着地
2Brigor-actionpack Phase 1ストロングパラメータ → ARカラムバリデーションクロスプラグインAPI(ADR-9)
2Crigor-factorybotファクトリー属性 → ARカラムバリデーションクロスプラグインAPI
2Drigor-actionpack Phase 2-4フィルタチェーン、レンダーターゲット、ルートヘルパー消費クロスプラグインAPI
3Arigor-rspeclet / subject / モックターゲットバリデーション現行API
3Brigor-punditポリシーメソッド存在 + authorize引数バリデーション現行API
3Crigor-sidekiqワーカーperformのアリティ、キュー設定現行API
3Drigor-graphqlスキーマ → リゾルバ引数型現行API
3Erigor-activestoragehas_one_attachedマクロ + 生成メソッドクロスプラグインAPI
3Frigor-actioncableチャンネルメソッド + ブロードキャスト名現行API

Tier 1+2が着地した後、rigor-railsはこれらの依存関係をgemspecで宣言し、ユーザーがGemfileに1行追加するだけでスタック全体にオプトインできるメタgemになります。

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 :name
  • get/post/patch/put/delete "/path", to: "controller#action", as: :name
  • root to: "controller#action"
  • ネストされたresources(1レベル深さ)
  • member do ... end / collection do ... end
  • namespace :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/:id
controllers/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に対してプラグインのHelperTablerails routes -Eの出力と比較します。demo/下の小さなRailsアプリがリファレンスを提供します。


Tier 1B — 現行APIconfig/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, ja
view.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-patternt(literal_key)のリテラル文字列ゲーティング)。IoBoundaryを通じてロケールパスをグロブループします。


Tier 1C — 現行API。メーラーコールの形状とビューパスの存在を検証します。

サーフェス:

class UserMailer < ApplicationMailer
def welcome(user)
@user = user
mail(to: user.email)
end
end
UserMailer.welcom(user).deliver_now # error: undefined method
UserMailer.welcome.deliver_now # error: missing required arg
UserMailer.welcome(user, foo: 1) # error: wrong arity

さらにapp/views/<mailer_underscore>/<method_name>.{html,text}.erbの存在チェック。

アーキテクチャ: rigor-activerecordModelDiscovererパターンをメーラークラス(ApplicationMailer / ActionMailer::Baseのサブクラス)に適用。ビューパスはIoBoundary経由でチェック。


Tier 1D — 現行API。ジョブクラスの#perform定義に対してJob.perform_laterの引数アリティを検証します。

サーフェス:

class WelcomeEmailJob < ApplicationJob
def perform(user_id, locale = "en")
...
end
end
WelcomeEmailJob.perform_later(123) # info
WelcomeEmailJob.perform_later # error: missing user_id
WelcomeEmailJob.perform_later(123, "ja", :foo) # error: wrong arity

アーキテクチャ:小規模——クラス発見 + コールごとのアリティチェック。rigor-actionmailerと同じパターン。


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`?)
end

ADR-9のservices.fact_storeからrigor-activerecord:model_indexファクトを読みます。requireのSymbol引数:userUserモデルに解決し、permitキーをテーブルに対して検証します。

class UsersController < ApplicationController
before_action :authenticate, only: [:create, :update]
# validates :authenticate exists as an instance method
# validates :create, :update exist as actions
end

コントローラークラス内での2パス: アクションメソッド宣言を収集し、フィルタ:method_nameonly: / except: Symbolリストを検証します。

def show
render partial: "users/profile", locals: { user: @user }
# validates app/views/users/_profile.html.erb exists
end

IoBoundaryでパーシャルファイルの存在を確認します。

def show
redirect_to user_path(@user)
end

ADR-9を通じてrigor-rails-routes:helper_tableファクトを消費します。コールサイトでヘルパー名 + アリティを検証します(コントローラー定義ファイルではなく——コントローラーはどこからでも呼び出される可能性があります)。


Tier 2C — クロスプラグインAPIが必要。ファクトリー属性のARカラムに対するバリデーション。

FactoryBot.define do
factory :user do
name { "Alice" }
invlid_attribute { "x" } # error if rigor-activerecord is loaded
end
end
create(:usre) # error: factory undefined (did you mean :user?)
build(:user, emial: "x") # error: column mismatch

2フェーズ: ファクトリー定義を発見(rigor-statesmanに類似)し、使用サイトを検証。ADR-9のfact_storeを通じてrigor-activerecordのモデルインデックスを消費します。


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
end
end

実装が重い(RSpec DSLは幅広い)。予想サイズ: 600行超。テスト側のバリデーションが明確な優先事項になったら作成——Railsアプリはコントローラー / モデル / ビューのバリデーションからより多くの恩恵を受けるため、おそらくTier 3。


Tier 3B — 現行API。ポリシーメソッドの存在 + authorize引数バリデーション。

authorize @user, :update?
authorize @user, :destory? # error: undefined policy method (did you mean :destroy?)

ポリシークラス発見器 + コールごとのバリデーション。慣習的なマッピング: UserUserPolicy、アクションメソッド:update?UserPolicy#update?cancancanは類似した形状だが異なる慣習を持つ別のプラグイン。


┌────────────────────────┐
│ 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"

プラグインごとの自己完結型デモ。各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コンシューマーとしてプラグインがロードされた状態でパスする。
  • [ ]プラグインのgemspecrigortypeに対する正しいsemver範囲を宣言している(例: >= 0.1.0, < 0.2.0)。
  • [ ]クロスプラグインファイル参照なし——クロスプラグインデータはservices.fact_store(ADR-9以降)または重複した読み取り(ADR-9以前)のみを通じて流れる。
  • READMEに抽出後にキューに入れられているものを説明する「将来の方向性」セクションがある。

すべてチェックが付いたら実行:

Terminal window
git subtree split --prefix=plugins/rigor-<id> -b rigor-<id>-extracted
git remote add rigor-<id> git@github.com:rigortype/rigor-<id>.git
git push rigor-<id> rigor-<id>-extracted:master

その後モノリポで: plugins/rigor-<id>/を削除し、対応するspec/integration/plugins/<id>_plugin_spec.rbを削除し、examples/README.mdの比較表からその行を削除し、README.mdのプラグインリストを更新します。

  1. 計画を文書化する — このファイル + ADR-9。(現在のコミット。)
  2. Tier 1プラグインを実装する(現行API)rigor-rails-routesrigor-rails-i18nrigor-actionmailerrigor-activejob。各々を独自のコミットとして、サブツリー分割の準備を念頭に置いて。
  3. クロスプラグインAPIを実装する(ADR-9)Plugin::FactStore + prepare(services)フック + consumes:マニフェストフィールド + Plugin::Loaderでのトポロジカルソート。パブリックAPIドリフトスナップショットを更新。SKILLセクションを追加。
  4. Tier 2プラグインを実装する(クロスプラグイン)rigor-actionpack Phase 1(ストロングパラメータ)、次にrigor-factorybot。これらは実際のコンシューマーに対してADR-9を実証します。
  5. 安定化 + 抽出 — 各プラグインのexamples/ディレクトリが2リリース以上安定したら、サブツリー分割フローを実行し、独立したリポジトリに移行します。
  6. 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.