Last Friday, Makis offered to pair on whatever I was working on. I just started working a bit with Rack. I did a bit of the pieces I thought I needed and I was proud of the path I was taking. It was clever, too clever. To the point of re-implementing some of the functionality provided by Rack.

For example the router. Only because I didn’t like the API; it seemed complex and rigid to me:

app = Rack::Builder.new do
  map "/route" do
    run lambda do |env|
      [200, {'Content-Type' => 'text/plain'}, ['OK']]
    end
  end
end

run app

You can see that the way of doing routing is by using Rack::Builder. You have to specify the full path with map, you cannot match the requests by other characteristics. And you cannot filter by GET, POST, etc. there. So I did my own implementation of a router:

class Router
  UNDEFINED_DEFAULT_HANDLER = lambda do |env|
    raise 'No default handler has been defined!'
  end

  def initialize(routes, default_handler = UNDEFINED_DEFAULT_HANDLER)
    self.routes = routes
    self.default_handler = default_handler
  end

  def call(environment)
    routes.each do |selector, handler|
      return handler.call(environment) if selector.call(environment)
    end
    default_handler.call(environment)
  end

  private
  attr_accessor :routes, :default_handler
end

The same client code would be like:

def path_starts_with?(partial_path)
  lambda { |env| env["REQUEST_PATH"].start_with?(partial_path)}
end

app = Router.new({
  path_starts_with?("/route") => lambda do |env|
    [200, {'Content-Type' => 'text/plain'}, ['OK']]
  end
})

run app

Very similar to the Rack::Builder syntax. But the Router I implemented is much more flexible and it does not depend on any class of Rack.

I thought it was simple: the logic of the router (call method) is only 4 lines long! But it took a bit more than I expected for Makis and Christoph to see what was happening there. I remember what Christoph said:

The router receives the routes. But wait... It is a hash and its keys are lambdas!!?

That was something unexpected. Simple code should be easy to maintain. And to be easy to maintain, has to be obvious. The Router is not obvious. It is expected for anyone looking to a Rack project to see Rack::Builder, not a custom Router. And even worse, it is not expected to see lambdas as keys in a Hash.

Makis also like another quote from Ward Cunningham:

Simplicity is the shortest path to a solution.

Writing those custom classes is not the shortest path.