]> git.cworth.org Git - sup/blob - lib/sup/source.rb
be01780ee8bb61ffec6894ce6ff18cbc18072235
[sup] / lib / sup / source.rb
1 module Redwood
2
3 class SourceError < StandardError; end
4
5 class Source
6   ## Implementing a new source is typically quite easy, because Sup
7   ## only needs to be able to:
8   ##  1. See how many messages it contains
9   ##  2. Get an arbirtrary message
10   ##  3. (optional) see whether the source has marked it read or not
11   ##
12   ## In particular, Sup doesn't need to move messages, mark them as
13   ## read, delete them, or anything else. (Well, at some point it will
14   ## need to delete them, but that will be an optional capability.)
15   ##
16   ## On the other hand, Sup assumes that you can assign each message a
17   ## unique integer id, such that newer messages have higher ids than
18   ## earlier ones, and that those ids stay constant across sessions
19   ## (in the absence of some other client going in and fucking
20   ## everything up). For example, for mboxes I use the file offset of
21   ## the start of the message. If a source does NOT have that
22   ## capability, e.g. IMAP, then you have to do a little more work to
23   ## simulate it.
24   ##
25   ## To write a new source, subclass this class, and implement:
26   ##
27   ## - start_offset
28   ## - end_offset (exclusive!)
29   ## - load_header offset
30   ## - load_message offset
31   ## - raw_header offset
32   ## - raw_full_message offset
33   ## - next (or each, if you prefer)
34   ##
35   ## ... where "offset" really means unique id. (You can tell I
36   ## started with mbox.)
37   ##
38   ## You can throw SourceErrors from any of those, but we don't catch
39   ## anything else, so make sure you catch *all* errors and reraise
40   ## them as SourceErrors, and set broken_msg to something if the
41   ## source needs to be rescanned.
42   ##
43   ## Also, be sure to make the source thread-safe, since it WILL be
44   ## pummeled from multiple threads at once.
45   ##
46   ## Two examples for you to look at, though sadly neither of them is
47   ## as simple as I'd like: mbox/loader.rb and imap.rb
48
49
50
51   ## dirty? described whether cur_offset has changed, which means the
52   ## source info needs to be re-saved to sources.yaml.
53   ##
54   ## broken? means no message can be loaded, e.g. IMAP server is
55   ## down, mbox file is corrupt and needs to be rescanned, etc.
56   bool_reader :usual, :archived, :dirty
57   attr_reader :uri, :cur_offset, :broken_msg
58   attr_accessor :id
59
60   def initialize uri, initial_offset=nil, usual=true, archived=false, id=nil
61     @uri = uri
62     @cur_offset = initial_offset
63     @usual = usual
64     @archived = archived
65     @id = id
66     @dirty = false
67     @broken_msg = nil
68   end
69
70   def broken?; !@broken_msg.nil?; end
71   def to_s; @uri.to_s; end
72   def seek_to! o; self.cur_offset = o; end
73   def reset!
74     @broken_msg = nil
75     begin
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
89   def is_source_for? uri; URI(self.uri) == URI(uri); end
90
91   def each
92     return if broken?
93     begin
94       self.cur_offset ||= start_offset
95       until done? || broken? # just like life!
96         n, labels = self.next
97         raise "no message" unless n
98         yield n, labels
99       end
100     rescue SourceError => e
101       self.broken_msg = e.message
102     end
103   end
104
105 protected
106   
107   def cur_offset= o
108     @cur_offset = o
109     @dirty = true
110   end
111
112   def broken_msg= m
113     @broken_msg = m
114 #    Redwood::log "#{to_s}: #{m}"
115   end
116 end
117
118 Redwood::register_yaml(Source, %w(uri cur_offset usual archived id))
119
120 end