module Unread class GarbageCollector def initialize(readable_class) @readable_class = readable_class.readable_parent end attr_reader :readable_class def run! ReadMark.reader_classes.each do |reader_class| readers_to_cleanup(reader_class).find_each do |reader| if oldest_timestamp = readable_class.read_scope(reader). unread_by(reader). minimum(readable_class.readable_options[:on]) # There are unread items, so update the global read_mark for this reader to the oldest # unread item and delete older read_marks update_read_marks_for_user(reader, oldest_timestamp) else # There is no unread item, so deletes all markers and move global timestamp readable_class.reset_read_marks_for_user(reader) end end end end private # Not for every reader a cleanup is needed. # Look for those readers with at least one single read mark def readers_to_cleanup(reader_class) reader_class. reader_scope. joins(:read_marks). where(ReadMark.table_name => { readable_type: readable_class.name }). group("#{ReadMark.quoted_table_name}.reader_type, #{ReadMark.quoted_table_name}.reader_id, #{reader_class.quoted_table_name}.#{reader_class.quoted_primary_key}"). having("COUNT(#{ReadMark.quoted_table_name}.id) > 1"). select("#{reader_class.quoted_table_name}.#{reader_class.quoted_primary_key}") end def update_read_marks_for_user(reader, timestamp) ReadMark.transaction do # Delete markers OLDER than the given timestamp reader.read_marks. where(readable_type: readable_class.name). single. older_than(timestamp). delete_all # Change the global timestamp for this reader rm = reader.read_mark_global(readable_class) || reader.read_marks.build rm.readable_type = readable_class.name rm.timestamp = timestamp - 1.second rm.save! end end end end