module Unread::Readable::ClassMethods

Public Instance Methods

assert_reader(reader) click to toggle source
# File lib/unread/readable.rb, line 100
def assert_reader(reader)
  assert_reader_class

  raise ArgumentError, "Class #{reader.class.name} is not registered by acts_as_reader." unless ReadMark.reader_classes.any? { |klass| reader.is_a?(klass) }
  raise ArgumentError, "The given reader has no id." unless reader.id
end
assert_reader_class() click to toggle source
# File lib/unread/readable.rb, line 107
def assert_reader_class
  raise RuntimeError, 'There is no class using acts_as_reader.' unless ReadMark.reader_classes
end
cleanup_read_marks!() click to toggle source
# File lib/unread/readable.rb, line 81
def cleanup_read_marks!
  assert_reader_class
  Unread::GarbageCollector.new(self).run!
end
mark_as_read!(target, options) click to toggle source
# File lib/unread/readable.rb, line 4
def mark_as_read!(target, options)
  raise ArgumentError unless options.is_a?(Hash)

  reader = options[:for]
  assert_reader(reader)

  if target == :all
    reset_read_marks_for_user(reader)
  elsif target.respond_to?(:each)
    mark_collection_as_read(target, reader)
  else
    raise ArgumentError
  end
end
mark_collection_as_read(collection, reader) click to toggle source
# File lib/unread/readable.rb, line 19
def mark_collection_as_read(collection, reader)
  ReadMark.transaction do
    global_timestamp = reader.read_mark_global(self).try(:timestamp)

    collection.each do |obj|
      raise ArgumentError unless obj.is_a?(self)
      timestamp = obj.send(readable_options[:on])

      if global_timestamp && global_timestamp >= timestamp
        # The object is implicitly marked as read, so there is nothing to do
      else
        mark_collection_item_as_read(obj, reader, timestamp)
      end
    end
  end
end
mark_collection_item_as_read(obj, reader, timestamp) click to toggle source
# File lib/unread/readable.rb, line 36
def mark_collection_item_as_read(obj, reader, timestamp)
  marking_proc = proc {
    rm = obj.read_marks.find_or_initialize_by(reader: reader)
    rm.timestamp = timestamp
    rm.save!
  }

  if using_postgresql?
    # With PostgreSQL, a transaction is unusable after a unique constraint vialoation.
    # To avoid this, nested transactions are required.
    # http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#module-ActiveRecord::Transactions::ClassMethods-label-Exception+handling+and+rolling+back
    ReadMark.transaction(requires_new: true) do
      begin
        marking_proc.call
      rescue ActiveRecord::RecordNotUnique
        # The object is explicitly marked as read, so rollback the inner transaction
        raise ActiveRecord::Rollback
      end
    end
  else
    begin
      marking_proc.call
    rescue ActiveRecord::RecordNotUnique
      # The object is explicitly marked as read, so there is nothing to do
    end
  end
end
read_scope(reader) click to toggle source

A scope with all items accessable for the given reader It's used in cleanup_read_marks! to support a filtered cleanup Should be overriden if a reader doesn't have access to all items Default: reader has access to all items and should read them all

Example:

def Message.read_scope(reader)
  reader.visible_messages
end
# File lib/unread/readable.rb, line 73
def read_scope(reader)
  self
end
readable_parent() click to toggle source
# File lib/unread/readable.rb, line 77
def readable_parent
  self.ancestors.find { |ancestor| ReadMark.readable_classes.include?(ancestor) }
end
reset_read_marks_for_user(reader) click to toggle source
# File lib/unread/readable.rb, line 86
def reset_read_marks_for_user(reader)
  assert_reader(reader)

  ReadMark.transaction do
    reader.read_marks.where(readable_type: self.readable_parent.name).delete_all
    rm = reader.read_marks.new
    rm.readable_type = self.readable_parent.name
    rm.timestamp = Time.current
    rm.save!
  end

  reader.forget_memoized_read_mark_global
end