D r y
Ruby gems for better code
Dry is a collection of Ruby gems that help you write clear, flexible, and maintainable Ruby code.
Covering business logic to everyday utilities, these gems work great together or standalone in any kind of app.
View the Dry docsBusiness logic
These gems help you write business logic that’s explicit, predictable, and testable.
Validation
With Dry validation contracts, you can specify precisely the data you want to accept for each use case in your app.
Since contracts are standalone, they help you keep your validation logic reusable, testable, and separate from your data persistence.
View validation docs
params do
required(:name).filled(:string)
required(:age).filled(:integer)
end
rule(:age) { key.failure("must be greater than 18") if value < 18 }
end
Types
With Dry types, you can build your app’s own library of self-documenting type checks and data transformations. You can use your types standalone, or bring them together in immutable structs.
Together, this helps you write more expressive, confident code, knowing you’re working with data you can trust.
View types docs = Dry.Types(default: :strict)
= String.constrained(format: /@/)
= String.enum("admin", "member", "guest")
end
attribute :email, Types::Email
attribute :name, Types::String
attribute :age, Types::Integer.constrained(gteq: 0)
attribute :role, Types::UserRole
end
Operations and results
Operations help you organize your essential business logic. Create your operation from a series of steps, each returning a success or failure. When you call your operation, if any step fails, the operation stops and that failure is returned right away.
You can chain operations together into higher-level workflows, and when you need even more control, you can tap into a full library of monads for advanced composition.
View operations docs
attrs = step validate(input)
user = step persist(attrs)
step notify(user)
user
end
private
# Return Success(attrs) or Failure(error)
end
# Return Success(user) or Failure(error)
end
# Return Success(true) or Failure(error)
end
end
Foundations
All sorts of goodies here: these gems are practical, standalone utilities for both libraries and apps.
Logging
Dry Logger is a powerful and flexible replacement for Ruby’s standard logger. Use it to log with consistent formatting and to multiple outputs.
You can tag entries and route logs where you need them.
View logger docs# From simple logging
logger = Dry.Logger(:my_app)
logger.info("App started")
# To structured logging and output routing
logger = Dry.Logger(:my_app, formatter: :json) {
setup.add_backend(stream: "logs/app.log", template: :details)
setup.add_backend(stream: "logs/json.log", formatter: :json)
setup.add_backend(stream: "logs/error.log", log_if: :error?)
}
logger.info("User signed in", user_id: 123, role: "admin")
Inflections
Dry Inflector is a lightweight inflections gem that you can use anywhere. It comes with a familiar API that does all the things you expect: pluralize, singularize, and convert cases, all based on your own configuration.
View inflector docsinflector = Dry::Inflector.new
# Standard transformations
inflector.pluralize("person") # => "people"
inflector.singularize("categories") # => "category"
inflector.camelize("preferred_name") # => "PreferredName"
inflector.underscore("PreferredName") # => "preferred_name"
# Configure for your domain
inflector = Dry::Inflector.new do
inflections.acronym("API", "JSON", "HTTP")
end
Initializers
Use Dry Initializer to avoid repetitive class constructors and just declare what your objects need, along with optional type checks and defaults.
View initializer docsextend Dry::Initializer
option :name, type: Types::String
option :age, type: Types::Integer, default: proc { 18 }
option :role, type: Types::String, default: proc { "member" }
end
user = User.new(name: "Alice", age: 30)
Configuration
Dry Configurable helps you fulfil a common pattern within Ruby: configurable objects with validation and type checking.
View configurable docsextend Dry::Configurable
setting :backend, constructor: Types::String
setting :ttl, default: 3600, constructor: Types::Integer
setting :redis do
setting :host, default: "localhost"
setting :port, default: 6379
setting :db, default: 0
end
end
CacheStore.config.backend = "redis"
CacheStore.config.redis.host = "redis.example.com"
CacheStore.config.redis.port = 6380
Architecture
Structure whole apps around clear boundaries and loosely-coupled components.
Systems and auto-injection
Take the core of the Hanami framework and tailor it exactly to your needs. Dry System and Dry Auto Inject help you construct apps from clear, focused components with explicit dependencies.
View system docs
configure do
config.component_dirs.add "lib"
end
end
= App.injector
# lib/users/create.rb
include Deps["user_repo", "emails.welcome_email"]
user = user_repo.create(attributes)
step welcome_email.delivery(user)
Success(user)
end
end
Command line tools
Create rich command line interfaces with argument parsing, subcommands, and help text.
Build your CLI like you would a real app, with commands as classes, and the full power of Ruby at your disposal.
View CLI docs
extend Dry::CLI::Registry
desc "Generate a new file"
argument :name, required: true, desc: "File name"
option :type, default: "rb", desc: "File type"
puts "Generating ...."
end
end
register "generate", Generate
end
end
end
Dry::CLI.new(MyApp::CLI::Commands).call
…and more!
We’ve just scratched the surface with Dry. There’s a lot more to love:
- A comprehensive toolkit. We offer 25+ gems spanning business logic, architecture, and everyday utilities. You can use what you need, and skip what you don’t.
- Works everywhere. Dry gems work everywhere, from Hanami to Rails to standalone gems. We make no framework assumptions, and don’t lock you in.
- Proven in production. Built on over a decade of refinement and real-world use, these are proven tools you can depend on for the long-term.
Built on community
For people who bring kindness, curiosity, and care
The Hanakai community is a place where people of all backgrounds and experience levels can feel respected, and can share and grow. A place for people to be proud of, and feel safe within.
We do not tolerate nazis, transphobes, racists, or any kind of bigotry. See our Code of Conduct for more.
Supported by
Hanakai is made possible by our wonderful sponsors
We’re also supported by our many community patrons.
Become a Hanakai sponsor or patron today, and help us build a diverse future for Ruby.
Support the Hanakai project