The intention behind this article is to help newcomers to the Cuba library. I’ve used Cuba in production and for my side projects and came to love the expressiveness it offers, or more accurately, the way Cuba harnesses the expressiveness of Ruby.

I’ve noticed a couple (literally, 2) common pitfalls when people start using Cuba and I’m afraid that they might leave the wrong impression of what the library is all about, so I will point them out and how I think they can be solved.

The following list is by no means exhaustive and is subjective to my experience, so feel free to reach out and tell me what you think.

Deeply nested blocks.

Cuba’s on method evaluates different conditions to decide how to route the request, and we also have some helpers like get, post, etc, so many people are inclined to write blocks like this:

Cuba.define do
  on get do
    on "users" do
      on ":id" do |id|
        on "profile" do
          # code code
        end
      end
    end
  end
end

You can see how this becomes uncomfortable. It’s better to pass all the arguments to on, so we can refactor the above to:

Cuba.define do
  on get, "users/:id/profile" do |id|
    # code code
  end
end

One of Cuba’s strongest points is that you can combine and change the matchers for your routes however it feels better for the code inside. We could, for example, add more routes as follows:

Cuba.define do
  on get, "users/:id" do |id|
    user = User[id]

    on "profile" do
      # render profile page
    end

    on "settings" do
      # render settings page
    end
  end
end

And then refactor to:

Cuba.define do
  on "users/:id" do |id|
    user = User[id]

    on get, "profile" do
      # render profile page
    end

    on get, "settings" do
      # render settings page
    end

    on put, "profile" do
      # update profile page
    end

    on put, "settings" do
      # update settings page
    end
  end
end

# or

Cuba.define do
  on "users/:id" do |id|
    user = User[id]

    on "profile" do
      on get do
        # render profile page
      end

      on put do
        # update profile page
      end
    end

    on "settings" do
      on get do
        # render settings page
      end

      on put do
        # update settings page
      end
    end
  end
end

Etc. How you nest on blocks and how you group matchers is a matter of convenience and taste. You’ll get the hang of it after a while, just keep an eye for deeply nested chunks of code.

The param matcher.

I believe that many people are under the impression that Cuba’s only way to access the parameters of a request is through the param matcher, so I often see things like the following:

Cuba.define do
  on "api" do
    on get do
      on "home", param("a"), param("b") do |a, b|
        res.write "Hello World!"
      end
    end
  end
end

Though this works, it may lead to unexpected results. If you request GET /api/home?a=foo you will see a 404. But why?! My route is there, the verb is right, the path is ok, what the hell is going on?!

The problem is that the param matcher requires the request to have that particular parameter. In this case, param("b") is preventing the request above to go through, because it’s missing the b param.

Keep in mind that each on call is saying “match all of the conditions or keep going”.

This example is ok if you want to enforce certain mandatory parameters for a route, but if you simply want the values you can use req.params. Cuba#req is just a Rack::Request (see here), so you can use all the helpers already available, like req.params. Let’s refactor:

Cuba.define do
  on "api" do
    on get, "home" do
      a, b = req.params["a"], req.params["b"]

      res.write "Hello World!"
    end
  end
end

I’m not saying to avoid the param matcher, just keep in mind that when you do use it, you’re filtering out requests without those parameters.

What you think?

Let me know if you find other painpoints when starting with this library. We can chat and extend this post with more tips for everyone taking their first steps with Cuba. You can drop me an email or find me at freenode’s #lesscode channel.