Piotr Sarnacki home

I'm writing a book about Ember.js. Sign up here for a notification email if you are interested.

Mountable apps tutorial

I finished my work on RubySOC a few months ago and I wanted to write this post quickly after that, but life, as usual, verified my plans. Unfortunately, attending a few great conferences left me with no money, so I needed some time of hard work to pay all the debts (nevertheless, all those trips were really worth it, especially rubyconf – I really felt in love with New Orleans!). I want to apologize to everyone who really waited for that, especially those who heard that this blog post will be written “very soon” ;) Now… let’s cut the emo talk and get to serious business!

I’ll start with a short introduction for those, who aren’t familiar with the concept of mountable apps in Rails 3 (edge) yet. The goal for implementing mountable apps was to allow running 2 rails applications in one process. Unfortunately, that goal was not achieved and we settled on a simpler approach. If you want to create a mountable application, you can use rails engines, which will be much more powerful in Rails 3.1. For the list of major changes, please check my last post on that topic.

Although almost all of the changes made to engines were usable shortly after summer, the process of setting up a new engine, suitable for mounting, was really rough. Of course, there is a great gem, enginex, by José, but at that time it only generated bare structure for any kind of rails extension and there were no things specific to mountable apps (as mountable apps haven’t existed during the time of writing enginex). That’s why I ported enginex to rails, with some changes that were needed to merge it to the core, to make that task simpler.

Let’s start with the tutorial. First things first. As the new APIs work only on rails edge, you need to get rails from github:

git clone https://github.com/rails/rails.git
cd rails
bundle install

To generate any rails extension in rails 3.1 you can use rails plugin new command. It generates a directory with lib directory, gemspec, tests and dummy application for testing. There are 2 really important things here:

To generate a mountable engine, we can use:

bundle exec ./bin/rails plugin new ../blog --edge --mountable
cd ../blog
bundle install

We need to add --dev or --edge option to ensure that Gemfile will point to rails edge version, either from local clone or github. I used edge here, cause it’s better for most of you – you can get newest rails version with simple bundle update. Mountable option will add files that will come in handy for developing a mountable engine, such as: integration tests, Blog::ApplicationController, config/routes.rb and so on.

Let’s see how the key files look like.

Probably the most important file is lib/blog/engine.rb, which keeps definition of the engine:

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

The thing that differs from what we could see in Rails 3.0 engine, is isolate_namespace which makes engine isolated from host application. If you don’t remember how it works exactly, please check the documentation or my last post.

Next important thing is config/routes.rb file:

Blog::Engine.routes.draw do
end

These are empty routes, but as you can see, they belong to the engine, not to the host application.

The last thing that you may not be familiar with is the dummy application located in test/dummy. This is a standard rails application that will be used to test engine, both with automated tests and manually during development. The nice thing about the new plugin generator is that with --mountable option, it automatically mounts engine in test/dummy/config/routes.rb

Rails.application.routes.draw do
  mount Blog::Engine => "/blog"
end

Ok, let’s write some code! Or… actually don’t write code, I’m too lazy, I’m gonna use scaffold generator:

rails g scaffold post title:string body:text

While the scaffold should behave exactly the same as the one generated in regular app, there are some differences. Notice that migration for that is called create_blog_posts instead of create_posts. Also almost all of the files are places in blog/ subdirectory. When you open app/models/blog/post.rb, you will see:

module Blog
  class Post < ActiveRecord::Base
  end
end

Everything is namespaced for a good reason: we want to avoid conflicts between engine and host application.

Ok, now we can migrate our database (sqlite3 by default):

rake db:migrate

Engine’s Rakefile includes all the tasks that are needed to manage database. Migrations are run from both engine’s and dummy’s application directories to make development and testing easier. Finally, it’s time to check it!

rails s

Now point your browser to http://localhost:3000/blog/posts. You should see the standard rails scaffold working – the only difference is that it’s namespaced with /blog path. It was easy, wasn’t it?

As you can see, creating a mountable application using engines is pretty straightforward. I haven’t shown any way to better integrate a mountable application with the host application (e.g. using User model from the host application or adding some configuration options to the engine). It’s on purpose ;) I haven’t got time to develop anything good yet. You can start playing with it yourself! Be sure to let me know if you come up with some good patterns.


If you liked this post consider following me on twitter.
blog comments powered by Disqus
Fork me on GitHub