dmathieu - Introduction to rails engines

Introduction to rails engines

Thursday, 3 February 2011 in Development Articles by Damien Mathieu Creative Commons License

Since Rails 2.3 as a plugin and Rails 3.0 natively, it’s possible, for a gem, to be also a “Rails Engine”.
Being that, the plugin will be able to patch your rails application by adding it several features without you having to configure anything.

That’s the case for cylon, which will add a /robots.txt file to your project unless you’re in production environment.
Therefor, your application won’t be indexed in search engines in your tests environments.

Rails::Engine

Cylon is nothing more than a rack middleware (see voir Cylon::Rack ).
So if you’re not using rails, you just have to add it to your stack.

But if you’re using rails, you don’t even have to do that. Indeed in any rails environment, the Cylon::Rails is included.

That class is quite simple.

1
2
3
class Cylon::Rails < Rails::Engine
    config.app_middleware.use Cylon::Rack
end

In three lines of code, I can include my middleware. And you discover the awesomeness of engines. The principle is very simple : any class which extends from Rails::Engine is an engine and can extend your application’s configuration.

So here, we add the middleware Cylon::Rack to our application’s stack. There’s strictly nothing more to configure.

Controllers, models and views

You’ve probably heard of the devise library, which allows you to implement an authentication solution in your application. Devise is an engine !

If you look at the base of the code of devise, you’ll see a folder named “app”, which is quite similar to your application’s app folder.
And there’s a very good reason to it ! Any engine can create it’s own controllers, models, views, helpers and mailers. They’ll be included automaticaly in your application and that without you having to do anything.

So you can develop real applications which will be in fact only engines and reuse them in other applications after. Inception, here we are !
Let’s see it with an other engine, oauned.

OAuned is a bit more evolved than Cylon. It allows you, in three lines of code, to make your application and OAuth provider.

The same way than devise, OAuned is an engine which has it’s own controller and it’s own view which you’ll find in the app folder of the project.

Initializers

Just like cylon, oauned has a class which extends from Rails::Engine, it’s Oauned::Engine.

1
2
3
4
5
6
7
8
9
10
11
12
module Oauned
    class Engine < ::Rails::Engine
         # Force routes to be loaded if we are doing any eager load.
        config.before_eager_load { |app| app.reload_routes! }

        initializer "oauned.controller_helpers", :after=> :disable_dependency_loading do
            ActiveSupport.on_load(:action_controller) do
                include Oauned::ControllerMethods
            end
        end
    end
end

You can, however, see that in that class, we do totally different things.

1
config.before_eager_load { |app| app.reload_routes! }

First of all, as we define routes, we reload them when the library is being loaded.

1
2
3
4
5
initializer "oauned.controller_helpers", :after=> :disable_dependency_loading do
    ActiveSupport.on_load(:action_controller) do
        include Oauned::ControllerMethods
    end
end

Then, oauned has several methods which are accessible in all of our controllers. So we have to include them.
We can’t do that when the library is called for the first time because our application isn’t fully loaded then.

That’s why we use an initializer. Initializers are executed one after the other at the boot of your application, allowing us to include our helper methods at the right moment.

By default, if you didn’t put any :after, you initializer could be executed at basically any moment of the boot.
Here, we specify it because we want it to be executed after the last rails’ initializer, at the end of the boot of our application.

Les routes

I’ve mentioned previously the /app folder in which an engine can create it’s own controllers.
In fact, an engine is to be visualized as a full rails application in itself. So you can create there a config/ folder and add a file routes.rb.

That’s what oauned had originally : config/routes.rb.
If you wish to force some routes in your engine, that’s the way to do it.

However, if you wish to allow your engine’s users to plug it the way they want, they need to be able to add the routes wherever they want. In a scope for example.
That’s why that routes.rb has disappeared from oauned, replaced by lib/oauned/rails/routing.rb.

Our routes are now a simple method defined in the object ActionDispatch::Routing::Mapper.
That way, we can define them really the way we want. scope ‘/oauth’ { oauned_routing }

Create my very own engine

Now that you have the basics about engines, you probably want to create your own. And that’s cool !
You can use José Valim’s enginex which will provide you the basic architecture for your engine.
It’ll even be included natively in Rails 3.1 !

To go further

This article only gives you a preview of the possibilities provided by rails engines, which are just enormour.
If you want more informations about it (and many things to hack into rails), I can only recommand that you buy the book Handcrafting Rails Applications.