Note: This was originally posted on my previous blog. It is archived here for sentimental, educational (to me) and, in the unlikely case, actual value
Note: This article was written when Rails 3 was still in the early beta stages. It has not been updated since and it is likely that some information is inaccurate or incomplete. I think, the general information may still pertinent, but please read with that in mind.
Digging deeper into Rails 3, this week, I spent a lot of time with the new Railtie API (Plugin API). I thought I would share some of what I learned. There is a lot of information. In this post I will address the big picture and things of note I found while working on a couple plugins.
The Rails Plugin API got a huge overhaul in Rails 3 (parts of it were included in Rails 2.3 although not in such a robust fashion). While a lot of things have changed, a lot has stayed the same as well.
Basic plugins can still live in the vendor/plugins directory just like in previous Rails versions. The file init.rb is, also, still run when the plugin is loaded. Many plugins can still use this approach. For example if you simply want to extend ActiveRecord with something like acts_as_* vendor/plugins will probably do just fine.
Under the hood plugins are handled very differently. Every plugin in the vendor/plugins directory is made into an instance of the new Rails::Plugin class. More on this a bit later. I will mention now though, you should never have a need to subclass Rail::Plugin yourself.
The Railtie is where the magic really starts to happen. In Rails 3, just like in previous versions, plugins can be distributed as gems. When a plugin is distributed as a gem many bonus features become available like an easy way to include rake tasks, generators and sub-applications.
In order to distribute your plugin as a gem that works with Rails you must make it a Railtie or an Engine. To do this, include a subclass of Rails::Railtie or its subclass Rails::Engine, respectively. Let’s add Rails::Engine to the list of things I will talk about later.
Making your plugin a Railtie allows it to hook into all parts of the initialization process, setup its own intializers and interact with the application object. The Railtie also allows you to specify generators for the orm, testing and templates as well as loading your own generators and rake tasks. The Rails 3 boot processes is entirely new as well and there are many points where your plugin can integrate and modify the application.
Once you install your gem and add it to the your app’s Gemfile rake tasks and generators will become available.
Rails::Engine (Apps inside an App)
Rails engines have been around for a while now but in Rails 3 they are given their official place. A Rails engine allows “sub-applications” to run inside another application. There are many examples of this but authorization is one example commonly tossed around. Any functionality you wish to share accross multiple Rails applications belongs in an engine.
Engines, like Railties have a special class that must be subclassed and included in your plugin. Rails::Engine is actually a subclass of Rails::Railtie but with a much richer feature set.
Engines have both their own configuration and share configuration with the application that includes it. For example, your engine can have completely separate view paths. You are not forced to use an app/ directory next to lib/. If you wish you can include controllers, views, models, and metal anywhere inside your engine’s root directory. Middleware configuration is shared between the application and engine, however.
Rails engines are crazy powerful and probably a favorite feature for any Rails developer. People will do really cool things with them and even cooler things stacking them up next to each other.
A little more on Rails::Plugin and a note on Rails::Application
I said I would talk a bit more about Rails::Plugin. As I have already mentioned, an instance of Rails::Plugin is instantiated for every plugin in vendor/plugins. Rails::Plugin should not be subclassed like Rails::Railtie or Rails::Engine. This is because it is loaded much later in the load process and has considerably less access to the application. The Rails::Plugin
On to Rails::Application. Every application you create in Rails is an instance of Rails::Application. Only one instance of a Rails::Application can run in a single thread at any given time. However, Rails::Application is simply a subclass of both Rails::Railtie and its subclass Rails::Engine. Other topics related to Rails::Application are out of the scope of this post. Read the edge guides for more info.
Listed below are some resources I used while working on several Rails 3 plugins this week.