well_formed-dry_types
dry-types coercion integration for WellFormed form objects.
Declare typed attributes with dry_attribute and coercion runs automatically before validation — keeping your forms free of manual casting logic.
Installation
bundle add well_formed-dry_types
Usage
Require the gem in your application:
require "well_formed-dry_types"
Include WellFormed::DryTypes in any form, then use dry_attribute in place of attribute for fields that need type coercion:
module Types
include Dry.Types()
end
class CreateOrderForm < WellFormed::ResourceForm
include WellFormed::DryTypes
resource_alias :order
dry_attribute :quantity, Types::Params::Integer
dry_attribute :amount, Types::Params::Decimal
dry_attribute :status, Types::String.enum("pending", "confirmed")
validates :quantity, presence: true, numericality: {greater_than: 0}
validates :amount, presence: true
validates :status, presence: true
end
Coercion runs before validation. If coercion fails, an error is added to the attribute and validation is skipped for that field:
form = CreateOrderForm.new(order, current_user, {quantity: "abc", amount: "9.99", status: "pending"})
form.valid?
# => false
form.errors[:quantity]
# => ["must be Params::Integer"] # dry-types error message
Custom error messages
Pass message: to override the default dry-types error message.
I18n key
dry_attribute :status, Types::String.enum("pending", "confirmed"), message: :inclusion
Passes the symbol to errors.add, which resolves it through your I18n translations.
Literal string
dry_attribute :amount, Types::Params::Decimal, message: "must be a number"
Default (no message option)
dry_attribute :quantity, Types::Params::Integer
Falls back to the error message from dry-types itself.
Inheritance
_dry_attributes merges up the superclass chain, so subclass forms inherit parent coercions and can add their own:
class BaseOrderForm < WellFormed::ResourceForm
include WellFormed::DryTypes
dry_attribute :amount, Types::Params::Decimal
end
class CreateOrderForm < BaseOrderForm
dry_attribute :quantity, Types::Params::Integer
# inherits :amount coercion from BaseOrderForm
end
API
| Class macro | Description |
|---|---|
dry_attribute(name, type, message: nil) |
Declares a coerced attribute. Coercion runs before validation via a before_validate callback. |
How it works
When WellFormed::DryTypes is included, a before_validate callback (_coerce_dry_attributes) is registered. Before each validation run, every dry_attribute is coerced in declaration order. On Dry::Types::CoercionError, an error is added and the raw value is left in place. Subsequent validators can still run against the failing attribute (e.g. a presence check on a nil optional).
License
MIT