Friday, October 5, 2007

Using Rubigen With Scope for Merb Plugins

I like to help where I can with contributing to merb. I tried to use the merb_sequel plugin the other day and found that there are no generators. It seems I've been spoiled by the generators in rails getting the basic empty model, migration and test stubs are great when I want to write a model. The only generator for merb_sequel was a migration one but I wanted more. That was ok though, I wanted to learn about generators anyway.

Merb has the rubigen gem in it to generate the application structure, so it's only natural to use it. Rubigen was extracted from rails by DrNic and I was lucky enough for him to explain the general workings of it to me. The major idea that I really liked was the ability to use scope in your gem generators.

What does using scope in your gem generators mean? Well, there is a line in a merb applications script/generate file


RubiGen::Base.use_component_sources! [:merb, :rspec]

What this line does is when you call script/generate it goes through all your gems and loads any generators found in the folders merb_generators and rspec_generators. [:merb, :rspec] are the generator scopes that merb has by default. All gems with merb or rspec scoped generators are loaded and available when script/generate is called. Newgem has some great examples of this in use. How do you scope these in your gems? For this example, include them in the following directories in your gem.

merb_genereators
rspec_generators

So if you do

ruby scirpt/generate

A list of all the generators of all the gems in those scopes. OK but what good is that?

In the merb_sequel plugin this allowed me to cater for both rspec and test unit stubs when generating a model. I just create the same named generator in a different scope and then whichever scope the user has selected is available. I used this when generating models to include the stubs. The directories I used were

merb_generators
sequel_migration
sequel_model
rspec_generators
sequel_model_test
test_unit_generators
sequel_model_test

Then in the model generator

record do
...
m.dependency "sequel_model_test", [@name]
end

This is where drnic's magic happens. The scope, set in script/generate, will only pick up the sequel_model_test generator from the rspec_generators directory and not the test_unit one. So when the dependency gets called it's the one defined in rspec_generators that it finds. Of course, if the scope is set to :test_unit

RubiGen::Base.use_component_sources! [:merb, :test_unit]

the sequel_model generator, when the dependency is called will find the seuqel_model_test defined in the test_unit_generators directory instead and use that.

This is great in that it allows you to select the flavor of testing that you like, and the generators will give it to you. That is providing that the gem has both rspec and test_unit generators defined.

By the way, if you call a dependency and there is neither scope is selected then an error will be raised. This is because there is no dependency of that name loaded to find.

In the merb_sequel plugin I've hacked a bit into it to check that an :rspec or :test_unit scope has been set. If a scope has not been set the test stub won't be generated and a message will show up.

I haven't found a way to dynamically set and then unset the scope which I'm guessing would be good for default scopes. That would be better in this case than the hack to not generate the test stubs.