Image may be NSFW.
Clik here to view.Photo courtesy Tambako the Jaguar.
As Rails matures, it’s becoming more and more common to see it powering large-scale applications with many moving parts. These can be a chore to maintain in any framework, but a reusability mechanism can make all the difference in the world, if only so you can break code into mentally digestible areas.
Until recently, Rails lacked a recommended way to tackle this. Plugins didn’t do the job, and engines weren’t officially supported. Without that support, it was difficult to justify making them a fundamental dependency of your application. Of course, engines were the way to go in the end, and in Rails 3 they were finally merged into Rails itself.
There are still a few speed bumps for engines in the current version (at the time of writing, 3.0.3). Luckily, Piotr Sarnacki devoted his summer to improving engine support and has done a fantastic job bringing us closer still to true mountable applications. So let’s try it out!
By the end of this article, you should have a functioning application and engine running on edge Rails (3.1 beta). We’ll cover the other aspects of this in a future article, but this will get you up and running.
The road to bundle install
In this first section, we just want to do enough to get our first bundle install
going.
Let’s create our main application and the engine.
rails new baby_bear
Now we’ll need a gem specification. Bundler has a command for this, but first we’ll need to get rid of the Gemfile that Rails created. We’re also going to update Bundler to ensure that the bundle gem
command works how we want it to—it fails with older versions.
sudo gem update bundler
yes | bundle gem baby_bear
The last command pipes bundle gem
through yes
, automatically overwriting the Rakefile and .gitignore files (we don’t need the original versions).
Now you need to declare a dependency on baby_bear from papa_bear. In papa_bear/Gemfile, add the following line:
By default, Rails assumes you want to depend on the version of Rails that you used to create the application. We want to use edge Rails, so we’ll have to change that.
Remove this line from papa_bear/Gemfile:
Then replace it with these lines:
gem 'arel', :git => 'git://github.com/rails/arel.git'
gem 'rack', :git => 'git://github.com/rack/rack.git'
(Edge Rails depends on edge Arel and edge Rack, also—release versions won’t cut it.)
For completeness, we should also be explicit that the baby_bear gem depends on Rails. For this, we can’t explicitly depend on Git (as we can in papa_bear’s Gemfile), but we should at least make the dependency meet 3.0. Since papa_bear’s Rails dependency satisfies baby_bear’s, this should be fine.
Add this to baby_bear.gemspec:
Alright, we’re ready to go!
bundle install
And our dependencies are installed.
Keep in mind that when working with engines, you only ever need to run bundle install
from the main application. It pulls in all the engines’ required gems.
Also, be sure not to run the last command as sudo.
Onto rails server
Because the engine isn’t a complete app, we can get rid of some configuration to reduce confusion.
rm config/application.rb
rm config/boot.rb
rm config/database.yml
rm config/environment.rb
rm -rf config/environments
rm -rf config/initializers
In papa_bear/config/routes.rb, add this line within the main block:
There isn’t actually a BabyBear::Engine right now, but before we create it let’s make sure we remove any references to BabyBear::Application
. If you’ve been following the instructions, this should only be in baby_bear/config/routes.rb. Replace BabyBear::Application.routes.draw
with BabyBear::Engine.routes.draw
.
Now the engine. In baby_bear/lib/baby_bear.rb, add the following line at the top:
Then create baby_bear/lib/baby_bear/engine.rb from this template:
module BabyBear
class Engine < Rails::Engine
end
end
We have a choice at this point: either we can namespace all of our engine’s application classes (controllers, models, etc.)—or not. Namespaces help prevent naming conflicts between your engine and the main application, and frankly, if you’re going to the trouble of developing an engine, you probably care about separation of responsibilities and reusability already. In any event, this tutorial will use namespaces.
So let’s add the following line to BabyBear::Engine
:
This creates a little more work for us up front because we’ll have to move our controllers, helpers, mailers, models, and views into their own baby_bear subdirectories.
app/helpers/baby_bear \
app/mailers/baby_bear \
app/models/baby_bear \
app/views/baby_bear
mv app/controllers/application_controller.rb app/controllers/baby_bear/
mv app/helpers/application_helper.rb app/helpers/baby_bear/
Be sure to change ApplicationController
to BabyBear::ApplicationController
. Ditto for ApplicationHelper
.
In order to make sure that the engine is running normally, we’ll need to create a page to test it. This is a good time to give you the bad news: engines can’t use the rails
command. Sorry, no code generation for baby_bear. In practice, I’ve found that the benefits of engines outweigh this (rather disappointing) drawback, however.
mkdir app/views/baby_bear/index
touch app/views/baby_bear/index/index.html.erb
Here’s the controller…
class IndexController < ApplicationController
def index_action
end
end
end
…and its corresponding view.
One last thing: we need a route. Add the following line to baby_bear’s config/routes.rb, in the main block:
Finally, we’re ready to start WEBrick.
rails server
Are you ready for this?! Go to http://localhost:3000/baby-bear.
Finally, success! Hopefully, anyway. If not, be sure to double-check your steps and check out the code linked below. Good luck!