In this post I’ll talk about the benefits of generating your Ruby on Rails routes more quickly, and how to implement it.
Background
Rails looks at the ./config/routes.rb
file to generate the mappings between URLs in your application and what controller actions those URLs correspond to. In your app, you can say redirect_to new_post_url
and Rails knows that you mean http://www.panozzaj.com/posts/new
(for example.)
The problem: Slow Routes Generation
Generating routes in a medium-sized Rails application could take upwards of ten seconds. This is a bit slow for feedback. Sometimes you are just exploring the routes in an application, and it could take a while to get better information. Instead of tens of seconds, why not get it down to a few seconds, or – even better – milliseconds.
Speeding Up Rails Route Generation
I’ll take an app that I am currently working on that has only a hundred or so routes defined and a bunch of gems installed. Before any changes, rake routes
takes about seven seconds to generate.
One method we can use to speed up our routes query is to use Zeus to speed up the output of rake routes
. This is as simple as installing Zeus, running a Zeus server, and running zeus rake routes
. This change alone reduced my time for the Zeus command to about 1.25 seconds, which is about a five-fold reduction.
A further step would be saving the output of route generation to a file and looking at that directly. Once this is done, lookups are essentially instantaneous (a few milliseconds to read the file and grep through it.) This can be tricky if you are actively modifying your routes, since you might be looking at a stale route output.
routes
Script
Here is a script that handles all of this for you, including using a cache file and regenerating it if needed:
#!/bin/sh
# regenerate tmp/routes if it is not already generated or it is older than the routes config file
if [ ! -f tmp/routes ] || [ tmp/routes -ot config/routes.rb ]; then
if [ ! -f tmp/routes ]; then
generating='Generating'
else
generating='Regenerating'
fi
[ -S .zeus.sock ] && z="zeus"
if [ -n "$z" ]; then
echo "$generating routes - Running under zeus."
else
echo "$generating routes - No Zeus enviroment detected."
fi
$z rake routes > tmp/routes
fi
echo "Using generated routes file."
query=$1
if [ $query ]; then
grep $query tmp/routes
else
cat tmp/routes
fi
Basically it will first look at ./tmp/routes
to see if there is cached output there. If that file exists and it is newer than the timestamp of the ./config/routes.rb
file, then use the cached version (since the routes probably haven’t changed since we last generated.) I like storing it in the ./tmp
directory since it is a generated file that should be ignored from source control anyway.
If there is no cache file or it is old, look to see if zeus
is running, and if it is, use it since it is faster and should give us the same results. Worst case, use the standard rake
command. Regardless, save the output in the cache file for future runs.
I often do a search on the routes output, so if you pass in a parameter (example: routes posts
), the script does a search on the routes output. Otherwise it just prints it out for reading or piping to another command.
A Faster Way to Generate Routes
This post covered a faster way to generate routes. I found this handy enough to write up and used it to great effect when working with a larger application that needed a conversion from Rails 2 style routes to Rails 3 style routes.
I hope that this helps you work more quickly with your routes, and let me know about any other tips for faster generation that you have used!
Kyle Shipley notes:
@panozzaj You can also visit /rails/info/routes in Rails 4, or just type in a garbage path and look at the new error page.
— Kyle Shipley (@kyleashipley) May 16, 2014