sup 0.0.6 → 0.0.7
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/HACKING +50 -0
- data/History.txt +12 -0
- data/Manifest.txt +2 -0
- data/Rakefile +5 -1
- data/bin/sup +24 -8
- data/bin/sup-add +106 -0
- data/bin/sup-import +85 -165
- data/doc/FAQ.txt +48 -11
- data/doc/TODO +45 -12
- data/doc/UserGuide.txt +54 -42
- data/lib/sup.rb +6 -2
- data/lib/sup/buffer.rb +20 -4
- data/lib/sup/contact.rb +22 -12
- data/lib/sup/draft.rb +27 -7
- data/lib/sup/imap.rb +107 -97
- data/lib/sup/index.rb +35 -25
- data/lib/sup/label.rb +2 -2
- data/lib/sup/mbox.rb +20 -15
- data/lib/sup/mbox/loader.rb +0 -1
- data/lib/sup/mbox/ssh-file.rb +58 -47
- data/lib/sup/mbox/ssh-loader.rb +13 -20
- data/lib/sup/message.rb +15 -9
- data/lib/sup/mode.rb +16 -6
- data/lib/sup/modes/compose-mode.rb +7 -3
- data/lib/sup/modes/contact-list-mode.rb +50 -25
- data/lib/sup/modes/edit-message-mode.rb +11 -8
- data/lib/sup/modes/inbox-mode.rb +19 -3
- data/lib/sup/modes/label-list-mode.rb +4 -12
- data/lib/sup/modes/log-mode.rb +6 -0
- data/lib/sup/modes/reply-mode.rb +19 -6
- data/lib/sup/modes/resume-mode.rb +13 -9
- data/lib/sup/modes/scroll-mode.rb +13 -2
- data/lib/sup/modes/thread-index-mode.rb +94 -43
- data/lib/sup/modes/thread-view-mode.rb +198 -130
- data/lib/sup/person.rb +1 -1
- data/lib/sup/poll.rb +60 -47
- data/lib/sup/sent.rb +3 -2
- data/lib/sup/source.rb +14 -6
- data/lib/sup/textfield.rb +1 -0
- data/lib/sup/thread.rb +76 -23
- data/lib/sup/update.rb +2 -3
- data/lib/sup/util.rb +13 -13
- metadata +14 -2
data/doc/FAQ.txt
CHANGED
@@ -1,27 +1,55 @@
|
|
1
1
|
Sup FAQ
|
2
2
|
-------
|
3
3
|
Q: What does Sup stand for?
|
4
|
-
A: It stands for "what's up?", which is more or less the question
|
5
|
-
|
4
|
+
A: It stands for "what's up?", which is more or less the question in
|
5
|
+
mind when I fire up my mail client.
|
6
6
|
|
7
7
|
Q: If you love GMail so much, why not just use it?
|
8
|
-
A: I hate
|
8
|
+
A: I hate ads, I hate using a mouse, and I hate non-programmability
|
9
9
|
and non-extensibility.
|
10
10
|
|
11
|
+
Also, GMail encourages top-posting in a variety of ways. THIS
|
12
|
+
CANNOT BE TOLERATED!
|
13
|
+
|
11
14
|
Q: Why the console?
|
12
|
-
A: There are many advantages to the console. As any Unix user knows, a
|
13
|
-
few keystrokes can accomplish the work of a hundred mouse clicks.
|
14
|
-
Also, you don't need web browser, and you get instantaneous response
|
15
|
-
and a simple interface.
|
16
15
|
|
17
|
-
|
18
|
-
|
16
|
+
A: Because a keystroke is with a hundred mouse clicks (as any Unix
|
17
|
+
user knows). Because you don't need web browser. Because you get
|
18
|
+
instantaneous response and a simple interface.
|
19
19
|
|
20
20
|
Q: How does Sup deal with spam?
|
21
21
|
A: You can manually mark messages as spam, which prevents them from
|
22
|
-
showing up in future searches, but that's
|
22
|
+
showing up in future searches, but that's as far as Sup goes. Spam
|
23
23
|
filtering should be done by a dedicated tool like SpamAssassin.
|
24
24
|
|
25
|
+
Q: How do I delete a message?
|
26
|
+
A: Press the 'd' key.
|
27
|
+
|
28
|
+
Q: But I want to delete it for real, not just add a 'deleted' flag in
|
29
|
+
the index. I want it gone from disk!
|
30
|
+
A: Deleting a message is an old-fashioned concept. In the modern
|
31
|
+
world, disk space is cheap enough that you should never have to
|
32
|
+
delete a message. If it's spam, save it for future analysis.
|
33
|
+
|
34
|
+
Q: C'mon, really now!
|
35
|
+
A: Ok, at some point I plan to have a batch deletion tool that will
|
36
|
+
run through a source and delete all messages that have a 'spam' or
|
37
|
+
'deleted' tags (and, for mbox sources, will update the offsets of
|
38
|
+
all later messages). But that doesn't exist yet.
|
39
|
+
|
40
|
+
Q: I got some error message about needing to run sup-import --rescan
|
41
|
+
when I tried to read a message. What's that about?
|
42
|
+
A: If messages have been moved, deleted, or altered in a source, Sup
|
43
|
+
may have to rebuild its index for that source. For example, for
|
44
|
+
mbox files, even reading a message changes the offsets of every
|
45
|
+
file on disk. Rather than rescanning every time, Sup assumes
|
46
|
+
sources don't change except by having new messages added. If that
|
47
|
+
assumption is violated, you'll have to run sup-import --rescan.
|
48
|
+
|
49
|
+
The alternative is to rescan every source when Sup starts
|
50
|
+
up. Because Sup is designed to work with arbitrarily large mbox
|
51
|
+
files, this would not be a good idea.
|
52
|
+
|
25
53
|
Q: What are all these "Redwood" references I see in the code?
|
26
54
|
A: That was Sup's original name. (Think pine, elm. Although I am a
|
27
55
|
Mutt user, I couldn't think of a good progression there.) But it was
|
@@ -31,9 +59,18 @@ A: That was Sup's original name. (Think pine, elm. Although I am a
|
|
31
59
|
Maybe one day I'll do a huge search-and-replace on the code, but it
|
32
60
|
doesn't seem that important at this point.
|
33
61
|
|
62
|
+
Q: I want to move messages from one source to another. (E.g., my
|
63
|
+
primary inbox is an IMAP server with a quota, and I want to move
|
64
|
+
some of those messages to local mbox files.) How do I do that while
|
65
|
+
preserving message state?
|
66
|
+
A: Move the messages from the source to the target using whatever tool
|
67
|
+
you'd like. Then (and this is the important part), sup-import
|
68
|
+
--rebuild both sources at once. If you do it one at a time, you may
|
69
|
+
lose message state. (Depending, actually, on which order you do it
|
70
|
+
in. But just do them both at once.)
|
71
|
+
|
34
72
|
Q: How is Sup possible?
|
35
73
|
A: Sup is only possible through the hard work of Dave Balmain, the
|
36
74
|
author of ferret, which is the search engine behind Sup. Ferret is
|
37
75
|
really a first-class piece of software, and it's due to the
|
38
76
|
tremendous amount of time and effort he's put in to it.
|
39
|
-
|
data/doc/TODO
CHANGED
@@ -1,25 +1,58 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
for 0.0.8
|
2
|
+
---------
|
3
|
+
create attachments
|
4
|
+
forward attachments
|
5
|
+
warnings: top-posting, missing attachment
|
3
6
|
maildir
|
4
|
-
|
5
|
-
|
7
|
+
|
8
|
+
for 0.0.9
|
9
|
+
---------
|
10
|
+
select all, starred, to me, etc
|
11
|
+
undo
|
6
12
|
use Net::SMTP
|
7
|
-
|
8
|
-
|
9
|
-
|
13
|
+
gmail
|
14
|
+
|
15
|
+
future
|
16
|
+
------
|
17
|
+
decode RFC 2047 ("encoded word") headers
|
18
|
+
- see: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/101949, http://dev.rubyonrails.org/ticket/6807
|
19
|
+
swappable keymappings
|
10
20
|
within-buffer search
|
21
|
+
bugfix: when returning from a shelling out, ncurses is crazy
|
22
|
+
bugfix: miscellaneous weirdnesses in buffer line editing
|
23
|
+
wide character support
|
24
|
+
i18n support
|
25
|
+
batch deletion
|
26
|
+
support for message-content modules such as ruby-talk:XXXXX detection
|
27
|
+
tab completion on labels, contacts
|
11
28
|
contact selector in edit-message-mode
|
12
|
-
undo
|
13
29
|
maybe: filters
|
14
30
|
maybe: rangefilter on the initial inbox to only consider the most recent 1000 messages
|
15
|
-
select all, starred, to me, etc
|
16
|
-
editing of arbitrary messages
|
17
31
|
annotations on messages
|
18
|
-
gmail
|
19
32
|
pop
|
20
|
-
move sup-import argument handling to getopt
|
21
33
|
be able to mark individual messages as spam in thread-view-mode
|
34
|
+
toggle wrapping
|
35
|
+
maybe: de-archived messages auto-added to inbox
|
22
36
|
|
37
|
+
done
|
38
|
+
----
|
39
|
+
x make 'A' archive in thread-view-mode
|
40
|
+
x remove stupid percent_done source methods (still useful; made it optional)
|
41
|
+
x don't quit while writing thread index state to disk or with unsaved drafts/messages
|
42
|
+
x bugfix: deleted threads are showing up (i don't see this any more)
|
43
|
+
x bugfix: changing IMAP ids
|
44
|
+
x bugfix: STILL new messages, drafts sometimes not showing up in inbox
|
45
|
+
x bugfix: killed threads
|
46
|
+
x bugfix: resuming a draft asks before discard
|
47
|
+
x add a flag to sup-import to force the creation of a new source (see http://rubyforge.org/forum/forum.php?thread_id=10973&forum_id=10340)
|
48
|
+
x use trollop to handle sup-devel args
|
49
|
+
x clean up import code and share between poll.rb and sup-import
|
50
|
+
x on startup, multi-threadedly call #connect on all sources
|
51
|
+
x bugfix: first time viewing a message only gets the first to:; subsequent views get them all (wtf)
|
52
|
+
x search for other messages from author in thread-view-mode
|
53
|
+
x resuming of arbitrary messages
|
54
|
+
x alias authors in thread-view-mode
|
55
|
+
x fix up contact list mode: should display while loading, and when you add an alias, should move everything else to the right
|
23
56
|
x fix bug: envelope-to thing still not working
|
24
57
|
x fix snippet repetitions with small snippets
|
25
58
|
x fix next and previous in thread-view-mode with <unreceived messages>
|
data/doc/UserGuide.txt
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
Welcome to Sup! Here's how to
|
1
|
+
Welcome to Sup! Here's how to get started.
|
2
2
|
|
3
|
-
First, try running 'sup'.
|
3
|
+
First, try running 'sup'. Since this is your first time, you'll be
|
4
4
|
confronted with a mostly blank screen, and a notice at the bottom that
|
5
5
|
you have no new messages. That's because Sup doesn't have any messages
|
6
6
|
in its index yet, and has no idea where to look for them anyways.
|
@@ -10,55 +10,67 @@ to cycle between buffers and 'x' to kill a buffer. There's probably
|
|
10
10
|
not too much interesting there, but there's a log buffer with some
|
11
11
|
cryptic messages. You can also press '?' at any point to get a list of
|
12
12
|
keyboard commands, but in the absense of any email, these will be
|
13
|
-
mostly useless. When you
|
14
|
-
|
15
|
-
|
16
|
-
message
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
13
|
+
mostly useless. When you get bored, press 'q' to quit.
|
14
|
+
|
15
|
+
I mentioned that Sup's index was empty. The index is where Sup stores
|
16
|
+
all message state (e.g. read or unread, any message labels), and all
|
17
|
+
information necessary for searching and for threading messages. Sup
|
18
|
+
only knows about messages in its index, so we've got to add some to
|
19
|
+
it.
|
20
|
+
|
21
|
+
We add messages to the index by telling Sup about the source where the
|
22
|
+
messages reside. Sources are things like imap folders, mbox folders,
|
23
|
+
maildir directories, and gmail accounts (in the future). Sup doesn't
|
24
|
+
duplicate the actual message content in the index. So when you search
|
25
|
+
for messages or view your inbox, Sup talks only to the index (stored
|
26
|
+
locally on disk). When you view a thread, Sup requests the full
|
27
|
+
content of all the messages from the source.
|
28
|
+
|
29
|
+
So let's add some sources to Sup's source list. Run 'sup-add' with
|
30
|
+
a URI pointing to an email source. The URI should be of the form:
|
27
31
|
- mbox://path/to/a/filename, for an mbox file on disk. (You can also
|
28
32
|
just provide the filename).
|
29
33
|
- imap://imap.server/folder or imaps://secure.imap.server/folder for
|
30
34
|
an IMAP folder. (Leave the folder blank for INBOX.)
|
31
35
|
- mbox+ssh://remote.machine/path/to/a/filename for a remote mbox file.
|
32
36
|
|
33
|
-
Before you
|
34
|
-
want
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
37
|
+
Before you add the source, you need make two decisions. The first is
|
38
|
+
whether you want Sup to regularly poll this source for new
|
39
|
+
messages. By default it will, but if this is a source that will never
|
40
|
+
have new messages, you can specify --unusual. Sup polls only "usual"
|
41
|
+
sources.
|
42
|
+
|
43
|
+
The second is whether you want messages from the source to be
|
44
|
+
automatically archived. An archived message will not show up in your
|
45
|
+
inbox, but will be found when you search. (Your inbox in Sup is, by
|
46
|
+
definition, the set of all all non-archived messages). Specify
|
47
|
+
--archive to automatically archive all messages from the source. This
|
48
|
+
is useful for sources that contain, for example, high-traffic mailing
|
49
|
+
lists that you don't want "polluting" your inbox.
|
50
|
+
|
51
|
+
If Sup requires account information, e.g. for IMAP servers and remote
|
52
|
+
mbox files, sup-add will ask for it.
|
53
|
+
|
54
|
+
Now that you've added the source, let's import all the current
|
55
|
+
messages from it, by running sup-import with the source URI. You can
|
56
|
+
specify --archive to automatically archive all messages in this
|
57
|
+
import; typically you'll want to specify this for every source you
|
58
|
+
import except your actual inbox. You can also specify --read to mark
|
59
|
+
all imported messages as read; the default is to preserve the
|
60
|
+
read/unread status from the source.
|
61
|
+
|
62
|
+
sup-import will now load all the messages from the source into the
|
63
|
+
index. Depending on the size of the source, this may take a
|
52
64
|
while. Don't panic! It's a one-time process.
|
53
65
|
|
54
|
-
|
55
|
-
~/.sup/config.yaml file. Replace "Your Name Here" with your
|
56
|
-
"your.email.here@domain.tld" with your email address, and fill
|
57
|
-
.signature file if you choose. You can also set the default
|
66
|
+
We're almost ready. But before we run 'sup' again, take a moment to
|
67
|
+
edit your ~/.sup/config.yaml file. Replace "Your Name Here" with your
|
68
|
+
name, "your.email.here@domain.tld" with your email address, and fill
|
69
|
+
in your .signature file if you choose. You can also set the default
|
70
|
+
editor.
|
58
71
|
|
59
|
-
|
60
|
-
|
61
|
-
working!
|
72
|
+
Ok, now run 'sup'. You should see the most recent unarchived messages
|
73
|
+
appear in your inbox. Congratulations, you've got Sup working!
|
62
74
|
|
63
75
|
If you're coming from the world of traditional email, there are a
|
64
76
|
couple differences you should be aware of at this point. First, Sup
|
data/lib/sup.rb
CHANGED
@@ -13,13 +13,13 @@ class Object
|
|
13
13
|
end
|
14
14
|
|
15
15
|
module Redwood
|
16
|
-
VERSION = "0.0.
|
16
|
+
VERSION = "0.0.7"
|
17
17
|
|
18
18
|
BASE_DIR = ENV["SUP_BASE"] || File.join(ENV["HOME"], ".sup")
|
19
19
|
CONFIG_FN = File.join(BASE_DIR, "config.yaml")
|
20
20
|
SOURCE_FN = File.join(BASE_DIR, "sources.yaml")
|
21
21
|
LABEL_FN = File.join(BASE_DIR, "labels.txt")
|
22
|
-
PERSON_FN
|
22
|
+
PERSON_FN = File.join(BASE_DIR, "people.txt")
|
23
23
|
CONTACT_FN = File.join(BASE_DIR, "contacts.txt")
|
24
24
|
DRAFT_DIR = File.join(BASE_DIR, "drafts")
|
25
25
|
SENT_FN = File.join(BASE_DIR, "sent.mbox")
|
@@ -34,6 +34,10 @@ module Redwood
|
|
34
34
|
begin
|
35
35
|
yield
|
36
36
|
rescue Exception => e
|
37
|
+
File.open("sup-exception-log.txt", "w") do |f|
|
38
|
+
f.puts "--- #{e.class.name} at #{Time.now}"
|
39
|
+
f.puts e.message, e.backtrace
|
40
|
+
end
|
37
41
|
$exception ||= e
|
38
42
|
raise
|
39
43
|
end
|
data/lib/sup/buffer.rb
CHANGED
@@ -14,7 +14,7 @@ module Ncurses
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def mutex; @mutex ||= Mutex.new; end
|
17
|
-
def sync &b; mutex.synchronize
|
17
|
+
def sync &b; mutex.synchronize(&b); end
|
18
18
|
|
19
19
|
## aaahhh, user input. who would have though that such a simple
|
20
20
|
## idea would be SO FUCKING COMPLICATED?! because apparently
|
@@ -139,7 +139,7 @@ class BufferManager
|
|
139
139
|
@minibuf_mutex = Mutex.new
|
140
140
|
@textfields = {}
|
141
141
|
@flash = nil
|
142
|
-
@shelled = false
|
142
|
+
@shelled = @asking = false
|
143
143
|
|
144
144
|
self.class.i_am_the_instance self
|
145
145
|
end
|
@@ -271,6 +271,21 @@ class BufferManager
|
|
271
271
|
b
|
272
272
|
end
|
273
273
|
|
274
|
+
def kill_all_buffers_safely
|
275
|
+
until @buffers.empty?
|
276
|
+
## inbox mode always claims it's unkillable. we'll ignore it.
|
277
|
+
return false unless @buffers.first.mode.is_a?(InboxMode) || @buffers.first.mode.killable?
|
278
|
+
kill_buffer @buffers.first
|
279
|
+
end
|
280
|
+
true
|
281
|
+
end
|
282
|
+
|
283
|
+
def kill_buffer_safely buf
|
284
|
+
return false unless buf.mode.killable?
|
285
|
+
kill_buffer buf
|
286
|
+
true
|
287
|
+
end
|
288
|
+
|
274
289
|
def kill_all_buffers
|
275
290
|
kill_buffer @buffers.first until @buffers.empty?
|
276
291
|
end
|
@@ -359,9 +374,9 @@ class BufferManager
|
|
359
374
|
ret
|
360
375
|
end
|
361
376
|
|
377
|
+
## returns true (y), false (n), or nil (ctrl-g / cancel)
|
362
378
|
def ask_yes_or_no question
|
363
|
-
r = ask_getch
|
364
|
-
case r
|
379
|
+
case(r = ask_getch question, "ynYN")
|
365
380
|
when ?y, ?Y
|
366
381
|
true
|
367
382
|
when nil
|
@@ -399,6 +414,7 @@ class BufferManager
|
|
399
414
|
|
400
415
|
def say s, id=nil
|
401
416
|
new_id = nil
|
417
|
+
|
402
418
|
@minibuf_mutex.synchronize do
|
403
419
|
new_id = id.nil?
|
404
420
|
id ||= @minibuf_stack.length
|
data/lib/sup/contact.rb
CHANGED
@@ -5,33 +5,43 @@ class ContactManager
|
|
5
5
|
|
6
6
|
def initialize fn
|
7
7
|
@fn = fn
|
8
|
-
@
|
8
|
+
@p2a = {} # person to alias map
|
9
|
+
@a2p = {} # alias to person map
|
9
10
|
|
10
11
|
if File.exists? fn
|
11
12
|
IO.foreach(fn) do |l|
|
12
13
|
l =~ /^(\S+): (.*)$/ or raise "can't parse #{fn} line #{l.inspect}"
|
13
14
|
aalias, addr = $1, $2
|
14
|
-
|
15
|
+
p = Person.for addr
|
16
|
+
@p2a[p] = aalias
|
17
|
+
@a2p[aalias] = p
|
15
18
|
end
|
16
19
|
end
|
17
20
|
|
18
21
|
self.class.i_am_the_instance self
|
19
22
|
end
|
20
23
|
|
21
|
-
def contacts; @
|
24
|
+
def contacts; @p2a.keys; end
|
22
25
|
def set_contact person, aalias
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
+
if(pold = @a2p[aalias]) && (pold != person)
|
27
|
+
drop_contact pold
|
28
|
+
end
|
29
|
+
@p2a[person] = aalias
|
30
|
+
@a2p[aalias] = person
|
26
31
|
end
|
27
|
-
def drop_contact person
|
28
|
-
|
29
|
-
|
30
|
-
|
32
|
+
def drop_contact person
|
33
|
+
if(aalias = @p2a[person])
|
34
|
+
@p2a.delete person
|
35
|
+
@a2p.delete aalias
|
36
|
+
end
|
37
|
+
end
|
38
|
+
def person_with aalias; @a2p[aalias]; end
|
39
|
+
def alias_for person; @p2a[person]; end
|
40
|
+
def is_contact? person; @p2a.member? person; end
|
31
41
|
def save
|
32
42
|
File.open(@fn, "w") do |f|
|
33
|
-
@
|
34
|
-
f.puts "#{
|
43
|
+
@p2a.each do |p, a|
|
44
|
+
f.puts "#{a}: #{p.full_address}"
|
35
45
|
end
|
36
46
|
end
|
37
47
|
end
|
data/lib/sup/draft.rb
CHANGED
@@ -10,7 +10,7 @@ class DraftManager
|
|
10
10
|
self.class.i_am_the_instance self
|
11
11
|
end
|
12
12
|
|
13
|
-
def self.source_name; "drafts
|
13
|
+
def self.source_name; "sup://drafts"; end
|
14
14
|
def self.source_id; 9999; end
|
15
15
|
def new_source; @source = DraftLoader.new; end
|
16
16
|
|
@@ -19,11 +19,15 @@ class DraftManager
|
|
19
19
|
fn = @source.fn_for_offset offset
|
20
20
|
File.open(fn, "w") { |f| yield f }
|
21
21
|
|
22
|
-
|
23
|
-
|
22
|
+
my_message = nil
|
23
|
+
@source.each do |thisoffset, theselabels|
|
24
|
+
m = Message.new :source => @source, :source_info => thisoffset, :labels => theselabels
|
24
25
|
Index.add_message m
|
25
|
-
UpdateManager.relay :add, m
|
26
|
+
UpdateManager.relay self, :add, m
|
27
|
+
my_message = m if thisoffset == offset
|
26
28
|
end
|
29
|
+
|
30
|
+
my_message
|
27
31
|
end
|
28
32
|
|
29
33
|
def discard mid
|
@@ -32,7 +36,7 @@ class DraftManager
|
|
32
36
|
raise ArgumentError, "not a draft: source id #{entry[:source_id].inspect}, should be #{DraftManager.source_id.inspect} for #{mid.inspect} / docno #{docid}" unless entry[:source_id].to_i == DraftManager.source_id
|
33
37
|
Index.drop_entry docid
|
34
38
|
File.delete @source.fn_for_offset(entry[:source_info])
|
35
|
-
UpdateManager.relay :delete, mid
|
39
|
+
UpdateManager.relay self, :delete, mid
|
36
40
|
end
|
37
41
|
end
|
38
42
|
|
@@ -48,9 +52,16 @@ class DraftLoader < Source
|
|
48
52
|
|
49
53
|
def id; DraftManager.source_id; end
|
50
54
|
def to_s; DraftManager.source_name; end
|
55
|
+
def uri; DraftManager.source_name; end
|
51
56
|
|
52
57
|
def each
|
53
|
-
|
58
|
+
ids = get_ids
|
59
|
+
ids.each do |id|
|
60
|
+
if id >= cur_offset
|
61
|
+
self.cur_offset = id + 1
|
62
|
+
yield [id, [:draft, :inbox]]
|
63
|
+
end
|
64
|
+
end
|
54
65
|
end
|
55
66
|
|
56
67
|
def gen_offset
|
@@ -96,7 +107,16 @@ class DraftLoader < Source
|
|
96
107
|
end
|
97
108
|
|
98
109
|
def start_offset; 0; end
|
99
|
-
def end_offset
|
110
|
+
def end_offset
|
111
|
+
ids = get_ids
|
112
|
+
ids.empty? ? 0 : (ids.last + 1)
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def get_ids
|
118
|
+
Dir.entries(@dir).select { |x| x =~ /^\d+$/ }.map { |x| x.to_i }.sort
|
119
|
+
end
|
100
120
|
end
|
101
121
|
|
102
122
|
Redwood::register_yaml(DraftLoader, %w(cur_offset))
|