structured_params gem の紹介(型安全な Params / Form Object)

この記事は https://github.com/Syati/structured_paramsstructured_params(Ruby gem) の紹介。

Rails のコントローラーやフォーム周りで、こんな悩みはよくあります。

  • params は基本的に文字列で入り、型変換が散らばる
  • ネストした構造(object/array)が増えると permit が複雑になる
  • バリデーションと変換(cast)の責務がモデルやコントローラーに漏れる

structured_params は、これらを ActiveModel ベースの Params クラスとして切り出し、型変換・バリデーション・Strong Parameters の permit までを一貫して扱えるようにする gem。

structured_params が提供するもの

structured_params の中心は StructuredParams::Params

  • attribute で「期待する型」を宣言できる
  • ActiveModel の validates がそのまま使える
  • ネストした object / array の型変換(cast)を扱える
  • Strong Parameters の permit リストを自動生成できる
  • RBS 型定義があり、型の補完/検査と相性がよい

クイックスタート

まず StructuredParams.register_types を呼んで、型キャストに必要な型を登録する。

# Gemfile
gem 'structured_params'

# どこか初期化(例: config/initializers/structured_params.rb)
StructuredParams.register_types

API パラメータバリデーション例

API のリクエストパラメータを「型変換 + バリデーション」して、モデルに渡すところまでを 1 つの Params クラスにまとめられる。

class AddressParams < StructuredParams::Params
  attribute :street, :string
  attribute :city, :string
end

class UserParams < StructuredParams::Params
  attribute :name, :string
  attribute :age, :integer
  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
  validates :age, numericality: { greater_than: 0 }
  validates :score, numericality: { greater_than_or_equal_to: 0 }
end

def create
  user_params = UserParams.new(params)

  if user_params.valid?
    User.create!(user_params.attributes)
  else
    render json: { errors: user_params.errors }, status: :unprocessable_entity
  end
end

permit を書き下す代わりに UserParams.new(params) を呼べるので、コントローラー側がすっきりする。

フォームオブジェクト例

structured_params はフォーム入力の扱いにも向いている。ActiveModel 互換なので、validates など Rails の文法でそのまま書ける。

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

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

  if form.valid?
    User.create!(form.attributes)
    redirect_to root_path
  else
    render :new
  end
end

いつ使う?(使いどころの考え方)

特に効果が大きいのは次のようなケース。

  • API が大きくなり、パラメータの型変換・バリデーションが散らばっている
  • ネスト(object/array)が増えて Strong Parameters の管理がつらい
  • “フォームの入力値” をモデルに変換するロジックが複雑になってきた

逆に、入力が単純で変換がほぼ不要な場合は、まずは params.require(...).permit(...) とモデルのバリデーションだけでも十分。

まとめ

  • structured_params は Rails で「型安全な Params / Form Object」を作るための gem
  • attribute と ActiveModel validations で、型変換と検証を 1 つのクラスに集約できる
  • Strong Parameters の permit リストも自動生成でき、ネスト構造でも扱いやすい

詳しくは upstream のドキュメント(README / docs)を参照。

Comments