Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .codex/config.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
approval_policy = "on-request"
sandbox_mode = "workspace-write"
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4.0.0
4.0.5
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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
```

Expand All @@ -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
Expand All @@ -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
Expand All @@ -77,15 +79,14 @@ end
# params.permit(tags: [])
```


### 2. Form Object

```ruby
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
Expand All @@ -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
Expand Down
48 changes: 25 additions & 23 deletions README_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand All @@ -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
Expand All @@ -55,7 +57,7 @@ end
# API コントローラーで使用
def create
user_params = UserParams.new(params)

if user_params.valid?
User.create!(user_params.attributes)
else
Expand All @@ -73,7 +75,7 @@ class UserParams < StructuredParams::Params
attribute :tags, :array, value_type: :string
end

# 相当する Strong Parameters:
# Strong Parameters と同等:
# params.permit(tags: [])
```

Expand All @@ -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 を使う
Comment thread
Syati marked this conversation as resolved.

def create
form = UserRegistrationForm.new(UserRegistrationForm.permit(params))

if form.valid?
User.create!(form.attributes)
redirect_to root_path
Expand All @@ -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) のもとで公開されています
14 changes: 13 additions & 1 deletion docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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`) |
Expand All @@ -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`.
2 changes: 1 addition & 1 deletion spec/params_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 6 additions & 2 deletions structured_params.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down