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