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 ... endAnd 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!
No comments:
Post a Comment