Nested data
dry-schema
supports validation of nested data.
Nested Hash
To define validation rules for a nested hash you can use the same DSL on a specific key:
schema = Dry::Schema.Params do
required(:address).hash do
required(:city).filled(:string, min_size?: 3)
required(:street).filled(:string)
required(:country).hash do
required(:name).filled(:string)
required(:code).filled(:string)
end
end
end
errors = schema.call({}).errors
puts errors.to_h.inspect
# { :address => ["is missing"] }
errors = schema.call(address: { city: 'NYC' }).errors
puts errors.to_h.inspect
# {
# :address => [
# { :street => ["is missing"] },
# { :country => ["is missing"] }
# ]
# }
Nested Maybe Hash
If a nested hash could be nil
, simply use maybe
macro with a block:
schema = Dry::Schema.Params do
required(:address).maybe do
hash do
required(:city).filled(:string, min_size?: 3)
required(:street).filled(:string)
required(:country).hash do
required(:name).filled(:string)
required(:code).filled(:string)
end
end
end
end
schema.(address: nil).success? # true
Nested Array
You can use the array
macro for validating each element in an array:
schema = Dry::Schema.Params do
required(:phone_numbers).array(:str?)
end
errors = schema.call(phone_numbers: '').messages
puts errors.to_h.inspect
# { :phone_numbers => ["must be an array"] }
errors = schema.call(phone_numbers: ['123456789', 123456789]).messages
puts errors.to_h.inspect
# {
# :phone_numbers => {
# 1 => ["must be a string"]
# }
# }
You can use array(:hash)
and schema
to validate an array of hashes:
schema = Dry::Schema.Params do
required(:people).array(:hash) do
required(:name).filled(:string)
required(:age).filled(:integer, gteq?: 18)
end
end
errors = schema.call(people: [{ name: 'Alice', age: 19 }, { name: 'Bob', age: 17 }]).errors
puts errors.to_h.inspect
# => {
# :people=>{
# 1=>{
# :age=>["must be greater than or equal to 18"]
# }
# }
# }
To add array predicates, use the full form:
schema = Dry::Schema.Params do
required(:people).value(:array, min_size?: 1).each do
hash do
required(:name).filled(:string)
required(:age).filled(:integer, gteq?: 18)
end
end
end
errors = schema.call(people: []).errors
puts errors.to_h.inspect
# => {
# :people=>["size cannot be less than 1"]
# }