# cat config.ru
require "roda"

class App < Roda
  route do |r|
    # GET / request
    r.root do
      r.redirect "/hello"
    end

    # /hello branch
    r.on "hello" do
      # Set variable for all routes in /hello branch
      @greeting = 'Hello'

      # GET /hello/world request
      r.get "world" do
        "#{@greeting} world!"
      end

      # /hello request
      r.is do
        # GET /hello request
        r.get do
          "#{@greeting}!"
        end

        # POST /hello request
        r.post do
          puts "Someone said #{@greeting}!"
          r.redirect
        end
      end
    end
  end
end

run App.freeze.app
Roda Features Usability Simplicity Extensibility Productivity Reliability Performance Roda uses a routing tree. At any point during routing, it allows you to operate on the current request. Roda supports and encourages immutability. Roda apps are designed to be frozen in production. Roda is the one of the fastest Ruby web frameworks. Roda is designed to be simple, both internally and externally, reducing cognative overhead. Roda is built completely out of plugins, which makes it very extensible. Roda makes it easy to develop applications quickly.

Comparison to Sinatra

Roda aims to take the ease of development and understanding that Sinatra brings, and enable it to scale to support the development of large web applications. Both Roda and Sinatra share the following basic concepts:

Some differences are listed below.

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 Roda and Sinatra:

Roda:
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['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].to_i]
    check_access(@artist)
    erb :artist
  end

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

  delete '/artist/:id' do
    @artist = Artist[params[:id].to_i]
    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 toolkit. This is a 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. 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 and multi_run plugins. 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 has lower per-request overhead, Roda is about 3 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 it offers a lot of different tools (in the form of plugins), and you can choose which tools you use to build your application. Some applications (e.g. form-based websites) are best built using some tools, and other applications (e.g. APIs) are best built using different tools. Roda provides a large variety of tools that work together to make it easy to build most web applications.

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

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, methods, and constants available in the route block scope, so that it is unlikely you will run into conflicts. If you've ever tried to use an instance variable named @request inside a Sinatra::Base subclass, you'll appreciate that Roda attempts to avoid polluting the scope.

Immutable

In production use, Roda applications are designed to be frozen (using the standard freeze method), which freezes all internal datastructures (except thead-safe caches used at runtime). This avoids possible thread-safety issues when using Roda.