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:
--skip-gemspecoption – I hope that this will help to move more people to gem plugins
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
--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,
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
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
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):
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!
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.