Don’t Repeat Yourself. Use Rails Templates.

Rails is a productive environment -- once you install gems, plugins, and initializers, configure routes, generate code, migrate databases, and tune your environments. Rails templates can automate all of that busy work.

Few things are as exhilarating as typing rails project. With that one teeny command, you can launch an Internet startup. Well. Almost. Of course, you’ll need to write some code, push some pixels around, and power on a server or two. But that’s nothing too extraordinary. Go ahead and call Lamborghini of San Francisco now.

Oh, wait. Hold on. Before you can spend your newfound millions, you must first configure your Rails project: install plug-ins, download (and perhaps unpack and freeze) gems, craft routes, add initializers, run generators, install your favorite library of rake tasks, and set options in each environment file. Sorry, the Gallardo must wait. For a while.

I exaggerate a little. Initial setup of Rails isn’t all that time consuming, but it can be laborious and frustrating because you repeat many little tasks over and over again for each new Web application. I don’t want to admit how many times I’ve had to relearn the restful-authentication plug-in. Twice was once too many times.

One alternative to reinventing the wheel is a kit, or a base Rails application, that provides a well-configured collection of software out-of-the-box. With a kit, you download and go. Bort is one popular kit, and you can find more than seventy base applications on Open Source Rails. Bort provides the essentials to launch a site, including login credentials, pagination, and asset packaging. Meanwhile, the kits found on Open Source Rails tend to be specialized applets. Both are better than tedium and redundancy.

However, kits typically do not mix well with existing Rails code. Moreover, if you could bootstrap a new application quickly, starting from scratch might be preferable, even ideal. What’s needed is some way to automate what you would otherwise shape with commands, text editors, and a few choice expletives.

Sure enough, necessity is the mother of invention. The Rails template, new to Rails 2.3, scripts application setup.

The Template Language

A Rails template is simply a Ruby script with some domain-specific extensions to facilitate application configuration. You can run a script with your initial rails project command, or run a script any time later via rake rails:template. The former option molds each new application you create. You might have one template for a wiki application and another for a daemon. The latter option can augment a virgin application or can enhance any application at any time. For example, you can create a template to install rspec, rspec-rails, and Cucumber to add behavior-driven development (BDD) to an existing Rails code base.

The Rails template language provides something for each common configuration task.

  • file creates a new file in your Rails project. Name the file relative to the RAILS_ROOT. For example, file("config/apach.conf", "your apache config") creates RAILS_ROOT/config/apach.conf.
  • initializer creates new code in config/initializers. You can use initializers to extend classes and add convenience methods or set class properties.
  • rakefile is similar to initializer: it creates code, albeit in lib/tasks. This is the operand to use to add rake tasks to your application.
  • environment modifies the global config/environment.rb or one of the individual environments environments/*.rb.
  • rake, as its name implies, runs the given rake task.
  • generate runs a generator with a list of options.
  • gem adds a config.gem entry to config/environment.rb. This is the preferred technique to list your application’s gem dependencies. If you are careful and accurately capture those requirements, the sole command sudo rake gems:install can install every gem in one fell swoop.
  • plugin installs the named plug-in.
  • The eponymous route operand adds a route to config/routes.rb.
  • inside is something like a subshell. It changes to the directory you specify and runs a block of code. inside will not with arbitrary directories anywhere on the file system. The directory will be appended to RAILS_ROOT to yield a fully-qualified path. So, for instance, inside(/tmp) yields RAILS_ROOT/tmp, not the system’s scratch pad in /tmp.
  • run executes a shell command.
  • git launches a git command.
  • ask and yes? are able to branch based on user feedback. You can use these two prompts to make decisions and confirm actions, such as overwriting an existing file. Remember, you have the entire Ruby language at your disposal, so a template can be smart and tune its processing to suit.

You can mix and match many of these. run within inside is common, as you’ll see promptly.

Example Templates

Over the past few weeks, I’ve amassed a handful of templates designed to jumpstart a new Rails project. Here are some, as a sample of what’s possible. (You can find these online in my all-about-ruby repository on GitHub.)

The first example, 001_runmefirst.rb, performs housekeeping. (I’ve numbered my templates to indicate my preference for execution order. I also have a template runner that runs all my templates in sequence based on file name. Sequencing is not required.)

run "gem sources -a http://gems.github.com"
run "rm public/index.html"
inside( 'config' ) do
  run "cp database.yml database.yml.default"
  run "cp environment.rb environment.rb.default"
  run "cp routes.rb routes.rb.default"
  run "cp -pr environments environments.default"

This template adds GitHub to the list of gem sources to consult, removes the public index file, and makes backup copies of the configuration files generated by the rails command. Backups make it easy for me to revert if I need to. All of the cp commands run within RAILS_ROOT/config.

For legibility, I prefer to write code such as…

if my_string.not_blank?

To extend the String clsss to add such a method, I create an initializer, re-open the class, and add the new instance method.

initializer 'string_mods.rb', <<-CODE
class String
  def not_blank?

The body of the “here” document is written to string_mods.rb.

Rails 2.3 added the config.gem entry to capture gem dependencies within the structure of the application itself. Once the code was deployed, a simple rake task installs the missing gems. You can add config.gem entries with the gem operator. It takes the options you'd typically find in environment.rb.

gem 'haml-edge',
  :lib      => 'haml'

gem 'mislav-will_paginate',
  :version  => '~> 2.2.3',
  :lib      => 'will_paginate',
  :source   => 'http://gems.github.com'

gem 'ruby-openid',
  :lib => 'openid'

plugin also installs software.

plugin 'rspec',
  :git => 'git://github.com/dchelimsky/rspec.git'

plugin 'rspec-rails',
  :git => 'git://github.com/dchelimsky/rspec-rails.git'

plugin 'squirrel',
  :git => 'git://github.com/thoughtbot/squirrel.git'

plugin 'webrat',
  :git => 'git://github.com/brynary/webrat.git vendor/plugins/webrat'

route modifies your routes. The argument to route is a string. Here are three of the routes restful-authentication requires.

route "map.login   '/login',  :controller => 'sessions',  :action => 'new'"
route "map.logout  '/logout', :controller => 'sessions',  :action => 'destroy'"
route "map.signup  '/signup', :controller => 'users',    :action => 'new'"

After the plug-ins are installed, it's time to run the generators to create tests, controllers, models, migrations, and more. You can automate the process to avoid missing a step with generate.

generate( :rspec )

generate( :cucumber )

generate( :authenticated,
  "user session",
  "--rspec" )

generate( :open_id_authentication_tables,
  "open_id"  )

Here, the first argument is the name of the generator and the rest of the arguments are options.

Finally, there is environment. With no arguments, it affects config/environment.rb, and all new code is inserted immediately after the line Rails::Initializer.run do |config|.

environment( "config.active_record.timestamped_migrations = false" )
environment( 'config.load_paths += %W( #{RAILS_ROOT}/extras )' )
environment( 'config.log_level  = :debug' )
environment( 'config.plugins    = [ :all ]' )
environment( "config.time_zone  = 'config.Eastern Time (US & Canada)'" )

If you instead call environment( :test, 'config.whiny_nils = true' ), the statement is appended to the end of the config/test.rb file. environment and other template operands also take blocks. When used in that form, the output of the block is added to the requisite file.

If you want to see the nitty-gritty of the template runner, the software that loads your template, view rails-2.3.x/lib/rails_generator/generators/applications/app/template_runner.rb.

To see more examples, point your browser to Jeremy McAnally's template collection on GitHub. McAnally is the author of the Rails template system.

Before you run off and type gem, consider the implications of installing anything manually. Are you going to install the same software again some other time? Are there lots of steps to recall? Now that you have a configuration scrpting language, you may want to write it down instead.

Happy tinkering!

Comments on "Don’t Repeat Yourself. Use Rails Templates."


awesome! lmao \”I don’t want to admit how many times I’ve had to relearn the restful-authentication plug-in\” – how about – I dont want to admit how many times I\’ve had to rewrite my extensions to restful authentication.

Thanks Martin – this is very helpful, and very timely for me.

Right here is the perfect website for anybody who really wants to understand this topic.
You understand a whole lot its almost tough to argue with you (not that
I personally would want to…HaHa). You definitely put a fresh spin
on a topic which has been written about for many years.
Excellent stuff, just great!

I’m really enjoying the design and layout of your website.
It’s a very easy on the eyes which makes it much more enjoyable for me to come here and visit
more often. Did you hire out a developer to create your theme?
Superb work!

Looking forward to reading more. Great blog.Really thank you! Cool.

Spot on with this write-up, I truly suppose this web site needs way more consideration. I’ll most likely be once more to learn rather more, thanks for that info.

Hello my loved one! I want to say that this post is awesome, nice written and come with almost all vital infos. I’d like to see more posts like this .

I got good info from your blog

Here are some links to web pages that we link to due to the fact we assume they may be really worth visiting.

Regards for sharing this very good web-site

Some truly interesting details you have written.Assisted me a lot, just what I was searching for : D.

I think the article is very helpful for people,it has solved our problem,thanks!
Wholesale 2140Oakley Sunglasses ID8210097 http://www.fleetsale.ru/new-arrival-oakleys-097.html

Leave a Reply