Looking for some new & interesting gems to try?
Then have a look at dry-rb, a set of gems that bring solutions to common problems. Dry-rb is composed of over 18 Ruby gems that can work together or by themselves.
Some of these gems include:
- dry-initializer
- dry-struct
- dry-validations
- dry-events
- dry-transaction
In this post, you’ll learn about 3 of these gems so you can get a taste of what dry-rb has to offer!
How to Create Better Structs With dry-struct
A Ruby Struct is a kind of object that you can create from the Struct class, but they have some limitations.
For example:
You can create a struct with a number of missing arguments & get no complaints from Ruby.
With the dry-struct
gem you can create stricter structs.
Here’s how:
require 'dry-struct'
module Types
include Dry::Types.module
end
class Video < Dry::Struct
attribute :title, Types::String
attribute :views, Types::Views
attribute :comments, Types::Array
end
Video.new(title: "abc", views: 10, comments: [])
Now if you miss one of the attributes, you get this error:
[Video.new] :comments is missing in Hash input
Not the most user-friendly error message, but it gets things done.
You can create your own types & add constraints to them:
module Types
include Dry::Types.module
Age = Integer.constrained(gt: 0)
end
This "gt" means "greater than".
So Age
must be an integer greater than 0.
Can you have default values?
Yes, here's how:
module Types
include Dry::Types.module
Name = String.default('')
end
There is a shorter version for creating Dry structs, which resembles regular Ruby structs.
Example:
Book = Dry::Struct(title: Dry::Types["string"])
Book.new(title: "Computer Science 101")
Implementing the Observer Pattern With dry-events
The Observer design pattern is when one source publishes an update to many listeners, this could be a news update, stock alerts, new message in a group chat, etc.
It's a common pattern.
Ruby includes an observable module that helps you implement this.
But dry-events
is another option.
You can create a publisher like this:
require 'dry/events/publisher'
class Blog
include Dry::Events::Publisher[:blog]
register_event('article.published')
register_event('article.upated')
end
Now:
Any class can register itself to receive these events when they happen.
Here's how:
class Reader
def on_article_published(event)
puts "New article published: #{event[:title]}"
end
def on_article_updated(event)
puts "Article has been updated: #{event[:title]}"
end
end
blog = Blog.new
reader = Reader.new
blog.subscribe(reader)
You can broadcast an event with the publish
method:
blog.publish('article.published', title: 'How to Use The dry-events Gem')
One thing I noticed with this gem is that if you subscribe or publish an event that doesn't exist it will fail silently.
Dependency Injection With dry-auto_inject
What is a dependency?
A dependency is something that a class needs to work, like other objects & classes.
You can create these objects directly in your class...
Or you can use dependency injection.
Dependency injection is when the dependencies are "injected" into the class. In other words, they are passed in as parameters.
Example:
require 'faraday'
class GitHubAPI
def initialize(http_client = Faraday.new)
@http_client = http_client
end
end
Doing this allows you to control the dependency & make testing easier.
Now:
dry-auto_inject
creates these objects for you. All you have to do is declare what dependencies are needed.
In this example Fruit
depends on Store
:
require 'dry/auto_inject'
class Store
def bananas
puts 'Have some organic bananas!'
end
end
Import = Dry::AutoInject({ 'store' => Store.new })
class Fruit
include Import['store']
def eat
store.bananas
end
end
fruit = Fruit.new
fruit.eat
You have to say how the object is created:
Import = Dry::AutoInject({ 'store' => Store.new })
And make it available for the class:
include Import['store']
Then you can create a Fruit
with a different Store
like this:
Fruit.new(store: MyStore.new)
You don't need a gem to use dependency injection, but it's interesting to see another way to do it.
Summary
You have learned about dry-rb, a set of interesting Ruby gems that you can use to solve specific design problems in your code.
Now it's your turn to practice & give this a try!
Thanks for reading.