Friday, April 23, 2010

Improving macro registration for Remarkable 4

Since one of the emerging design principle for Remarkable 4 is to shadow Rails 3, and Rails 3's biggest strength is its modularity, I've decided to split Remarkable into components following the Rails 3 modules. For now, we have remarkable (core), remarkable-activemodel, and remarkable-activerecord. I plan for remarkable-rails to be split into remarkable-rack and maybe remarkable-actioncontroller.

However, with all of this, we now introduced the idea of macro collections depending on other macro collections. However, the way these macros are loaded into Rspec does not easily support this proliferation of modules.

Currently, macro developer has to do this:

Remarkable.include_matchers!(Remarkable::ActiveModel, Rspec::Core::ExampleGroup)

But seriously, do the developers need to know they are targeting Rspec::Core::ExampleGroup? This should be a default, with it being overridden by people who know what they are doing.

The other thing is that due to the modularity of Rails 3, it makes sense to have a set of macros built on top of other macros. So for example, ActiveRecord macros are built on top of ActiveModel macros. Not everyone will want to include the entire Rails stack just to use the macros, and we certainly don't want to expose a target (at least, not without having to dig).

Here is a mock API of what I'm thinking of:

The developer adds this:

Remarkable.register_macros(:activemodel, Remarkable::ActiveModel)
Remarkable.register_macros(:activemodel, Remarkable::ActiveModel,
:target => Rspec::Core::ExampleGroup)
Remarkable.register_macros(:activerecord, Remarkable::ActiveRecord,
:depends_on => :activemodel

This makes it easy for plugin developers:

Remarkable.register_macros(:foreigner, Remarkable::Foreigner,
:depends_on => :activerecord)
Remarkable.register_macros(:paperclip, Remarkable::Paperclip,
:depends_on => :activerecord)

If you are using only pieces, then you can active the macros:


Which will idempotently include the macros and its dependencies into Rspec2.

Remarkable::Rails, being the gregarious gem it is, will call:


... which will load up all of the macros. This would be in keeping with the expected convention for Rails while still providing modularity.

I've opened up this issue on the github to discuss this further.

1 comment:

  1. I realized a day later that this idea is a dumb idea. Rubygems/Bundler already handles dependencies, so why not just do things like require 'remarkable/active_record' and be done with it?