1 ## Index interface, subclassed by Ferret indexer.
9 debug "optional 'chronic' library not found; date-time query restrictions disabled"
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
31 def lockfile; File.join @dir, "lock" end
34 debug "locking #{lockfile}..."
37 rescue Lockfile::MaxTriesLockError
38 raise LockError, @lock.lockinfo_on_disk
42 def start_lock_update_thread
43 @lock_update_thread = Redwood::reporting_thread("lock update") do
51 def stop_lock_update_thread
52 @lock_update_thread.kill if @lock_update_thread
53 @lock_update_thread = nil
56 def possibly_pluralize number_of, kind
57 "#{number_of} #{kind}" +
58 if number_of == 1 then "" else "s" end
61 def fancy_lock_error_message_for e
62 secs = (Time.now - e.mtime).to_i
66 possibly_pluralize secs , "second"
68 possibly_pluralize mins, "minute"
72 Error: the sup index is locked by another process! User '#{e.user}' on
73 host '#{e.host}' is running #{e.pname} with pid #{e.pid}. The process was alive
82 $stderr.puts fancy_lock_error_message_for(e)
85 You can wait for the process to finish, or, if it crashed and left a
86 stale lock file behind, you can manually delete #{@lock.path}.
93 if @lock && @lock.locked?
94 debug "unlocking #{lockfile}..."
100 SourceManager.load_sources
105 debug "saving index and sources..."
106 FileUtils.mkdir_p @dir unless File.exists? @dir
107 SourceManager.save_sources
115 def add_message m; unimplemented end
116 def update_message m; unimplemented end
117 def update_message_state m; unimplemented end
127 def contains? m; contains_id? m.id end
133 def empty?; size == 0 end
135 ## Yields a message-id and message-building lambda for each
136 ## message that matches the given query, in descending date order.
137 ## You should probably not call this on a block that doesn't break
138 ## rather quickly because the results can be very large.
139 def each_id_by_date query={}
143 ## Return the number of matches for query in the index
144 def num_results_for query={}
148 ## yield all messages in the thread containing 'm' by repeatedly
149 ## querying the index. yields pairs of message ids and
150 ## message-building lambdas, so that building an unwanted message
151 ## can be skipped in the block if desired.
153 ## only two options, :limit and :skip_killed. if :skip_killed is
154 ## true, stops loading any thread if a message with a :killed flag
156 def each_message_in_thread_for m, opts={}
160 ## Load message with the given message-id from the index
165 ## Delete message with the given message-id from the index
170 ## Given an array of email addresses, return an array of Person objects that
171 ## have sent mail to or received mail from any of the given addresses.
172 def load_contacts email_addresses, h={}
176 ## Yield each message-id matching query
181 ## Yield each message matching query
182 def each_message query={}, &b
183 each_id query do |id|
184 yield build_message(id)
188 ## Implementation-specific optimization step
193 ## Return the id source of the source the message with the given message-id
199 class ParseError < StandardError; end
201 ## parse a query string from the user. returns a query object
202 ## that can be passed to any index method with a 'query'
205 ## raises a ParseError if something went wrong.
211 index_name = ENV['SUP_INDEX'] || $config[:index] || DEFAULT_INDEX
213 when "xapian"; require "sup/xapian_index"
214 when "ferret"; require "sup/ferret_index"
215 else fail "unknown index type #{index_name.inspect}"
217 Index = Redwood.const_get "#{index_name.capitalize}Index"
218 debug "using index #{Index.name}"