1 ## Index interface, subclassed by Ferret indexer.
9 Redwood::log "optional 'chronic' library not found (run 'gem install chronic' to install)"
16 class LockError < StandardError
21 def method_missing m; @h[m.to_s] end
26 def initialize dir=BASE_DIR
28 @lock = Lockfile.new lockfile, :retries => 0, :max_age => nil
29 self.class.i_am_the_instance self
32 def lockfile; File.join @dir, "lock" end
35 Redwood::log "locking #{lockfile}..."
38 rescue Lockfile::MaxTriesLockError
39 raise LockError, @lock.lockinfo_on_disk
43 def start_lock_update_thread
44 @lock_update_thread = Redwood::reporting_thread("lock update") do
52 def stop_lock_update_thread
53 @lock_update_thread.kill if @lock_update_thread
54 @lock_update_thread = nil
57 def possibly_pluralize number_of, kind
58 "#{number_of} #{kind}" +
59 if number_of == 1 then "" else "s" end
62 def fancy_lock_error_message_for e
63 secs = (Time.now - e.mtime).to_i
67 possibly_pluralize secs , "second"
69 possibly_pluralize mins, "minute"
73 Error: the sup index is locked by another process! User '#{e.user}' on
74 host '#{e.host}' is running #{e.pname} with pid #{e.pid}. The process was alive
83 $stderr.puts fancy_lock_error_message_for(e)
86 You can wait for the process to finish, or, if it crashed and left a
87 stale lock file behind, you can manually delete #{@lock.path}.
94 if @lock && @lock.locked?
95 Redwood::log "unlocking #{lockfile}..."
101 SourceManager.load_sources
106 Redwood::log "saving index and sources..."
107 FileUtils.mkdir_p @dir unless File.exists? @dir
108 SourceManager.save_sources
116 ## Syncs the message to the index, replacing any previous version. adding
117 ## either way. Index state will be determined by the message's #labels
119 def sync_message m, opts={}
131 def contains? m; contains_id? m.id end
137 def empty?; size == 0 end
139 ## Yields a message-id and message-building lambda for each
140 ## message that matches the given query, in descending date order.
141 ## You should probably not call this on a block that doesn't break
142 ## rather quickly because the results can be very large.
143 def each_id_by_date query={}
147 ## Return the number of matches for query in the index
148 def num_results_for query={}
152 ## yield all messages in the thread containing 'm' by repeatedly
153 ## querying the index. yields pairs of message ids and
154 ## message-building lambdas, so that building an unwanted message
155 ## can be skipped in the block if desired.
157 ## only two options, :limit and :skip_killed. if :skip_killed is
158 ## true, stops loading any thread if a message with a :killed flag
160 def each_message_in_thread_for m, opts={}
164 ## Load message with the given message-id from the index
169 ## Delete message with the given message-id from the index
174 ## Given an array of email addresses, return an array of Person objects that
175 ## have sent mail to or received mail from any of the given addresses.
176 def load_contacts email_addresses, h={}
180 ## Yield each message-id matching query
185 ## Yield each message matching query
186 def each_message query={}, &b
187 each_id query do |id|
188 yield build_message(id)
192 ## Implementation-specific optimization step
197 ## Return the id source of the source the message with the given message-id
203 class ParseError < StandardError; end
205 ## parse a query string from the user. returns a query object
206 ## that can be passed to any index method with a 'query'
209 ## raises a ParseError if something went wrong.
217 require 'lib/sup/ferret_index'
218 Redwood::Index = Redwood::FerretIndex