Rack::Reloader not reloading? There's `to_app` for that
I burnt a bunch of time over the last few days trying to get the
Rack::Reloader middleware to… you know… reload stuff.
TL;DR - call to_app on your Rack::Builder block
I had a Rack app that was being mounted in a parent Rails app. Not wanting to
stop/start Rails whenever I tweaked that little Rack app, I
figured I’d try to get code in the app auto loading after each request. Tried
(and failed) to do it as part of the parent Rails app, so I added use Rack::Reloader in to the middleware stack.
Because this is a mapped rack app, it didn’t have it’s own config.ru to
set up the middleware. Instead, the Rack app that gets mounted in Rails
routes.rb file is an instance of Rack::Builder. Something like this:
class HelloRack
def self.call(env)
[200, {}, ["Hello, World"]]
end
endrequire 'hello_rack'
# WARNING! This doesn't reload... don't copy+paste this as the solution. Read on for the answer!
MyApp = Rack::Builder.new do
use Rack::Reloader
run HelloRack
endAnd then in our Rails routes file, we hook it all up
Rails.application.routes.draw do
...
mount MyApp, at: "/my_app".
...
endThis all looks good, and when we change our code in hello_rack.rb it should
pick it up, right?…
So I try it out. Then… nothing. No reloading… No reason why.
Time to crack open the Rack source code…

Later that day…
It turns out that using the Rack::Builder instance directly as the Rack app
causes it to recreate the middleware stack every time a request is made -
which calls .new on each middleware class defined.
Normally this is fine, but Rack::Reloader keeps state of what source files are loaded, and
the last modified time of them. If the Rack::Reloader instance doesn’t know
about the file, it records it’s current modified time, and moves on without
reloading it - the assumption being that if the app has just started, then the
file doesn’t need to be reloaded - because it’s only just been loaded.
Except it hasn’t just been loaded - Rack::Reloader only thinks it has, as it’s a brand new instance, and this is the first time it’s ever seen the file. By creating a new Rack::Reloader instance for every request, all the state about file modified time gets thrown away, and nothing ever gets reloaded.
The fix? #to_app
The fix for this is really easy. Just call Rack::Builder#to_app and use that instead of using the Rack::Builder instance directly.
#to_app generates the Rack app with all the mappings and middleware in place - which is what we want - but only once. Rack::Builder#call delegates to that every time causing brand new middelware to be instantiated for every request (I’m not sure if that is a bug or a feature…)
If we change our my_app.rb file to look like this: everything works beautifully.
require 'hello_rack'
MyApp = Rack::Builder.new do
use Rack::Reloader
run HelloRack
end.to_app # note `to_app` here - that's the secret sauceNote that you won’t see this problem if you’re just writing a rackup file (i.e. config.ru) and using rackup to execute that - rackup calls Rack::Server.start - which does exactly this under the hood.
Programming - minutes of addictive joy from the flow of writing code, then hours of staring at the screen swearing trying to untangle which library is making everything behave weirdly.