Tuesday 3 April 2007

Plugins and engines and gems, oh my!

So I was talking to friend of mine about what I was doing and how a gem was too heavy for what I wanted. He pointed out that I should consider doing it as an engine instead of a plugin.

What's the difference between a plugin and an engine you ask?

I didn't know either... engines seem to be the forgotten children of Rails in all the current plugin-love that's going around (not knocking it - I like plugins too).

Anyway, it turns out that an engine is a plugin - with some differences. An engine requires that you install the "engines" plugin first - you can't just script/install the plugin itself. However, adding that engines plugin allows your own baby to do a hell of a lot more than your standard plugin-ly functions.

An engine is a complete, vertical slice of MVC. An ordinary plugin, by comparison, is generally a small chunk of functionality such as a helper library. Engines can have an /app/ directory structure identical to a full rails site. You can also add routes and migrations (with some user-tweaking required). This allows a fully encapsulated chunk of functionality (such as what I was proposing for my little blog).

Now, mephisto and typo are both gems. This is because they contain a huge amount of functionality (they well deserve their leading status in the Rails-blog field). They contain more functionality than what most people associate with a simple plugin... more than what people would associate with an engine (if people used engines more commonly) - so they are distributed as gems.

So, to sum up:

  • plugins are for small bundles of helpful code
  • engines are for encapsulated slices of vertical functionality and
  • gems are for complex, multi-functional systems.

So how do I make an engine?

The information on how to make an engine is a bit scattered about the web. There doesn't seem to be a whole lot out there in the way of tutorials (at least not compared to plugins).

Some of the info seems to be downright incorrect. The article that looked most promising was: the alterthoughts one, but it seems to be based on an old version of engine-construction that Just Doesn't Work. I couldn't get script/generate engine my_blog to give me anything but "can't find a generator for engine". This article is clearly built on an earlier version of both Rails and engines

The railsdoc on engines is fairly complete (though it's not a step-by-step tutorial). It turns out that you can just start out with a rails app - and turn it into an engine. Two things to remember:

Routes
Copy your engine's routes.rb into the root directory. Then instruct your users to add map.from_plugin :your_plugin into their routes.rb.
Migrations
If you have migrations, make sure they are numbered from 001. Then leave instructions for your user to run script/generate plugin_migration, then rake db:migrate.
Shared plugins
A blog needs to share the authentication plugin. But which authentication mechanism are you using? I could assume you're using the same as me (RESTful auth right?) but that's not likely. The best option is to write a wrapper library that allows people to override the functions with their own authentication system, if they choose.
Other plugins
If your plugin depends on other plugins there may be some duplication. I'm sure there must be a way that minimises double-loading of plugins eg if your plugin uses acts_as_taggable, and so does their site - maybe your install.rb could check for that and not bother installing it - but I think that may be over-optimisation.

I'm sure there must be a way to put all these tasks into "install.rb" - but I haven't had a chance to do that yet.

So, what have you done?

So I wrote a very tiny blog app (currently all is does are posts). It was enough to demonstrate the point for me while I figure out how to do the engine thing. I'm still playing with it in my spare time - but I probably won't get in installed until after the Easter long-weekend. At that point I'll also find somewhere to upload it so people can have a play with it.

No comments: