Creating an activity feed
A lot of applications (well, a few at least) have required some sort of “recent activity” feed. You know, for a dashboard or something. It’s a pretty straight-forward thing to do, but I thought I might mention how I typically go about it, and see if anyone else has suggestions/improvements. I have some ideas for a useful plugin, so I’ll be working toward that soon.
An activity feed isn’t really all that useful if it’s only showing you the latest of one thing. Like comments, for example. More than likely you’ll want the most recent comments, articles, uploaded pictures, messages, etc, all in one place, and all over one iteration. What I do is create a class in the app/models directory that doesn’t inherit from ActiveRecord. Then, using named_scope’s, I just build a collection of objects. Here’s an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Activity def initialize(user) @user = user end def recent(options = {}) limit = options[:limit] || 15 returning([]) do |collection| collection << Article.recent.all(:limit => limit) collection << Asset.parents.recent.all(:limit => limit) collection << Message.sent_to(@user).recent.all(:limit => limit) end.flatten.sort_by(&:updated_at).reverse.slice(0, limit) end end |
Then to get an array of the most recent articles, assets, and messages, you can simply do:
1 | @activity = Activity.new(current_user).recent(:limit => 10) |
The next thing I typically do is match icons up to the object type, and render those by convention. So when I’m iterating over an article, for example, I might render an “article-icon.png” as the representative icon. Assets would have “asset-icon.png” and so on.
Keeping this stuff in its own class is nice because you can add methods as needed. For instance, maybe you need some special logic for an RSS feed, well you could add a to_rss method or something that would handle it.
This type of thing typically comes with a group of helpers that format the collection appropriately and represent it in a useful way, but I’ll leave that up to you.

Chris Scharf Friday, 24 Apr, 2009 Posted at 12:49PM
The way I approached activity in slate v0.4 was to have an
ActiveRecord::Observerwhich observed various models. When an object was created, for example, it added an entry to theactivitiestable stating the action (created) and the id and class type of the observed object. I think observers seem to be neglected for the most part, but I’ve found them extremely useful in situations like this.Ryan Friday, 24 Apr, 2009 Posted at 07:53PM
Chris – I like that approach. I use observers for keeping track of who created/updated things (similar to John Nunemaker’s user_stamp). But you’re right, I definitely neglect observers in situations when they’d be quite handy. Thanks for the reminder.
For this particular case, the “client” wanted to pick and choose what goes into their activity feed, and I thought this approach would be easy to inject content into the stream. Plus the changes would be instantaneous once updated. I’ve yet to build the picking and choosing part of it, so I’ll re-think it to see if observers would make more sense.
I love having so many options in ruby/rails :-)
Chris Scharf Friday, 24 Apr, 2009 Posted at 10:33PM
As for picking and choosing, you could easily go fine-grained by having a checkbox in the admin: “Record activity for this item” or something like that. Then your observer could inspect that flag before attempting to record activity.
And yes, ruby and rails are awesome!
Brian Webb Thursday, 28 May, 2009 Posted at 04:29PM
What if your activity feed was in a social site, where you not only had the activities of your own, but also all your friends, much like a facebook style feed. Wouldn’t that put a pretty heavy strain on the database? Especially if that was loaded on quite a few pages.
Emili ParreƱo Wednesday, 28 Jul, 2010 Posted at 04:43AM
This is a wrong approach. If the limit for activity feed is 10, you are getting 10 last articles, 10 last comments and so on, but the 10 last articles could be previous to the last 10 comments and you shouldn’t show the last 10 articles but the last 20 comments for example. The best way to make an activity feed is store the events in a separate table,this method it’s faster and you avoid the problems with creation dates.
My 2 cent.
Ryan Friday, 30 Jul, 2010 Posted at 09:28AM
Emili -
Yes, this is not the best approach, but not necessarily for the reasons you brought up (this iwasn’t for a facebook-ish stream, where it makes sense to put the comments in context with posts).
But I’ve since changed to using observers and the data now lives in its own table.