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
Manage your app’s objects in a system
require "app/import"
class CreateArticle
include App::Import[repo: "repositories.articles"]
def call(input)
# Work with the injected dependency
repo.create(input)
end
end
Build callable functional objects with automatic dependency resolution
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()
Safely model complex, chained transformations
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
Use the Result monad to model success and failure and match on the result