Cache Sweeper instead of ActiveRecord Observers
Monday, June 22nd, 2009We’re working on a current project that utilizes observers to update activity and notification feeds, and we’ve run into a few stumbling blocks while implementing our solution and I wanted to share our experiences after scouring the internet for the proper execution method. It turns out that the Cache Sweeper is just what the doctor ordered.
1) Standard ActiveRecord::Observers wouldn’t work
The problem with the ActiveRecord::Observer class is that it doesn’t know enough. It’s great if you’re doing email notification based on a simple create, update, destroy action, but unless you’re passing a user model in when you perform those actions you won’t have visibility into who’s doing the creating or destroying. Enter the Cache Sweeper observer. It does a great job of observing models and controllers at the same time. Using the Cache Sweeper we were able to identify the current user through the sessions parameter we use. For instance, we were able to put this code in place and see who was doing the action.
1 2 3 4 5 6 7 8 9 10 11 12 13 | class ActionSweeper < ActionController::Caching::Sweeper observe :comment, :connection, :topic, :post, :membership, :video, :album, :item, :entry #have to use the after_save hook for created records because the permission isn't saved and can't be interpolated by the activities model def after_save(object) Activity.create(:user_id => controller.session[:user], :object => object, :action => 'after_create') if controller.params[:action] == "create" Activity.create(:user_id => controller.session[:user], :object => object, :action => 'after_update') if controller.params[:action] == "update" end def after_destroy(object) Activity.create(:user_id => controller.session[:user], :object => object, :action => 'after_destroy') end end |
You may ask why we’re using the after_save method instead of the after_create and after_update methods. We have a plugin that the activity model references and that plugin is tied to the after_save method for some of the referenced models. If we use the after_save method, the observer fires before the plugin has a chance to save the corresponding models. By using the after_save method in this way, we get around it. I’d like it to be a little cleaner, but it works.
2) We can observe controller actions
Because the Cache Sweeper class also observes controllers, we were able to tie in to specific actions, like the show action. Some might say we could observe the after_find callback. Unfortunately, the after_find callback fires every time a record is found no matter how many records there are. So a Record.find(:all) call would fire the after_find method. Because we need to know when someone views a record, but we don’t want to incur the overhead of 100 after_find callbacks, we use the Cache Sweeper to observe the Show method of a controller which will only fire once. With that knowledge, we can use the code below to watch a controller’s show action:
1 2 3 4 5 | class NotificationSweeper < ActionController::Caching::Sweeper def after_blogs_show Notification.hide(@entry) end end |
To observe the controller, simply name your method like so [before|after]_[controller name]_[controller action].
It’s not perfect, of course. You do have to add cache_sweeper :notification_sweeper to each controller you want to observe, which is a real drag. It is however the only way I could find that gives us access to the params array and session information which is necessary if we want to know who’s doing what.
Update
Looking to access an instance variable in the Cache Sweeper. Just use:
1 | entry = assigns("entry") |



