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))
endDevelopment
bundle exec bin/console # interactive console
bundle exec rspec # run testsStatus
- Grape adapter (production ready)
- Rails adapter (experimental)
- Nested contracts support
- Custom type mappings
License
MIT