Making Rack::Reloader work with Sinatra
According to the Sinatra FAQ, source reloading was taken out of Sinatra in version 0.9.2 due to “excess complexity” (in my opinion, that’s a great idea, because it’s not a feature that needs to be in minimal a web framework like Sinatra). Also, according to the FAQ, Rack::Reloader (included in Rack) can be added to a Sinatra application to do source reloading, so I decided to try it out.
Setting up Rack::Reloader is easy:
require 'sinatra' require 'rack' configure :development do use Rack::Reloader end get "/hello" do "hi!" end
$ ruby hello.rb == Sinatra/0.9.4 has taken the stage on 4567 for development with backup from Thin >> Thin web server (v1.2.4 codename Flaming Astroboy) >> Maximum connections set to 1024 >> Listening on 0.0.0.0:4567, CTRL+C to stop [on another terminal] $ curl http://localhost:4567/hello hi!
If you add another route, you can access it without restarting Sinatra:
get "/goodbye" do "bye!" end
$ curl http://localhost:4567/goodbye bye!
But what happens when you change the contents of a route?
get "/hello" do "greetings!" end
$ curl http://localhost:4567/hello hi!
You still get the old value! What is going on here?
Rack::Reloader simply looks at all files that have been required and, if they have changed on disk, re-requires them. So each Sinatra route is re-evaluated when a reload happens.
However, identical Sinatra routes do NOT override each other. Rather, the first route that is evaluated is used (more precisely, all routes appended to a list and the first matching one is used, so additional identical routes are never run).
We can see this with a simple example:
require 'sinatra' get "/foo" do "foo" end get "/foo" do "bar" end
$ curl http://localhost:4567/foo foo # The result is 'foo', not 'bar'
Clearly, Rack::Reloader is not very useful if you can’t change the contents of any route. The solution is to throw away the old routes when the file is reloaded using
, like so:
configure :development do Sinatra::Application.reset! use Rack::Reloader end
$ curl http://localhost:4567/hello greetings!
A word of caution: you MUST call
very early in your file – before you add any middleware, do any other configuration, or add any routes.
This method has worked well enough for our Sinatra application. However, code reloading is always tricky and is bound to occasionally produce some weird results. If you want to significantly reduce the chances for strange bugs (at the expense of code loading time), try Shotgun or Rerun. Happy reloading!