If you're a dev at product shop, you probably don't need to create a greenfield Rails application all that often. But at an agency like Smashing Boxes, we frequently create products for clients from scratch. We are constantly evolving our best practices, critically reviewing our commonly used gems, and assessing new technologies for our clients. At any given time, we have a robust set of customizations - from replacing MiniTest with RSpec, to more complex elements like setting up Travis for continuous integration tests or Tape for deployment.I was tasked with setting up a Rails app from scratch for the first time about two months into working at Smashing Boxes. I spent hours sorting through company documentation for current best practices, and realized that setting up a customized Rails app manually is both time-consuming and prone to human error. What gems are we currently using? How are we locally linting our code? What other third party services are we integrating? Not to worry - Rails Templates to the rescue!
WHAT IS A RAILS TEMPLATE?
A Rails Template is a solution for standardizing the customizations of a new Rails app for the whole team. The Rails Template API is powered by Thor, which is "a toolkit for building powerful command line interfaces”. When we generate a Rails Template with $ rails new, all the files that are created are thanks to Thor. There is not a lot of documentation out there on how to create a Rails template, but you can check out this Rails guide or this Thor documentation for some guidance.By leveraging both Thor's methods and the Rails application template API, we can use commands to remove, add, and edit files, run gem installations, and even create a git repo. The final product is a template that generates a fully customized Rails app, without having to write any additional code. And to think: We used to do this configuration manually!Check out Boxcar, the open source Rails Template we created.
HOW DOES IT WORK?
The template generator itself, typically called template.rb, is the cornerstone of a Rails Template. This file is a Ruby source file containing methods made available by Thor and the Rails Template API, and run in the context of Rails.
The first modification you may want to make is to your Gemfile. You have two options here:
- Keep the default $ rails new Gemfile but add/remove gems as desired. The downside: the file will be modified to include your desired gems and gem groups, but they won't end up in the exact right formatted place, and to rid yourself of certain gems, use Regex and the helpful thor command gsub_file:# add gems for a particular group
gem_group :production, :staging do
gsub_file "Gemfile", /^gem\s+["']sqlite3["'].*$/,''
- If you like to keep your Gemfile tidy, I found it easier to just completely replace the file. The downside is that you have to keep an eye on Rails to see if best practices of gem versions get updated - but you'll have to maintain anyway (read on below).
Either way, try not to lock in gem versions unless you have to. For example, we locked in a Rails version to avoid security issues in newer versions.
CREATING NEW FILES
You can create new files inside your Rails Template by including a set of templates. For example, to create a new Gemfile, include a file with the code you want generated. Then, render the file using this helper method:def render_file(path)
file = IO.read(path)
To create the file, simply reference your template:file "Gemfile", render_file("path}/files/Gemfile")
You must specify both the path for the file inside your Rails app - in this case the root - and the template file.
EDITING EXISTING FILES
Thor provides a number of helpful methods to allow you to edit files generated by the boilerplate rails new. A few include:
- gsub_file, e.g. to remove Turbolinks auto-generated code:gsub_file 'app/views/layouts/application.html.erb',
/, 'data-turbolinks-track' => true/, ""
- inject_into_file or insert_into_file, e.g. to include CodeClimate config inside your spec_helper file:inside 'spec' do
inject_into_file 'spec_helper.rb', after: "# users commonly want.\n" do
Note that the above code should be properly indented for its insertion into your Rails app. For example, the two inserted lines should not be indented in our app, so they are not indented above.
- append_file, e.g. to append additional files to your .gitignore:append_file '.gitignore' do
# Ignore all secrets and database config files
- remove_dir or remove_file; e.g. to remove existing files or directories:remove_dir "test"
- run commands; e.g. to create a database or install deviserun 'rake db:create'
Keep in mind that the order of operations matters here. For example, you cannot add a gem config to rails_helper until you've installed Rspec. You can't remove sqlite3 from your Gemfile and try to install Devise without creating your postgresql database file.
LET THE DEVELOPER CHOOSE
As our Rails Template is being generated, we can provide questions for a developer using the template to answer, thereby further customizing the end result. We have a few options here as well:
- Yes/No questions:if yes?("Add devise_token_auth? (y/n)")
@devise_auth = true
inject_into_file 'Gemfile', after: "gem 'taperole'\n" do
- Ask a question and then use the developer’s answer. For example, to generate devise using the correct model name, you can use:model_name = ask("What would you like the user model to be called? [user]")
model_name = "user" if model_name.blank?
generate "devise" model_name
Remember to remain flexible. If your template is too specific, you run the risk of your team not using it. For example, some of our team prefers ActiveAdmin, others like Thoughtbot's Administrate, so we chose to eliminate this installation as an auto-function. Try not to enforce those decisions on your team.
You can include simple git commands in your template, including:git :init
git add: "."
git commit: "-a -m 'Initial commit'"
We chose just to init, and leave it to the developer when they want to do their initial commit. Again, important to stay flexible!
To execute your Rails Template, you have a few options:
- Clone and run the template file on your machine. The downside is you'll have to reclone every time you want to use it to ensure no updates were made. For example:$ git clone https://github.com/smashingboxes/boxcar.git
$ rails _4.2.5_ new [app_name] -m boxcar/template.rb -B
- Make your repo public and hit the raw file. Your template is now part of the Open Source community - which is even more awesome!
Note: if you bundle install inside your template generation, you can add the command --skip-bundle or -B to bypass the bundle at the end.
Some developers argue that Rails Templates take longer to create and maintain than simply customizing each app as you create it. But if you follow a few simple best practices, you should be able to save plenty of time. A few tips:
- Get feedback from your team. We provided instructions and survey questions to members of the backend team, and then implemented their requests.
- Make sure every instruction your template follows has a single responsibility. Yes, even the creation of a Template itself should be object oriented. This makes it easier to edit later on.
- MAINTAIN! Schedule time monthly to ensure your template is still up-to-date with Rails best practices. Otherwise, your team will find it outdated and stop using it altogether.
We created the Smashing Boxes template in just a few days, and hopefully this blog post will help you create yours even faster! Rails application templates are a useful tool in helping our team spin up greenfield Rails apps much more quickly and benefit from the consistency of our best practices across projects.Part 2 coming soon on how to test your Rails Template.In the meantime, feel free to create your own template or use ours. Reach out if you have any questions or suggestions. And thanks for reading!