Welcome! There isn’t a lot of documentation specific to Rails because Dry-rb is a toolkit intended for all Ruby code. There are a handful of specific Rails integrations provided by dry-rails but those are simply providing a convention for you to follow, it isn’t anything you couldn’t write yourself with only a little effort.
Schema Processor Types
The difference between params
and json
in your example relates to the schema processor. Ultimately, this mostly just changes the default dry-types that are used.
Schemas in dry-schema process keys in a series of steps:
- Key Validation: is a required key missing
- Key Coercion: transform a string key to symbol
- Value Filter: screen out values before they are coerced
- Value Coercion: apply any transformations defined in the type spec
- Value Type: does the value pass the type spec
- Value predicate: does the predicate (if defined) return true
Validation contracts tack on a final pass, applying rules to the entire schema that can encompass multiple keys.
The reason for params
and json
processor types comes from the different coercion rules you want to apply depending on whether you are dealing with HTTP params or JSON data.
For instance, it’s commonplace to see empty string returned in HTTP params that params
automatically transforms to nil
. That doesn’t happen with json
. Likewise, JSON defines boolean types so those are not coerced, but HTTP params need to transform string values into booleans. Different rules for different use-cases.
Macros
The distinction between value
, filled
, and maybe
are different macro types.
filled
is just a special-case macro for a very common thing you want to do: declare a type but ensure it’s non-empty. I believe I remember reading somewhere this is deprecated, so I would recommend using the predicate instead.
value
is the primary macro for a value. It applies the type spec to the value to ensure its correctness, and coerce it if desired.
value(:string)
is just a shorthand for value(Dry::Types['strict.string'])
.
A macro can apply two rules to a value:
- type spec
- predicate
Predicates come from dry-logic and as you might suspect, they are just predicate methods that answer a question about an object.
So the filled
macro is doing two things: a type spec, and a predicate.
value(Dry::Types['strict.string'], :filled?)
So that will ensure that the value is a string, and it is non-empty.
The filled predicate may also be built into the type spec as a constraint using the same logic operation
Types = Dry.Types(default: :strict)
module Types
FilledString = String.constrained(filled: true)
end
class NewUserContract < Dry::Validation::Contract
params do
required(:email).value(Types::FilledString)
required(:age).value(:integer)
end
end