A Social Network in Rails: Activity Feed

This post is part of an ongoing feature about creating a social network in Rails. You can see part 1 here, about creating elegant permalinks for your content.

Today I'm presenting a wonderful gem called public_activity , which lets you create activity feeds with a few lines of code:
To begin with, add the gem to your Gemfile, as usual:
gem "public_activity"

Set up the DB table for the activities (assuming you'll use ActiveRecord for it, if you want Mongoid or MongoMapper check the readme):
rails g public_activity:migration
rake db:migrate

We'll use a similar data model as the permalinks post for this guide, where user has_many photos and, now, photos has_one details. We want to show on our feed each time a user creates or updates a photo, and each time a user checks a photo's details. This way, we'll show 2 different ways of tracking activities:

1. Via model callbacks
We set up PublicActivity on our Photo model to track every creation and update, and the user (found via the has_many relationship) as the owner:
class Photo < ActiveRecord::Base
  include PublicActivity::Model

  tracked only: [:create, update], owner: :user
  ...
end

2. Manually in the controller
We want to track and show on the feed when a user checks the details of a photo. For this, we add the Common module to the Details model, since we don't need the tracked method this time:
# in app/models/details.rb
class Details < ActiveRecord::Base
  include PublicActivity::Common
  ...
end
And then we create the activity in the controller, just before we render the photo's details. This time we use the current_user helper to get the user that read the photo's details:
# in app/controllers/details_controller.rb
def show
  @details = Details.find(params[:id])
  @details.create_activity :read, owner: current_user
end

Now, let's list those activities on a nice feed! To get all the activities related to a user, we grab the ones which he owns plus the ones related to his photos (through the details):
# in config/routes.rb
resources :activities, only: :index

# in app/controllers/activities_controller.rb
class ActivitiesController < ApplicationController
  def index
    details_ids = User.photos.map { |photo| photo.details.id }

    activities_by_owner = PublicActivity::Activity.where(owner: current_user)
    activities_by_reads = PublicActivity::Activity.where("trackable_type = 'Details' AND trackable_id in ?", details_ids)

    @activities = activities_by_owner.or(activities_by_reads).distinct
  end
end

# in app/views/activities/index.html.erb
<% @activities.each do |activity| %>
    <%= activity.owner.name if activity.owner %>
      <%= render_activity activity %>
  <% end %>
<% end %>
(To add that nifty or method to your app, use this monkey patch) Lastly, let's create a partial for each kind of activity, so they can be tailored to its content. Mind the file names!
# in app/views/public_activity/photo/_create.html.erb
<% if photo = activity.trackable %>
  added <%= link_to photo.name, photo_path(photo) %>
<% else %>
  added a photo that does not exist anymore.
<% end %>

# in app/views/public_activity/comment/_read.html.erb
<% if comment = activity.trackable %>
  read a comment on <%= link_to comment.photo.name, photo_path(comment.photo) %>
<% else %>
  read a comment on a photo that does not exist anymore.
<% end %>
And that's it! One more step towards our Rails-powered social network :D

PD: There's also a no-gem version of this by jules2689, check it out!

2 comments:

  1. Hey to everyone, it’s my first visit of the blog site; this blog includes awesome and actually best info for the visitors.
    www.mediaonlines.com

    ReplyDelete
  2. The hub page is going to be the page that drives the most conversions, too.
    xenforo 2.1.1

    ReplyDelete