]> git.cworth.org Git - sup/blob - lib/sup/index.rb
index: choose index implementation with config entry or environment variable
[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   ## Syncs the message to the index, replacing any previous version.  adding
117   ## either way. Index state will be determined by the message's #labels
118   ## accessor.
119   def sync_message m, opts={}
120     unimplemented
121   end
122
123   def save_index fn
124     unimplemented
125   end
126
127   def contains_id? id
128     unimplemented
129   end
130
131   def contains? m; contains_id? m.id end
132
133   def size
134     unimplemented
135   end
136
137   def empty?; size == 0 end
138
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={}
144     unimplemented
145   end
146
147   ## Return the number of matches for query in the index
148   def num_results_for query={}
149     unimplemented
150   end
151
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.
156   ##
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
159   ## is found.
160   def each_message_in_thread_for m, opts={}
161     unimplemented
162   end
163
164   ## Load message with the given message-id from the index
165   def build_message id
166     unimplemented
167   end
168
169   ## Delete message with the given message-id from the index
170   def delete id
171     unimplemented
172   end
173
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={}
177     unimplemented
178   end
179
180   ## Yield each message-id matching query
181   def each_id query={}
182     unimplemented
183   end
184
185   ## Yield each message matching query
186   def each_message query={}, &b
187     each_id query do |id|
188       yield build_message(id)
189     end
190   end
191
192   ## Implementation-specific optimization step
193   def optimize
194     unimplemented
195   end
196
197   ## Return the id source of the source the message with the given message-id
198   ## was synced from
199   def source_for_id id
200     unimplemented
201   end
202
203   class ParseError < StandardError; end
204
205   ## parse a query string from the user. returns a query object
206   ## that can be passed to any index method with a 'query'
207   ## argument.
208   ##
209   ## raises a ParseError if something went wrong.
210   def parse_query s
211     unimplemented
212   end
213 end
214
215 index_name = ENV['SUP_INDEX'] || $config[:index] || DEFAULT_INDEX
216 begin
217   require "sup/#{index_name}_index"
218 rescue LoadError
219   fail "invalid index name #{index_name.inspect}"
220 end
221 Index = Redwood.const_get "#{index_name.capitalize}Index"
222 Redwood::log "using index #{Index.name}"
223
224 end