diff --git a/.codex/config.toml b/.codex/config.toml index 9d7012c..0d5a805 100644 --- a/.codex/config.toml +++ b/.codex/config.toml @@ -1,2 +1 @@ approval_policy = "on-request" -sandbox_mode = "workspace-write" diff --git a/.ruby-version b/.ruby-version index fcdb2e1..7636e75 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -4.0.0 +4.0.5 diff --git a/README.md b/README.md index d9dd1b9..bd5bf19 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ English | [日本語](README_ja.md) -**Type-safe API parameter validation and form objects for Rails** +**Typed parameter objects and form objects for Rails.** + +Supports Ruby `>= 3.2` and Rails / ActiveModel `>= 7.2, < 9.0`. StructuredParams solves these challenges: @@ -23,10 +25,10 @@ Built on ActiveModel, making nested objects and arrays easy to handle. ## Quick Start ```ruby -# Installation +# Gemfile gem 'structured_params' -# Initialize +# config/initializers/structured_params.rb StructuredParams.register_types ``` @@ -44,7 +46,7 @@ class UserParams < StructuredParams::Params attribute :score, :integer attribute :tags, :array, value_type: :string # Primitive array attribute :address, :object, value_class: AddressParams # Nested object - + # validate raw string before type casting validates_raw :score, format: { with: /\A\d+\z/, message: 'must be numeric string' } validates :name, presence: true @@ -55,7 +57,7 @@ end # Use in API controller def create user_params = UserParams.new(params) - + if user_params.valid? User.create!(user_params.attributes) else @@ -77,7 +79,6 @@ end # params.permit(tags: []) ``` - ### 2. Form Object ```ruby @@ -85,7 +86,7 @@ class UserRegistrationForm < StructuredParams::Params attribute :name, :string attribute :email, :string attribute :terms_accepted, :boolean - + validates :name, :email, presence: true validates :terms_accepted, acceptance: true end @@ -95,7 +96,7 @@ end # Unlike API usage, form objects need require to scope to the correct key. def create form = UserRegistrationForm.new(UserRegistrationForm.permit(params)) - + if form.valid? User.create!(form.attributes) redirect_to root_path diff --git a/README_ja.md b/README_ja.md index 767e1f9..fca7447 100644 --- a/README_ja.md +++ b/README_ja.md @@ -2,31 +2,33 @@ [English](README.md) | 日本語 -**Rails で型安全な API パラメータバリデーションとフォームオブジェクトを実現する gem** +**Rails の型付きパラメータオブジェクトとフォームオブジェクトを提供する gem。** + +対応範囲は Ruby `3.2+`、Rails / ActiveModel `7.2` 以上 `9.0` 未満です。 StructuredParams は、以下の課題を解決します: -- **API エンドポイント**: リクエストパラメータの型チェック・バリデーション・自動キャスト -- **フォームオブジェクト**: 複雑なフォーム入力の検証とモデルへの変換 +- **API エンドポイント**: リクエストパラメータの型チェック、バリデーション、型変換 +- **フォームオブジェクト**: 複雑なフォーム入力の検証とモデル変換 -ActiveModel をベースに、ネストしたオブジェクトや配列も簡単に扱えます。 +ActiveModel をベースに、ネストしたオブジェクトや配列も簡潔に扱えます。 -## 主な特徴 +## 主な機能 - ✅ **API パラメータバリデーション** - 型安全なリクエスト検証 -- ✅ **フォームオブジェクト** - 複雑なフォームロジックのカプセル化 -- ✅ **ネスト構造対応** - オブジェクトや配列を自動キャスト -- ✅ **Strong Parameters 統合** - permit リストを自動生成 -- ✅ **ActiveModel 互換** - バリデーション、シリアライゼーションなど標準機能をサポート +- ✅ **フォームオブジェクト** - 複雑なフォームロジックをカプセル化 +- ✅ **ネスト構造のサポート** - object / array の自動キャスト +- ✅ **Strong Parameters 連携** - permit リストの自動生成 +- ✅ **ActiveModel 互換** - バリデーション、シリアライズなど標準機能を利用可能 - ✅ **RBS 型定義** - 型安全な開発体験 ## クイックスタート ```ruby -# インストール +# Gemfile gem 'structured_params' -# 初期化 +# config/initializers/structured_params.rb StructuredParams.register_types ``` @@ -44,7 +46,7 @@ class UserParams < StructuredParams::Params attribute :score, :integer attribute :tags, :array, value_type: :string # プリミティブ配列 attribute :address, :object, value_class: AddressParams # ネストオブジェクト - + # 型変換前の生文字列をバリデーション validates_raw :score, format: { with: /\A\d+\z/, message: 'must be numeric string' } validates :name, presence: true @@ -55,7 +57,7 @@ end # API コントローラーで使用 def create user_params = UserParams.new(params) - + if user_params.valid? User.create!(user_params.attributes) else @@ -73,7 +75,7 @@ class UserParams < StructuredParams::Params attribute :tags, :array, value_type: :string end -# 相当する Strong Parameters: +# Strong Parameters と同等: # params.permit(tags: []) ``` @@ -84,17 +86,18 @@ class UserRegistrationForm < StructuredParams::Params attribute :name, :string attribute :email, :string attribute :terms_accepted, :boolean - + validates :name, :email, presence: true validates :terms_accepted, acceptance: true end # コントローラーで使用 -# permit は params.require(:user_registration).permit(...) を内部で呼び出す +# permit は内部で params.require(:user_registration).permit(...) を呼びます。 # API と異なり、フォームオブジェクトでは require によるキー絞り込みが必要なため permit を使う + def create form = UserRegistrationForm.new(UserRegistrationForm.permit(params)) - + if form.valid? User.create!(form.attributes) redirect_to root_path @@ -107,18 +110,17 @@ end ## ドキュメント - **[インストールとセットアップ](docs/installation.md)** - StructuredParams の始め方 -- **[基本的な使用方法](docs/basic-usage.md)** - パラメータクラス、ネストオブジェクト、配列 -- **[バリデーション](docs/validation.md)** - ネスト構造での ActiveModel バリデーション使用 -- **[Strong Parameters](docs/strong-parameters.md)** - 自動 permit リスト生成 -- **[エラーハンドリング](docs/error-handling.md)** - フラットと構造化エラーフォーマット +- **[基本的な使い方](docs/basic-usage.md)** - パラメータクラス、ネストオブジェクト、配列 +- **[バリデーション](docs/validation.md)** - ネスト構造での ActiveModel バリデーション +- **[Strong Parameters](docs/strong-parameters.md)** - permit リストの自動生成 +- **[エラーハンドリング](docs/error-handling.md)** - フラット形式と構造化形式のエラー - **[シリアライゼーション](docs/serialization.md)** - パラメータのハッシュと JSON 変換 - **[フォームオブジェクト](docs/form-objects.md)** - Rails ビューとのフォームオブジェクトパターン - ## コントリビューション バグレポートやプルリクエストは GitHub の https://github.com/Syati/structured_params で歓迎しています。 ## ライセンス -この gem は [MIT License](https://opensource.org/licenses/MIT) の条件の下でオープンソースとして利用可能です。 +この gem は [MIT License](https://opensource.org/licenses/MIT) のもとで公開されています。 diff --git a/docs/installation.md b/docs/installation.md index 3911f18..c5e0bf3 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -36,7 +36,15 @@ Register the custom types in a Rails initializer: StructuredParams.register_types ``` -This registers the `:object` and `:array` types with ActiveModel::Type. +This registers the `:object` and `:array` types with `ActiveModel::Type`. + +The registration is explicit on purpose: + +- `:object` and `:array` are convenient names in application code +- They are also generic names, so auto-registering them at gem load time could silently collide with other code +- Keeping registration in an initializer makes the opt-in explicit and keeps custom aliases available when needed + +If you skip this step, `attribute :name, :object` and `attribute :name, :array` will not resolve in ActiveModel type lookup. ## Configuration @@ -54,6 +62,8 @@ StructuredParams.configure do |config| end ``` +If you only need configuration and want to avoid the default names, use `register_types_as(...)` first and then configure in the same initializer. + | Option | Default | Description | |--------|---------|-------------| | `array_index_base` | `0` | Index base for array elements in error messages (`0` or `1`) | @@ -77,3 +87,5 @@ class UserParams < StructuredParams::Params attribute :hobbies, :structured_array, value_class: HobbyParams end ``` + +Prefer custom names if your app or another gem already uses `:object` or `:array` in `ActiveModel::Type`. diff --git a/spec/params_spec.rb b/spec/params_spec.rb index e1c38cd..dabb34e 100644 --- a/spec/params_spec.rb +++ b/spec/params_spec.rb @@ -417,7 +417,7 @@ context 'with default (en) locale' do it 'includes index in full_message' do full_messages = user_param.errors.map(&:full_message) - expect(full_messages).to include(match(/Hobbies 0 Name/)) + expect(full_messages).to include('Hobbies 0 Name can\'t be blank') end end diff --git a/structured_params.gemspec b/structured_params.gemspec index e4e868a..0489492 100644 --- a/structured_params.gemspec +++ b/structured_params.gemspec @@ -9,8 +9,12 @@ Gem::Specification.new do |spec| spec.authors = ['Mizuki Yamamoto'] spec.email = ['mizuki-y@syati.info'] - spec.summary = 'Type-safe parameter validation and form objects for Rails.' - spec.description = '' + spec.summary = 'Typed parameter objects and form objects for Rails.' + spec.description = ( + 'StructuredParams provides typed parameter objects for Rails APIs and forms, ' \ + 'with nested object and array support, Strong Parameters integration, and ' \ + 'raw-input validation via validates_raw.' + ) spec.homepage = 'https://github.com/Syati/structured_params' spec.license = 'MIT' spec.required_ruby_version = '>= 3.2.0'