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.



37
38
39
40
41
42
# File 'lib/sup/thread.rb', line 37

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.



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

def containers
  @containers
end

Instance Method Details

#<<(c) ⇒ Object



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

def << c
  @containers << c
end

#apply_label(t) ⇒ Object



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

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

#authorsObject



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

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

#dateObject



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

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

#direct_participantsObject



120
121
122
# File 'lib/sup/thread.rb', line 120

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

#dirty?Boolean

Returns:

  • (Boolean)


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

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

#drop(c) ⇒ Object



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

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

#dump(f = $stdout) ⇒ Object

unused



53
54
55
56
57
# File 'lib/sup/thread.rb', line 53

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).



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

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



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

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

#empty!Object



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

def empty!; @containers.clear; end

#empty?Boolean

Returns:

  • (Boolean)


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

def empty?; @containers.empty?; end

#firstObject



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

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

#has_label?(t) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#has_message?Boolean

Returns:

  • (Boolean)


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

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

#labelsObject



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

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

#labels=(l) ⇒ Object

Raises:

  • (ArgumentError)


131
132
133
134
# File 'lib/sup/thread.rb', line 131

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

#latest_messageObject



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

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



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

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

#remove_label(t) ⇒ Object



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

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

#set_labels(l) ⇒ Object



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

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

#sizeObject



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

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

#snippetObject



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

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

#sort_keyObject



153
154
155
156
# File 'lib/sup/thread.rb', line 153

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

#subjObject



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

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

#to_sObject



149
150
151
# File 'lib/sup/thread.rb', line 149

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

#toggle_label(label) ⇒ Object



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

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