Francis's Octopress Blog

A blogging framework for hackers.

Rails 3.1 Engines – Mountable or Full? – Part 1

Rails 3.1 Engines – Mountable or Full? – Part 1

I decided to create my first rails engine about a week or so ago.  I figured this was a great way to learn Rails 3.1 and the new engine generator; however, I quickly ran into a problem.  Do I want a mountable or full engine?  What’s the difference anyway?  At the time, a quick google search showed that not many people really knew. The Rails code comments are great, but after reading them, I was still a bit confused.  For those of you out there in the same boat, here’s my attempt at highlighting some of the differences.

Full Engines

Engine Details

To generate a full engine, run rails plugin new myengine --full on the console. This will generate the engine’s basic structure. The first thing I noticed was that the typical assets associated with a normal Rails application were not generated; application.js and application.css were not created. In fact, the application controller was completely omitted, alluding that perhaps a full engine is feared toward supplementing a parent application only. As far as routing goes, the engine’s route looked like this:
1
2
3
4
# my_engine/config/routes.rb
 
Rails.application.routes.draw do
end
… and the dummy application’s routes file looked like this:
1
2
3
4
5
# my_engine/test/dummy/config/routes.rb
 
Dummy::Application.routes.draw
  # route stuff
end
Another item that was missing from the basic application structure was the database folder. For engine configuration, a full engine is not namespaced, so the engine.rb file was essentially blank.
1
2
3
4
5
6
# my_engine/lib/my_engine/engine.rb
 
module MyEngine
  class Engine < Rails::Engine
  end
end
Now, the rails comments state that engines allow you to add a “subset of functionality to an existing Rails application.” Since the full engine is missing an application controller, it seems that its purpose is to plug into an existing application without namespacing to add functionality to the parent application. To test this theory, I generated a new application and referenced my engine in the parent application’s Gemfile.

Routing

Since the engine is not namespaced, the engine’s routes are incorporated into the parent application’s routes directly. To test this, I created a model within the engine called “Post”, along with the migration, controller, and helper to match. To create the migration, I also had to build the my_engine/db/migrate/ folder, which was not created when I generated the engine. Running rake routes within the parent application resulted in the following:
1
2
3
4
5
6
7
    posts GET    /posts(.:format)          {:action=>"index", :controller=>"posts"}
          POST   /posts(.:format)          {:action=>"create", :controller=>"posts"}
 new_post GET    /posts/new(.:format)      {:action=>"new", :controller=>"posts"}
edit_post GET    /posts/:id/edit(.:format) {:action=>"edit", :controller=>"posts"}
     post GET    /posts/:id(.:format)      {:action=>"show", :controller=>"posts"}
          PUT    /posts/:id(.:format)      {:action=>"update", :controller=>"posts"}
          DELETE /posts/:id(.:format)      {:action=>"destroy", :controller=>"posts"}
As you can see, the Post routes from the engine were included directly into the parent application. After running rake my_engine:install:migrations and rake db:migrate in the parent application, I was ready to test how the engine’s controllers and helpers would integrate. In my engine, I had a simple view template:
1
2
3
# my_engine/app/views/posts/index.html.erb
 
<p>Hi There!</p>
Of course, navigating to /posts in the parent application showed an almost blank page with the words “Hi There!” The controller actions and views from the engine were incorporated into the parent application. One helpful feature of engines is the fact that their controller methods and views can be overridden simply by placing similar files within the parent application. For example, I created a new view template for the Post index action within the parent application that looked like this:
1
2
3
# parent_app/app/views/posts/index.html.erb
 
<p>Good Bye!</p>
Now visiting /posts in the parent application shows “Good Bye!” That’s great, but what about the engine’s helpers? I tested that functionality by adding a method to the Post helper back inside the engine.
1
2
3
4
5
6
7
8
9
# my_engine/app/helpers/posts_helper.rb
 
module PostsHelper
 
  def test
    raw("<p>hello world</p>")
  end
 
end
I then changed the parent application’s view template to use the helper.
1
2
3
4
# parent_app/app/views/posts/index.html.erb
 
<p>Good Bye!</p>
<%= test %>
This time, visiting the page again in the parent application resulted in the words “Good Bye!” and “hello world.” Therefore, the engine’s helper methods were directly exposed to the parent application, allowing the parent to use them.

Summary

In summary, it seems as though full engines are best suited to augment a parent application. The engine routes, controllers, models, helpers, etc. are exposed to the parent application which allows for easy access, but could result in namespace conflicts. It looks like this post is getting a bit lengthy so I’ll stop it here and talk about mountable engines soon.