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.
… and the dummy application’s routes file looked like this:
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.
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.
As you can see, the Post routes from the engine were included directly into the parent application.
After running
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:
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.
I then changed the parent application’s view template to use the helper.
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.
Full Engines
Engine Details
To generate a full engine, runrails 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 |
1
2
3
4
5 |
# my_engine/test/dummy/config/routes.rb Dummy::Application.routes.draw # route stuff end |
1
2
3
4
5
6 |
# my_engine/lib/my_engine/engine.rb module MyEngine class Engine < Rails::Engine end end |
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. Runningrake 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" } |
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> |
1
2
3 |
# parent_app/app/views/posts/index.html.erb <p>Good Bye!</p> |
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 |
1
2
3
4 |
# parent_app/app/views/posts/index.html.erb <p>Good Bye!</p> <%= test %> |
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.