27

Basically, I want to create a notification like Facebook and Stackoverflow. Specifically, in a Post-Comments system, when a post get commented, everyone involved (people who creates the post and who create comments, except the new commenter) gets a notification message that this post get commented. And the notification get dismissed when people have read it.

I have tried to use mailboxer gem to implement it, but saddly there is no example available using its related methods, including social_stream itself.

Is there other way to create the Notification System?

And when I try to create it from scratch I get several problems:

    Model Notification
    topic_id: integer
    user_id: integer
    checked: boolean #so we can tell whether the notification is read or not
  1. Dismissing the notication after being read by users

I think we just need to turn every notification messages' "checked" attribute to true after user visit the index of notification.(In the NotificationsController)

    def index
      @notifications=current_user.notication.all
      @notification.each do |notification|
         notification.checked = true
      end
      @notification.save!
    end

2.Selecting users to notify(and exclude the user making new comment)

I just have no idea in wrting queries....

3.Creating notifications

I think this should be something like

    #in CommentController
    def create
      #after creating comments, creat notifications
      @users.each do |user|
        Notification.create(topic_id:@topic, user_id: user.id)
      end
    end

But I think this is really ugly

There is no need to anwer the 3 problems above, Any simple solution to the Notification System is preferable , thanks....

ZK Zhao
  • 19,885
  • 47
  • 132
  • 206
  • 1
    This answer may give some leads: http://stackoverflow.com/a/7049110/1096545 - It shows how to use an ActiveRecord observer for that purpose. – João Daniel Mar 13 '13 at 13:27

2 Answers2

13

I think you are on the right path.

A slightly better notifications#index

def index
  @notifications = current_user.notications
  @notifications.update_all checked: true
end
  1. Notify this users

    User.uniq.joins(:comments).where(comments: {id: @comment.post.comment_ids}).reject {|user| user == current_user }
    

Unique users that participated in the @comment's post comments, reject (remove from result) current_user.

  1. An observer as pointed out by João Daniel, it is preferred over an after_create. This "Rails best practice" describes it pretty well: http://rails-bestpractices.com/posts/2010/07/24/use-observer
astreal
  • 3,383
  • 21
  • 34
Leonel Galán
  • 6,993
  • 2
  • 41
  • 60
  • A great thank to you! but.....How can I add the post.user.id into the User.uniq.joins... query? In that query only commenter get notified. – ZK Zhao Mar 13 '13 at 15:39
  • Disregard my previous comment (deleted), since we already have the post object in memory you could do `(User.uniq.joins(:comments).where(comments: {id: @comment.post.comment_ids}) | [post.user]).reject {|user| user == current_user }`. This means: Add the post.user to the array if it doesn't exist before rejecting current_user – Leonel Galán Mar 13 '13 at 15:57
  • I got another problem....What is the difference between User.uniq.joins(:comments).... and User.joins(:comments)....uniq? That is, to put 'uniq' first or later. – ZK Zhao Mar 14 '13 at 09:58
  • I'm trying something like `users=User.joins(:replies).where(:replies =>{ topic_id: topic_id}).push(topic.user).delete(sender).uniq`, the sender is the new commenter which should not be notified. But I'm not sure whether I'm using the push/delete correctly. May I ask how should I write this? – ZK Zhao Mar 14 '13 at 13:23
  • and when I use the array above, I get `undefined method `each' for #`. I use `users.each do |users|` after the query to create notifications. Do you have any idea about that? thanks! – ZK Zhao Mar 14 '13 at 13:26
  • The first `uniq`, will do a DISTINCT on SQL and the second will do a `uniq` on the collection (after retrieving duplicates). `delete` will return the deleted object, you probably want `reject` instead. `b.push(a).uniq` it's equivalent to `b | [a]`. You are getting the error because `delete` is returning the deleted user, instead of the array. – Leonel Galán Mar 14 '13 at 14:13
  • After millions times of debugging... I finally get this `users= User.joins(:replies).where(replies: {id: topic.reply_ids}).push(topic.user).reject {|user| user == comment }.uniq`. It is working not, but I'm not sure its selecting the right users. Could you please help me review this code? – ZK Zhao Mar 14 '13 at 15:36
  • and I'm worrying about that what if `users` is empty(e.g, the user just comment his own topic), would the `usesrs.each` break? In my expeirence it certainly doesnt break, but I still like to aks it here.....A big thank to you, the code finally gets working – ZK Zhao Mar 14 '13 at 15:38
  • Looks good, I'm not a fan of the `uniq` at the end, but it does work. I recommend you write a test for it and if you found a corner case where it doesn't write a failing test at work until both pass, but that's for another question (when it fails). We don't want to make this discussion any longer (StackOverflow doesn't like that), so I'm glad it is working for you. For your last comment, that's an easy thing to test: `[].each { |i| puts i }`, it doesn't break, it just won't execute the content inside the block. – Leonel Galán Mar 14 '13 at 15:41
9

there is an amazing gem called public activity ,,you can customize it as you want and here is a screencast about it in railscast http://railscasts.com/episodes/406-public-activity hope that could help you.

Update

In my rails application I made a similar notifications system like yours to send notifications to all users but in index action you can user

current_user.notifications.update_all(:checked=>true)

and also to send only one notifications to the user once not several times that somebody commented on the post you can use unique_by method

  @comments =@commentable.comments.uniq_by {|a| a[:user_id]}

then you can send notifications to only users of previous comments

 @comments.each do |comment|
 comment.user.notifications.create!(....
 end 

hope that could help you

Francisco Quintero
  • 730
  • 1
  • 13
  • 20
Remon Amin
  • 1,506
  • 2
  • 19
  • 44
  • 2
    I've whated this episode, but I think this is for status feed, not suitable for a notification system? – ZK Zhao Mar 13 '13 at 15:35