Module: Redwood

Defined in:
lib/sup/imap.rb,
lib/sup.rb,
lib/sup.rb,
lib/sup/mode.rb,
lib/sup/sent.rb,
lib/sup/poll.rb,
lib/sup/hook.rb,
lib/sup/label.rb,
lib/sup/draft.rb,
lib/sup/index.rb,
lib/sup/person.rb,
lib/sup/buffer.rb,
lib/sup/logger.rb,
lib/sup/tagger.rb,
lib/sup/thread.rb,
lib/sup/crypto.rb,
lib/sup/source.rb,
lib/sup/update.rb,
lib/sup/suicide.rb,
lib/sup/maildir.rb,
lib/sup/account.rb,
lib/sup/contact.rb,
lib/sup/colormap.rb,
lib/sup/textfield.rb,
lib/sup/mbox/loader.rb,
lib/sup/mbox/ssh-file.rb,
lib/sup/modes/log-mode.rb,
lib/sup/modes/poll-mode.rb,
lib/sup/mbox/ssh-loader.rb,
lib/sup/modes/text-mode.rb,
lib/sup/modes/help-mode.rb,
lib/sup/modes/inbox-mode.rb,
lib/sup/modes/reply-mode.rb,
lib/sup/modes/scroll-mode.rb,
lib/sup/modes/resume-mode.rb,
lib/sup/modes/compose-mode.rb,
lib/sup/modes/forward-mode.rb,
lib/sup/horizontal-selector.rb,
lib/sup/modes/completion-mode.rb,
lib/sup/modes/label-list-mode.rb,
lib/sup/modes/thread-view-mode.rb,
lib/sup/modes/line-cursor-mode.rb,
lib/sup/modes/buffer-list-mode.rb,
lib/sup/modes/edit-message-mode.rb,
lib/sup/modes/thread-index-mode.rb,
lib/sup/modes/contact-list-mode.rb,
lib/sup/modes/file-browser-mode.rb,
lib/sup/modes/search-results-mode.rb,
lib/sup/modes/label-search-results-mode.rb,
lib/sup/modes/person-search-results-mode.rb

Overview

Herein lies all the code responsible for threading messages. It's basically an online version of the JWZ threading algorithm: www.jwz.org/doc/threading.html

I didn't implement it for efficiency, but thanks to our search engine backend, it's typically not applied to very many messages at once.

At the top level, we have a ThreadSet, which represents a set of threads, e.g. a message folder or an inbox. Each ThreadSet contains zero or more Threads. A Thread represents all the message related to a particular subject. Each Thread has one or more Containers. A Container is a recursive structure that holds the message tree as determined by the references: and in-reply-to: headers. Each Container holds zero or one messages. In the case of zero messages, it means we've seen a reference to the message but haven't (yet) seen the message itself.

A Thread can have multiple top-level Containers if we decide to group them together independent of tree structure, typically if (e.g. due to someone using a primitive MUA) the messages have the same subject but we don't have evidence from in-reply-to: or references: headers. In this case Thread#each can optionally yield a faked root object tying them all together into one tree structure.

Defined Under Namespace

Modules: CanAliasContacts, MBox Classes: Account, AccountManager, Buffer, BufferListMode, BufferManager, Colormap, CompletionMode, ComposeMode, ContactListMode, ContactManager, Container, CryptoManager, DraftLoader, DraftManager, EditMessageMode, FatalSourceError, FileBrowserMode, ForwardMode, HelpMode, HookManager, HorizontalSelector, IMAP, InboxMode, Index, InputSequenceAborted, LabelListMode, LabelManager, LabelSearchResultsMode, LineCursorMode, LogMode, Logger, Maildir, Mode, OutOfSyncSourceError, Person, PersonSearchResultsMode, PollManager, PollMode, ReplyMode, ResumeMode, ScrollMode, SearchResultsMode, SendmailCommandFailed, SentLoader, SentManager, Source, SourceError, SuicideManager, Tagger, TextField, TextMode, Thread, ThreadIndexMode, ThreadSet, ThreadViewMode, UpdateManager

Constant Summary

VERSION =
"git"
BASE_DIR =
ENV["SUP_BASE"] || File.join(ENV["HOME"], ".sup")
CONFIG_FN =
File.join(BASE_DIR, "config.yaml")
COLOR_FN =
File.join(BASE_DIR, "colors.yaml")
SOURCE_FN =
File.join(BASE_DIR, "sources.yaml")
LABEL_FN =
File.join(BASE_DIR, "labels.txt")
CONTACT_FN =
File.join(BASE_DIR, "contacts.txt")
DRAFT_DIR =
File.join(BASE_DIR, "drafts")
SENT_FN =
File.join(BASE_DIR, "sent.mbox")
LOCK_FN =
File.join(BASE_DIR, "lock")
SUICIDE_FN =
File.join(BASE_DIR, "please-kill-yourself")
HOOK_DIR =
File.join(BASE_DIR, "hooks")
YAML_DOMAIN =
"masanjin.net"
YAML_DATE =
"2006-10-01"

Instance Attribute Summary (collapse)

Instance Method Summary (collapse)

Instance Attribute Details

- (Object) exceptions (readonly)

Returns the value of attribute exceptions



70
71
72
# File 'lib/sup.rb', line 70

def exceptions
  @exceptions
end

Instance Method Details

- (Object) finish



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

def finish
  Redwood::LabelManager.save if Redwood::LabelManager.instantiated?
  Redwood::ContactManager.save if Redwood::ContactManager.instantiated?
  Redwood::BufferManager.deinstantiate! if Redwood::BufferManager.instantiated?
end

- (Object) load_yaml_obj(fn, compress = false)



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

def load_yaml_obj fn, compress=false
  if File.exists? fn
    if compress
      Zlib::GzipReader.open(fn) { |f| YAML::load f }
    else
      YAML::load_file fn
    end
  end
end

- (Object) log(s)



248
# File 'lib/sup.rb', line 248

def log s; Logger.log s; end

- (Object) record_exception(e, name)



71
72
73
74
75
76
# File 'lib/sup.rb', line 71

def record_exception e, name
  @exception_mutex.synchronize do
    @exceptions ||= []
    @exceptions << [e, name]
  end
end

- (Object) report_broken_sources(opts = {})

not really a good place for this, so I'll just dump it here.

a source error is either a FatalSourceError or an OutOfSyncSourceError. the superclass SourceError is just a generic.



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/sup.rb', line 138

def report_broken_sources opts={}
  return unless BufferManager.instantiated?

  broken_sources = Index.sources.select { |s| s.error.is_a? FatalSourceError }
  unless broken_sources.empty?
    BufferManager.spawn_unless_exists("Broken source notification for #{broken_sources.join(',')}", opts) do
      TextMode.new(<<EOM)
Source error notification
-------------------------

Hi there. It looks like one or more message sources is reporting
errors. Until this is corrected, messages from these sources cannot
be viewed, and new messages will not be detected.

#{broken_sources.map { |s| "Source: " + s.to_s + "\n Error: " + s.error.message.wrap(70).join("\n        ")}.join("\n\n")}
EOM
#' stupid ruby-mode
    end
  end

  desynced_sources = Index.sources.select { |s| s.error.is_a? OutOfSyncSourceError }
  unless desynced_sources.empty?
    BufferManager.spawn_unless_exists("Out-of-sync source notification for #{broken_sources.join(',')}", opts) do
      TextMode.new(<<EOM)
Out-of-sync source notification
-------------------------------

Hi there. It looks like one or more sources has fallen out of sync
with my index. This can happen when you modify these sources with
other email clients. (Sorry, I don't play well with others.)

Until this is corrected, messages from these sources cannot be viewed,
and new messages will not be detected. Luckily, this is easy to correct!

#{desynced_sources.map do |s|
"Source: " + s.to_s + 
 "\n Error: " + s.error.message.wrap(70).join("\n        ") + 
 "\n   Fix: sup-sync --changed #{s.to_s}"
end}
EOM
#' stupid ruby-mode
    end
  end
end

- (Object) reporting_thread(name)



78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/sup.rb', line 78

def reporting_thread name
  if $opts[:no_threads]
    yield
  else
    ::Thread.new do
      begin
        yield
      rescue Exception => e
        record_exception e, name
      end
    end
  end
end

- (Object) save_yaml_obj(object, fn, safe = false)

one-stop shop for yamliciousness



95
96
97
98
99
100
101
102
103
104
# File 'lib/sup.rb', line 95

def save_yaml_obj object, fn, safe=false
  if safe
    safe_fn = "#{File.dirname fn}/safe_#{File.basename fn}"
    mode = File.stat(fn).mode if File.exists? fn
    File.open(safe_fn, "w", mode) { |f| f.puts object.to_yaml }
    FileUtils.mv safe_fn, fn
  else
    File.open(fn, "w") { |f| f.puts object.to_yaml }
  end
end

- (Object) start



116
117
118
119
120
121
122
123
124
125
126
# File 'lib/sup.rb', line 116

def start
  Redwood::SentManager.new Redwood::SENT_FN
  Redwood::ContactManager.new Redwood::CONTACT_FN
  Redwood::LabelManager.new Redwood::LABEL_FN
  Redwood::AccountManager.new $config[:accounts]
  Redwood::DraftManager.new Redwood::DRAFT_DIR
  Redwood::UpdateManager.new
  Redwood::PollManager.new
  Redwood::SuicideManager.new Redwood::SUICIDE_FN
  Redwood::CryptoManager.new
end