`rigor-dry-validation` — スライシング決定
ステータス:設計ノート。rigor-dry-typesスライス4コミットで2026-05-17に著作。rigor-dry-typesとrigor-dry-struct(両方ともv0.1.6で着地)を超えた次のdry-rbアダプタのスライス順序を決定する。
docs/design/20260509-dry-plugins-roadmap.md
§「dry-validation」がgemの3つのプラグイン関連DSLサーフェスを記述する;ユーザー向けプログラミング形は次のいずれか:
class NewUserContract < Dry::Validation::Contract params do # (1) params { ... }アダプター — dry-schemaに委譲 required(:email).filled(:string) required(:age).value(:integer) end
rule(:email) do # (2) rule { ... }ブロック — 型寄与なし key.failure('has invalid format') unless EMAIL_RE.match?(value) endend
result = contract.call(...) # (3) Contract#call → Dry::Validation::Resultresult.success? # Resultをナローイングresult.to_h # 型付きparamsハッシュを表面化プラグインは、Rigorが次に答えられるときにユーザー価値を得る:
contract.call(...)の型は何か? → contract形に関係なくDry::Validation::Result。- 成功時の
result.to_hの型付き形は何か? → dry-schemaから派生したHashShape。dry-schemaが必要。 - 与えられたContractにどのparamsキーが存在するか?
→
params { ... }ブロック(これは変装したdry-schema)からの:email/:age/ …のセット。dry-schemaが必要。 rule(:email)は型付けに何をするか? → 何もしない;純粋なビジネスルール。
┌──── rigor-dry-types (v0.1.6 — スライス1+2+3+4) │rigor-dry-schema (まだ) ←── :dry_type_aliasesを消費 │ ↓rigor-dry-validation (まだ) ←── dry-schemaのparams形を消費スタンドアロンのrigor-dry-validation(dry-schemaなし)はContract#call → Resultファクトのみを寄与できる。リッチなペイロード — 型付きresult.to_h、paramsキー、キーごとの型 — はdry-schemaから流れる。dry-schemaなしでは、dry-validationは1行のRBS寄与:
module Dry module Validation class Contract def call: (Hash[Symbol, untyped]) -> Result end
class Result def success?: () -> bool def failure?: () -> bool def to_h: () -> Hash[Symbol, untyped] end endendそれは10行のRBSオーバーレイ。専用プラグインスライスの価値はない — 類似の境界と並んで将来の「dry-rbコアRBSバンドル」に畳み込む。
決定: rigor-dry-validationの前にrigor-dry-schemaをスライスする。スキーマ認識なしのvalidationプラグインは非常に少ししか寄与しない;それを伴うと、価値はユーザーコード内のスキーマ使用でスケールする。
rigor-dry-schemaの最小実行可能形
Section titled “rigor-dry-schemaの最小実行可能形”dry-pluginsロードマップ §「dry-schema」エントリーに従い:
NewUserSchema = Dry::Schema.Params do required(:email).filled(:string) required(:age).value(:integer)end
result = NewUserSchema.call(input)result.to_h # => HashShape[{email: String, age: Integer}]result.errors.to_h # => Hash[Symbol, Array[String]]プラグイン契約(提案):
- プロジェクトのトップレベルでまたはクラスレベル定数として
Foo = Dry::Schema.{Params,JSON,define} { ... }を認識。 - ブロック本体を
required(:key).<predicates>とoptional(:key).<predicates>呼び出しで歩く。 - 各述語サフィックス(
filled(:string)、value(:integer)、value(:date)…)をrigor-dry-typesが使うのと同じCANONICAL_ALIASESテーブル経由で基底クラスにマップする(:string→String、:integer→Integer、……)。ユーザー著作の参照(value(Types::Email)はクロスプラグインファクトチャネルを通じて解決される)に対しては:dry_type_aliasesファクトを消費する。 :dry_schema_tableファクトを公開:{schema_const_fqn => {required: {key => underlying_class}, optional: {...}}}。- ADR-16基板(
:dry_schema_tableを消費するreturns_from_arg:を持つTier CHeredocTemplate)または基板のパラメータ化された戻り値がその時点でスコープ外の場合はカスタムウォーカーのいずれかを経由してresult.to_hの型付き戻りを合成する。
rigor-dry-schemaのスライス1フロア: 認識 + ファクト公開(まだ診断なし)、rigor-dry-typesスライス1の形をミラー。
rigor-dry-validationのスライシング — 提案された3つのスライス
Section titled “rigor-dry-validationのスライシング — 提案された3つのスライス”rigor-dry-schemaが基底の形を提供したら、validationプラグインは基板にクリーンにマップする。
スライス1 — Contract認識 + Resultキャリア
Section titled “スライス1 — Contract認識 + Resultキャリア”- プロジェクトを
class X < Dry::Validation::Contractサブクラスのために歩く。 X#call(Hash[Symbol, untyped]) → Resultを合成する(ジェネリックなResult、まだスキーマ認識なし)。Dry::Validation::Result#{success?, failure?, to_h}の手書きRBSオーバーレイ、そのためcontract.call(...).to_hチェーンがHash[Symbol, untyped]に解決する。
フロア: すべてのcontract呼び出しサイトは下流のメソッドチェーン推論のために型付きのResultレシーバーを持つ。
スライス2 — params { ... }のdry-schemaとの統合
Section titled “スライス2 — params { ... }のdry-schemaとの統合”- contractボディ内の
params do ... endブロックを認識。 - それをdry-schema宣言として扱う(
rigor-dry-schemaのウォーカーに委譲するか、プラグインカップリングが問題なら関連サブセットを複製する)。 :dry_validation_paramsファクトを公開:{contract_const_fqn => HashShape}。Contract#callの戻りを精緻化し、result.to_hがuntyped値ではなくcontractごとの形に対して型付けされるように。
フロア: NewUserContract.new.call(email: "x@y", age: 17).to_hがHashShape[{email: String, age: Integer}]に解決する。
スライス3 — json { ... }アダプタパリティ
Section titled “スライス3 — json { ... }アダプタパリティ”json { ... }ブロックはparamsと同じ形だがより厳密な型期待を適用する(文字列から整数への強制なし)。同じウォーカーを適用;同じファクトを別のキー(:dry_validation_jsonまたはkind:判別子を持つ共有された:dry_validation_schema)で発行。
フロア: paramsとのパリティ。プロジェクトがjson { ... }を使わない場合は需要駆動。
必要なADR修正(あれば)
Section titled “必要なADR修正(あれば)”上記のスライシングに対しては何もなし。dry-validationはrigor-dry-monadsが必要とするResult[T, E]キャリア修正を必要としない(下記 §「オープン観察」を参照) — Dry::Validation::Resultは直和型ではなくジェネリッククラス。その#to_hペイロードが型付き形そのものであり、#success? / #failure?述語は既存のboolフローファクトを通じて下流チェーンをナローイングする。
オープン観察 — rigor-dry-monadsは別途ブロックされている
Section titled “オープン観察 — rigor-dry-monadsは別途ブロックされている”ロードマップは両方とも次のティアのdry-rbプラグインであるため、rigor-dry-validationとrigor-dry-monadsをグループ化する。しかしdry-monadsは別の軸でブロックされている: メソッドごとの戻り型ラッピング(def x; Success(42); end → Result[Integer, untyped])を望む。ラップされたResult[T, E] / Maybe[T]キャリアは今日のRigor::Type::*階層には存在しない。
2つのルート:
- (a)
Result[T, E]/Maybe[T]キャリアを新しいRigor::Type::*値クラスとして実装する。ADR-3修正レベルの作業(新しい型種別、正規化ルール、RBS消去、表示契約、等価性 / 確実性サーフェス)。 - (b)
Result[T, E]をUnion[T, E]として、Maybe[T]をUnion[T, NilClass]として表現する。Success(v)対Failure(e)を精密にする「タグ」曖昧性解消を失うが、フロアとして機能するかもしれない。
決定: 2つのルートの少なくとも1つが具体的になるまでdry-monadsを先送り。dry-validationはmonadsなしで出荷可能 — 依存は逆方向に行く(dry-validationはdry-monadsではなくdry-types + dry-schemaを使う)。
作業順(キュー、需要駆動):
rigor-dry-schemaスライス1(認識 + ファクト公開)rigor-dry-schemaスライス2+(スキーマごとの形合成)rigor-dry-validationスライス1(Contract認識 +Resultキャリア)rigor-dry-validationスライス2(paramsのdry-schema統合)rigor-dry-validationスライス3(jsonアダプタパリティ)rigor-dry-monads— (a)または(b)がResult/Maybeキャリア質問を解決した後にのみ
合計: 5〜6の小〜中スライス。特定の層に対する具体的なユーザー需要はそれを前倒しすることを正当化する。
© 2026 TypedDuck. Licensed under CC BY-SA 4.0.