sup 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sup might be problematic. Click here for more details.
- data/History.txt +14 -1
- data/Manifest.txt +4 -1
- data/README.txt +13 -7
- data/Rakefile +3 -3
- data/bin/sup +17 -8
- data/bin/sup-add +20 -14
- data/bin/sup-config +222 -0
- data/bin/sup-dump +31 -0
- data/bin/sup-sync +235 -0
- data/doc/FAQ.txt +52 -25
- data/doc/Philosophy.txt +19 -19
- data/doc/TODO +37 -11
- data/doc/UserGuide.txt +42 -41
- data/lib/sup.rb +48 -4
- data/lib/sup/buffer.rb +22 -5
- data/lib/sup/draft.rb +2 -2
- data/lib/sup/imap.rb +20 -30
- data/lib/sup/index.rb +42 -54
- data/lib/sup/logger.rb +1 -1
- data/lib/sup/maildir.rb +127 -0
- data/lib/sup/mbox/loader.rb +40 -38
- data/lib/sup/mbox/ssh-file.rb +16 -25
- data/lib/sup/mbox/ssh-loader.rb +2 -11
- data/lib/sup/message.rb +14 -8
- data/lib/sup/mode.rb +1 -1
- data/lib/sup/modes/edit-message-mode.rb +1 -1
- data/lib/sup/modes/inbox-mode.rb +4 -1
- data/lib/sup/modes/line-cursor-mode.rb +3 -2
- data/lib/sup/modes/reply-mode.rb +1 -1
- data/lib/sup/modes/thread-index-mode.rb +6 -6
- data/lib/sup/modes/thread-view-mode.rb +24 -11
- data/lib/sup/poll.rb +45 -36
- data/lib/sup/sent.rb +3 -3
- data/lib/sup/source.rb +29 -47
- data/lib/sup/thread.rb +1 -1
- data/lib/sup/util.rb +30 -5
- metadata +12 -7
- data/bin/sup-import +0 -159
data/lib/sup/mbox/ssh-loader.rb
CHANGED
@@ -3,12 +3,7 @@ require 'net/ssh'
|
|
3
3
|
module Redwood
|
4
4
|
module MBox
|
5
5
|
|
6
|
-
## this is slightly complicated because SSHFile (and thus @f or
|
7
|
-
## @loader) can throw a variety of exceptions, and we need to catch
|
8
|
-
## those, reraise them as SourceErrors, and set ourselves as broken.
|
9
|
-
|
10
6
|
class SSHLoader < Source
|
11
|
-
attr_reader_cloned :labels
|
12
7
|
attr_accessor :username, :password
|
13
8
|
|
14
9
|
def initialize uri, username=nil, password=nil, start_offset=nil, usual=true, archived=false, id=nil
|
@@ -32,7 +27,6 @@ class SSHLoader < Source
|
|
32
27
|
## heuristic: use the filename as a label, unless the file
|
33
28
|
## has a path that probably represents an inbox.
|
34
29
|
@labels = [:unread]
|
35
|
-
@labels << :inbox unless archived?
|
36
30
|
@labels << File.basename(filename).intern unless File.dirname(filename) =~ /\b(var|usr|spool)\b/
|
37
31
|
end
|
38
32
|
|
@@ -41,7 +35,6 @@ class SSHLoader < Source
|
|
41
35
|
def filename; @parsed_uri.path[1..-1] end
|
42
36
|
|
43
37
|
def next
|
44
|
-
return if broken?
|
45
38
|
safely do
|
46
39
|
offset, labels = @loader.next
|
47
40
|
self.cur_offset = @loader.cur_offset # superclass keeps @cur_offset which is used by yaml
|
@@ -62,11 +55,9 @@ class SSHLoader < Source
|
|
62
55
|
def safely
|
63
56
|
begin
|
64
57
|
yield
|
65
|
-
rescue Net::SSH::Exception, SocketError, SSHFileError, SystemCallError => e
|
58
|
+
rescue Net::SSH::Exception, SocketError, SSHFileError, SystemCallError, IOError => e
|
66
59
|
m = "error communicating with SSH server #{host} (#{e.class.name}): #{e.message}"
|
67
|
-
|
68
|
-
self.broken_msg = @loader.broken_msg = m
|
69
|
-
raise SourceError, m
|
60
|
+
raise FatalSourceError, m
|
70
61
|
end
|
71
62
|
end
|
72
63
|
|
data/lib/sup/message.rb
CHANGED
@@ -138,7 +138,6 @@ class Message
|
|
138
138
|
end
|
139
139
|
private :read_header
|
140
140
|
|
141
|
-
def broken?; @source.broken?; end
|
142
141
|
def snippet; @snippet || chunks && @snippet; end
|
143
142
|
def is_list_message?; !@list_address.nil?; end
|
144
143
|
def is_draft?; DraftLoader === @source; end
|
@@ -148,8 +147,7 @@ class Message
|
|
148
147
|
end
|
149
148
|
|
150
149
|
def save index
|
151
|
-
|
152
|
-
index.update_message self if @dirty
|
150
|
+
index.sync_message self if @dirty
|
153
151
|
@dirty = false
|
154
152
|
end
|
155
153
|
|
@@ -177,8 +175,8 @@ class Message
|
|
177
175
|
## this is called when the message body needs to actually be loaded.
|
178
176
|
def load_from_source!
|
179
177
|
@chunks ||=
|
180
|
-
if @source.
|
181
|
-
[Text.new(error_message(@source.
|
178
|
+
if @source.has_errors?
|
179
|
+
[Text.new(error_message(@source.error.message.split("\n")))]
|
182
180
|
else
|
183
181
|
begin
|
184
182
|
## we need to re-read the header because it contains information
|
@@ -192,6 +190,9 @@ class Message
|
|
192
190
|
read_header @source.load_header(@source_info)
|
193
191
|
message_to_chunks @source.load_message(@source_info)
|
194
192
|
rescue SourceError, SocketError, MessageFormatError => e
|
193
|
+
## we need force_to_top here otherwise this window will cover
|
194
|
+
## up the error message one
|
195
|
+
Redwood::report_broken_sources :force_to_top => true
|
195
196
|
[Text.new(error_message(e.message))]
|
196
197
|
end
|
197
198
|
end
|
@@ -202,8 +203,13 @@ class Message
|
|
202
203
|
#@snippet...
|
203
204
|
|
204
205
|
***********************************************************************
|
205
|
-
|
206
|
-
|
206
|
+
An error occurred while loading this message. It is possible that
|
207
|
+
the source has changed, or (in the case of remote sources) is down.
|
208
|
+
You can check the log for errors, though hopefully an error window
|
209
|
+
should have popped up at some point.
|
210
|
+
|
211
|
+
The message location was:
|
212
|
+
#@source##@source_info
|
207
213
|
***********************************************************************
|
208
214
|
|
209
215
|
The error message was:
|
@@ -259,7 +265,7 @@ private
|
|
259
265
|
ret = [] <<
|
260
266
|
case m.header.content_type
|
261
267
|
when "text/plain", nil
|
262
|
-
m.body && body = m.decode or raise MessageFormatError, "
|
268
|
+
m.body && body = m.decode or raise MessageFormatError, "For some bizarre reason, RubyMail was unable to parse this message."
|
263
269
|
text_to_chunks body.normalize_whitespace.split("\n")
|
264
270
|
when /^multipart\//
|
265
271
|
nil
|
data/lib/sup/mode.rb
CHANGED
data/lib/sup/modes/inbox-mode.rb
CHANGED
@@ -61,7 +61,7 @@ protected
|
|
61
61
|
return false unless @curpos < lines - 1
|
62
62
|
if @curpos >= botline - 1
|
63
63
|
page_down
|
64
|
-
set_cursor_pos
|
64
|
+
set_cursor_pos topline
|
65
65
|
else
|
66
66
|
@curpos += 1
|
67
67
|
unless buffer.dirty?
|
@@ -77,8 +77,9 @@ protected
|
|
77
77
|
def cursor_up
|
78
78
|
return false unless @curpos > @cursor_top
|
79
79
|
if @curpos == topline
|
80
|
+
old_topline = topline
|
80
81
|
page_up
|
81
|
-
set_cursor_pos [
|
82
|
+
set_cursor_pos [old_topline - 1, topline].max
|
82
83
|
else
|
83
84
|
@curpos -= 1
|
84
85
|
unless buffer.dirty?
|
data/lib/sup/modes/reply-mode.rb
CHANGED
@@ -119,7 +119,7 @@ protected
|
|
119
119
|
def reply_body_lines m
|
120
120
|
lines = ["Excerpts from #{@m.from.name}'s message of #{@m.date}:"] +
|
121
121
|
m.basic_body_lines.map { |l| "> #{l}" }
|
122
|
-
lines.pop while lines.last
|
122
|
+
lines.pop while lines.last =~ /^\s*$/
|
123
123
|
lines
|
124
124
|
end
|
125
125
|
|
@@ -56,11 +56,7 @@ class ThreadIndexMode < LineCursorMode
|
|
56
56
|
|
57
57
|
## open up a thread view window
|
58
58
|
def select t=nil
|
59
|
-
t ||= @threads[curpos]
|
60
|
-
|
61
|
-
## this isn't working entirely. TODO:figure out why
|
62
|
-
# t = t.clone # required so that messages added later on don't completely
|
63
|
-
# screw everything up
|
59
|
+
t ||= @threads[curpos] or return
|
64
60
|
|
65
61
|
## TODO: don't regen text completely
|
66
62
|
Redwood::reporting_thread do
|
@@ -74,6 +70,10 @@ class ThreadIndexMode < LineCursorMode
|
|
74
70
|
BufferManager.draw_screen # lame TODO: make this unnecessary
|
75
71
|
## the first draw_screen is needed before topline and botline
|
76
72
|
## are set, and the second to show the cursor having moved
|
73
|
+
|
74
|
+
t.remove_label :unread
|
75
|
+
update_text_for_line curpos
|
76
|
+
UpdateManager.relay self, :read, t
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
@@ -275,7 +275,7 @@ class ThreadIndexMode < LineCursorMode
|
|
275
275
|
def apply_to_tagged; @tags.apply_to_tagged; end
|
276
276
|
|
277
277
|
def edit_labels
|
278
|
-
thread = @threads[curpos]
|
278
|
+
thread = @threads[curpos] or return
|
279
279
|
speciall = (@hidden_labels + LabelManager::RESERVED_LABELS).uniq
|
280
280
|
keepl, modifyl = thread.labels.partition { |t| speciall.member? t }
|
281
281
|
label_string = modifyl.join(" ")
|
@@ -3,7 +3,7 @@ module Redwood
|
|
3
3
|
class ThreadViewMode < LineCursorMode
|
4
4
|
## this holds all info we need to lay out a message
|
5
5
|
class Layout
|
6
|
-
attr_accessor :top, :bot, :prev, :next, :depth, :width, :state, :color
|
6
|
+
attr_accessor :top, :bot, :prev, :next, :depth, :width, :state, :color, :star_color, :orig_new
|
7
7
|
end
|
8
8
|
|
9
9
|
DATE_FORMAT = "%B %e %Y %l:%M%P"
|
@@ -26,6 +26,7 @@ class ThreadViewMode < LineCursorMode
|
|
26
26
|
k.add :edit_as_new, "Edit message as new", 'D'
|
27
27
|
k.add :save_to_disk, "Save message/attachment to disk", 's'
|
28
28
|
k.add :search, "Search for messages from particular people", 'S'
|
29
|
+
k.add :compose, "Compose message to person", 'm'
|
29
30
|
k.add :archive_and_kill, "Archive thread and kill buffer", 'A'
|
30
31
|
end
|
31
32
|
|
@@ -52,6 +53,8 @@ class ThreadViewMode < LineCursorMode
|
|
52
53
|
@layout[m] = Layout.new
|
53
54
|
@layout[m].state = initial_state_for m
|
54
55
|
@layout[m].color = altcolor ? :alternate_patina_color : :message_patina_color
|
56
|
+
@layout[m].star_color = altcolor ? :alternate_starred_patina_color : :starred_patina_color
|
57
|
+
@layout[m].orig_new = m.has_label? :unread
|
55
58
|
altcolor = !altcolor
|
56
59
|
if latest_date.nil? || m.date > latest_date
|
57
60
|
latest_date = m.date
|
@@ -111,10 +114,22 @@ class ThreadViewMode < LineCursorMode
|
|
111
114
|
def search
|
112
115
|
p = @person_lines[curpos] or return
|
113
116
|
mode = PersonSearchResultsMode.new [p]
|
114
|
-
BufferManager.spawn "
|
117
|
+
BufferManager.spawn "Search for #{p.name}", mode
|
115
118
|
mode.load_threads :num => mode.buffer.content_height
|
116
119
|
end
|
117
120
|
|
121
|
+
def compose
|
122
|
+
p = @person_lines[curpos]
|
123
|
+
mode =
|
124
|
+
if p
|
125
|
+
ComposeMode.new :to => [p]
|
126
|
+
else
|
127
|
+
ComposeMode.new
|
128
|
+
end
|
129
|
+
BufferManager.spawn "Compose message", mode
|
130
|
+
mode.edit
|
131
|
+
end
|
132
|
+
|
118
133
|
def toggle_starred
|
119
134
|
m = @message_lines[curpos] or return
|
120
135
|
if m.has_label? :starred
|
@@ -231,7 +246,7 @@ class ThreadViewMode < LineCursorMode
|
|
231
246
|
end
|
232
247
|
|
233
248
|
def collapse_non_new_messages
|
234
|
-
@layout.each { |m, l| l.state =
|
249
|
+
@layout.each { |m, l| l.state = l.orig_new ? :open : :closed if m.is_a? Message }
|
235
250
|
update
|
236
251
|
end
|
237
252
|
|
@@ -246,9 +261,7 @@ class ThreadViewMode < LineCursorMode
|
|
246
261
|
end
|
247
262
|
|
248
263
|
def cleanup
|
249
|
-
@
|
250
|
-
UpdateManager.relay self, :read, @thread
|
251
|
-
@layout = @text = nil
|
264
|
+
@layout = @text = nil # for good luck
|
252
265
|
end
|
253
266
|
|
254
267
|
def archive_and_kill
|
@@ -290,7 +303,7 @@ private
|
|
290
303
|
l = @layout[m] or next # TODO: figure out why this is nil sometimes
|
291
304
|
|
292
305
|
## build the patina
|
293
|
-
text = chunk_to_lines m, l.state, @text.length, depth, parent, @layout[m].color
|
306
|
+
text = chunk_to_lines m, l.state, @text.length, depth, parent, @layout[m].color, @layout[m].star_color
|
294
307
|
|
295
308
|
l.top = @text.length
|
296
309
|
l.bot = @text.length + text.length # updated below
|
@@ -327,7 +340,7 @@ private
|
|
327
340
|
end
|
328
341
|
end
|
329
342
|
|
330
|
-
def message_patina_lines m, state, start, parent, prefix, color
|
343
|
+
def message_patina_lines m, state, start, parent, prefix, color, star_color
|
331
344
|
prefix_widget = [color, prefix]
|
332
345
|
widget =
|
333
346
|
case state
|
@@ -338,7 +351,7 @@ private
|
|
338
351
|
end
|
339
352
|
imp_widget =
|
340
353
|
if m.has_label?(:starred)
|
341
|
-
[
|
354
|
+
[star_color, "* "]
|
342
355
|
else
|
343
356
|
[color, " "]
|
344
357
|
end
|
@@ -398,7 +411,7 @@ private
|
|
398
411
|
p.longname + (ContactManager.is_contact?(p) ? " (#{ContactManager.alias_for p})" : "")
|
399
412
|
end
|
400
413
|
|
401
|
-
def chunk_to_lines chunk, state, start, depth, parent=nil, color=nil
|
414
|
+
def chunk_to_lines chunk, state, start, depth, parent=nil, color=nil, star_color=nil
|
402
415
|
prefix = " " * INDENT_SPACES * depth
|
403
416
|
case chunk
|
404
417
|
when :fake_root
|
@@ -406,7 +419,7 @@ private
|
|
406
419
|
when nil
|
407
420
|
[[[:missing_message_color, "#{prefix}<an unreceived message>"]]]
|
408
421
|
when Message
|
409
|
-
message_patina_lines(chunk, state, start, parent, prefix, color) +
|
422
|
+
message_patina_lines(chunk, state, start, parent, prefix, color, star_color) +
|
410
423
|
(chunk.is_draft? ? [[[:draft_notification_color, prefix + " >>> This message is a draft. To edit, hit 'e'. <<<"]]] : [])
|
411
424
|
when Message::Attachment
|
412
425
|
[[[:mime_color, "#{prefix}+ MIME attachment #{chunk.content_type}#{chunk.desc ? ' (' + chunk.desc + ')': ''}"]]]
|
data/lib/sup/poll.rb
CHANGED
@@ -43,10 +43,17 @@ class PollManager
|
|
43
43
|
@mutex.synchronize do
|
44
44
|
Index.usual_sources.each do |source|
|
45
45
|
# yield "source #{source} is done? #{source.done?} (cur_offset #{source.cur_offset} >= #{source.end_offset})"
|
46
|
-
|
46
|
+
begin
|
47
|
+
yield "Loading from #{source}... " unless source.done? || source.has_errors?
|
48
|
+
rescue SourceError => e
|
49
|
+
Redwood::log "problem getting messages from #{source}: #{e.message}"
|
50
|
+
Redwood::report_broken_sources
|
51
|
+
next
|
52
|
+
end
|
53
|
+
|
47
54
|
num = 0
|
48
55
|
numi = 0
|
49
|
-
|
56
|
+
add_messages_from source do |m, offset, entry|
|
50
57
|
## always preserve the labels on disk.
|
51
58
|
m.labels = entry[:label].split(/\s+/).map { |x| x.intern } if entry
|
52
59
|
yield "Found message at #{offset} with labels {#{m.labels * ', '}}"
|
@@ -69,47 +76,49 @@ class PollManager
|
|
69
76
|
end
|
70
77
|
|
71
78
|
## this is the main mechanism for adding new messages to the
|
72
|
-
## index. it's called both by sup-
|
79
|
+
## index. it's called both by sup-sync and by PollMode.
|
73
80
|
##
|
74
|
-
## for each
|
75
|
-
##
|
76
|
-
## the
|
77
|
-
##
|
81
|
+
## for each message in the source, starting from the source's
|
82
|
+
## starting offset, this methods yields the message, the source
|
83
|
+
## offset, and the index entry on disk (if any). it expects the
|
84
|
+
## yield to return the message (possibly altered in some way), and
|
85
|
+
## then adds it (if new) or updates it (if previously seen).
|
78
86
|
##
|
79
|
-
## the labels of the yielded message are the source
|
80
|
-
## likely that callers will want to replace these with
|
81
|
-
## labels, if they exist, so that state is not lost when
|
82
|
-
## version of a message from a mailing list comes in.
|
83
|
-
def
|
84
|
-
|
85
|
-
|
86
|
-
source.each do |offset, labels|
|
87
|
-
if source.broken?
|
88
|
-
Redwood::log "error loading messages from #{source}: #{source.broken_msg}"
|
89
|
-
return
|
90
|
-
end
|
87
|
+
## the labels of the yielded message are the default source
|
88
|
+
## labels. it is likely that callers will want to replace these with
|
89
|
+
## the index labels, if they exist, so that state is not lost when
|
90
|
+
## e.g. a new version of a message from a mailing list comes in.
|
91
|
+
def add_messages_from source
|
92
|
+
begin
|
93
|
+
return if source.done? || source.has_errors?
|
91
94
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
if m.source_marked_read?
|
97
|
-
m.remove_label :unread
|
98
|
-
labels.delete :unread
|
95
|
+
source.each do |offset, labels|
|
96
|
+
if source.has_errors?
|
97
|
+
Redwood::log "error loading messages from #{source}: #{source.broken_msg}"
|
98
|
+
return
|
99
99
|
end
|
100
|
+
|
101
|
+
labels.each { |l| LabelManager << l }
|
102
|
+
labels += [:inbox] unless source.archived?
|
103
|
+
|
104
|
+
begin
|
105
|
+
m = Message.new :source => source, :source_info => offset, :labels => labels
|
106
|
+
if m.source_marked_read?
|
107
|
+
m.remove_label :unread
|
108
|
+
labels.delete :unread
|
109
|
+
end
|
100
110
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
Index.add_message m
|
108
|
-
UpdateManager.relay self, :add, m
|
111
|
+
docid, entry = Index.load_entry_for_id m.id
|
112
|
+
m = yield(m, offset, entry) or next
|
113
|
+
Index.sync_message m, docid, entry
|
114
|
+
UpdateManager.relay self, :add, m unless entry
|
115
|
+
rescue MessageFormatError => e
|
116
|
+
Redwood::log "ignoring erroneous message at #{source}##{offset}: #{e.message}"
|
109
117
|
end
|
110
|
-
rescue MessageFormatError, SourceError => e
|
111
|
-
Redwood::log "ignoring erroneous message at #{source}##{offset}: #{e.message}"
|
112
118
|
end
|
119
|
+
rescue SourceError => e
|
120
|
+
Redwood::log "problem getting messages from #{source}: #{e.message}"
|
121
|
+
Redwood::report_broken_sources
|
113
122
|
end
|
114
123
|
end
|
115
124
|
end
|
data/lib/sup/sent.rb
CHANGED
@@ -12,7 +12,7 @@ class SentManager
|
|
12
12
|
|
13
13
|
def self.source_name; "sup://sent"; end
|
14
14
|
def self.source_id; 9998; end
|
15
|
-
def new_source; @source = SentLoader.new; end
|
15
|
+
def new_source; @source = Recoverable.new SentLoader.new; end
|
16
16
|
|
17
17
|
def write_sent_message date, from_email
|
18
18
|
need_blank = File.exists?(@fn) && !File.zero?(@fn)
|
@@ -22,8 +22,8 @@ class SentManager
|
|
22
22
|
yield f
|
23
23
|
end
|
24
24
|
@source.each do |offset, labels|
|
25
|
-
m = Message.new :source => @source, :source_info => offset, :labels => labels
|
26
|
-
Index.
|
25
|
+
m = Message.new :source => @source, :source_info => offset, :labels => @source.labels
|
26
|
+
Index.sync_message m
|
27
27
|
UpdateManager.relay self, :add, m
|
28
28
|
end
|
29
29
|
end
|
data/lib/sup/source.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module Redwood
|
2
2
|
|
3
3
|
class SourceError < StandardError; end
|
4
|
+
class OutOfSyncSourceError < SourceError; end
|
5
|
+
class FatalSourceError < SourceError; end
|
4
6
|
|
5
7
|
class Source
|
6
8
|
## Implementing a new source is typically quite easy, because Sup
|
@@ -30,31 +32,31 @@ class Source
|
|
30
32
|
## - load_message offset
|
31
33
|
## - raw_header offset
|
32
34
|
## - raw_full_message offset
|
35
|
+
## - check
|
33
36
|
## - next (or each, if you prefer)
|
34
37
|
##
|
35
38
|
## ... where "offset" really means unique id. (You can tell I
|
36
39
|
## started with mbox.)
|
37
40
|
##
|
38
|
-
##
|
39
|
-
##
|
40
|
-
##
|
41
|
-
##
|
41
|
+
## All exceptions relating to accessing the source must be caught
|
42
|
+
## and rethrown as FatalSourceErrors or OutOfSyncSourceErrors.
|
43
|
+
## OutOfSyncSourceErrors should be used for problems that a call to
|
44
|
+
## sup-sync will fix (namely someone's been playing with the source
|
45
|
+
## from another client); FatalSourceErrors can be used for anything
|
46
|
+
## else (e.g. the imap server is down or the maildir is missing.)
|
42
47
|
##
|
43
|
-
##
|
48
|
+
## Finally, be sure the source is thread-safe, since it WILL be
|
44
49
|
## pummeled from multiple threads at once.
|
45
50
|
##
|
46
|
-
##
|
47
|
-
##
|
51
|
+
## Examples for you to look at: mbox/loader.rb, imap.rb, and
|
52
|
+
## maildir.rb.
|
48
53
|
|
49
|
-
|
50
|
-
|
51
|
-
## dirty? described whether cur_offset has changed, which means the
|
52
|
-
## source info needs to be re-saved to sources.yaml.
|
54
|
+
## let's begin!
|
53
55
|
##
|
54
|
-
##
|
55
|
-
##
|
56
|
+
## dirty? means cur_offset has changed, so the source info needs to
|
57
|
+
## be re-saved to sources.yaml.
|
56
58
|
bool_reader :usual, :archived, :dirty
|
57
|
-
attr_reader :uri, :cur_offset
|
59
|
+
attr_reader :uri, :cur_offset
|
58
60
|
attr_accessor :id
|
59
61
|
|
60
62
|
def initialize uri, initial_offset=nil, usual=true, archived=false, id=nil
|
@@ -64,41 +66,26 @@ class Source
|
|
64
66
|
@archived = archived
|
65
67
|
@id = id
|
66
68
|
@dirty = false
|
67
|
-
@broken_msg = nil
|
68
69
|
end
|
69
70
|
|
70
|
-
def broken?; !@broken_msg.nil?; end
|
71
71
|
def to_s; @uri.to_s; end
|
72
72
|
def seek_to! o; self.cur_offset = o; end
|
73
|
-
def reset!
|
74
|
-
|
75
|
-
|
76
|
-
seek_to! start_offset
|
77
|
-
rescue SourceError
|
78
|
-
end
|
79
|
-
end
|
80
|
-
def == o; o.to_s == to_s; end
|
81
|
-
def done?;
|
82
|
-
return true if broken?
|
83
|
-
begin
|
84
|
-
(self.cur_offset ||= start_offset) >= end_offset
|
85
|
-
rescue SourceError => e
|
86
|
-
true
|
87
|
-
end
|
88
|
-
end
|
73
|
+
def reset!; seek_to! start_offset; end
|
74
|
+
def == o; o.uri == uri; end
|
75
|
+
def done?; (self.cur_offset ||= start_offset) >= end_offset; end
|
89
76
|
def is_source_for? uri; URI(self.uri) == URI(uri); end
|
90
77
|
|
78
|
+
## check should throw a FatalSourceError or an OutOfSyncSourcError
|
79
|
+
## if it can detect a problem. it is called when the sup starts up
|
80
|
+
## to proactively notify the user of any source problems.
|
81
|
+
def check; end
|
82
|
+
|
91
83
|
def each
|
92
|
-
|
93
|
-
|
94
|
-
self.
|
95
|
-
|
96
|
-
|
97
|
-
raise "no message" unless n
|
98
|
-
yield n, labels
|
99
|
-
end
|
100
|
-
rescue SourceError => e
|
101
|
-
self.broken_msg = e.message
|
84
|
+
self.cur_offset ||= start_offset
|
85
|
+
until done?
|
86
|
+
n, labels = self.next
|
87
|
+
raise "no message" unless n
|
88
|
+
yield n, labels
|
102
89
|
end
|
103
90
|
end
|
104
91
|
|
@@ -108,11 +95,6 @@ protected
|
|
108
95
|
@cur_offset = o
|
109
96
|
@dirty = true
|
110
97
|
end
|
111
|
-
|
112
|
-
def broken_msg= m
|
113
|
-
@broken_msg = m
|
114
|
-
# Redwood::log "#{to_s}: #{m}"
|
115
|
-
end
|
116
98
|
end
|
117
99
|
|
118
100
|
Redwood::register_yaml(Source, %w(uri cur_offset usual archived id))
|