"Hidden" problems with inheritance in Rails (noob prob? :)
Yesterday I have discovered a pretty annoying mistake in my own projects design.
As I have wrote previousely (simple caching in Rails) , I had simple front page caching implemented for my site's front page. The caching code was defined in ApplicationController, which in turn was responsible for generating the front page content.
To put it simple, it looked like this:
class ApplicationController < ActionController::Base
caches_action :index
before_filter :index_cache_expirer, :only => [:index]
The code caches (and expires) action named :index. But, there is a may be invisible at first glance, but very big problem with this code.
The problem is that "caches_action :index" directive will propagate to all other pages on the site, effectively making all :index actions in all controllers to cache their content in the same way as the front page.
Even pages which must be avoided to be cached (like /admin area on multiuser sites). Here's a simple example - a multiuser blog with /admin/posts action, showing a list of blog posts for each user, depending on who is logged in. So, say, user "mike" logs in, and watches his posts list (which gets caches, since the caches_action :index gets propagated from ApplicationController to Admin::PostsController). Soon after that, a user "jane" logs into the same admin/posts trying to have a look at her posts list - only to get served a cached page containing lists of posts of user "mike"! The page will expire eventually and "jane" will get her list of posts, but think about the next user - he or she will get Jane's posts list instead, and so on, and so on.
One of the biggest probs is that this scenario doesn't work if you are working in development mode, because caching is only active in production mode by default! So it looks like that - you happily debug your code in development mode, deploy to public server, and the caching problems begin to appear.
Now, I didn't tell why exactly this gets propagated. But that's very easy to notice if you just look at, say, PostsController definition:
class Admin::PostsController < ApplicationController
See how PostsController inherits from ApplicationController, which in turn has caching to action :index implemented? Therefore, PostsController will have the same exact set of functionality as ApplicationController, including caching of its own :index action!
So, the point is - putting too much stuff into ApplicationController is definitely not a good idea, as well as it seems to be now very wise to use ApplicationController to render site's front page (in case this page has alot of custom functionality, caching and stuff like that).
Solution? It's pretty simple, at least for the caching problem I've been having. You just have to make a separate controller for front page only, and define all these front page specific stuff inside of that controller.
So, watch out for the "hidden" inheritance problems. And happy coding :)