We’re taking Dry to the next level, and we need your help.Become a patron today

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