If you’ve spent any time developing a Rails application, you’re likely familiar with the command
rake routes. If you’re not, this little marvel will greatly simplify your dev experience, as it prints out all the available routes in your application and their associated path helpers.
However, as of late it has slowed down considerably. I’m not sure if it was with the changes in Ruby 1.9.2 or Rails 3, but running that command will take a ridiculous amount of time depending on the complexity of your project.
For a base Rails project (I literally just ran
For a moderately-sized Rails project (a client project I’m currently working on):
Nearly 30 seconds just to get a list of the available routes, completely unacceptable.
I set out yesterday with the idea of caching the output of this command in a temporary file, and to print out the cached version as long as the routes file hadn’t been updated since the last cache time. Unfortunately overwriting a Rake task isn’t as easy as it should be, so I accomplished this goal with a simple Ruby script in the project directory:
While this worked, I hated that I was having to adjust to running a new command, and it meant that, if I added new routes, I was still 30 seconds away from seeing what my path helpers were. The ideal was to have the routes cached automatically as soon as I changed my routes file.
Enter Guard and Guard-Annotate. Guard is a Ruby gem that wraps file observer functionality across all platforms, and Guard-Annotate is a module for Guard that hooks into Annotate. Annotate allows you to add the schema of your models as comments to the bottom of the model file, as well as add the output of
rake routes to the bottom of your routes.rb.
With those 2 gems, I could just create a Guardfile in my project directory, and run
guard start alongside my Rails server and have all the annotations performed automatically for me. This is a giant step in the right direction, but I also didn’t want to have to run another daemon (as undoubtedly I’d forget to run it 90% of the time).
Another issue I ran into: Guard-Annotate was simply shelling out to
bundle exec annotate, which in turn was shelling out to
rake routes for the routes portion of the annotation. This meant that I was still 30 seconds away from savings my routes.rb and seeing the updated annotations. Plus there was really no need to load the entire Rails stack again (via Rake) when this is all happening in the context of a running Rails app.
My solution came in a few steps:
Guard.start(:notify => true)in a thread that I spawn inside
development.rb. That way Guard is run automatically when I launch my server in the dev environment.
- I decided to cache the routes to a tempfile instead of annotating
routes.rbsince it can get fairly lengthy and most of the devs I work with are used to using
- Create my own Guard extensions to handle Route caching. I ripped out all the code from the
rake routestask and included it in the extension so no more loading another environment or shelling out unnecessarily.
- Create another extension to handle Model annotations (since that was taking > 15 seconds as well due to shelling out to
- Hack my
Rakefileto bypass loading the environment if my cached routes file exists, and just print out its contents.
The code is a little too long to include here, so instead I’ve provided this pastie with all the relevant bits.
The final outcome: Routes are cached within 0.15 seconds of saving
routes.rb, models updated within 0.25 seconds of updating
schema.rb, faster than I was able to switch to the relevant file or ALT+Tab to my terminal. And rake routes? See for yourself: