Introduction

In Rails world it’s just a piece of code, which plugged to existing application, extends its basic functionality. Good examples would be: ActiveAdmin, AmberBit’s Translator or Devise. Also Rails application is example of an engine.

Really nice use for engine that comes to my mind would be API versioning. A website would have no API at all on start. One instance of it would be deployed with /api/v1 mounted, and other /api/v2/ . Clean, easily separable and providing only functionality that is needed in given case.

There is also detailed guide available on RailsGuides.

Getting started

All you really need is to create new directory for your engine, let’s say foo_baz , .gemspec file and a engine.rb file within lib/foo_baz directory. The key thing here is to inherit from ::Rails::Engine .

module FooBaz class Engine < ::Rails::Engine isolate_namespace FooBaz end end

And to mount it into existing Rails application, add newly created library to Gemfile :

... gem 'foo_baz', path: 'lib/engines/foo_baz' ...

Nothing unusual in foo_baz.gemspec file as well. Very basic, minimal content would be for example:

$:.push File.expand_path("../lib", __FILE__) Gem::Specification.new do |s| s.name = "foo_baz" s.version = "0.0.1" s.authors = ["Kamil Dziemianowicz"] s.summary = "Summary of FooBaz." s.files = Dir["{app,config,db,lib}/**/*"] s.add_dependency "rails", "~> 4.2.4" end

Of course fully-featured engine will be much more complex. Rails has built-in generator which will create dummy structure & files for your engine and even more. To run it, simply invoke rails plugin new foo_bar with option --mountable . It will generate directory structure not surprisingly similar to standar Rails application structure.

create create README.rdoc create Rakefile create foo_bar.gemspec create MIT-LICENSE create .gitignore create Gemfile create app create app/controllers/foo_bar/application_controller.rb create app/helpers/foo_bar/application_helper.rb create app/mailers create app/models create app/views/layouts/foo_bar/application.html.erb create app/assets/images/foo_bar create app/assets/images/foo_bar/.keep create config/routes.rb create lib/foo_bar.rb create lib/tasks/foo_bar_tasks.rake create lib/foo_bar/version.rb create lib/foo_bar/engine.rb create app/assets/stylesheets/foo_bar/application.css create app/assets/javascripts/foo_bar/application.js create bin create bin/rails create test/test_helper.rb create test/foo_bar_test.rb append Rakefile create test/integration/navigation_test.rb

Most important parts of it are:

foo_bar.gemspec - has the same function as you would expect from gem’s .gemspec file

- has the same function as you would expect from gem’s .gemspec file lib/foo_bar.rb - FooBar module definition

- FooBar module definition lib/foo_bar/engine.rb - mentioned before Engine file

- mentioned before Engine file config/routes.rb - defining routes engine will provide, same as Rails app

Also noticable things are similar to Rails application application_controller.rb and application_helper.rb , both namespaced into FooBar module. There is folder for image assets. Analogously, JavaScript and CSS files would go to app/assets/javascripts/foo_bar/* and app/assets/stylesheets/foo_bar/* folders. Views shoud be put into app/views/foo_bar/* folder and so on… The rule of thumb is that everything should be namespaced, and if it’s namespaced, it should be put where it usually goes in Rails app, but including foo_bar subfolder.

Isolate namespace

Very important part of engine class definition is declaration of namespaces isolation:

... isolate_namespace FooBar ...

It will force scoping all classes (AR models, controllers, helpers etc…) into FooBar module. Thereby both base application and engine library could define User class, with corresponding database table, separated views, and they won’t clash. Of course it is not mandatory to isolate engine from base application. It could be useful when created engine won’t be used by other people, or developer is aware of all classes and routes defined in it. Generally, better keep them namespaced.

Rails generator

Created engine comes with executable bin/rails script. Its main purpose is to help generate proper models/views/controllers. Keep in mind, that all engine resources should be namespaces inside engine module name. To run sample generator:

$ bin/rails g model kitty name:string

In result, proper folders structure and class-module hierarchy will be preserved:

... invoke active_record create app/models/foo_bar/kitty.rb ...

And content of Kitty model file:

module FooBar class Kitty < ActiveRecord::Base end end

Tie things up

Let’s start with routes. Most likely you’d like your engine to add new pages with own paths to base app. To achieve this, simply amend routes.rb file with new definitions:

FooBar::Engine.routes.draw do resources :kitties end

In order to activate those routings in base application, it’s routes.rb file also needs to be amended as follows:

Rails.application.routes.draw do ... mount FooBar::Engine, at: "/foo_bar" ... end

To see it actually works, let’s check all available routes with $ rake routes command:

Prefix Verb URI Pattern Controller#Action foo_bar /foo_bar FooBar::Engine root GET / pages#home Routes for FooBar::Engine: kitties GET /kitties(.:format) foo_bar/kitties#index POST /kitties(.:format) foo_bar/kitties#create new_kitty GET /kitties/new(.:format) foo_bar/kitties#new edit_kitty GET /kitties/:id/edit(.:format) foo_bar/kitties#edit kitty GET /kitties/:id(.:format) foo_bar/kitties#show PATCH /kitties/:id(.:format) foo_bar/kitties#update PUT /kitties/:id(.:format) foo_bar/kitties#update DELETE /kitties/:id(.:format) foo_bar/kitties#destroy

Notice how engine path is mounted under defined /foo_bar part. This allows having the same resources names in both main Rails application and mounted engine. The tricky part about routes is accessing base application routing helpers in engine and vice versa. Imagine engine view, where we want to include link to root path / . Usually you would just write:

<%= link_to "Home", root_path %>

But you’ll end up with familiar screen and error message

Showing /home/kaa/amberbit/blog/engine/foo_bar/app/views/foo_bar/kitties/index.html.erb where line #1 raised: undefined local variable or method `root_path' for #<#<Class:0x000000048abed0>:0x000000048ab318>

Why is that? Simply because FooBar engine doesn’t define own root path, and don’t know about base application routing. To access it, all helpers must prefix helpers with main_app :

<%= link_to "Home", main_app.root_path %>

When using engine paths helpers from engine itself, you don’t need to prefix it. And analogously, don’t need to prefix base app routes in app itself, but that’s kinda obvious.

And how can I reference engine routes from main app? Using foo_bar prefix of course!

<%= link_to "Kitties", foo_bar.kitties_path %>

If engine is not namespaced, its routing proxy prefix would be foo_bar_engine instead of foo_bar .

Tip for curious ones: main_app and foo_bar are ActionDispatch::Routing::RoutesProxy instances.

When dealing with many engines, it sometimes could be troublesome to mount them all in app routes. To ease this task, engine could actually mount itself. But keep in mind that this fact will be hidden from developer at first glance:

module FooBar class Engine < Rails::Engine isolate_namespace FooBar initializer "foo_bar", before: :load_config_initializers do |app| Rails.application.routes.append do mount FooBar::Engine, at: "/foo_bar" end end end end

We used initializer to hook up into configuration before application runs.

Controllers inside engine

There is nothing magical happening in engine’s controller. The only thing to remember is to wrap them into correct module (FooBar in this example), and following hierarchy pattern put it in app/controllers/foo_bar/ subdirectory. Created views should go to app/views/foo_bar/[resource]/ subdirectory.

Generated engine comes with own ApplicationController . Nice thing to do would be amending it to inherit from base application’s ApplicationController instead of standard ActionController::Base , so engine will have access to its helpers methods, trigger before_actions and inherit all other logic. To do that, just point to not scoped ApplicationController :

module FooBar class ApplicationController < ::ApplicationController end end

Another useful tip would be to re-use base application layout. As we have seen in first paragraph, generated engine comes with own layout file and assets manifests files. So when it’s possible to easily separate engine pages from base app by providing different layout, it’s often desirable not to do so. It’s just matter of declaring it correct way:

module FooBar class ApplicationController < ::ApplicationController layout "application" end end

But wait, the default for engine is also called application . The significant difference is (again) namespacing, and originally it would be:

... layout "foo_bar/application" ...

Defining models & database tables

Because it is possible to have the same class names in base application and inside engine, database tables needs to be somehow distinguished. Let’s take a look at model scaffolding again:

$ bin/rails g model kitty name:string invoke active_record create db/migrate/20151011103843_create_foo_bar_kitties.rb create app/models/foo_bar/kitty.rb invoke test_unit create test/models/foo_bar/kitty_test.rb create test/fixtures/foo_bar/kitties.yml

First hint is migration file name. And its content:

class CreateFooBarKitties < ActiveRecord::Migration def change create_table :foo_bar_kitties do |t| t.string :name t.timestamps null: false end end end

Table name got prefixed with foo_bar_ . We could have expected that. While not so obvious, it’s also configurable option. To do that, simply edit lib/foo_bar.rb module definition in following manner:

module FooBar def self.table_name_prefix "cute_" end end

Rails generator won’t pick that change though, and we would have to manually amend all create_table migrations to reflect this. Let’s see it in action. In Rails console:

> FooBar::Kitty.all FooBar::Kitty Load (0.3ms) SELECT "cute_kitties".* FROM "cute_kitties" SQLite3::SQLException: no such table: cute_kitties: SELECT "cute_kitties".* FROM "cute_kitties" ActiveRecord::StatementInvalid: SQLite3::SQLException: no such table: cute_kitties: SELECT "cute_kitties".* FROM "cute_kitties" ...

Other very important point to notice is that base application has no knowledge of engine migrations. Rails preferred way of dealing with it is to copy over migration files to db/migrate . And there are even tasks helps doing just that:

$ rake foo_bar:install:migrations Copied migration 20151011105543_create_foo_bar_kitties.foo_bar.rb from foo_bar

There is also other, more generic task, which will copy all not-yet-present migrations from mounted engines:

$ rake railties:install:migrations

You may sometimes forget to do it, and then pondering why your application is throwing exceptions. Luckily, there is a walkaround involving initialized tuning we’ve already seen in one of previous paragraphs. Just put following code in engine.rb file:

module FooBar class Engine < Rails::Engine ... initializer "foo_bar", before: :load_config_initializers do |app| ... config.paths["db/migrate"].expanded.each do |expanded_path| Rails.application.config.paths["db/migrate"] << expanded_path end end end end

It will push all engine migration files into base application migrations list. As simple as that. And running migrate will also check engines for pending migrations:

$ rake db:migrate == 20151011103843 CreateFooBarKitties: migrating ============================== -- create_table(:foo_bar_kitties) -> 0.0008s == 20151011103843 CreateFooBarKitties: migrated (0.0008s) =====================

Loading files

What is loaded out of the box? Everything from engine you would expect from Rails application will be loaded on boot. That includes models, controllers, helpers, routes, locale files ( config/locales/* ) and tasks ( lib/tasks/* ). But what about custom directories you would probably have, like app/services , app/workers for instance? They needs to be loaded manually. And a good place to do it automatically is, once again, engine.rb . I prefer splitting this task though. List of required files goes to lib/foo_bar.rb module definition file:

module FooBar ... def self.load_files [ "app/workers/mailer_worker", "app/services/kitty_washer" ] end end

and loading task to lib/foo_bar/engine.rb :

initializer "foo_bar", after: :load_config_initializers do |app| FooBar.load_files.each { |file| require_relative File.join("../..", file) } end

Once again, app initializer is used.

Extending base application classes

Other thing your engine may want to do is to extend existing base application classes functionality. Quick & dirty way is to use Ruby class_eval and instance_eval functions. Given User class being defined in application, you could do lots of interesting stuff, but that’s just regular Ruby. F.ex. create user.rb file in app/models directory with:

User.class_eval do attr_accessor :name has_many :kitties, class_name: FooBar::Kitty User::MAX_KITTIES = Float::INFINITY def cutest_kitty kitties.sample end end

Also remember to declare class constants in module scope ( User::MAX_KITTIES ), otherwise it will be declared as toplevel constant. I learned it hard way:

$ rails c > User::MAX_KITTIES (irb):2: warning: toplevel constant MAX_KITTIES referenced by User::MAX_KITTIES => Infinity

Of course, you could take more elegant approach and use ActiveSupport::Concern, but that’s topic for whole new article. Just remember to load such files manually (engine won’t do it as they live outside foo_bar subdirectory).

Engine own executables

Adding executable scripts is not something entirely tied to an engine. Any gem library could do that, but I found it very useful and it’s worth mentioning. Add extra line to .gemspec and any executable file placed in engine bin/ directory will be available for base application. No need for manual includes this time.

Gem::Specification.new do |spec| ... spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } ... end

Similar applies to Rake files, anything put in engine’s lib/tasks folder will be runnable from base application, and furthermore rake -T will list those tasks. For start, engine generator creates one dummy .rake file with name corresponding library name.

$ rake -T ... rake some_task:buy_kitties # Buy only cute kitties rake foo_bar:buy_kitties # Buy moar kitties rake foo_bar:install:migrations # Copy migrations from foo_bar to application ...

Conclusion

Rails engine is a broad topic. Everything related to Rails application could be applied here as well. I tried to focus on some non-standard tricks I picked up when writing one. What do you think? Maybe you also have some good practises worth sharing? Any constructive criticism is welcome :)