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.
-
#usual ⇒ Object
readonly
Implementing a new source should be easy, because Sup only needs to be able to: 1.
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.
-
#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
-
#labels?(info) ⇒ Boolean
Returns an array containing all the labels that are currently in the location filename.
-
#poll ⇒ Object
Yields values of the form [Symbol, Hash] add: info, labels, progress delete: info, progress.
- #read? ⇒ Boolean
-
#supported_labels? ⇒ Boolean
Returns an array containing all the labels that are natively supported by this source.
- #synchronize(&block) ⇒ Object
- #to_s ⇒ Object
- #try_lock ⇒ Object
- #unlock ⇒ Object
- #valid?(info) ⇒ Boolean
Constructor Details
#initialize(uri, usual = true, archived = false, id = nil) ⇒ Source
Returns a new instance of Source.
60 61 62 63 64 65 66 67 68 69 |
# File 'lib/sup/source.rb', line 60 def initialize uri, usual=true, archived=false, id=nil raise ArgumentError, "id must be an integer: #{id.inspect}" unless id.is_a? Integer if id @uri = uri @usual = usual @archived = archived @id = id @poll_lock = Monitor.new end |
Instance Attribute Details
#id ⇒ Object
Returns the value of attribute id.
58 59 60 |
# File 'lib/sup/source.rb', line 58 def id @id end |
#uri ⇒ Object (readonly)
Returns the value of attribute uri.
57 58 59 |
# File 'lib/sup/source.rb', line 57 def uri @uri end |
#usual ⇒ Object (readonly)
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.)
Messages are identified internally based on the message id, and stored with an unique document id. Along with the message, source information that can contain arbitrary fields (set up by the source) is stored. This information will be passed back to the source when a message in the index (Sup database) needs to be identified to its source, e.g. when re-reading or modifying a unique message.
To write a new source, subclass this class, and implement:
-
initialize
-
load_header offset
-
load_message offset
-
raw_header offset
-
raw_message offset
-
store_message (optional)
-
poll (loads new messages)
-
go_idle (optional)
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.rb and maildir.rb.
56 |
# File 'lib/sup/source.rb', line 56 bool_accessor :usual, :archived |
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!
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/sup/source.rb', line 130 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 #debug "warning: error decoding RFC 2047 header (#{e.class.name}): #{e.message}" v end end header end |
Instance Method Details
#==(o) ⇒ Object
75 |
# File 'lib/sup/source.rb', line 75 def == o; o.uri == uri; end |
#file_path ⇒ Object
overwrite me if you have a disk incarnation
72 |
# File 'lib/sup/source.rb', line 72 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).
83 |
# File 'lib/sup/source.rb', line 83 def go_idle; end |
#is_source_for?(uri) ⇒ Boolean
76 |
# File 'lib/sup/source.rb', line 76 def is_source_for? uri; uri == @uri; end |
#labels?(info) ⇒ Boolean
Returns an array containing all the labels that are currently in the location filename
91 |
# File 'lib/sup/source.rb', line 91 def labels? info; [] end |
#poll ⇒ Object
Yields values of the form [Symbol, Hash] add: info, labels, progress delete: info, progress
96 97 98 |
# File 'lib/sup/source.rb', line 96 def poll unimplemented end |
#read? ⇒ Boolean
78 |
# File 'lib/sup/source.rb', line 78 def read?; false; end |
#supported_labels? ⇒ Boolean
Returns an array containing all the labels that are natively supported by this source
87 |
# File 'lib/sup/source.rb', line 87 def supported_labels?; [] end |
#synchronize(&block) ⇒ Object
104 105 106 |
# File 'lib/sup/source.rb', line 104 def synchronize &block @poll_lock.synchronize(&block) end |
#to_s ⇒ Object
74 |
# File 'lib/sup/source.rb', line 74 def to_s; @uri.to_s; end |
#try_lock ⇒ Object
108 109 110 111 112 113 114 115 116 |
# File 'lib/sup/source.rb', line 108 def try_lock acquired = @poll_lock.try_enter if acquired debug "lock acquired for: #{self}" else debug "could not acquire lock for: #{self}" end acquired end |
#unlock ⇒ Object
118 119 120 121 |
# File 'lib/sup/source.rb', line 118 def unlock @poll_lock.exit debug "lock released for: #{self}" end |
#valid?(info) ⇒ Boolean
100 101 102 |
# File 'lib/sup/source.rb', line 100 def valid? info true end |