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