Project

dry_params

0.0
The project is in a healthy, maintained state
Automatically generate Grape params or Rails strong params from Dry::Validation contracts
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

>= 1.0
~> 13.0
~> 3.0
~> 1.0

Runtime

 Project Readme

DryParams

Work in Progress - This gem is under active development and currently used in production with Grape only.

If you use dry-validation extensively, you know the pain of duplicating field definitions between your contracts and your API params. This gem eliminates that repetition by automatically generating Grape params (or Rails strong params) directly from your Dry::Validation contracts.

Installation

gem 'dry_params'

Grape

class UserContract < Dry::Validation::Contract
  params do
    required(:name).filled(:string)
    required(:age).filled(:integer)
    optional(:email).maybe(:string)
  end
end

DryParams.from(UserContract)
# => {
#   name: { type: String, desc: "Name", required: true, documentation: { param_type: "body" } },
#   age: { type: Integer, desc: "Age", required: true, documentation: { param_type: "body" } },
#   email: { type: String, desc: "Email", required: false, documentation: { param_type: "body" } }
# }

Custom Descriptions

Use comment annotations to provide custom descriptions for your params:

class AppointmentCreateContract < Dry::Validation::Contract
  params do
    # @title = Title of the appointment
    required(:title).filled(:string)
    # @start_time = Start time in ISO8601 format
    required(:start_time).filled(:time)
    # @duration_minutes = Duration in minutes (default: 60)
    optional(:duration_minutes).filled(:integer)
  end
end

DryParams.from(AppointmentCreateContract)
# => {
#   title: { type: String, desc: "Title of the appointment", required: true, ... },
#   start_time: { type: Time, desc: "Start time in ISO8601 format", required: true, ... },
#   duration_minutes: { type: Integer, desc: "Duration in minutes (default: 60)", required: false, ... }
# }

The annotation format is # @field_name = Your description here. Without annotations, the field name is humanized (e.g., start_time becomes "Start time").

Usage:

desc "Create user", { params: DryParams.from(UserContract) }

For query params:

desc "List users", { params: DryParams.from(UserFilterContract, param_type: 'query') }

Rails

DryParams.from(UserContract, adapter: :rails)
# => [:name, :age, :email]

With arrays and hashes:

class PostContract < Dry::Validation::Contract
  params do
    required(:title).filled(:string)
    optional(:tags).filled(:array)
    optional(:metadata).filled(:hash)
  end
end

DryParams.from(PostContract, adapter: :rails)
# => [:title, { tags: [], metadata: {} }]

Usage:

def post_params
  params.permit(DryParams.from(PostContract, adapter: :rails))
end

Development

bundle exec bin/console  # interactive console
bundle exec rspec        # run tests

Status

  • Grape adapter (production ready)
  • Rails adapter (experimental)
  • Nested contracts support
  • Custom type mappings

License

MIT