Ruby gem to manage read/unread status of ActiveRecord objects - and it's fast.
- Manages unread records for anything you want users to read (like messages, documents, comments etc.)
- Supports mark as read to mark a single record as read
- Supports mark all as read to mark all records as read in a single step
- Gives you a scope to get the unread records for a given user
- Needs only one additional database table
- Most important: Great performance
- Ruby 1.9.3 or newer
- Rails 3 (including 3.0, 3.1, 3.2) and Rails 4. For use with Rails 2.3 there is a branch named "rails2"
- Needs a timestamp field in your models (like created_at or updated_at) with a database index on it
https://github.com/ledermann/unread/releases
Step 1: Add this to your Gemfile:
gem'unread'and run
bundleStep 2: Generate and run the migration:
rails g unread:migration
rake db:migrateclassUser < ActiveRecord::Baseacts_as_readerendclassMessage < ActiveRecord::Baseacts_as_readable:on=>:created_atendmessage1=Message.create!message2=Message.create!## Get unread messages for a given userMessage.unread_by(current_user)# => [ message1, message2 ]message1.mark_as_read!:for=>current_userMessage.unread_by(current_user)# => [ message2 ]## Get all messages including the read status for a given usermessages=Message.with_read_marks_for(current_user)# => [ message1, message2 ]messages[0].unread?(current_user)# => falsemessages[1].unread?(current_user)# => trueMessage.mark_as_read!:all,:for=>current_userMessage.unread_by(current_user)# => [ ]# Optional: Cleaning up unneeded markers.# Do this in a cron job once a day.Message.cleanup_read_marks!The main idea of this gem is to manage a list of read items for every reader after a certain timestamp.
The gem defines a scope doing a LEFT JOIN to this list, so your app can get the unread items in a performant manner. Of course, other scopes can be combined.
It will be ensured that the list of read items will not grow up too much:
- If a user uses "mark all as read", his list gets deleted and the timestamp is set to the current time.
- If a user never uses "mark all as read", the list will grow and grow with each item he reads. But there is help: Your app can use a cleanup method which removes unnecessary list items.
Overall, this gem can be used for large data. Please have a look at the generated SQL queries, here is an example:
# Assuming we have a user who has marked all messages as read on 2010-10-20 08:50current_user=User.find(42)# Get the unread messages for this userMessage.unread_by(current_user)Generated query:
SELECT messages.*FROM messages
LEFT JOIN read_marks ONread_marks.readable_type='Message'ANDread_marks.readable_id=messages.idANDread_marks.member_id=42ANDread_marks.timestamp>=messages.created_atWHEREread_marks.id IS NULLANDmessages.created_at>'2010-10-20 08:50:00'Hint: You should add a database index on messages.created_at.
There are two other gems/plugins doing a similar job:
Unfortunately, both of them have a lack of performance, because they calculate the unread records doing a find(:all), which should be avoided for a large amount of records. This gem is based on a timestamp algorithm and therefore it's very fast.
Copyright (c) 2010-2014 Georg Ledermann, released under the MIT license


