Implementing Django’s Modular Apps Feature in Rails
Mar 31st, 2007 by phil
It turns out that I have more to say on the topic of what Rails can learn from other frameworks. This time I have a solution for implementing a cool feature from Django in Rails. With Django a single web site is composed of a “project”, which contains the configuration and settings, and many “applications”. Each one of these applications is a vertical slice of your site that can be shared with different projects. The Django Tutorial explains it like this:
Projects vs. apps
What’s the difference between a project and an app? An app is a Web application that does something — e.g., a weblog system, a database of public records or a simple poll app. A project is a collection of configuration and apps for a particular Web site. A project can contain multiple apps. An app can be in multiple projects.
One of Django’s killer features is an automatic admin interface:
Automatic admin interface
Save yourself the tedious work of creating interfaces for people to add and update content. Django does that automatically, and it’s production-ready.
It turns out that this admin interface is an good example of a modular application.
I have a project coming up that could really take advantage of this modular applications architecture. After doing a little digging around I discovered that it is very easy to get the same thing in Rails. Here is a blow by blow account of what I did to get modular applications in a Rails project.
Most of the magic is performed by the Engines plugin available at rails-engines.org. So, to get started I generated a test Rails application:
$ rails apptest
$ cd apptest
Then I installed the Engines plugin:
$ script/plugin install http://svn.rails-engines.org/plugins/engines
Now I can create a plugin and add the directories for the models, views and controllers:
$ script/generate plugin my_app
$ cd vendor/plugins/my_app
$ mkdir -p app/controllers app/views app/models
At this point I can create a simple controller to test that everything is working. I created a new file in vendor/plugins/my_app/app/controllers called my_app_controller.rb:
class MyAppController < ApplicationController
def index
render :text => 'Hello from my app controller!'
end
end
If I now run script/server and browse to http://localhost:3000/my_app I see the message
Hello from my app controller!
So far, so good. However, I really don’t like the idea of having large slices of my application under vendor/plugins. This is also simple to change. I added the following two lines to config/environment.rb in the initializer block:
config.plugin_paths = %W( vendor/plugins apps )
config.plugins = %W( engines my_app )
This allows me to continue to put all of my “normal” plugins in vendor/plugins, but any plugins that are intended to be modular applications can go in the apps directory. I can now move the my_app directory from vendor/plugins to apps:
$ mv vendor/plugins/my_app apps
A quick restart of the server and refresh of the browser reveals that everything is still working as intended.
One minor annoyance is the depth of the directory hierarchy for app code. For example, the full path to the controller I created earlier is $RAILS_ROOT/apps/my_app/app/controllers/my_app_controller.rb. The app directory under my_app seems a bit redundant. For now, I am pretty much out of luck on this front. I can get part of the way there by adding the following code to my_app/init.rb:
plugin = Engines.current
plugin.code_paths = %W( controllers helpers models )
plugin.controller_paths = %W( controllers )
This will allow me to create the directories controllers, helpers and models directly under my_app. Unfortunately, the location of the views directory is more or less hardcoded in the Engines plugin to be app/views. I am sure there is a way around this, but I haven’t found it yet.
Another minor issue is that you can’t use the normal Rails generators to create code for your new modular apps. You could use the generators to create the code and then copy it by hand into the appropriate location under apps/my_app. That seems like a lot of work, though. A better solution would be a set of custom generators that created the code in the right place. If this ends up bothering me enough, I may do that.
One of the great things about the Rails framework is that it is flexible and dynamic enough that you can alter its behavior in fairly drastic ways without actually changing any core Rails code. The Engines plugin is a good example of that. This means that features from other frameworks like Django can be implemented in Rails without having to change the Rails core. New and experimental features can be distributed and evaluated by the Rails community at large without needing approval or acceptance from the core team. Some functionality may even be better implemented in plugins than in the core framework.





I agree that the Django approach to a project, with apps within it, makes a lot of sense - especially for websites that have more than one application running in them (which is many of them).
Django’s “free” Admin interface is a killer feature. Rails is getting there with Streamlined and AjaxScaffold, but still has some catching up to do.
That’s not to say I prefer Django to Rails in general, but it would be nice if Rails could implement the best of Django.
One cool way to get into Django (and to take advantage of modularity in general) is by using it for the admin interface while maintaining the rest of your legacy Rails site. There’s no reason not to mix and match, if you’ve got legacy Ruby code.
@zerohalo: There is also Roar
Their approach is quite different, but very easy to implement admins views to your existing controllers (thinking in a REST way, of course).
Its good to see that engines now is just a simpler “patch” to rails 1.2 to provide additional paths to controllers, models and views, compared to previous versions ;-)
Good article, btw :-)
Another way to support multiple apps (used by Pylons) is Paste Deploy — which is more of a package-based system (gem-based in Ruby). It uses WSGI for dispatching, which with Rack is now something of a possibility in Ruby as well.
Another way to support multiple apps (used by Pylons) is Paste Deploy — which is more of a package-based system (gem-based in Ruby). It uses WSGI for dispatching, which with Rack is now something of a possibility in Ruby as well.
Another way to support multiple apps (used by Pylons) is Paste Deploy — which is more of a package-based system (gem-based in Ruby). It uses WSGI for dispatching, which with Rack is now something of a possibility in Ruby as well.
“There’s no reason not to mix and match, if you’ve got legacy Ruby code.”
Yeah, there is — you now need a team that can develop, debug, deploy, maintain and optimize in both Ruby/Rails and Python/Django.
That’s not the end of the world, and I personally like both languages and both frameworks.
But I don’t think adding another language and framework to a site is a decision that should be taken lightly.
‘Legacy’ rails code????
That’s rich.