Rails templates 57

Posted by pratik
on Thursday, December 04

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 :

1
2
3
4
5
6
7
8
9
# 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 :

1
2
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? :

1
2
3
4
5
6
7
8
9
10
11
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 :

1
2
3
4
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 :

1
2
3
4
5
6
7
8
9
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 :

1
2
3
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 :

1
2
3
4
5
6
7
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 !

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

And bort ?

Here’s what a bort template would look like :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 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

Comments

Leave a response

  1. Paul BarryDecember 04, 2008 @ 02:04 AM

    Wow. This is fantastic, can’t wait to try it out.

  2. Kevin MarshDecember 04, 2008 @ 02:36 AM

    Wow, great to see so much cool stuff in Rails coming so quickly!

  3. grosserDecember 04, 2008 @ 07:33 AM

    looks promising, at last no more template-rails-applications to customize, a simple config can do :)

  4. LuisDecember 04, 2008 @ 10:45 AM

    Awesome! I was doing this with shell scripts, generators and such. This is great!

  5. JeromeDecember 04, 2008 @ 01:41 PM

    Hmm, that is awesome!

    Will cut quite some development time at the beginning, just like Bort does, but now I can take it much further much more easily!

  6. Jim NeathDecember 04, 2008 @ 01:49 PM

    Looks like I’ll have less maintaining to do on Bort now :)

  7. Harry Jr.December 04, 2008 @ 01:51 PM

    But most of that stuff is easily accomplished with a Rake task, apart from maybe the route and initializer-stuff. Why not add it as rake-stuff, like Vlad?

  8. PratikDecember 04, 2008 @ 02:02 PM

    @Jim : Heh. Thanks for your work on Bort! If you run into any issues with rails templates, please do let me know. Same for feature requests too.

    @Harry : That’s a good idea. Making this a rake task would be a 3-4 lines of code. Just need to call Rails::TemplateRunner.new(Rails.root, ‘template_path’) and you’re done. Probably you can play around with that and submit a patch or something.

  9. subbuDecember 05, 2008 @ 06:53 AM

    Ah! Things move so fast. Few days back it was bort. Now templates. Some rake tasks later to simplify even these templates. Good stuff.

  10. Peter CooperDecember 05, 2008 @ 07:12 AM

    This is absolutely awesome. I’m enjoying playing with it.

    Now.. it might be a bit late or I haven’t had enough caffeine, but is there a nice-ish way of using git submodules in with this? For example, those plugins that are being installed from git.

  11. PratikDecember 05, 2008 @ 08:19 AM

    @Peter : Not at the moment. Jeremy had support for Braid in initial versions of rg. But we decided to remove that and wait to see what people use and demand for. I think adding support for git submodules sound reasonable and easy.

    I don’t really know how git submodules work, so if someone wants to help me with a patch or submit one, you’re most welcome!

  12. Peter CooperDecember 05, 2008 @ 10:14 AM

    I imagine someone will come up with an idea quicker than me but I’ll definitely have a look :) Git submodules make life really easy.

    Also, the example template in the post has an interesting quirk. Might just be me, so I’m intrigued. The line:

    route “map.root :controller => :person”

    The final app won’t work with that for me. It needs to be ‘people’ rather than :person. I’m pointing it out, though, in case I’m doing something boneheaded and you actually can use symbols in routes (something I’ve wanted for a long time).

  13. PratikDecember 05, 2008 @ 10:57 AM

    Oops..your’re right. Updated it in the post. Thanks!

  14. lomakinDecember 05, 2008 @ 10:59 AM

    Pretty cool!!!

  15. Peter CooperDecember 05, 2008 @ 11:06 AM

    Just submitted a patch for it:

    http://rails.lighthouseapp.com/projects/8994/tickets/1517-option-for-template-generator-to-install-plugins-as-git-submodules

    Might ultimately not be the right way to do it though, but it works for me here.

    The only other alternative I can think of would be to add the submodule feature to /script/plugin instead.. that might be a better idea really.. I’ll take a look at that next.

  16. Rahsun McAfeeDecember 05, 2008 @ 03:25 PM

    Hats off to Jeremy and Pratik. This feature is wonderful. I can’t wait to rock it.

  17. SpyouDecember 05, 2008 @ 03:33 PM

    Thanks a lot for this feature.

    This is going to be very useful

  18. Carlo PecchiaDecember 05, 2008 @ 04:08 PM

    Nice! Really nice feature… I’m sure an interesting “marketplace” of pre-configured application will emerge. And that’s another weapon for Ruby communities..!

  19. PratikDecember 05, 2008 @ 11:25 PM

    @Peter Applied the patch. Thanks!

  20. NattsDecember 06, 2008 @ 12:52 AM

    Just translated and posted this in russian at my blog. Trackbacks and backlinks are in place =)

    Than you, folks! This is just great.. I’ve tried it out and created some templates. Will contribute tomorrow / in some days.

  21. Liam MorleyDecember 06, 2008 @ 05:14 AM

    Hey, that’s pretty awesome.

  22. Anil WadghuleDecember 08, 2008 @ 05:03 AM

    This is really FTW!

  23. PratikDecember 08, 2008 @ 02:08 PM

    @Harry Jr : I also added a rake task to apply a template to an existing Rails application. Check UPDATE 2 :)

  24. ArhimedDecember 09, 2008 @ 09:44 PM

    Impressive!

  25. Eric BerryDecember 11, 2008 @ 04:49 PM

    Excellent! Now we need a site where people can post their application templates and be able to categorize and rate them. Any takers?

  26. Sergio RomanoDecember 12, 2008 @ 01:05 PM

    In Assembla we have already done it (although not using this wonderful new feature).

    You can create your own template online workspace, put any code you want in your subversion (git or mercurial) repository and add any other documentation or tool you think you will need for your projects. Then, you can post it in our preconfigured package catalog (http://www.assembla.com/preconfigured_spaces) and allow other users to copy it.

    As an example, we have created a Rails package that comes with a Subversion Repository which includes a Rails Skeleton with some useful deploy tasks. And also, some documentation for developers to setup RoR or to configure their servers to deploy.

    Check the Ruby on Rails package here: http://www.assembla.com/preconfigured_spaces/10-Ruby-on-Rails-Package It’s FREE for Open Source projects

  27. MiroslavDecember 14, 2008 @ 08:30 PM

    This is so cool, but how do i get rails command up to edge version?? with rails 2.2.2 i of course get error “invalid option: -m” i know how to install edge rails in existing app, but still, i think it does not replace my rails command as far as i have googled…

    thx for help

  28. Brian HardyDecember 19, 2008 @ 04:50 PM

    To get your bort template to work with edge rails (as of 12/18/2008), I had to modify the gem lines for rubyist-aasm and ruby-openid as follows:

    gem ‘rubyist-aasm’, :lib => ‘aasm’ gem ‘ruby-openid’, :lib => ‘openid’

    That took me a while to figure out, but otherwise, great stuff!

  29. SamDecember 27, 2008 @ 08:38 PM

    Nice article, certainly cleared a few things up for me. I love how fast great ideas for rails can quickly be implemented into the core.

  30. Andrew TimberlakeDecember 28, 2008 @ 12:19 PM

    @miroslav To run the rails command out of edge rails, run the command with the full path – <edge_rails_directory>/railties/bin/rails -m <template_path> <project_path>

  31. LacKacDecember 31, 2008 @ 11:52 AM

    I’ve created a modularized collection of application templates called App LEGO: http://github.com/lackac/app_lego/tree/master Some ideas came from Jeremy’s template repo, but this one is more like a collection of modules working well together. Check it out. It also has support for braid.

  32. Daniel LopesDecember 31, 2008 @ 03:56 PM

    Amazing feature… It will be very usefull, but my question is about projects like Bort … This kind of projects generate a skeleton for some repeated tasks like login ( with all bells and wishes like roles, email activation, reminder and etc ), exception notification, localization and etc…

    It will not be possible with this new builtin feature, right? Projects like bort still valid?

  33. lucapetteJanuary 16, 2009 @ 03:46 PM

    I didn’t know it. This is simply fantastic.

  34. Daniel DraperJanuary 28, 2009 @ 02:25 AM

    Be great if you could do svn instead of just git. We still use and love svn :)

  35. Justin SmestadFebruary 16, 2009 @ 06:07 PM

    Is it possible to get rails templates to yield the output of “run” commands. For example, running “compass —rails .” will prompt you twice with 2 questions on file locations, this seems to not get passed through into the rails templates script.

  36. MichaelFebruary 18, 2009 @ 05:29 PM

    How do you rename items retrieved via git using the “plugin” directive? For instance, the note for “restful-authentication” regarding Rails 2.1 users at github (http://github.com/technoweenie/restful-authentication/tree/master#INSTALL). The directory needs to be renamed to “restful_authentication”. I see nothing under the options for “plugin” that would allow this.

    Thanks.

  37. Nathaniel BrownFebruary 20, 2009 @ 05:14 AM

    This is just about the coolest thing I have seen in Rails since Engines came out. You can create full on install scripts with this in addition to a ton of other stuff. Massive future for this. Nicely done!

  38. Sébastien LuquetFebruary 23, 2009 @ 07:07 PM

    Does it work on Windows ?

  39. Sébastien LuquetFebruary 23, 2009 @ 08:48 PM

    Sorry http://github.com/rails/rails/commit/2414fdb244cc0ba97620dd3f50e269d2e26c7392

  40. jd barnhartFebruary 25, 2009 @ 06:43 AM

    Great stuff! Is the rails project name passed to the template, and if so, how can it be accessed?

  41. Anthony BaileyMarch 05, 2009 @ 07:51 PM

    (Coming here late, from 2.3RC2 release notes. Overall this seems nice!)

    In paranoid mode: the combination of getting the template live via http and the ability of said template to run arbitrary shell commands might be considered a little risky.

  42. EricMarch 11, 2009 @ 09:07 PM

    Just a little helpful snippet if you’re trying to copy in migrations from an existing source while maintaining the current timestamped migrations

    inside (‘db/migrate’) do
    run “cat <dir_to_copy_from>/*_create_pages.rb > temp.rb”
    #this grabs the new migration in your newly generated app
    run “find *_create_pages.rb | xargs mv temp.rb”
    end

  43. Hampton CatlinMarch 11, 2009 @ 09:07 PM

    I’m proud to announce that I’ve built a Rails Template Generator site called “RailsBoost”

    http://www.railsboost.com

    You just use checkboxes to configure your stack, and then you have a one-liner for generating your new project.

  44. Oliver SteeleMarch 23, 2009 @ 10:02 PM

    This is very cool! I just made a template for the OpenLaszlo plugin: http://gist.github.com/83809.

  45. RonApril 09, 2009 @ 12:03 AM

    Question: Some plugins require you to make edits to application files. For example, rails-authorization-plugin needs you to add some stuff to the beginning of environment.rb.

    Is there a standard way to do this?

  46. hectorsqApril 10, 2009 @ 01:05 AM

    How can I see the prompt of generator when it needs confirmation?

    This is the scenario:
    my_template.rb runs a model generator:
    generate(:model, “Product”, “name:string”)

    I run rake rails:template LOCATION=my_template.rb
    I modify the contents of Product.rb
    I run rake rails:template LOCATION=my_template.rb again

    The rake tasks appears to be stuck. It is currently waiting for my confirmation to overwrite Product.rb but there is no prompt for this.

  47. NicoApril 11, 2009 @ 08:04 AM

    Is it possible to pass in parameters to the template? I couldn’t find anything about it…

  48. Vlad DidenkoApril 15, 2009 @ 03:32 AM

    Having hard time to make the gem line out of my batch scipt entry:

    sudo env ARCHFLAGS=“-arch i386” \ gem install postgres — \ —with-pgsql-lib=/usr/local/pgsql/lib \ —with-pgsql-include=/usr/local/pgsql/include

    Any help?

  49. AntonApril 18, 2009 @ 10:15 AM

    Thank you for the great overview!

    Since there seems to be no standard way to append something to the environment.rb file… for the people that think File IO is to low level a simple sulution ;)

    env = IO.read ‘config/environment.rb’
    ins = <<-ENDOFTEXT

    # for example if you do not like the 7.000 character long new migration files… config.active_record.timestamped_migrations = false

    ENDOFTEXT
    env.gsub!(/^end$/, “#{ins}end”) unless env.include?(ins)
    File.open(‘config/environment.rb’, ‘w’) do |env_out|
    env_out.write(env)
    end

    But a standarized solution would be much better!

  50. PapipenMay 11, 2009 @ 08:35 AM

    When i use rake(“db:create”) on widows XP, i have an error message :

    kernel/agnostics.rb:7:in ``’: Exec format error – rake db:create RAILS_ENV=development

    What does it mean ?

  51. RickzMay 28, 2009 @ 06:24 AM

    Hi this looks like great stuff..

  52. bobstaMay 29, 2009 @ 05:21 PM

    Hi would it be possible to pass in parameters such as database name and passwords and such so i could use that to create a database.yml and create the database ‘on the fly’ so to speak?

  53. Mark RichmanJune 09, 2009 @ 10:02 PM

    @Papipen – I get this same error…did you resolve it?

  54. Ant PeacockeJune 15, 2009 @ 02:39 PM

    @hectorsq I had the same problem with the generator hanging because it was (invisibly asking for confirmation to overwrite a file)

    The fix for me was to pass in the force option to force script/generate to overwrite existing files.

    generate :sugo_ecommerce, ‘-f’

  55. TsenYingJuly 17, 2009 @ 06:18 PM

    @bobsta
    To set database.yml username password, I do:

    username = ask(“***Database username?”)
    password = ask(“***Database password?”)
    run(“sed -e s/username:.*/\”username: #{username}\“/ -e s/password:.*/\”password: #{password}\“/ config/database.yml >config/database.yml.tmp”)
    run(“mv config/database.yml.tmp config/database.yml”)

    or you can do it in ruby:
    File.open(‘config/database.yml’,‘r+’) do |f|
    s = f.read.gsub(/username:(.)$/,“username: #{username}”)
    s = s.gsub(/password:(.
    )$/,“password: #{password}”)
    # puts s
    f.rewind
    f.write s
    end

    Probably a better way to do this.

  56. Brendon WilsonJuly 22, 2009 @ 06:57 PM

    Hmm, I’m wondering – is there a variable set somewhere that provides the name of the application passed to the invocation of “rails”. That would allow application templates to use it for other purposes that might be useful…

  57. Tom WilsonSeptember 11, 2009 @ 05:48 PM

    Do application templates REALLY on a Windows dev machine ??

    I see http://github.com/rails/rails/commit/2414fdb244cc0ba97620dd3f50e269d2e26c7392 which seems to indicate that rails -m option should work on Windows machines.

    However, it looks like the various templates themselves will NOT work under Windows. As an example, the bort.rb script includes these commands: run “ln -s ~/commit-rails/rails rails” rake(“gems:install”, :sudo => true)

    The first one tries to execute an “ln” command and the second tries to sudo the rake command. Of course Windows is not happy with either of these actions!

    Am I doing something stupid or missing something obvious? I have always run my dev commands in a Windows CMD console, but templates with nx commands are clearly not going to work in my environment.

Comment