Ruby Microframeworks Round-up

ruby_microframeworks

Ask anybody to name a Ruby frameworks and most people will answer ‘Ruby on Rails’. If you ask for a smaller framework, they’ll probably answer ‘Sinatra’ (which technically isn’t a framework it’s a Domain Specific Language, but let’s not get too technical). Rails and Sinatra are the darlings of the Ruby web development world. Together, they seem to have all the bases covered, which I concluded in this post. Given the success of these two ‘frameworks’, is there a need for any others?

Well, it turns out that there are actually quite a few Ruby microframeworks kicking around (technically, some of these are DSLs too, but the term microframework seems to have stuck). Here are six projects that I found on GitHub:

My first reaction was that they seemed to be occupying a very similar space to Sinatra, which I thought had the ‘microframework’ area of Ruby web development covered. I wondered why all these microframeworks exist. What’s the point?

So, in an effort to find out, I got in touch with the creators of these microframeworks and asked them the following questions:

  • What inspired you to create your own Ruby framework?
  • How did you come up with the name?
  • Sinatra is a very minimalist Ruby framework that already has a lot of traction. What makes your framework different from Sinatra?
  • How come you didn’t contribute the extra features that your framework offers to Sinatra, either as a patch or extension?
  • What have you gained from writing your own framework? Has it improved your Ruby skills?
  • What does the future hold for your framework?

I also asked them to provide a couple of code snippets that demonstrated how their microframework worked.

After reading their replies, it’s clear that many of the creators thought that Sinatra wasn’t small or focused enough. All of these projects have a much smaller code base than Sinatra (and they are microscopic compared to Rails!) Many of them also report being faster than Sinatra in benchmark tests. In a number of cases, this has been achieved by sticking closer to Rack and avoiding the syntactic sugar provided by many of Sinatra’s helpers (such as using Rack’s render method directly). In other cases (such as Brooklyn) it was by keeping a much narrower scope than Sinatra and avoiding legacy support. Some of them, particularly Cuba and Scorched were driven by the desire to use a different methodolgy than Sinatra with regard to route handling in particular. It was also interesting to hear many of the developers report that their knowledge of Rack improved as a result of developing these projects.

Here are the replies:

Question 1 – What inspired you to create your own Ruby framework?

Patricio Mac Adden, creator of Hobbit:

I contributed to Sinatra since last year. That made me dig into Rack and I wanted to experiment doing a framework as smaller as possible and as closer to Rack as possible.

So these were my guidelines:

  • Hobbit must be fast.
  • Hobbit must have in its core just the basic stuff: define routes.
  • Hobbit must be extensible with standard ruby classes and modules. These extensions (i.e. rendering views, handling sessions, filters, exception handling) will not be loaded by default.
  • Zero configuration

Andrei Lisnic, creator of New York New York:

I think the main reason I wrote it is to learn. I am a web developer, and I wanted to know how all the Rack magic works.

Luis Lavena, creator of Brooklyn:

For a project at our company, we had to output a big histogram as JSON (128K). We noticed that even with high level of caching, the number of requests our application was able to serve were not enough to what we needed.

We discovered that Sinatra was taking a big performance chunk, even running on production mode.

As the application single purpose was to serve this JSON, we started to look into what could be the issue, and stripped down pretty much everything on top of Rack.

That’s how Brooklyn was born, to serve that single purpose. I called it a web tool and not a microframework, simply because it doesn’t handle a bunch of things like Sinatra could do. Is just a prettier face on top of Rack.

Tom Wardrop, creator of Scorched:

As much as I like Sinatra, there were some limitations I found frustrating and unnecessary when using it in the real-world. These were mainly issues of code management and scalability. You either had to put all your routes for your entire application in a single “controller”, in which they shared helpers, filters, configuration, and so on, or otherwise try and break your application out into multiple mini Sinatra applications, and mount them under different mappings using Rack. Neither solution was elegant. I thought Padrino might address a few of those issues, but found it equally, if not more, frustrating.

Given that I had a pretty good idea of what I wanted and because it was simple enough (maybe not in hindsight), I decided to roll my own, knowing that I’d not only be scratching my itch, but the itches of many other developers who inadvertantly run into issues of practicaility when using Sinatra.

Michel Martens, creator of Cuba:

I discovered a small script called Rum, created by the author of Rack, Christian Neukirchen. It had what in my opinion are two great ideas: first, you can create codepaths in your application based on any aspect of the request, not only on the HTTP method and the request URI. That is very powerful because, for example, you may want to take a different route depending on whether or not a visitor is authenticated, something you can’t guess by looking at the HTTP method and URI alone. And that’s just an example, because the on construction in Cuba behaves mostly as a control flow keyword: on default ... is as valid as on 2 + 2 ..., so you can use any condition.

The second idea is the fact that by nesting on constructions, the routing you build behaves as a deterministic finite automaton. It means, for example, that once you have matched part of the request, you don’t need to match it again. That makes it very fast in real world scenarios.

There are a couple of open source websites built with Cuba such as Redis and the Punchgirls Jobs Board, and there are also there some slides explaining some of the basic concepts.

Guillermo Iguaran, creator of Nancy:

I was inspired by some articles, talks and books about Simplicity of authors like Richard Gabriel, Rich Hickey and John Maeda recommended by Michel Martens and some of the other members of #rubysur community. The work of Michel Martens (@soveran) and Cyril David (@cyx) in their micro framework Cuba was and continues being a big inspiration.

Question 2 – How did you come up with the name?

Patricio Mac Adden, creator of Hobbit:

I first named it “bonsai”, but a “bonsai” rubygem already existed with that name. Naming things is really hard, but fortunately, talking with a friend of mine I came up with the name Hobbit.

Andrei Lisnic, creator of New York New York:

Initially, NYNY was positioned as a small Sinatra clone, therefore the name (New York, New York is a song by Frank Sinatra). At this point however, NYNY has a different set of features.

Luis Lavena, creator of Brooklyn:

The company I work for (AREA 17), have an office in New York, more precisely, in Brooklyn. There is a trend of Sinatra-like framworks to be on the topic of Frank Sinatra, NYNY, then why not another borough of New York? ;-)

Tom Wardrop, creator of Scorched:

It’s a play on the DRY acronym. Much of Scorched is designed to provide ample opportunity for developers to reduce repetition.

Michel Martens, creator of Cuba:

As it is inspired by Rum, I though of Cuba instantly. And Cuba is small and efficient, which are traits shared by the library.

Guillermo Iguaran, creator of Nancy:

I consider Nancy like a ‘child’ of Sinatra, that is why I chose to Nancy as the name, it’s a reference to Nancy Sinatra, the daughter of Frank Sinatra. Besides that, I am a big fan of the music of Nancy Sinatra.

Question 3 – What makes your framework different from Sinatra?

Patricio Mac Adden, creator of Hobbit:

It’s faster and it allocates less memory than Sinatra. If you want to see some benchmarks, I recommend you to see https://github.com/luislavena/bench-micro by Luis Lavena.

Andrei Lisnic, creator of New York New York:

If Sinatra is very minimalist, then I’d say that NYNY is very, VERY minimalist. Sinatra’s source code has ~2k lines. In contrast, NYNY has only 300. At the same time, there’s not a lot of stuff that NYNY lacks and Sinatra provides. NYNY is also ~25% faster and has a more powerful router (it uses Rails’ router), but lacks a lot of convenience helpers that Sinatra has. (like erb :something, which is sugar on top of render)

Luis Lavena, creator of Brooklyn:

Sinatra implemented a bunch of things that I believe are bad practices. Thankfully, the newer versions encourage the usage of Sinatra::Base classes to build your applications.

It also does a bunch of things to workaround previous versions of Rack, or deal with particular scenarios on some type of requests, instead of leveraging the Rack middleware approach to build those.

What Brooklyn does is not even close to what Sinatra provides, and it will never be, it was born to serve some type of content (JSON) and not thinking too hard about views or other things.

It is aimed to be mounted and composed with other Rack middleware into a bigger application and not to be used standalone to create software.

Tom Wardrop, creator of Scorched:

Scorched is very similar to Sinatra from a DSL perspective, so at first glance people wonder why I seem to have just rewritten Sinatra. The main difference from Sinatra is the introduction of the controller concept. These controllers can be composed and nested in many creative ways, and allow you to scope helpers, filters, error handlers, conditions, configuration, etc, to related groups of routes.

Another difference is that Scorched attempts to be even simpler and more predictable than Sinatra, and if it’s possible, be even less opinionated. There’s a few things in Sinatra that can surprise you. Sinatra sometimes does a little more than you might expect. I’d like to think I’ve achieved these goals as a result of a more thoughtful design, resisting the temptation to add too many smarts, and by not providing functionality that overlaps with Rack. Sinatra like most frameworks, provides a lot of helpers that abstract away Rack. With Scorched, I want to expose developers to Rack as much as reasonably possible. The idea being that knowledge of Rack is portable between frameworks, where as helpers and DSL’s are not. If someone is already familiar with Rack, that experience can be leveraged to lower the learning curve for Scorched, and vice versa.

Michel Martens, creator of Cuba:

I used Sinatra a lot in the past, and even created a now defunct framework on top of it called Monk. But what made me move to Cuba was the flexibility it provides with very little overhead. I now find Sinatra too centered around paths, and with Cuba I can build many sub-apps that can be mounted anywhere and based on any criteria. As a by-product, the nested control flow idea and the fact that it has less code and less responsibilities make it more performant, and probably those reasons make it easier to teach and understand.

Guillermo Iguaran, creator of Nancy:

I wouldn’t consider Sinatra to be a ‘very minimalist’ framework, you need 22 pages to print the full code in paper and even the main maintainer can get lost digging in the code!! In contrast you can print full Nancy code in just 1 page.

Nancy is very small in comparison with Sinatra and other minimalist frameworks and is 2x faster than Sinatra according to benchmarks.

Question 4 – How come you didn’t contribute the extra features that your framework offers to Sinatra?

Patricio Mac Adden, creator of Hobbit:

I already contribute to Sinatra. Most of Hobbit features are inspired in Sinatra, but the purpose of Hobbit is different and most of them are not compatible with Sinatra.

Andrei Lisnic, creator of New York New York:

All the features that NYNY has are already in Sinatra (besides the Rails’ router which NYNY uses), either in core in in contrib packages. I just implemented them in a more simpler way. (again, the goal was to learn by reinventing the wheel)

Luis Lavena, creator of Brooklyn:

Sinatra is a single-file with ~1700 SLoC that defines several classes and connects them together. Attempts to remove some of the legacy stuff Sinatra does or change a bit these things might not be accepted due the size of such changes.

Tom Wardrop, creator of Scorched:

Because the main features of Scorched are architectural. Applying such changes to Sinatra would break backwards compatibility, though I like to imagine Scorched as being an appropriate starting point for Sinatra 2.0.

Michel Martens, creator of Cuba:

Sinatra tries to do one thing and do it well, and it takes a very specific approach. The approach it takes is very good and Cuba provides a different approach. I’m against the idea of adding features to solve different problems with the same tool. Whatever Cuba does could be cooked into Sinatra and vise-versa, but the result would be a more complex and fragile tool.

Guillermo Iguaran, creator of Nancy:

Nancy doesn’t have any extra features that Sinatra doesn’t offer already, Nancy has a ’subset’ of Sinatra features.

Question 5 – What have you gained from writing your own framework? Has it improved your Ruby skills?

Patricio Mac Adden, creator of Hobbit:

Yes, absolutely. Writing your own libraries and contributing to other people’s libraries definitely improves your skills. In my case, I learned a lot from Luis Lavena, who reviewed Hobbit and pointed out a lot of things to me.

Andrei Lisnic, creator of New York New York:

A lot of knowledge of inner workings of Rack, and a little gem I can confidently show as a example of my programming skills. (I’ve put a lot of effort and soul into it)

Luis Lavena, creator of Brooklyn:

I learned a lot about Rack framework and middleware composition. I learned about class variable inheritance and reduction of object allocation. Last but not least, I learned about thread safety caused by reuse of same object instance to serve multiple requests.

Tom Wardrop, creator of Scorched:

Firstly, it makes you appreciate the amount of work one must put into even relatively simple frameworks and libraries. I’m also, now, much more familiar with Rack and it’s limitations. It’s certainly sparked my interest in a potential Rack 2.0. I suppose my Ruby skills have also inadvertantly been refined. It doesn’t really matter what project I’m working on, I always like to explore as many creative solutions as possible trying to find that perfect solution, bumping into the limitations of the language, of which there are so few in Ruby, hence why I love it.

Michel Martens, creator of Cuba:

I gained the chance to experiment with Rum’s ideas. The first version of Cuba was Rum with some fixes and a few additions to pack it into a gem. Later on some coworkers started using it and liked it, and then some people started doing client work with it, which proved that other people were also interested in Rum’s approach.

Writing Cuba wasn’t challenging, because the ideas behind it are very simple. I don’t think writing Cuba or reading its source code could really improve someone’s Ruby skills.

Guillermo Iguaran, creator of Nancy:

I have improved my Ruby skills significantly. Thanks to the project I learned a lot about Rack, this helped me a lot to understand bigger frameworks like Rails and become a member of Rails core team.

Question 6 – What does the future hold for your framework?

Patricio Mac Adden, creator of Hobbit:

I really don’t know. I hope people use it and enjoy it as much as I did developing it and using it.

I’ve also written Hat, an application template for Hobbit: . It’s kind of a more complete framework on top of Hobbit.

Andrei Lisnic, creator of New York New York:

At the moment, I feel that NYNY is pretty complete. I use it both at work and in my free time for hobby projects. The philosophy is that NYNY should stay simple and understandable. Anything fancy that comes to mind I implement as an extension to NYNY. (like sprockets integration)

Luis Lavena, creator of Brooklyn:

I think it will not grow beyond what is now. It served it’s purpose and does what was aimed to. It might get a bit better on some performance paths (attempting to reduce object allocation and URL matching techniques)

Tom Wardrop, creator of Scorched:

There’s no reason it can’t reach the popularity of Sinatra, though it would need a significant amount of documentation tailored to those less experienced with Ruby and other Ruby web frameworks. I would find it hard to imagine someone could like Sinatra but not Scorched. At the moment Scorched is targeted at more experienced Ruby web developers; those already familiar with Rack and Sinatra.

As for future development. I hope to release a v1.0 soon. I still need to put the framework through it’s paces in some real-world applications to ensure there are no obvious things I’ve missed. A contrib library is also on the cards, though probably not until a v1.0 release. I’ll likely just bundle any contrib-esque extras into the core for the moment, such as my simplifed implementation of content_for for ERB templates which I had to create for a project I’m porting from Padrino to Scorched.

Michel Martens, creator of Cuba:

It’s a small tool and it’s stable, there’s no work ahead of it right now. We are at version 3.1.1, which came out about a month ago, but there was only one tiny change from the previous version. It didn’t have any major changes in about two years, and that’s a sign that it’s working fine.

Guillermo Iguaran, creator of Nancy:

The framework has gained some attention and traction recently, there are some interest in consider it as one of the projects for Rails Girls Summer of Code this year, hopefully I’ll be working mentoring to a group of students improving it during this summer!!!

Code Samples

I wanted to see some examples of using these microframeworks, so I asked each of the creators to provide two code snippets that achieved the same results as a Sinatra app I had written.

Snippet 1 – Howdy

The first code sample was a simple ‘Hello World’ Program that grabbed a named parameter from the URL. I used a helper and inline views in Sinatra. It was interesting to note that only one of these frameworks (NYNY) provided inline views and all of them used a class-based style of application that is similar to Sinatra’s modular style. There is certainly a lot of overlap with Sinatra and many of the microframeworks used similar ideas in some cases. For example, both Hobbit and Scorched treat all class methods as helpers:

require 'sinatra'

helpers do
  def greeting name
    "Howdy #{name}"
  end
end

get '/:name' do
  @message = greeting params[:name]
  erb :hello
end

__END__
@@hello
<h1><%= @message %></h1>

Hobbit

require 'hobbit'
require 'hobbit/contrib'

class App < Hobbit::Base
  include Hobbit::Render

  # all methods in this class are helpers!
  def greeting(name)
    "Howdy #{name}"
  end

  get '/:name' do
    @message = greeting request.params[:name]
    render 'hello', {}, layout: false
  end
end

run App.new

views/hello.erb:

<h1><%= @message %></h1>

NYNY

require 'nyny'
TEMPLATE = Tilt::ERBTemplate.new { DATA.read }

class App < NYNY::App
  helpers do
    def greeting name
      "Howdy #{name}"
    end
  end

  get '/:name' do
    @message = greeting params[:name]
    TEMPLATE.render(self)
  end
end

App.run!

__END__
<h1><%= @message %></h1>

Brooklyn

require "brooklyn"

class HelloApp < Brooklyn::App
  helpers do
    def greeting(name)
      "Howdy #{name}"
    end
  end

  get "/:name" do
    "<h1>#{greeting(params[:name])}</h1>"
  end
end

Scorched

require 'scorched'

class App < Scorched::Controller
  get '/:name' do
    @message = greeting(captures[:name])
    render :hello
  end

  def greeting(name)
    "Howdy #{name}"
  end
end

views/hello.erb:

<h1><%= @message %></h1>

Cuba

require "cuba"
require "hache"

def greeting(name)
  "Howdy #{Hache.h(name)}"
end

Cuba.define do
  on get, :name do |name|
    res.write sprintf('<h1>%s</h1>', greeting(name))
  end
end

Nancy

require 'nancy'
require 'nancy/render'

module Helpers
  def greeting name
    "Howdy #{name}"
  end
end

class App < Nancy::Base
  include Nancy::Render
  include Helpers

  get '/:name' do
    @message = greeting params['name']
    render 'hello.erb'
  end
end

hello.erb

<h1><%= @message %></h1>

Snippet 2 – To Do List

The second code snippet was an example of a RESTful To Do List app that used a database backend (I used DataMapper, which admittedly is getting a bit old, but it works!). Here was my example in Sinatra:

require 'sinatra'
require 'datamapper-core'

DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/development.db")
class Task
  include DataMapper::Resource
  property :id, Serial
  property :name, String, :required => true
  property :completed_at, DateTime
end

get '/tasks' do
  @tasks = Task.all
  erb :index


get '/task/:id' do
  @task = Task.get(params[:id])
  erb :task
end

post '/task' do

  Task.create(:name => params[:name])
  redirect '/tasks'

end

put '/task/:id' do
 task = Task.get(params[:id])
 task.completed_at = params[:completed] ? Time.now : nil task.name = (params[:name])

  redirect '/tasks'

end

delete '/task/:id' do
  Task.get(params[:id]).destroy
  redirect '/tasks' 
end

And here are the examples from the microframeworks. It was interesting to note that some of the authors used different database adapters. Similarly, a number of them also used extra gems or plugins to achieve some of the results that Sinatra achieves out of the box, which fits with their minimalist ambitions. Some of these frameworks even have their own ‘contrib’ gems that add extra functionality if needed (something that Sinatra also does).

Hobbit

require 'hobbit'
require 'hobbit/contrib'
require 'datamapper-core'

DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/development.db")

class Task
  include DataMapper::Resource
  property :id, Serial
  property :name, String, :required => true
  property :completed_at, DateTime
end

class App < Hobbit::Base
  include Hobbit::Render

  use Rack::MethodOverride

  get '/tasks' do
    @tasks = Task.all
    render 'index'
  end

  get '/task/:id' do
    @task = Task.get(request.params[:id])
    render 'task'
  end

  post '/task' do
    Task.create(:name => request.params[:name])
    redirect '/tasks'
  end

  put '/task/:id' do
    task = Task.get(request.params[:id])
    task.completed_at = request.params[:completed] ? Time.now : nil task.name = (request.params[:name])
    response.redirect '/tasks'
  end

  delete '/task/:id' do
    Task.get(request.params[:id]).destroy
    response.redirect '/tasks'
  end
end

run App.new

NYNY

require 'nyny'
require 'data_mapper'

DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/development.db")
class Task
  include DataMapper::Resource
  property :id, Serial
  property :name, String, :required => true
  property :completed_at, DateTime
end

class App < NYNY::App
  get '/tasks' do
    @tasks = Task.all
    render 'index.erb'
  end

  get '/task/:id' do
    @task = Task.get(params[:id])
    render 'index.erb'
  end

  post '/task' do
    Task.create(:name => params[:name])
    redirect '/tasks'
  end

  put '/task/:id' do
    task = Task.get(params[:id])
    task.completed_at = params[:completed] ? Time.now : nil
    task.name = (params[:name])
    task.save!
    redirect '/tasks'
  end

  delete '/task/:id' do
    Task.get(params[:id]).destroy
    redirect '/tasks'
  end
end

App.run!

Brooklyn

Luis Lavena said that Brooklyn was not designed to build an application like this and suggested using Hobbit.

Scorched

require 'scorched'
require 'sequel'

Sequel::Model.db = Sequel.sqlite("development.db")

class Tasks < Sequel::Model
  db.create_table? :tasks do
    primary_key :id
    String :name
    DateTime :completed_at
  end
end

class App < Scorched::Controller
  controller '/tasks' do
    get '/' do
      @tasks = Tasks.all
      render :index
    end

    get '/:id' do
      render :task, locals: {task: task}
    end

    post '/' do
      id = Tasks.insert(:name => request.POST['name'])
      redirect "/tasks/#{id}" 
    end

    put '/:id' do
      task.update(completed_at: (request.POST['completed'] ? Time.now : nil), name: request.POST['name'])
      redirect "/tasks/#{captures[:id]}" 
    end

    delete '/:id' do
      task.delete
      redirect "/tasks" 
    end

    def task(id = captures[:id])
      Tasks[id] || halt(404)
    end
  end
end

Cuba

require "cuba"
require "cuba/contrib"
require "mote"
require "ohm"

# Plugins
Cuba.plugin Cuba::Mote
Cuba.plugin Cuba::TextHelpers

# Middlewares
Cuba.use Rack::MethodOverride

# Ohm is an object-hash mapper for Redis.
class Task < Ohm::Model
  attribute :name
  attribute :completed_at
end

Cuba.define do

  # Here we match the path "tasks" no matter which HTTP method is
  # used. Later on we will inspect the method to decide what to do.
  on "tasks" do

    # This matcher (a symbol) captures a segment in the requested
    # path. In our case, it will match the id of the task.
    on :id do |id|

      # As the `on` blocks are closures, we can fetch the task here
      # and use it in the subsequent blocks.
      task = Task[id]

      # This block will match only if we found a task.
      on task do

        # If the request method was a GET...
        on get do
          res.write view("task", task: task)
        end

        # If the request method was a DELETE...
        on delete do
          task.delete

          res.redirect "/tasks"
        end

        # If the request method was a PUT...
        on put do

          # Check if we got the `completed` parameter.
          on param(:completed) do
            task.update(completed_at: Time.now)
            res.redirect "/tasks"
          end

          # Check if we got the `name` parameter.
          on param(:name) do |name|
            task.update(name: name)
            res.redirect "/tasks"
          end

          # If no block matched so far, it was a bad request.
          on default do
            res.status = 400
            res.write "Bad request"
          end
        end
      end

      # If we didn't find a task, we return 404.
      on default do
        res.status = 404
        res.write "Not Found"
      end
    end

    # This matches "/" from the place the route is mounted. In this
    # case, as this block is nested inside "tasks", it will match if
    # nothing else comes after "/tasks/".
    on root do

      # If the request method was a GET...
      on get do
        res.write view("tasks", tasks: Task.all)
      end

      # If the request method was a POST...
      on post do |name|
        Task.create(name: req[:name])

        res.redirect "/tasks"
      end

      # If no other block matched...
      on default do
        res.status = 404
        res.write "Not Found"
      end
    end
  end

  # This matches "/".
  on root do
    res.write view("home")
  end
end

Nancy

require 'nancy'
require 'nancy/render'
require 'data_mapper'

DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/development.db")

class Task
  include DataMapper::Resource
  property :id, Serial
  property :name, String, required: true
  property :completed_at, DateTime
end

DataMapper.finalize

class TaskApp < Nancy::Base
  include Nancy::Render
  use Rack::MethodOverride
  use Rack::Static, :urls => ["/javascripts", "/stylesheets"], root: 'public'

  get '/' do
    redirect '/tasks'
  end

  get '/tasks' do
    @tasks = Task.all
    render('views/layout.erb'){ render 'views/index.erb' }
  end

  get '/task/:id' do
    @task = Task.get(params['id'])
    render('views/layout.erb'){ render('views/task.erb', { task: @task }) }
  end

  post '/task' do
    Task.create(name: params['task']['name'])
    redirect '/tasks'
  end

  put '/task/:id' do
    task = Task.get(params['id'])
    task.completed_at = params['task']['completed'] ? Time.now : nil
    task.name = params['task']['name'] if params['task']['name']
    task.save
    redirect '/tasks'
  end

  delete '/task/:id' do
    Task.get(params['id']).destroy
    redirect '/tasks'
  end
end

There’s More!

While I was writing this article, I found even more Ruby microframeworks that might be worth looking into – here they are:

That’s All Folks

That concludes my whirlwind look at Ruby microframworks, I hope you found it interesting. Have you heard of any of these before, or even used them? Do you know of any that I’ve missed?

Have you been tempted to give some of them a try? Maybe you could have a go at rewriting a project using one of them? Did you like the sound of any of the conventions some of them used? With Sinatra 2.0 on the horizon, maybe it could borrow some of the concepts used in them.

Do you think that Ruby needs so many ‘frameworks’? Or have you even been inspired to have a go at writing your own Ruby microframework (or have you done so already)?

As usual, leave your answers in the comments below.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

No Reader comments

Comments on this post are closed.