Check out the examples below to get a taste for how our libraries work
Use schemas and powerful rules to validate data with explicitness, clarity and precision.
class NewUserContract < Dry::Validation::Contract
params do
required(:name).filled(:string)
required(:age).filled(:integer)
end
rule(:age) { key.failure("must be greater than 18") if value < 18 }
end
contract = NewUserContract.new
contract.("name" => "Jane", "age" => "30").success? # .() is a Ruby shortcut for .call()
# => true
contract.("name" => "Jane", "age" => "17").failure?
# => true
contract.("name" => "Jane", "age" => "17").errors.to_h
# => {:age=>["must be greater than 18"]}
Build your own data types, constraints and coercions with dry-types.
module Types
include Dry.Types
Greeting = String.enum("Hello", "Hola")
end
Types::Greeting["Hello"]
# => "Hello"
Types::Greeting["Goodbye"]
# Dry::Types::ConstraintError: "Goodbye" violates constraints...
Define your own data structs using typed attributes powered by dry-types.
class Person < Dry::Struct
attribute :name, Types::String
attribute :age, Types::Integer
end
Person.new(name: "Jane", age: 30)
# => #<Person name="Jane" age=30>
Person.new(name: "Jane", age: nil)
# Dry::Struct::Error ([Person.new] nil (NilClass) has invalid type for :age
# violates constraints (type?(Integer, nil) failed))
Use dry-system to build your application from a collection of discrete, single-purpose components. Compose these objects and easily achieve larger functionality with auto-injection.
class App < Dry::System::Container
configure do |config|
config.auto_register = %w[lib]
end
load_paths! "lib"
end
require "app/import"
class CreateArticle
include App::Import[repo: "repositories.articles"]
def call(input)
# Work with the injected dependency
repo.create(input)
end
end
Safely and gracefully model complex transformations with monads, and use pattern matching to make robust error handling a first-class feature of your application.
Dry::Monads::Maybe(user).fmap(&:address).fmap(&:street)
# If user with address exists
# => Some("Street Address")
# If user or address is nil
# => None()
class CreateArticle
include Dry::Monads[:result]
include Dry::Matcher.for(:call, with: Dry::Matcher::ResultMatcher)
def call(input)
if success?(input)
output = some_action(input)
Success(output)
else
Failure(input)
end
end
end
create = CreateArticle.new
create.(input) do |m|
m.success do |output|
# Handle success
end
m.failure do |err|
# Handle failure
end
end