dry-rb is a collection of next-generation Ruby libraries

dry-rb helps you write clear, flexible, and more maintainable Ruby code. Each dry-rb gem fulfills a common task, and together they make a powerful platform for any kind of Ruby application.

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