Engines have been around for a while now in some for or another, but with the release of Rails 3, they became an integral part of the framework, and IMO they make it easy for any developer to create a plugin. Rails 3.1 will make Engines even better thanks to the SOC work done by Piotr Sarnacki, but I will be focusing on what is available in Rails 3.0.3 in this post.
Engine? Say what??
In case you are not familiar with Rails 3 Engines, in a nutshell… they are sub-applications that are packaged as gems and can run inside a standard Rails application. They give you most of the functionality of a standard rails app, including the controllers, views, helpers, configuration, etc… Engines lay the groundwork of having “mountable” apps in Rails.
So, why should I care?
I think this is useful on 2 fronts. The first being fully functional sub-applications such as a blogging engine. You could drop the engine into your Gemfile and you would immediately have the functionality of a full-featured blog that is running in the same process as your parent app. The second use is of more interest to me, although I have seen little discussion about it. I guess you could call it building blocks. These engines would be smaller than something like a blog. For example, lets say you want to add comments to your application. Right now I think most people would probably look at something like this: https://github.com/jackdempsey/acts_as_commentable. Pretty cool. It gives you a migration, a model and the polymorphic association needed to make a model “commentable”. But you are left with implementing the views & controller yourself. Although it is not terribly difficult to implement this, it still takes some time to do it. Let’s look at how an Engine could improve this plugin. You could basically do everything with 4 lines of code and a few commands.
#Gemfile gem 'comment_engine' # command shell - add the migration and run it rails g comment_engine rake db:migrate #model class Article < ActiveRecord::Base is_commentable end #view <%=comment_form(@article) %> <%=comment_list(@article) %>
So you add the engine to your Gemfile, you make your model commentable and you use 2 helpers in your view. This will give you a fully functional comment form in your view and list of comments associated with that model. So how does the Engine make this so easy? You can use controllers, routes, partials, and helpers. So the plugin would include the following:
1. comment.rb – your comment model
2. generator – migration template for Comment
3. Commentable module – AR::Base will extend this module. Uses polymorphic associations to allow models to be “commentable”
4. Helper module – included in the parent app’s application_helper. This provides some shortcuts like comment_form and comment_list. Basically these methods take the model and generate a form or list based on that commentable model.
5. comments_controller.rb – has a create method that creates the comment and redirects to :back.
6. routes.rb – adds resource routes for comments.
While these are the necessities, there are plenty of other things that you could add to an engine like this, for example: attaching the authenticated user to the comment.
This makes development a breeze, as long as you have good defaults in the Engine. One of the main things that “building block” engine authors should focus on is the view portion of this. This is a fairly new concept when it comes to Rails plugins and I have not seen it implemented in many plugins. The plugin needs to be easy to style or give the developer the choice to completely override the view with their own. I guess it is also important to allow the parent app developer to override or add functionality to the controllers and models. The tricky part about that is re-opening the classes. Rails blocks the engine classes from loading if they are the same file name/path as something in the parent app. This is a concept that I am still exploring. Ideally, you would want to allow the developer to drop in a class with the same path that would reopen the class from the engine. Although this could be dangerous if the app developer is not aware of the class in the Engine, IMO it would be very useful if you use it wisely.
How I have been using Engines
I started working with Engines using the Rails 3 betas, and I am still working on figuring out the best practices. Although I am still experimenting quite a bit, Engines have become a major part of my workflow. IMO, the Engine plugin is the most important addition to Rail 3, since they make your code extremely modular and can increase your productivity by quite alot. I am really looking forward to seeing what kind of “building blocks” are created using this plugin architecture. Here is what I have focused on so far for my own environment:
1. authentication_engine – basic and simple authentication that includes signup, login and roles. Views and controllers included.
2. social_engine - includes comments, ratings, reviews, votes, reputation, and helpers for things like facebook like and tweetmeme. Most if this is impelemented in the same manner as I described for the comments engine at the beginning of this post
I am working on extracting alot of the code into an open source plugin for #2 and #3. I should have the social_engine on github in the next few weeks. The UI engine definitely needs alot of work, in order to be useful to developers other than myself.
I love Rails and Rails 3 is leaps and bounds above the previous versions in terms of modularity and extensibility. I like the fact that it has a solid base architecture that plugins can sit on top of. Although there are plenty of productivity gains by using Rails alone, I think the plugins that sit on top of Rails give you the greatest productivity gains. I am looking forward to seeing the Engine ecosystem that is sure to develop in the next few years. I think it will remove many of the mundane development tasks that developers have to worry about currently, allowing us to focus more on stitching together the building blocks that we need for our applications to function… not on the details of creating these building blocks.
Thoughts? Leave me a comment