DEV Community

Cristian Molina
Cristian Molina

Posted on

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

Discussion (5)

Collapse
carc1n0gen profile image
Carson Evans • Edited on

Hi, I'm a PHP and Python developer who was looking in to ruby frameworks and wanted to try roda. So I found this guide hoping it would help me figure out how to get going. But nothing here shows anything the official documentation doesn't. So from a non-ruby developer, how the heck do you run a roda app?

Collapse
megatux profile image
Cristian Molina Author

Hi Carson, you are right, this little guide assumes Ruby web experience.
Basically you need a modern Ruby, the Roda gem installed and a 'config.ru' file with something like:

require './app'
run App

Then, you execute the app with

$ rackup config.ru

I'll add this to the guide or made a better comment here.
Thanks

Collapse
carc1n0gen profile image
Carson Evans

Thank you! I've been looking for this everywhere! So many ruby micro frameworks assume I already knew what rack was!

Collapse
andrewbrown profile image
Andrew Brown 🇚🇊

I'm not a fan of Roda's DSL but the speed is very impressive and it has some sane security defaults.

Collapse
m0rozov profile image
Andrey

Thank you for the article!
I'll go see the guides

Thank you for the reference to the book 📖