I don't think there's any special way to do this. You just have to do it.
Let's say you could efficiently grab all activities and their associated users. You likely can't do this efficiently, for something as frequent as an activity on a website, but hypothetically let's say you could, for simplicity's sake.
What you want is to iterate your activities, and collect lines you want to display. The easy road, in my humble opinion, would simply be a finder-like method on your Activity
model, that returns arrays of consecutive actions by a user.
Leave your thresholds to the view. (No more than 2 consecutive actions, no more than 10 lines total.)
If Ruby has some fancy way of collecting consecutive elements from an array, then I'm probably going to look stupid, but here's some (untested) code:
class Activity < ActiveRecord::Base
belongs_to :user
def self.each_consecutive_group(options={})
default_options = { :order => 'created_at DESC' }
current_group = nil
last_user_id = false
self.all(default_options.merge(options)).each do |activity|
# Look for consecutive activities from the same user.
# (This is never true on the first iteration.)
if last_user_id == activity.user_id
current_group << activity
else
# The nil case happens during first iteration.
yield current_group unless current_group.nil?
# Initialize state for the next group of activities.
current_group = [activity]
last_user_id = activity.user_id
end
end
# Do we still have something to yield?
yield current_group if current_group
end
# Just return the above as an array, rather than iterating.
def self.consecutive_groups(options={})
groups = []
self.each_consecutive_group(options) { |group| groups << group }
groups
end
end
# Usage:
Activity.each_grouped(:include => :user, :limit => 30) do |group|
# [...]
end
# Or if you want to pass something from the controller to the view:
@activity_groups = Activity.grouped(:include => :user, :limit => 30)
To then mitigate the performance problem, I'd simply take a safe margin. If 10 lines is what you want, you could pick a scope of 20 activities to search through, and there would be room for 10 records to be hidden. Any more, and you might see less than 10 lines. Selecting 20 records is hardly a lot though, and you may choose to expand that number.
If you're die-hard on always showing 10 lines, then you probably have to move the threshold code into the above any ways. You could then use find_in_batches
, rather than :limit
, and keep looping until your threshold is exceeded.