Class: Redwood::Thread

Inherits:
Object show all
Includes:
Enumerable
Defined in:
lib/sup/thread.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Enumerable

#argfind, #argmin, #between, #map_to_hash, #map_with_index, #max_of, #shared_prefix, #sum

Constructor Details

#initializeThread

Returns a new instance of Thread.



35
36
37
38
39
40
# File 'lib/sup/thread.rb', line 35

def initialize
  ## ah, the joys of a multithreaded application with a class called
  ## "Thread". i keep instantiating the wrong one...
  raise "wrong Thread class, buddy!" if block_given?
  @containers = []
end

Instance Attribute Details

#containersObject (readonly)

Returns the value of attribute containers.



34
35
36
# File 'lib/sup/thread.rb', line 34

def containers
  @containers
end

Instance Method Details

#<<(c) ⇒ Object



42
43
44
# File 'lib/sup/thread.rb', line 42

def << c
  @containers << c
end

#apply_label(t) ⇒ Object



101
# File 'lib/sup/thread.rb', line 101

def apply_label t; each { |m, *o| m && m.add_label(t) }; end

#authorsObject



99
# File 'lib/sup/thread.rb', line 99

def authors; map { |m, *o| m.from if m }.compact.uniq; end

#dateObject



90
# File 'lib/sup/thread.rb', line 90

def date; map { |m, *o| m.date if m }.compact.max; end

#direct_participantsObject



118
119
120
# File 'lib/sup/thread.rb', line 118

def direct_participants
  map { |m, *o| [m.from] + m.to if m }.flatten.compact.uniq
end

#dirty?Boolean

Returns:

  • (Boolean)


89
# File 'lib/sup/thread.rb', line 89

def dirty?; any? { |m, *o| m && m.dirty? }; end

#drop(c) ⇒ Object



48
# File 'lib/sup/thread.rb', line 48

def drop c; @containers.delete(c) or raise "bad drop"; end

#dump(f = $stdout) ⇒ Object

unused



51
52
53
54
55
# File 'lib/sup/thread.rb', line 51

def dump f=$stdout
  f.puts "=== start thread with #{@containers.length} trees ==="
  @containers.each { |c| c.dump_recursive f; f.puts }
  f.puts "=== end thread ==="
end

#each(fake_root = false) ⇒ Object

yields each message, its depth, and its parent. the message yield parameter can be a Message object, or :fake_root, or nil (no message found but the presence of one deduced from other messages).



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
# File 'lib/sup/thread.rb', line 61

def each fake_root=false
  adj = 0
  root = @containers.find_all { |c| c.message && !Message.subj_is_reply?(c.message.subj) }.argmin { |c| c.date }

  if root
    adj = 1
    root.first_useful_descendant.each_with_stuff do |c, d, par|
      yield c.message, d, (par ? par.message : nil)
    end
  elsif @containers.length > 1 && fake_root
    adj = 1
    yield :fake_root, 0, nil
  end

  @containers.each do |cont|
    next if cont == root
    fud = cont.first_useful_descendant
    fud.each_with_stuff do |c, d, par|
      ## special case here: if we're an empty root that's already
      ## been joined by a fake root, don't emit
      yield c.message, d + adj, (par ? par.message : nil) unless
        fake_root && c.message.nil? && root.nil? && c == fud
    end
  end
end

#each_dirty_messageObject



116
# File 'lib/sup/thread.rb', line 116

def each_dirty_message; each { |m, *o| m && m.dirty? && yield(m) }; end

#empty!Object



47
# File 'lib/sup/thread.rb', line 47

def empty!; @containers.clear; end

#empty?Boolean

Returns:

  • (Boolean)


46
# File 'lib/sup/thread.rb', line 46

def empty?; @containers.empty?; end

#firstObject



87
# File 'lib/sup/thread.rb', line 87

def first; each { |m, *o| return m if m }; nil; end

#has_label?(t) ⇒ Boolean

Returns:

  • (Boolean)


115
# File 'lib/sup/thread.rb', line 115

def has_label? t; any? { |m, *o| m && m.has_label?(t) }; end

#has_message?Boolean

Returns:

  • (Boolean)


88
# File 'lib/sup/thread.rb', line 88

def has_message?; any? { |m, *o| m.is_a? Message }; end

#labelsObject



128
# File 'lib/sup/thread.rb', line 128

def labels; inject(Set.new) { |s, (m, *o)| m ? s | m.labels : s } end

#labels=(l) ⇒ Object

Raises:

  • (ArgumentError)


129
130
131
132
# File 'lib/sup/thread.rb', line 129

def labels= l
  raise ArgumentError, "not a set" unless l.is_a?(Set)
  each { |m, *o| m && m.labels = l.dup }
end

#latest_messageObject



134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/sup/thread.rb', line 134

def latest_message
  inject(nil) do |a, b|
    b = b.first
    if a.nil?
      b
    elsif b.nil?
      a
    else
      b.date > a.date ? b : a
    end
  end
end

#participantsObject



122
123
124
# File 'lib/sup/thread.rb', line 122

def participants
  map { |m, *o| [m.from] + m.to + m.cc + m.bcc if m }.flatten.compact.uniq
end

#remove_label(t) ⇒ Object



102
# File 'lib/sup/thread.rb', line 102

def remove_label t; each { |m, *o| m && m.remove_label(t) }; end

#set_labels(l) ⇒ Object



114
# File 'lib/sup/thread.rb', line 114

def set_labels l; each { |m, *o| m && m.labels = l }; end

#sizeObject



126
# File 'lib/sup/thread.rb', line 126

def size; map { |m, *o| m ? 1 : 0 }.sum; end

#snippetObject



91
92
93
94
95
96
97
98
# File 'lib/sup/thread.rb', line 91

def snippet
  with_snippets = select { |m, *o| m && m.snippet && !m.snippet.empty? }
  first_unread, * = with_snippets.select { |m, *o| m.has_label?(:unread) }.sort_by { |m, *o| m.date }.first
  return first_unread.snippet if first_unread
  last_read, * = with_snippets.sort_by { |m, *o| m.date }.last
  return last_read.snippet if last_read
  ""
end

#sort_keyObject



151
152
153
154
# File 'lib/sup/thread.rb', line 151

def sort_key
  m = latest_message
  m ? [-m.date.to_i, m.id] : [-Time.now.to_i, ""]
end

#subjObject



127
# File 'lib/sup/thread.rb', line 127

def subj; argfind { |m, *o| m && m.subj }; end

#to_sObject



147
148
149
# File 'lib/sup/thread.rb', line 147

def to_s
  "<thread containing: #{@containers.join ', '}>"
end

#toggle_label(label) ⇒ Object



104
105
106
107
108
109
110
111
112
# File 'lib/sup/thread.rb', line 104

def toggle_label label
  if has_label? label
    remove_label label
    false
  else
    apply_label label
    true
  end
end