As a long time Rails consultant, every new Rails project I come on to I go through the same dance:
- Does this project have a useful
README.markdown? Maybe with setup instructions?
- No? Just Rail’s default
- Does this project have a database.yml file?
- No? Great. Does this project have a sample database.yml file I can copy and get sane defaults for this project?
- Does this file have a
- Yes? Great. Does this
.ruby-gemsethave sane defaults, or is the gemset named
projector something non-obvious?
- Is there a redis or solr config file I need to edit?
- Do I need to set up machine specific variables or settings anywhere? (For example, in
config/secrets.yml, or even just in
- No? Ok, great, does the app assume I’m running it on OS X with packages installed via Homebrew? (Because I’m usually not. And if I am running your project on bare metal, I prefer Macports.)
- Is there a Procfile?
- Yes? Great. Does that Procfile work for development, or is it a production-only Procfile?
- No Procfile? What services do I need to run to use the app? Redis? Solr? Some kind of worker queue mechanism?
- How do I run all the tests?
- Did something fail because I missed a config setting or some service isn’t running? If true, fix and GOTO 15.
- Awesome, it worked.
- Are there Cucumber or Selenium tests?
- Run those, somehow.
- Fire up Rails server
- When I visit the development version of the site, is there a special account I log in as? Or do I register a user account then browse through the code figuring out how to make myself an admin or registered user or what, then do that via
You could split these questions up into configuration questions and runtime questions. This blog entry will show best practices I try to install on (almost) every Rails project I touch.
Runtime is the easiest, so I’ll tackle it first.
In my mind this is mostly solved by Foreman and a good Procfile, or set of Procfiles.
Setup with Procfiles and Foreman
A Procfile will launch all the services your app needs to run. Maybe you need Solr, Redis, the Rails server, and MongoDB up: you can set up a Procfile to launch and quit those services all together when you’re done.
Heroku uses Procfiles to make sure everything’s up for your app. Heroku’s usually my default, “pre-launch to mid-traction point” hosting choice because of its easy scaling and 2 minute setup process.
Heroku also provides tons of addons, adding features to your app. Sometimes these features are bug reporting or analytics, and sometimes the Heroku addons provide additional services. Two addons that do this are Redis 2 Go, and ElasticSearch from Bonsai.io.
If an app uses Redis, is deployed to Heroku, and uses the Redis 2 Go addon, then the app doesn’t need to have Redis in its
However, when I’m developing the app I need these services running locally.
Foreman takes care of this, reading a Procfile (a default Procfile or one I specify) and firing up all of the services just like Heroku does. Don’t Repeat Yourself in action.
When I’m setting up a project that’s getting deployed to Heroku I create two Procfiles: one
Procfile and one
Procfile.development.sample. (I add
Procfile.development to the
.gitignore file in Git).
Procfile.development.sample is important for two reasons:
- It lists all the services I’ll need to be running as a developer
- It can be used as is, or if a developer has say Mongo already running via a startup script, but the
Procfile.development.sampletries to launch it again, they can copy the file, rename it to
Procfile.development, and remove the line about starting up Mongo.
When I’m not deploying to Heroku I’ll still create a
Procfile.development.sample for ease of starting up servers.
Running all the tests
When I’m setting up a Rails project I write a quick Rake task to run all the test suites. For RSpec + Cucumber it looks something like this:
namespace :test do desc "Run both RSpec test and Cucumber tests" task "all" => ["spec", "cucumber"] end
As a developer on the project – especially a new developer – I just want to type in one command and know I’ve tested all the app there is to test.
When I’m setting up a project I create sample files for each configuration file that might be modified by a developer. So, files with names like:
But this still doesn’t solve our song and dance from the beginning of the blog entry: there’s still a lot to configure, even if I have sample files to copy and rename!
Like any good geek, I’ve replaced this frustration with a small shell script (template). Each project is different, and so each
bin/install.sh will look a little different, but here’s a recent one I made for a non-trivial project:
#!/bin/bash # If you want to go fancier, see some prompts in # <http://stackoverflow.com/questions/226703/how-do-i-prompt-for-input-in-a-linux-shell-script> if [ ! -e Procfile.development ] then cp Procfile.development.sample Procfile.development echo "Do you wish to edit Procfile.development?" select yn in "Yes" "No"; do case $yn in Yes ) $EDITOR Procfile.development; break;; No ) break;; esac done fi if [ ! -e config/database.yml ] then cp config/database.yml.example config/database.yml echo "See the default database.yml?" select yn in "Yes" "No"; do case $yn in Yes ) cat config/database.yml.example; break;; No ) break;; esac done echo "Do you wish to edit this database.yml?" select yn in "Yes" "No"; do case $yn in Yes ) $EDITOR config/database.yml; break;; No ) break;; esac done fi if [ ! -e config/redis.yml ] then cp config/redis.yml.example config/redis.yml echo "Do you wish to edit redis.yml?" select yn in "Yes" "No"; do case $yn in Yes ) $EDITOR config/redis.yml; break;; No ) break;; esac done fi if [ ! -e .ruby-gemset ] then echo "Do you wish to create a .ruby-gemset file and edit it?" select yn in "Yes" "No"; do case $yn in Yes ) cp .ruby-gemset.copy .ruby-gemset; $EDITOR .ruby-gemset; break;; No ) break;; esac done fi if [ ! -e .env ] then cp .env.sample .env echo "Do you wish to edit .env?" select yn in "Yes" "No"; do case $yn in Yes ) $EDITOR .env; break;; No ) break;; esac done fi
It’s not the prettiest example of a shell script ever, but it’s easy and fast to modify and should run in all shells (I avoided fancy zsh tricks, even though zsh is my primary shell).
Run this and it will guide you through all the files you need to copy, asking you if you want to edit the config file when it’s in place. For opinionated files, like
.ruby-gemset, the script will ask what you want to do.
Each of my sample files contain sane default values, which should work for the developer, but they don’t have to.
Thoughtbot has some initial thoughts on project setup too (they call it bin/setup), but they take a slightly different approach (and automatically set up different things). You could use there shell script along with mine if you wished.
My Ultimate New-To-This-Project Developer Experience
Since we’re talking about developer automation and project setup, I’d like to share my own dream experience:
- checkout code from Git repo
- “Oh, look, a Vagrantfile”
$ vagrant up
- (15 minute coffee break while Vagrant boots up box and provisions it all for me, including Ruby setup)
- (During 15 minute coffee break, glance through the project’s
README.markdown, see mention of running
$ vagrant ssh
$ cd $PROJECT
- (Answers questions in install.sh and gets settings tweaked for this VM)
$ rake db:setup
$ foreman start -f Procfile.development
$ rake test:allin a new tab. All tests pass.
Low barriers to entry, very automated project setup – help me get it set up right the first time. Help me be more productive faster.
You notice I called
rake db:setup which creates a new database, loads the schema, and loads
db/seeds.rb. Replace this step with “run migrations from 0” and “load in initial bootstrap data” if you wish. I’m usually in the “migrate from 0” camp, but usually find myself in a minority.
Anyway, If you compare the top list with this list you’ll see that the steps followed are very different. The first set of steps is hesitant: does this thing exist? Do I need to do X? The second set of setups is confident: The machine set this up for me and so hopefully everything is right.
Here’s the best practices to take away from this blog entry:
- Consider creating a Vagrant setup for your project, including provisioning.
- Documentation in the
README.markdownwith basic “how to setup this project” instructions.
- Sample config files with values that are opinionated, but since they’re copied into place, easily changable.
bin/install.shscript like mine, or
bin/setupscript, like Thoughtbot’s.
Procfilejust for developers
- A way to run all the tests, easily
- Load just enough sample data on a developer’s machine to allow them to get to major sections of your app without having to learn how to use it on day 1.
The easier it is for a developer to get up to speed on your project, the faster they can start getting real work done!