Why Roda?

There are already plenty of Ruby web frameworks. Why another one? Well, Roda has a very useful combination of features that make web development easy. It's designed to be fast, DRY, and correct. It's probably easiest to explain the advantages by comparing it with Sinatra, which it tries to emulate in many respects.

Differences with Sinatra

Routing Tree

The primary difference between Roda and Sinatra is that Roda uses a routing tree, while Sinatra uses a list of routes. At any point in the routing tree, Roda allows you to operate on the current request. If your URLs reflect your application architecture, this allows you to have DRYer code. Let's examine code examples for a very simple app, implemented in both Sinatra and Roda:

Roda:
require 'roda'

class App < Roda
  plugin :render
  plugin :all_verbs

  route do |r|
    r.root do
      view :index
    end

    r.is 'artist/:id' do |artist_id|
      @artist = Artist[artist_id]
      check_access(@artist)

      r.get do
        view :artist
      end

      r.post do
        @artist.update(r['artist'])
        r.redirect
      end

      r.delete do
        @artist.destroy
        r.redirect '/'
      end
    end
  end
end
Sinatra:
require 'sinatra/base'

class App < Sinatra::Base
  get '/' do
    erb :index
  end

  get '/artist/:id' do
    @artist = Artist[params[:id]]
    check_access(@artist)
    erb :artist
  end

  post '/artist/:id' do
    @artist = Artist[params[:id]]
    check_access(@artist)
    @artist.update(params[:artist])
    redirect(request.path_info)
  end

  delete '/artist/:id' do
    @artist = Artist[params[:id]]
    check_access(@artist)
    @artist.destory
    redirect '/' 
  end
end

While the Roda code is slightly longer, it should be apparent that it is actually simpler. Instead of setting the @artist variable and checking that access is allowed in all three of the sinatra routes, 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 framework. This is a very simplified example, but if you see this type of duplication in a lot of the Sinatra code you write, your app could probably be made simpler by converting to Roda.

Faster

In addition to being more maintainable, Roda's approach is also faster in general. Sinatra routes requests by testing each of the stored routes against the current request, in order. With Roda, once one branch of the tree matches, only routes inside that branch are considered, not any routes after that branch. Roda also has support for a single route that dispatches to multiple branches via the multi_route plugin. For large applications, routing in Sinatra is roughly O(n), where n is the number of routes, while routing in Roda can be close to O(log n), depending on how you structure your routing tree. For small applications, because Roda is simpler internally, Roda is about 2.5 times faster than Sinatra.

Plugin System

Another difference between Roda and Sinatra is that Roda has a very small core, with a plugin system modeled on Sequel's. All parts of Roda (class/instance/request/response) can be overridden by plugins and call super to get the default behavior. This includes when plugins are applied to the Roda class itself (affecting all subclasses). The reason Roda is referred to as a toolkit is that by using different combinations of plugins, you can build the routing tree web framework that meets your needs.

Many features that are built into Sinatra are shipped as plugins in Roda. This way they can easily be used if needed, but if you don't use them you don't pay the cost for loading them. Near the top of the Roda example, you can see plugin :render, which adds support for template rendering, and plugin :all_verbs, which adds routing methods for all HTTP request methods.

Less Pollution

Finally, Roda is very concerned about pollution. In this case, pollution of the scope in which the route block operates. Roda purposely limits the instance variables, instance methods, and class namespaces available in the route block scope, so that it is very unlikely you will run into conflicts. If you've ever tried to use an instance variable named @request or a model named Response inside a Sinatra::Base subclass, you'll appreciate that Roda attempts to avoid polluting the scope.

Differences with Cuba

Roda started out as a fork of Cuba. Both share the idea of using a routing tree, but there are some important differences.

Response Bodies

Cuba takes a explicit approach for handling response bodies, where you must always call res.write "body" inside your routes. Roda assumes that if the block returns a string and the response body has not already been written to, that the block response should be used as the response body. This DRYs up a lot of code. Here's a simple example:

Roda:
Roda.route do |r|
  r.on 'foo' do
    '1'
  end

  r.on 'bar' do
    '2'
  end
end
Cuba:
Cuba.define do
  on 'foo' do
    res.write '1'
  end

  on 'bar' do
    res.write '2'
  end
end

Canonical Routes

Roda has built in support for canonical routes, using r.is. Canonical, in this case, means no further entries are in the path. In Cuba, if you want a canonical route, you have to manually use a regexp or string ending with \z. Using this example:

Roda:
Roda.route do |r|
  r.is 'foo' do
    '1'
  end
end
Cuba:
Cuba.define do
  on 'foo' do
    res.write '1'
  end
end

Cuba will return a response of 1 for /foo, /foo/bar, and /foo/bar/baz, where Roda will only return a response of 1 for /foo, not handling /foo/bar or /foo/bar/baz.

Less Pollution

Roda makes more of an effort to avoid polluting the scope, though compared to most other web frameworks, Cuba does a good job. With Cuba, all route matching methods are defined in the same scope, which has 23 additional methods over Object as opposed to the 6 additional methods in Roda. Roda is able to do this by moving the route matching methods into the request class, which is why in Roda you use r.on instead of on. Cuba does not prefix instance variables or classes in the scope, which makes namespace clashes more likely.

Plugin System

While both Roda and Cuba have plugin systems, Roda's is based on Sequel's and is more flexible, allowing overriding of all Roda methods, even when plugins are used directly on the Roda class. Additionally, Roda ships with many plugins, and those plugins are tested in the same test suite as the core Roda code, ensuring that updates to Roda will not break any of the plugins.