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
3. ui_engine – includes good defaults for the base layout, a configuration for the layout, css, javascript, and plenty of partials that you can override in the parent app. Based on HTML5 boilerplate. My intention with this engine is to get rid of all the BS work you have to do for your front end. I know layout and view stuff differs alot per application, but I find myself doing alot of the same things for each app when it comes to UI.
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.
Conclusion
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




#1 by Arvind on April 18, 2011 - 1:58 am
Quote
Hi… i am a newbie to rails, i have an idea about Engine and it’s purpose.. but i am in a bit confusion about How to create engines and how to integrate them with main application… i would be happy if you suggest me a better approach.
Thank you
#2 by cowboycoded on April 18, 2011 - 5:16 am
Quote
Here is a good starter tutorial:
http://www.themodestrubyist.com/2010/03/05/rails-3-plugins—part-2—writing-an-engine/
As a newbie, you should probably make sure you understand Ruby and Ruby on Rails fairly well before you dive into Engines. I would recommend reading at least one book on Ruby first. You will be able to write much better code if you understand Ruby before you learn the Rails framework. Then read a book on Rails. Then read a book on Ruby metaprogramming, which will introduce you to alot of the concepts that are used to create the Rails framework.
#3 by johnmartirano on May 24, 2011 - 11:18 am
Quote
Agreed on the importance of Rails 3 engines – I have recently refactored several Rails 2.3.5 apps which relied on over a dozen plugins into Rails 3, converting all the plugins into engines in the process. There are still a number of outstanding issues to resolve IMO, but these issues were unresolved in Rails 2.3.5 as well. Namely a nice solution for database migrations and seeds from engines which may be included in multiple applications and which are under active development. The the generator approach has serious limitations and flaws!
#4 by cowboycoded on May 24, 2011 - 12:17 pm
Quote
John, thanks for the comment! What problems are you running into using the generator approach? I have been using generators and while it is necessary to do that extra step to run the generator, I still don’t think it is a bad approach. Have you been using a different approach?
Also, have you run into situations where you need to add functionality to an existing controller or model that is contained within the app? That is my main gripe, since you can’t add a class with the same name and just reopen it to add methods. Rails will not load the engine file if it is the same path as the app file (like /app/models/widget.rb). There is a monkeypatch that can be applied to make it load both files, but it is a serious hack.
All in all, I think engines are killer, but like you said, they can still be improved upon.
#5 by Adam on July 26, 2011 - 11:00 am
Quote
Hey John,
Have you managed to put up your social_engine to github yet? In particular, I’m interested in seeing how to pass an authenticated user to the engine. I’m attempting to build an engine to provide customer care features. My first attempt is using the acts_as format to establish my engine’s associations with the parent’s User model, but I’m not sure how to detect from the engine point of view if a particular user is signed in to the parent app or not.
https://github.com/astjohn/cornerstone
Thanks!
#6 by cowboycoded on July 26, 2011 - 11:48 am
Quote
Adam,
here is the repo:
https://github.com/charlotte-ruby/social_engine
Its still very much a work in progress, but that may help.
You should be able to use the current_user in your controllers and views, just like you do in a regular rails app. Can you point me to the code in your repo where you are trying to do this?