A very short intro to the Roda web toolkit
Introduction
There are several “lightweight” Ruby web & API libraries out there. Sinatra is probably the most known but there are others that are newer, faster, cleaner, more flexible and/or better maintained.
One of those is Roda, created by Jeremy Evans, the creator of the Sequel gem. It started as a Cuba fork but then evolved a lot. It is specially notably by its speed and plugin system, shared characteristics with its sibling Sequel gem, where everything can be overridden.
It is auto-defined as a “Routing tree web toolkit”. Why is that? Because of the way the routes are defined & run. Let me show you an example:
require "roda"
class App < Roda
plugin :render
plugin :all_verbs
route do |r|
r.root do
view :index
end
r.is "artist", Integer do |artist_id|
@artist = Artist[artist_id]
check_access(@artist)
r.get do
view :artist
end
r.post do
@artist.update(r.params[ "artist "])
r.redirect
end
r.delete do
@artist.destroy
r.redirect "/"
end
end
end
end
If you compare with other libraries like Sinatra, instead of setting the @artist variable and checking the access is allowed in all three of the actions, the variables are set as soon as that branch of the tree is taken, and can be used in all routes under that branch. This is why Roda is called a routing tree web toolkit.
More examples of the routing flexibility:
route do |r|
# array matchers
r.on %w[hello hi] do |greeting|
"#{greeting}, world!"
end
# method matchers
r.is "hello", method: [:post, :put, :patch] do
"All your base are belong to us"
end
# Regexp matchers
r.on "users" do
r.get /(\d+)/ do |user_id|
"Your id is #{user_id}"
end
end
end
Roda’s plugin system design is shared with the Sequel gem but also copied in other projects, like in the Shrine file attachment toolkit, Cuba(v3).
You can start with a small, compact core of an application and grow complexity over time and learning as you go, having fine grained control on features and performance.
For instance, wants websockets, checked:
plugin :websockets
route do |r|
r.get "room" do
# Matches if it's a websocket request
r.websocket do |ws|
ws.on(:message) { ... }
ws.on(:close) { ... }
# ...
end
# If the request is not a websocket request, we render a template
view "room"
end
end
Do you want a JSON API? The json plugin responds with JSON automatically for arrays & hashes:
plugin :json
route do |r|
r.root do
[1, 2, 3]
end
r.is "foo" do
{ "a" => "b"}
end
end
But you can customize it easily, for example, adding ActiveRecord relations:
plugin :json, classes: [Array, Hash, ActiveRecord::Base, ActiveRecord::Relation],
serializer: proc { |object|
case object
when Array, Hash
object.to_json
else
Serializer.new(object).as_json
end
}
route do |r|
r.get "albums/recent" do
Album.recent
end
r.get "albums/:id" do |id|
Album.find(id)
end
end
The complete list of bundled plugins is huge, more than 90, and there are several community extensions, so you are pretty much covered here. In case you want to do one, this is the structure:
class Roda
module RodaPlugins
module MyPlugin
module ClassMethods
end
module InstanceMethods
end
module RequestClassMethods
end
module RequestMethods
end
module ResponseClassMethods
end
module ResponseMethods
end
end
register_plugin(:my_plugin, MyPlugin)
end
end
Performance
An image is worth a thousand words. Compare the request per second and the memory usage:
Pretty close to raw Rack values, but with a nicer DSL.
Maintenance
Jeremy responses in the mailing list are quick & he resolves any issues, too:
(Look Ma, no issues)
Some disadvantages
- No routes introspection (although there is a plugin that helps with this).
- Less gems, as many are tightly coupled with default framework.
- Less community support vs Rails or Sinatra.
- Some reinventing the wheel may be unavoidable.
- The routing structure may not be your style (but there is a plugin to use a Sinatra like class route definitions)
Extra links:
- This is a nice free Roda book.
- The official mailing list.
- If you want to know more about this plugin system pattern, check this post from the Shrine’s creator.
- Other benchmarks: ruby web frameworks micro benchmarks project, the famous TechEmpower web benchmarks (filtered for Ruby frameworks) and also this project.