Making Generators for Rails 3 with Thor
Rails 3 replaces the old built-in generator system with Thor which is “a scripting framework that replaces rake and sake”. Not only that, because during Google Summer of Code José Valim extended it with Thor::Actions, a module with methods useful for generators, and integrated it with Rails. Some things have changed, others haven’t. This article will walk you through the process of making a gem with a built-in generator for Rails 3.
First Things First
The gem we will make is very simple. It’s job will be to copy one JavaScript (specifically the ColorBox jQuery plugin) into the public/javascripts/ directory of an application. Start out by creating a directory for the gem wherever you prefer to store your code:
mkdir colorbox_rails/
Previously Rails would search for generators in the rails_generators/ or just generators/ directory of a gem or plugin, but the new Thor-based system changes that. Now the locations are lib/generators/ or alternatively lib/rails_generators/.
cd colorbox_rails/ mkdir lib/ mkdir lib/generators/
Inside lib/generators/ the rules are as follows: One directory per generator, each of which should contain a file of which the filename is simply the name of the generator suffixed with _generator.rb. So in our case the file of the generator will be named colorbox_generator.rb.
cd lib/generators/ mkdir colorbox/ cd colorbox/ touch colorbox_generator.rb
The Generator
Open the newly created file in your favorite text-editor. The first line should require the generator system.
require 'rails/generators'
This little piece of code actually introduces another change, because the module used to be called Rails::Generator. Next, we need a class which inherits from Rails::Generators::Base.
class ColorboxGenerator < Rails::Generators::Base end
The name of a generator class should simply be the name of the generator in CamelCase suffixed with Generator, staying consistent with the filename. Rails::Generators::Base inherits from Thor::Group which works in a special way. When invoked it will call all instance methods, or "tasks" in Thor terminology, of the class automatically, allowing you to divide the code of your generator in related parts — and naming them, notably. In our case we only need one task, though.
class ColorboxGenerator < Rails::Generators::Base def install_colorbox end end
Now for the actual copying of the JavaScript. This is where Thor::Actions comes in. It has a few submodules with methods we can use in our generator. (Please refer to the Thor documentation for an overview.) In this case, what we're interested in is the copy_file method.
def install_colorbox
copy_file(
'jquery.colorbox-min.js',
'public/javascripts/jquery.colorbox-min.js'
)
end
UPDATE As José Valim has kindly pointed out in the comments: Rails::Generators::Base also has a set of Rails-related actions available which live in Rails::Generators::Actions which basically contains the Rails templates API. That means that generators and templates are merged in Rails 3! Check out the source of Rails::Generators::Actions.
Go ahead and download the jQuery plugin and copy the minified version into templates/, which should be located in the same directory as your generator file.
Before we make the generator a gem, we need to add a class method to the generator. It's intended to set the source root (where the generator will look for templates) to the templates/ directory of our generator. I honestly don't know why this is necessary — perhaps it isn't. I've been browsing through the source of the Rails generator system, trying to find a clear definition of where your templates should be located in order to not have to specify it manually, but with no success. If you can help me out here, please feel free to share your knowledge in the comments.
def self.source_root File.join(File.dirname(__FILE__), 'templates') end
Making the Generator a Gem
In order to use our generator inside a Rails application we want to install it as a gem. All that takes, really, is a .gemspec file, and since this is just an example we will keep it as minimalistic as possible. Create a file called colorbox_rails.gemspec in the root of your gem directory and paste in the following:
Gem::Specification.new do |gem| gem.name = 'colorbox_rails' gem.version = '0.0.0' gem.summary = 'The ColorBox jQuery plugin on Rails.' gem.files = Dir['lib/**/*'] gem.add_dependency 'rails', '3.0.pre' end
Before we install our gem we need the 3.0.pre version of Rails. Since it hasn't been released officially as a gem we have to install it from source. Active Record depends on Arel 2.0.pre which also hasn't been released as a gem, so first we'll install that from source, too.
git clone git://github.com/rails/arel.git cd arel/ gem build arel.gemspec gem install arel-0.2.pre.gem git://github.com/rails/rails.git cd rails/ sudo rake install
With that done we are ready to install our own generator as a gem.
cd colorbox_rails/ gem build colorbox_rails.gemspec gem install colorbox_rails-0.0.0.gem
Using the Generator
In order to use our new generator we need a Rails 3.0.pre application. Unfortunately, even though we installed 3.0.pre as a gem we can't use the rails executable. Go ahead and try and observe the following error:
/usr/local/lib/site_ruby/1.8/rubygems.rb:384:in `bin_path': can't find executable rails for rails-3.0.pre (Gem::Exception) from /usr/local/bin/rails:19
Right... Luckily, the binary is (naturally) to be found in the Rails Git repository which we just cloned, so that's not a problem.
ruby rails/railties/bin/rails colorbox_rails_test cd colorbox_rails_test/
To enable the generator we need to add the gem to the Gemfile.
gem "colorbox_rails", "0.0.0"
Now we just need to bundle the gem, before we should be able to run our generator.
gem bundle
script/generate colorbox
Tada! We have successfully created a Rails 3-compatible generator with Thor, installed it as a gem, and used it in a 3.0.pre application. But as good Ruby programmers there is one more thing we should do.
Testing the Generator
Head back to the root directory of our gem and create a test/ directory. Inside that we want a test_helper.rb file, and a generators/colorbox/ directory. Inside the latter we want a colorbox_generator_test.rb file, staying consistent with the structure of the lib/ directory. Open test_helper.rb and paste in the following:
require 'test/unit' require 'rubygems'
Inside colorbox_generator_test.rb we want to require test_helper.rb and the generator file.
require 'test_helper' require 'generators/colorbox/colorbox_generator'
Beneath we define our test case.
class ColorboxGeneratorTest < Test::Unit::TestCase end
Inside we want one test method which tests for the presence and content of the jQuery plugin.
class ColorboxGeneratorTest < Test::Unit::TestCase
def test_install_colorbox
assert File.exists?(
File.join(@destination, 'public', 'javascripts', 'jquery.colorbox-min.js')
)
assert_equal(
File.read(File.join(@source, 'jquery.colorbox-min.js')),
File.read(File.join(@destination, 'public', 'javascripts', 'jquery.colorbox-min.js'))
)
end
end
Obviously, a couple of instance variables are missing.
def setup
@destination = File.join('tmp', 'test_app')
@source = ColorboxGenerator.source_root
ColorboxGenerator.start('', :destination_root => @destination)
end
Let us also clean up after ourselves, shall we?
def teardown FileUtils.rm_rf(@destination) end
To run our tests let us define a task in Rakefile.
require 'rake/testtask' Rake::TestTask.new do |test| test.pattern = 'test/**/*_test.rb' test.libs << 'test' end task :default => :test
Run rake and watch it go green.
Conclusion
This walk-through barely scratched the surface of the new generator system, but it hopefully gave you an idea of what has changed and what hasn't — and if you have never written a generator for Rails before, you can now! Don't forget to checkout the documentation of Thor.
Wonderful! Good Simple tutorial
my one issue with it is the gem installation to get rails3/master working. I’d say following yehuda’s instructions was probably a better line to go down as you don’t get left with any funny gem versions installed except for in your test app. instructions here: http://yehudakatz.com/2009/12/31/spinning-up-a-new-rails-app/
Lenary
Thanks for the excellent write up on Rails Generators! I just want to point that besides the Thor::Actions, as you pointed out, Rails inserts its own actions, available at Rails::Generators::Actions. If you take a look at it, you will see it’s exactly Rails templates API, meaning that generators API and templates API were merged in Rails 3.0.
http://github.com/rails/rails/blob/master/railties/lib/rails/generators/actions.rb
Regards!
[...] David Trasbo wrote a nice guide about how to make your first Rails 3 generator, it covers up all the basic steps including setting it up in a gem. He also puts the deserved [...]
I tried to follow the guide step by step.
The colorbox gets installed in the test rails app correctly inside the /vendor/gems dir.
But when I run it, I get some mysterious errors…
I get these kinds of errors a lot after having switched to rails 3
—
Installing colorbox_rails (0.0.0)
Done.
kristian-mandrups-macbook-pro:colorbox_rails_test kristianconsult$ script/generate colorbox
/opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `gem_original_require’: no such file to load — active_support/dependencies/autoload (LoadError)
from /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `require’
from /Users/kristianconsult/my_rails/rails3_generators/colorbox_rails_test/vendor/gems/gems/actionpack-3.0.pre/lib/action_dispatch.rb:27
from /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require’
from /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require’
from /Users/kristianconsult/my_rails/rails3_generators/colorbox_rails_test/vendor/gems/gems/actionpack-3.0.pre/lib/action_dispatch/railtie.rb:1
from /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require’
from /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require’
from /Users/kristianconsult/my_rails/rails3_generators/colorbox_rails_test/vendor/gems/gems/railties-3.0.pre/lib/rails.rb:20
from /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require’
from /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require’
from /Users/kristianconsult/my_rails/rails3_generators/colorbox_rails_test/vendor/gems/gems/railties-3.0.pre/lib/rails/all.rb:1
from /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require’
from /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require’
from /Users/kristianconsult/my_rails/rails3_generators/colorbox_rails_test/config/boot.rb:16
from /Users/kristianconsult/my_rails/rails3_generators/colorbox_rails_test/config/application.rb:1:in `require’
from /Users/kristianconsult/my_rails/rails3_generators/colorbox_rails_test/config/application.rb:1
from /Users/kristianconsult/my_rails/rails3_generators/colorbox_rails_test/config/environment.rb:2:in `require’
from /Users/kristianconsult/my_rails/rails3_generators/colorbox_rails_test/config/environment.rb:2
from script/generate:2:in `require’
from script/generate:2
–
I get the same error using ruby 1.9.1
$ rvm use 1.9.1
Now using ruby 1.9.1 p376
kristian-mandrups-macbook-pro:colorbox_rails_test kristianconsult$ script/generate colorbox
/Users/kristianconsult/my_rails/rails3_generators/colorbox_rails_test/vendor/gems/gems/actionpack-3.0.pre/lib/action_dispatch.rb:27:in `require’: no such file to load — active_support/dependencies/autoload (LoadError)
Kristian,
I have never stumbled upon that error before, to be honest. How did you install the 3.0.pre version of Rails?
[...] Making Generators for Rails 3 with Thor (01/15/10) — A thorough tutorial on how to create, gemify, and test Rails 3 generators. [...]
I got Rails 3 running
I tried to create my own generator using Jeweler and rails 3 script/generate generator
My result can be found here:
http://github.com/kristianmandrup/mongo_model_r3
If I install the gem locally and then in Gemfile:
gem \mongo_modl\
$ gem bundle
Installs the gem under vendor/gems/ruby/1.9.1/gems/mongo_modl-0.2.1
But the gem isn’t recognized as a generator!? ;(
Can you make the colorbox example work using the jeweler and script/generate generator approach?
I has to add a line:
gem.files=FileList['lib/**.*.rb] to my jeweler Rakefile in order for it to include all the files in lib correctly. Otherwise it would only \pull in\ mongo_modl.rb from lib.
I finally figured it out by taking a closer look at the source of your gem
A working Mongo DB model generator ready to be installed in any Rails 3 project is now available at http://github.com/kristianmandrup/mongo_model_r3 and from gemcutter as mongo_model 0.2.1
A Bash utility script to create a skeleton for a generator as a jewel (gem) can be found at http://gist.github.com/286946.
To create a generator gem ‘my_generator_gem’ within an existing rails 3 project just do:
my_rails_project $ rails3_gen my_generator_gem
Cheers! Kristian
I have also attempted to create a generator as a gem that should generate other generators as gems, in this case model generators…
http://github.com/kristianmandrup/rails3_generator_gem_generator
[...] Making generators for Rails 3 with Thor [...]
[...] Making Generators for Rails 3 With Thor – David Trasbo demonstrates how to use Thor (essentially a better Rake – sorta) to produce new generators for Rails 3 projects. This is a code heavy walkthrough. There's more on generators here. [...]
[...] 3.0 Beta: 36 Links and Resources To Get You Going, in which my Rails 3 generator guide is [...]
[...] Criando geradores para Rails 3 com Thor – David Trasbo mostra como usar o Thor, que é uma espécie de Rake melhorado, para criar novos geradores para projetos em Rails 3. É um tutorial com muito código e você pode ler mais sobre geradores aqui. [...]
[...] Making Generatoren für Rails 3 Mit Thor – David Trasbo veranschaulicht, wie Thor verwenden (im Wesentlichen eine bessere Rake – sorta) auf die neuen Generatoren für Rails produzieren 3 Projekte. Dies ist ein Code schwere Lösungsweg. Es gibt mehr auf Generatoren hier. [...]
[...] Making Generatoren für Rails 3 Mit Thor – David Trasbo veranschaulicht, wie Thor verwenden (im Wesentlichen eine bessere Rake – sorta) auf die neuen Generatoren für Rails produzieren 3 Projekte. Dies ist ein Code schwere Lösungsweg. Es gibt mehr auf Generatoren hier. [...]
Excellent, very good and simple article
[...] * dieses Ding: http://caffeinedd.com/guides/331-making-generators-for-rails-3-with-thor [...]