Class: Redwood::Source
Direct Known Subclasses
Instance Attribute Summary collapse
-
#id ⇒ Object
Returns the value of attribute id.
-
#uri ⇒ Object
readonly
Returns the value of attribute uri.
Class Method Summary collapse
-
.parse_raw_email_header(f) ⇒ Object
utility method to read a raw email header from an IO stream and turn it into a hash of key-value pairs.
Instance Method Summary collapse
- #==(o) ⇒ Object
-
#file_path ⇒ Object
overwrite me if you have a disk incarnation (currently used only for sup-sync-back).
-
#go_idle ⇒ Object
release resources that are easy to reacquire.
-
#initialize(uri, usual = true, archived = false, id = nil) ⇒ Source
constructor
A new instance of Source.
- #is_source_for?(uri) ⇒ Boolean
-
#poll ⇒ Object
Yields values of the form [Symbol, Hash] add: info, labels, progress delete: info, progress.
- #read? ⇒ Boolean
- #to_s ⇒ Object
-
#usual ⇒ Object
Implementing a new source should be easy, because Sup only needs to be able to: 1.
- #valid?(info) ⇒ Boolean
Constructor Details
#initialize(uri, usual = true, archived = false, id = nil) ⇒ Source
Returns a new instance of Source.
67 68 69 70 71 72 73 74 |
# File 'lib/sup/source.rb', line 67 def initialize uri, usual=true, archived=false, id=nil raise ArgumentError, "id must be an integer: #{id.inspect}" unless id.is_a? Fixnum if id @uri = uri @usual = usual @archived = archived @id = id end |
Instance Attribute Details
#id ⇒ Object
Returns the value of attribute id.
65 66 67 |
# File 'lib/sup/source.rb', line 65 def id @id end |
#uri ⇒ Object (readonly)
Returns the value of attribute uri.
64 65 66 |
# File 'lib/sup/source.rb', line 64 def uri @uri end |
Class Method Details
.parse_raw_email_header(f) ⇒ Object
utility method to read a raw email header from an IO stream and turn it into a hash of key-value pairs. minor special semantics for certain headers.
THIS IS A SPEED-CRITICAL SECTION. Everything you do here will have a significant effect on Sup’s processing speed of email from ALL sources. Little things like string interpolation, regexp interpolation, += vs <<, all have DRAMATIC effects. BE CAREFUL WHAT YOU DO!
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/sup/source.rb', line 108 def self.parse_raw_email_header f header = {} last = nil while(line = f.gets) case line ## these three can occur multiple times, and we want the first one when /^(Delivered-To|X-Original-To|Envelope-To):\s*(.*?)\s*$/i; header[last = $1.downcase] ||= $2 ## regular header: overwrite (not that we should see more than one) ## TODO: figure out whether just using the first occurrence changes ## anything (which would simplify the logic slightly) when /^([^:\s]+):\s*(.*?)\s*$/i; header[last = $1.downcase] = $2 when /^\r*$/; break # blank line signifies end of header else if last header[last] << " " unless header[last].empty? header[last] << line.strip end end end %w(subject from to cc bcc).each do |k| v = header[k] or next next unless Rfc2047.is_encoded? v header[k] = begin Rfc2047.decode_to $encoding, v rescue Errno::EINVAL, Iconv::InvalidEncoding, Iconv::IllegalSequence => e #debug "warning: error decoding RFC 2047 header (#{e.class.name}): #{e.message}" v end end header end |
Instance Method Details
#==(o) ⇒ Object
80 |
# File 'lib/sup/source.rb', line 80 def == o; o.uri == uri; end |
#file_path ⇒ Object
overwrite me if you have a disk incarnation (currently used only for sup-sync-back)
77 |
# File 'lib/sup/source.rb', line 77 def file_path; nil end |
#go_idle ⇒ Object
release resources that are easy to reacquire. it is called after processing a source (e.g. polling) to prevent resource leaks (esp. file descriptors).
88 |
# File 'lib/sup/source.rb', line 88 def go_idle; end |
#is_source_for?(uri) ⇒ Boolean
81 |
# File 'lib/sup/source.rb', line 81 def is_source_for? uri; uri == @uri; end |
#poll ⇒ Object
Yields values of the form [Symbol, Hash] add: info, labels, progress delete: info, progress
93 94 95 |
# File 'lib/sup/source.rb', line 93 def poll unimplemented end |
#read? ⇒ Boolean
83 |
# File 'lib/sup/source.rb', line 83 def read?; false; end |
#to_s ⇒ Object
79 |
# File 'lib/sup/source.rb', line 79 def to_s; @uri.to_s; end |
#usual ⇒ Object
Implementing a new source should be easy, because Sup only needs to be able to:
1. See how many messages it contains
2. Get an arbitrary message
3. (optional) see whether the source has marked it read or not
In particular, Sup doesn’t need to move messages, mark them as read, delete them, or anything else. (Well, it’s nice to be able to delete them, but that is optional.)
On the other hand, Sup assumes that you can assign each message a unique integer id, such that newer messages have higher ids than earlier ones, and that those ids stay constant across sessions (in the absence of some other client going in and fucking everything up). For example, for mboxes I use the file offset of the start of the message. If a source does NOT have that capability, e.g. IMAP, then you have to do a little more work to simulate it.
To write a new source, subclass this class, and implement:
-
start_offset
-
end_offset (exclusive!) (or, #done?)
-
load_header offset
-
load_message offset
-
raw_header offset
-
raw_message offset
-
check (optional)
-
go_idle (optional)
-
next (or each, if you prefer): should return a message and an array of labels.
… where “offset” really means unique id. (You can tell I started with mbox.)
All exceptions relating to accessing the source must be caught and rethrown as FatalSourceErrors or OutOfSyncSourceErrors. OutOfSyncSourceErrors should be used for problems that a call to sup-sync will fix (namely someone’s been playing with the source from another client); FatalSourceErrors can be used for anything else (e.g. the imap server is down or the maildir is missing.)
Finally, be sure the source is thread-safe, since it WILL be pummelled from multiple threads at once.
Examples for you to look at: mbox/loader.rb, imap.rb, and maildir.rb.
63 |
# File 'lib/sup/source.rb', line 63 bool_accessor :usual, :archived |
#valid?(info) ⇒ Boolean
97 98 99 |
# File 'lib/sup/source.rb', line 97 def valid? info true end |