Dry-rb quick intro: dry-types, dry-struct and dry-validation


Created by Stan on 08-04-2023


dry-rb is a collection of Ruby libraries that aims to provide a more functional approach to Ruby programming, emphasizing the DRY (Don't Repeat Yourself) principle. Some of the most notable gems in the dry-rb ecosystem include dry-validation, dry-types, dry-struct, dry-container, and dry-system. In this example, we will demonstrate how to implement dry-validation, dry-types, and dry-struct in a Ruby project.

Installing the necessary gems

Add the following gems to your Gemfile:

gem 'dry-validation'
gem 'dry-types'
gem 'dry-struct'

Then, run bundle install to install the gems.

Using dry-types

dry-types is a gem that provides a robust and flexible type system for Ruby. It allows you to define custom types with constraints and coercions. Here's an example:

# types.rb
require 'dry-types'

module Types
  include Dry.Types()

  Age = Types::Integer.constrained(gt: 0, lt: 150)
  Email = Types::String.constrained(format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i)
end

In this example, we define two custom types: Age and Email. The Age type is constrained to be an integer greater than 0 and less than 150. The Email type is constrained to match a regular expression for email validation.

Using dry-struct

dry-struct is a gem that allows you to define simple data structures with typed attributes. Here's an example of using dry-struct with the custom types defined above:

# user.rb
require 'dry-struct'
require_relative 'types'

class User < Dry::Struct
  attribute :name, Types::String
  attribute :age, Types::Age
  attribute :email, Types::Email
end

In this example, we define a User class that extends Dry::Struct and uses the custom types from our Types module. The User class now enforces type constraints on its attributes.

Using dry-validation

dry-validation is a gem that provides a powerful way to validate data structures in Ruby. It works well with dry-types and dry-struct. Here's an example of defining a schema to validate a user:

# user_contract.rb
require 'dry-validation'
require_relative 'types'

class UserContract < Dry::Validation::Contract
  params do
    required(:name).filled(:str?)
    required(:age).filled(Types::Age)
    required(:email).filled(Types::Email)
  end 
end

In this example, we define a UserSchema that enforces the same constraints as our User class. You can now validate user data using this schema:

# client.rb
require_relative 'user_contract'
require_relative 'user'

input = {
  name: 'John Doe',
  age: 28,
  email: '[email protected]'
}

contract = UserContract.new
result = contract.call(input)

if result.success?
  user = User.new(result.to_h)
  puts "User created: #{user.inspect}"
else
  puts "Validation errors: #{result.errors.to_h}"
end

In this example, we validate the input data using UserSchema. If the validation is successful, we create a new User instance with the validated data. Otherwise, we display the validation errors.

By using dry-rb gems like dry-types, dry-struct, and dry-validation in your Ruby projects, you can write cleaner, more maintainable, and more robust code by adhering to the DRY principle and leveraging the functional programming style provided by the dry-rb ecosystem.



Related Posts