Module: Unread::Readable::ClassMethods

Defined in:
lib/unread/readable.rb

Instance Method Summary collapse

Instance Method Details

#assert_reader(reader) ⇒ Object

Raises:

  • (ArgumentError)


100
101
102
103
104
105
# 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_classObject

Raises:

  • (RuntimeError)


107
108
109
# 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!Object



81
82
83
84
# 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) ⇒ Object

Raises:

  • (ArgumentError)


4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 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) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 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) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 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) ⇒ Object

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


73
74
75
# File 'lib/unread/readable.rb', line 73

def read_scope(reader)
  self
end

#readable_parentObject



77
78
79
# 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) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
# 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