]> git.cworth.org Git - sup/blob - lib/sup/index.rb
Merge branch 'logging'
[sup] / lib / sup / index.rb
1 ## Index interface, subclassed by Ferret indexer.
2
3 require 'fileutils'
4
5 begin
6   require 'chronic'
7   $have_chronic = true
8 rescue LoadError => e
9   debug "optional 'chronic' library not found; date-time query restrictions disabled"
10   $have_chronic = false
11 end
12
13 module Redwood
14
15 class BaseIndex
16   class LockError < StandardError
17     def initialize h
18       @h = h
19     end
20
21     def method_missing m; @h[m.to_s] end
22   end
23
24   include Singleton
25
26   def initialize dir=BASE_DIR
27     @dir = dir
28     @lock = Lockfile.new lockfile, :retries => 0, :max_age => nil
29   end
30
31   def lockfile; File.join @dir, "lock" end
32
33   def lock
34     debug "locking #{lockfile}..."
35     begin
36       @lock.lock
37     rescue Lockfile::MaxTriesLockError
38       raise LockError, @lock.lockinfo_on_disk
39     end
40   end
41
42   def start_lock_update_thread
43     @lock_update_thread = Redwood::reporting_thread("lock update") do
44       while true
45         sleep 30
46         @lock.touch_yourself
47       end
48     end
49   end
50
51   def stop_lock_update_thread
52     @lock_update_thread.kill if @lock_update_thread
53     @lock_update_thread = nil
54   end
55
56   def possibly_pluralize number_of, kind
57     "#{number_of} #{kind}" +
58         if number_of == 1 then "" else "s" end
59   end
60
61   def fancy_lock_error_message_for e
62     secs = (Time.now - e.mtime).to_i
63     mins = secs / 60
64     time =
65       if mins == 0
66         possibly_pluralize secs , "second"
67       else
68         possibly_pluralize mins, "minute"
69       end
70
71     <<EOS
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
74 as of #{time} ago.
75 EOS
76   end
77
78   def lock_or_die
79     begin
80       lock
81     rescue LockError => e
82       $stderr.puts fancy_lock_error_message_for(e)
83       $stderr.puts <<EOS
84
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}.
87 EOS
88       exit
89     end
90   end
91
92   def unlock
93     if @lock && @lock.locked?
94       debug "unlocking #{lockfile}..."
95       @lock.unlock
96     end
97   end
98
99   def load
100     SourceManager.load_sources
101     load_index
102   end
103
104   def save
105     debug "saving index and sources..."
106     FileUtils.mkdir_p @dir unless File.exists? @dir
107     SourceManager.save_sources
108     save_index
109   end
110
111   def load_index
112     unimplemented
113   end
114
115   def add_message m; unimplemented end
116   def update_message m; unimplemented end
117   def update_message_state m; unimplemented end
118
119   def save_index fn
120     unimplemented
121   end
122
123   def contains_id? id
124     unimplemented
125   end
126
127   def contains? m; contains_id? m.id end
128
129   def size
130     unimplemented
131   end
132
133   def empty?; size == 0 end
134
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={}
140     unimplemented
141   end
142
143   ## Return the number of matches for query in the index
144   def num_results_for query={}
145     unimplemented
146   end
147
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.
152   ##
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
155   ## is found.
156   def each_message_in_thread_for m, opts={}
157     unimplemented
158   end
159
160   ## Load message with the given message-id from the index
161   def build_message id
162     unimplemented
163   end
164
165   ## Delete message with the given message-id from the index
166   def delete id
167     unimplemented
168   end
169
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={}
173     unimplemented
174   end
175
176   ## Yield each message-id matching query
177   def each_id query={}
178     unimplemented
179   end
180
181   ## Yield each message matching query
182   def each_message query={}, &b
183     each_id query do |id|
184       yield build_message(id)
185     end
186   end
187
188   ## Implementation-specific optimization step
189   def optimize
190     unimplemented
191   end
192
193   ## Return the id source of the source the message with the given message-id
194   ## was synced from
195   def source_for_id id
196     unimplemented
197   end
198
199   class ParseError < StandardError; end
200
201   ## parse a query string from the user. returns a query object
202   ## that can be passed to any index method with a 'query'
203   ## argument.
204   ##
205   ## raises a ParseError if something went wrong.
206   def parse_query s
207     unimplemented
208   end
209 end
210
211 index_name = ENV['SUP_INDEX'] || $config[:index] || DEFAULT_INDEX
212 case index_name
213   when "xapian"; require "sup/xapian_index"
214   when "ferret"; require "sup/ferret_index"
215   else fail "unknown index type #{index_name.inspect}"
216 end
217 Index = Redwood.const_get "#{index_name.capitalize}Index"
218 debug "using index #{Index.name}"
219
220 end