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 def add_message m; unimplemented end
117 def update_message m; unimplemented end
118 def update_message_state m; unimplemented end
128 def contains? m; contains_id? m.id end
134 def empty?; size == 0 end
136 ## Yields a message-id and message-building lambda for each
137 ## message that matches the given query, in descending date order.
138 ## You should probably not call this on a block that doesn't break
139 ## rather quickly because the results can be very large.
140 def each_id_by_date query={}
144 ## Return the number of matches for query in the index
145 def num_results_for query={}
149 ## yield all messages in the thread containing 'm' by repeatedly
150 ## querying the index. yields pairs of message ids and
151 ## message-building lambdas, so that building an unwanted message
152 ## can be skipped in the block if desired.
154 ## only two options, :limit and :skip_killed. if :skip_killed is
155 ## true, stops loading any thread if a message with a :killed flag
157 def each_message_in_thread_for m, opts={}
161 ## Load message with the given message-id from the index
166 ## Delete message with the given message-id from the index
171 ## Given an array of email addresses, return an array of Person objects that
172 ## have sent mail to or received mail from any of the given addresses.
173 def load_contacts email_addresses, h={}
177 ## Yield each message-id matching query
182 ## Yield each message matching query
183 def each_message query={}, &b
184 each_id query do |id|
185 yield build_message(id)
189 ## Implementation-specific optimization step
194 ## Return the id source of the source the message with the given message-id
200 class ParseError < StandardError; end
202 ## parse a query string from the user. returns a query object
203 ## that can be passed to any index method with a 'query'
206 ## raises a ParseError if something went wrong.
212 index_name = ENV['SUP_INDEX'] || $config[:index] || DEFAULT_INDEX
214 when "xapian"; require "sup/xapian_index"
215 when "ferret"; require "sup/ferret_index"
216 else fail "unknown index type #{index_name.inspect}"
218 Index = Redwood.const_get "#{index_name.capitalize}Index"
219 Redwood::log "using index #{Index.name}"