Rails templates
Published over 5 years ago

So now that Edge Rails got templates ( Thanks to Jeremy ) I just wanted to give a top level overview.

Templates are simple ruby files containing DSL for adding plugins/gems/initializers etc. to your freshly created Rails project. To apply the template, you need to provide rails generator with location of the template you wish to apply, using -m option :

rails blog -m ~/template.rb

Thanks to the magic of open-uri, the template location can be a URL too :

rails blog -m http://gist.github.com/31208.txt

You can even apply templates to your existing Rails application using rails:template rake task and supplying LOCATION environment variable :

rake rails:template LOCATION=~/template.rb

A very simple template would look like :

# template.rb
run "rm public/index.html"
generate(:scaffold, "person name:string")
route "map.root :controller => 'people'"
rake("db:migrate")

git :init
git :add => "."
git :commit => "-a -m 'Initial commit'"

That’s very self explanatory. Here are the key methods for the template DSL :

gem(name, options = {})

Adds a config.gem entry for the supplied gem to generated application’s config/environment.rb

So if your application depends on bj and hpricot :

gem "bj"
gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"

Please note that this will NOT install the gems for you. So you may want to run rake gems:install:

rake "gems:install"

And let Rails take care of installing the required gems if they’re not already installed.

plugin(name, options = {})

Installs a plugin to the generated application.

Plugin can be installed from Git :

plugin 'authentication', :git => 'git://github.com/foor/bar.git'

You can even install plugins as git submodules :

plugin 'authentication', :git => 'git://github.com/foor/bar.git', :submodule => true

Please note that you need to git :init before you can install a plugin as a submodule

Or use plain old SVN :

plugin 'wtfsvn' :svn => 'svn://crap.com/wtf/trunk'

initializer(filename, data = nil, &block)

Adds an initializer to the generated application’s config/initializers directory.

So personally, I like using Object#not_nil? and Object#not_blank? :

initializer 'bloatlol.rb', <<-CODE
class Object
  def not_nil?
    !nil?
  end

  def not_blank?
    !blank?
  end
end
CODE

Similarly lib() creates a file in lib/ directory and vendor() creates a file in vendor/ directory. There is also file(), which accepts a relative path from RAILS_ROOT and creates all the directories/file needed :

file 'app/components/foo.rb', <<-CODE
class Foo
end
CODE

That’ll create app/components directory and put foo.rb in there.

rakefile(filename, data = nil, &block)

Creates a new rake file under lib/tasks with the supplied tasks :

rakefile("bootstrap.rake") do
  <<-TASK
    namespace :boot do
      task :strap do
        puts "i like boots!"
      end
    end
  TASK
end

And that creates lib/tasks/bootstrap.rake with a boot:strap rake task!

generate(what, args)

Runs the supplied rails generator with given arguments. For example, I love to scaffold some whenever I’m playing with Rails :

generate(:scaffold, "person", "name:string", "address:text", "age:number")

run(command)

Executes an arbitrary command. Just like the backticks. My main use case is to remove public/index.html :

run "rm public/index.html"

rake(command, options = {})

So you scaffolded, but who’s gonna run the db:migrate rake task !? Here’s who :

rake "db:migrate"

Simple enough.

You can also run rake tasks in a different rails environment :

rake "db:migrate", :env => 'production'

Or even use sudo :

rake "gems:install", :sudo => true

route(routing_code)

This adds a routing entry to config/routes.rb file. In above steps, we generated a person scaffold and also removed public/index.html. Now to make PeopleController#index as the default page for the application :

route "map.root :controller => :person"

Voila!

inside(dir)

I have my edge rails lying at ~/commit-rails/rails. So every time i have to manually symlink edge from my new app. But now :

inside('vendor') do
  run "ln -s ~/commit-rails/rails rails"
end

So inside() runs the command from the given directory.

ask(question)

ask gives you a chance to get some feedback from the user and use it in your templates. Lets say you want your user to name the new shiny library you’re adding :

lib_name = ask("What do you want to call the shiny library ?")
lib_name << ".rb" unless lib_name.index(".rb")

lib lib_name, <<-CODE
class Shiny
end
CODE

yes?(question) or no?(question)

And you can even ask questions from templates and decide the flow based on user’s answer. Lets say you want to freeze rails only if the user want to :

rake("rails:freeze:gems") if yes?("Freeze rails gems ?")

no?(question) acts just the opposite.

git(:must => “-a love”)

As we all love git/hub, Rails templates let you do the git stuff too !

git :init
git :add => "."
git :commit => "-a -m 'Initial commit'"

And bort ?

Here’s what a bort template would look like :

# bort.rb
inside('vendor') do
  run "ln -s ~/commit-rails/rails rails"
end

plugin 'rspec', 
  :git => 'git://github.com/dchelimsky/rspec.git'
plugin 'rspec-rails', 
  :git => 'git://github.com/dchelimsky/rspec-rails.git'
plugin 'exception_notifier', 
  :git => 'git://github.com/rails/exception_notification.git'
plugin 'open_id_authentication', 
  :git => 'git://github.com/rails/open_id_authentication.git'
plugin 'asset_packager', 
  :git => 'http://synthesis.sbecker.net/pages/asset_packager'
plugin 'role_requirement', 
  :git => 'git://github.com/timcharper/role_requirement.git'
plugin 'restful-authentication', 
  :git => 'git://github.com/technoweenie/restful-authentication.git'

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

rake("gems:install", :sudo => true)

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

Shamelessly inspired/yanked from Jeremy’s templates repo

Save that code in bort.rb and :

[lifo@null Rails]$ ruby ~/commit-rails/rails/railties/bin/rails bortapp -m bort.rb

Contribute

If you like this template stuff and want to share your templates with the rest of us, please contribute to Jeremy’s rails-templates project – which will be a collection of Rails templates.

And as usual, any bugs/feature requests can go to Rails lighthouse

UPDATE 1 Add an example of installing plugins as a git submodule. Thanks to Peter Cooper for the patch.
UPDATE 2 Add an example of rake rails:template LOCATION=foo task