A Ruby Gem: strict_templates
Keeping each layer of your application contained can be a difficult task, especially given the expressiveness of templating languages today.
One thing that has bitten me more than a few times is that, within a Rails application, rendering a template will often have side effects. Specifically, you’ll accidentally head back to the database to pull some more information out to be rendered in the template. This can be a silent killer for performance, and is difficult to track down.
Ideally, rendering a template would be a pure function. Given the flexibility of ERB, this is not always the case:
Depending on the code within your controller, you may incur database requests within your template. Such non-determinism can make performance issues difficult to track down.
If your controller looked like the following:
class BlogsController < ApplicationController
def show
@blog = Blog.find(params[:id])
end
end
Rails will issue the following request within the template to populate the list of blog posts:
SELECT "posts".* FROM "posts" WHERE "posts"."blog_id" = $1
Such behavior is definitely less than ideal.
Enter strict_templates
To solve this problem, I put together a gem called strict_templates
. After installation, it
will error hard whenever the database is accessed from a template. All it takes is a new line in your Gemfile
gem 'strict_templates'
And then including a simple Concern:
class ApplicationController < ActionController::Base
include StrictTemplates::Concern
end
In my previous BlogsController
snippet, a new SQLPerformedWithinTemplateError
will now get raised when the template pops off a database request. To move the database request out of the template and into the controller, we change our code to eagerly load the association:
class BlogsController < ApplicationController
def show
@blog = Blog.includes(:posts).find(params[:id])
end
end
strict_templates
is designed to give some bumpers for teams to keep their models, views, and controllers separate.
This is still a young gem and is a work in progress. I’ve tested it on Rails 5. Your mileage may vary with other versions of Rails.
The problem addressed by this gem could also be avoided entirely by choosing a less expressive templating language. For existing teams on large projects, swapping out a templating language is not an easy task. That’s why this is packaged as a concern: you can slowly mix it into controllers before pulling the dependency up to your ApplicationController
.
View the source on GitHub and let me know what you think on Twitter. Pull Requests welcome!