1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
| | # Copyright (C) 2016 all contributors <msgthr-public@80x24.org>
# License: GPL-2.0+ <https://www.gnu.org/licenses/gpl-2.0.txt>
# An internal container class, this is exposed for Msgthr#thread!
# Msgthr#order! and Msgthr#walk_thread APIs through block parameters.
# They should should not be initialized in your own code.
#
# One container object will exist for every message you call Msgthr#add! on,
# so there can potentially be many of these objects for large sets of
# messages.
class Msgthr::Container
# Unique message identifier, typically the Message-Id header for mail
# and news messages. This may be any hashable object, Integer values
# are allowed and will not be coerced to string values.
attr_reader :mid
# Opaque data pointer, may be used by the user for any purpose.
# This is +nil+ to denote missing (aka "ghost") messages.
attr_accessor :msg
# You probably do not need to use this.
# It is only safe to access this after Msgthr#order!
# This contains an Array of Msgthr::Container objects which have the
# +parent+ field pointing to us
attr_accessor :children
# You probably do not need to use this; and you should only use
# this after Msgthr#order! This points to the +parent+ of the
# message if one exists, and +nil+ if a message has no parent.
# This will only be accurate once all messages are added to
# a Msgthr set via Msgthr#add
attr_accessor :parent # :nodoc:
def initialize(mid) # :nodoc:
@mid = mid
@children = {} # becomes an Array after order!
@parent = nil
@msg = nil # opaque pointer supplied by user
end
# Returns the topmost message container with an opaque message pointer
# in it. This may be +nil+ if no message is available.
# This is preferable to using the container yielded by Msgthr#order!
# directly when handling incomplete message sets.
def topmost
q = [ self ]
while cont = q.shift
return cont if cont.msg
q.concat(cont.children.values)
end
nil
end
def add_child(child) # :nodoc:
raise 'cannot become child of self' if child == self
cid = child.mid
# reparenting:
parent = child.parent and parent.children.delete(cid)
@children[cid] = child
child.parent = self
end
def has_descendent(child) # :nodoc:
seen = {}
while child
return true if self == child || seen[child]
seen[child] = true
child = child.parent
end
false
end
# only called by Msgthr#order!
def order! # :nodoc:
seen = { @mid => true }
q = [ self ]
while cur = q.shift
c = []
cur.children.each do |cmid, cont|
next if seen[cmid]
c << cont
seen[cmid] = true
end.clear
yield(c) if c.size > 1
cur.children = c
q.concat(c)
end
end
end
|