We are making lots of new apps at TaskRabbit and have many shared components. All of this stuff (gems, best practices, test setup, etc) needs to get into the new project quickly so the team can focus on the business of providing the intended functionality. My first attempt to solve this problem was to create a Rails app template with all that stuff, but switched to a new way that involves a true template application.
Conventional app templates seem to work for a lot of people and Rails Composer looks to be a good option in this space. This method works by running Ruby code that puts down files. This level of indirection is likely a positive thing when projects vary (rspec one time, test-unit the next). However, we’re trying as hard as we can to be consistent across apps, so when I found that this indirection made working with the templates more difficult, I decided that it was not worth it.
The alternative method is pretty simple. I made a Rails app called Warren that was exactly what I wanted to see new projects use. It contained all our our gems for authentication, shared styles, inter-app communication, and developer tools. You could basically log in, log out, and go to a page where you saw who was logged in. It had its own test suite to test these things along with all the normal stuff: yml files, initializers, .rvmrc, .gitigore, ApplicationController, favicon, etc. It was simply a working Rails app.
The only addition is the
app.rake file that I put in the /lib/tasks directory. That provides this functionality:
% rake app:create[new_name]
This will create a new app called NewName in a peer folder to Warren. The full process looks something like this:
localhost:warren brian$ rake app:create[new_name] localhost:warren brian$ cd ../new_name === RVM Message === Do you wish to trust this .rvmrc file? (/Users/brian/taskrabbit/new_name/.rvmrc) y[es], n[o], v[iew], c[ancel]> yes Using /Users/brian/.rvm/gems/ruby-1.9.3-p194 with gemset new_name localhost:new_name brian$ bundle install Fetching gem metadata from http://rubygems.org/...... Installing all-the-things (3.9.2) Your bundle is complete! localhost:new_name brian$ rake db:create db:migrate db:test:prepare localhost:new_name brian$ rails s >> Listening on 0.0.0.0:3000, CTRL+C to stop
And http://localhost:3000 works.
Everything is set up. As a small example, the application.rb and routes.rb have the right stuff in them.
module NewName class Application < Rails::Application # normal stuff end end NewName::Application.routes.draw do # routes end
As well as the database.yml
development: adapter: mysql2 database: new_name_development host: ...
And in application.html.erb
<head> <title>NewName</title> </head>
There are obviously tons of places that are customized with NewName or new_name and the main point is that it’s the same places that were customized with Warren or warren. So there is no magic here. I just copy, find, and replace.
NewName works because Warren works. I know Warren works because I can actually use it (and test it with rspec). With app templates, I was regenerating every time that I made a change to a generator file to see if it worked.
I’m not sure where to check this in. I was going to make a gem/generator to add this one file to your template project, but that goes against the spirit of what I’m talking about. So I just put it here:
You’ll note that my instructions had me
cding into the directory. I tried to do that in the script, too, but I couldn’t get the RVM stuff to work. If anyone wants to take a look at the
rvm_stuff_that_does_not_work method, I’d love some help.