For a long time I’ve been wanting to incorporate a core application into another rails application. I’ve been working on a very deeply coded forum software called Ki-Rin and thought it would be wonderful if I could just move it entirely to an engine and let another application use it without restricting either application’s core functionality.
With Rails 3, and especially 3.1 (edge rails), you now have the ability to create a fully functioning engine and add it to your application. However, with every new learning step, it’s sometimes difficult to see the entire picture without having some code to use as a reference point. Therefore, I created a very simple example engine called baby_dove
which will allow you to explore engines within Rails, and give you an idea of where to proceed.
Because I like living on the edge, I wanted to make an example that works with the latest rails version, 3.1.
You will be creating a Main Application and adding the baby_dove engine as a gem to your application. By adding the engine, you will be in effect sharing routes, controllers, models, views, libs, and rails dependencies globally.
Find a directory on your system where the mainapp and baby_dove will exist. They will be located in the same root directory, per below:
- some root folder
-
mainapp
- Your main application -
baby_dove
- The rails 3 custom engine
The mainapp will be its own rails edge application. The baby_dove app will be a fully functioning rails engine. Do not create either directory yet. Follow the instructions below:
From your root folder as per above..
-
git clone [email protected]/elricstorm/baby_dove.git
-
rails new mainapp -d mysql
create create README create Rakefile create config.ru create .gitignore create Gemfile create app create app/controllers/application_controller.rb create app/helpers/application_helper.rb create app/mailers create app/models create app/views/layouts/application.html.erb etc....
-
cd mainapp (your main application - not the engine).
-
Edit config/database.yml from your main application and update as necessary.
-
Edit your mainapp gemfile and add/substract the following, and then save it:
-
#comment out gem ‘rails’, ‘3.0.3’ => #gem ‘rails’, ‘3.0.3’
- add
-
gem
‘rails’, :git => ‘git://github.com/rails/rails.git’
- add
-
gem
‘arel’, :git => ‘git://github.com/rails/arel.git’
- add
-
gem
‘rack’, :git => “git://github.com/rack/rack.git”
- add
-
gem
‘baby_dove’, :path => “../baby_dove”
- add
-
gem
‘coderay’
- add
-
gem
‘jquery-rails’
Your mainapp gemfile should look like this:
source 'http://rubygems.org' gem 'mysql' gem 'rails', :git => 'git://github.com/rails/rails.git' gem 'arel', :git => 'git://github.com/rails/arel.git' gem 'rack', :git => "git://github.com/rack/rack.git" gem 'baby_dove', :path => "../baby_dove" gem 'coderay' gem 'jquery-rails'
The first three gems we added allow you to use edge rails. The baby_dove gem is being required from a direct path. You can add it as a gem once you compile it, but by doing it this way, you can update the gem engine without having to rebuild it all of the time. It’s like being on “edge engines”..
The coderay gem is being added because it’s a part of our engine example, and jquery-rails is what we’ll be using in our main app for javascript. Prototype, while okay as a basic javascript library, has been replaced by many developers who now enjoy using Jquery.
-
Delete all javascript files in your main app minus application.js and rails.js.
-
Delete the index.html file in your main apps public directory.
-
cd mainapp (your main application not the engine).
-
Type bundle install and hit enter.
-
After bundling is finished type rails generate baby_dove and hit enter.
You should see something similar to
=> create db/migrate/20101231052109_create_baby_dove_model_data.rb
What just occurred is that the baby_dove engine created a migration file in your main application that will handle the installation of any tables or data we want to add. You can find out more by looking into the engine source directories, especially under:
baby_dove/lib/generators
You can go inspect the migration that was installed from the engine to your mainapp.
-
–> Run rake db:create
-
–> Run rake db:migrate
-
–> Run rails server
Open up your localhost page and go to the following url:
localhost:3000/birds (or replace what your localhost:port is)
By default, I’ve set the routes.rb file in the engine to use the birds view as the default root.
You should see the view being added from birds. Play around with the birds controller and start inspecting every file in the engine and make sure to read the notes. You’ll begin to understand just how easy it is to create an engine in rails 3.1.
-
baby_dove/app/controllers/baby_dove - contains our engine controller files
-
baby_dove/app/helpers/baby_dove - contains our engine helper files
-
baby_dove/app/models - contains our engine models
-
baby_dove/app/views/baby_dove - contains our engine views.
-
baby_dove/app/views/layouts - contains the baby_dove layout (yes you can use your own engine layout).
-
baby_dove/config/initializers - you can include any initializers you want to work with your engine here.
-
baby_dove/config/routes.rb - contains all of the engine routes.
-
baby_dove/lib/baby_dove.rb - houses our starting engine and includes requires.
-
baby_dove/lib/bird_feeder.rb - a simple module that I created to show how you can extend modules/classes/methods.
-
baby_dove/lib/baby_dove - houses our engine.rb file
-
baby_dove/lib/extensions - has an action_controller handle for custom methods
-
baby_dove/lib/generators - handles migrations
-
baby_dove/lib/nbproject - I created a netbeans project so if you use netbeans you can open this as a project in the IDE. Netbeans is not necessary to use this engine.
-
baby_dove/lib/public - With engines you now have the ability to serve static assets or incorporate them into your engine.
-
Run rake routes from your main app so you can see how it’s pulling in the engine routes as well as the application routes.
-
Open up all of the folders and files in the engine and read any notes I’ve added which will offer further explanations.
There are two very important decisions that you will ultimately decide on when constructing your engine and that is whether or not you will be namespacing it. If you completely namespace it, including the models, the model table names will be prefixed with engine_tablename. For instance, if baby_dove were completely namespaced on the models, the table for birds would be named baby_dove_birds instead of birds. Namespacing is fantastic on the surface, depending on how you utilize it, because you keep the engine completely separated from conflicts with the main application. However, there are some annoyances that bother me and I really think if you can be certain that your engine isn’t going to create conflicts, then decide whether you really want to namespace it entirely, or whether you want to namespace certain aspects of it.
You can find more about namespacing mountable apps here: piotrsarnacki.com/2010/12/21/mountable-apps-tutorial/
I’m more than happy to answer questions as I find time to browse my email. If you have a question about engines or just want to chat, write to [email protected] and send me some email. I look forward to hearing from you!