Class: Redwood::Message

Inherits:
Object show all
Defined in:
lib/sup/message.rb

Overview

a Message is what’s threaded.

it is also where the parsing for quotes and signatures is done, but that should be moved out to a separate class at some point (because i would like, for example, to be able to add in a ruby-talk specific module that would detect and link to /ruby-talk:d+/ sequences in the text of an email. (how sweet would that be?)

this class cathces all source exceptions. if the underlying source throws an error, it is caught and handled.

Constant Summary collapse

SNIPPET_LEN =
80
RE_PATTERN =
/^((re|re[\[\(]\d[\]\)]):\s*)+/i
QUOTE_PATTERN =
/^\s{0,4}[>|\}]/
BLOCK_QUOTE_PATTERN =
/^-----\s*Original Message\s*----+$/
QUOTE_START_PATTERN =
/(^\s*Excerpts from)|(^\s*In message )|(^\s*In article )|(^\s*Quoting )|((wrote|writes|said|says)\s*:\s*$)/
SIG_PATTERN =
/(^-- ?$)|(^\s*----------+\s*$)|(^\s*_________+\s*$)|(^\s*--~--~-)|(^\s*--\+\+\*\*==)/
MAX_SIG_DISTANCE =

lines from the end

15
DEFAULT_SUBJECT =
""
DEFAULT_SENDER =
"(missing sender)"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts) ⇒ Message

if you specify a :header, will use values from that. otherwise, will try and load the header from the source.



48
49
50
51
52
53
54
55
56
57
58
# File 'lib/sup/message.rb', line 48

def initialize opts
  @source = opts[:source] or raise ArgumentError, "source can't be nil"
  @source_info = opts[:source_info] or raise ArgumentError, "source_info can't be nil"
  @snippet = opts[:snippet] || ""
  @have_snippet = !opts[:snippet].nil?
  @labels = [] + (opts[:labels] || [])
  @dirty = false
  @chunks = nil

  parse_header(opts[:header] || @source.load_header(@source_info))
end

Instance Attribute Details

#bccObject (readonly)

Returns the value of attribute bcc.



40
41
42
# File 'lib/sup/message.rb', line 40

def bcc
  @bcc
end

#ccObject (readonly)

Returns the value of attribute cc.



40
41
42
# File 'lib/sup/message.rb', line 40

def cc
  @cc
end

#chunksObject (readonly)

Returns the value of attribute chunks.



40
41
42
# File 'lib/sup/message.rb', line 40

def chunks
  @chunks
end

#dateObject (readonly)

Returns the value of attribute date.



40
41
42
# File 'lib/sup/message.rb', line 40

def date
  @date
end

#fromObject (readonly)

Returns the value of attribute from.



40
41
42
# File 'lib/sup/message.rb', line 40

def from
  @from
end

#idObject (readonly)

Returns the value of attribute id.



40
41
42
# File 'lib/sup/message.rb', line 40

def id
  @id
end

#labelsObject

Returns the value of attribute labels.



40
41
42
# File 'lib/sup/message.rb', line 40

def labels
  @labels
end

#list_addressObject (readonly)

Returns the value of attribute list_address.



40
41
42
# File 'lib/sup/message.rb', line 40

def list_address
  @list_address
end

#list_subscribeObject (readonly)

Returns the value of attribute list_subscribe.



40
41
42
# File 'lib/sup/message.rb', line 40

def list_subscribe
  @list_subscribe
end

#list_unsubscribeObject (readonly)

Returns the value of attribute list_unsubscribe.



40
41
42
# File 'lib/sup/message.rb', line 40

def list_unsubscribe
  @list_unsubscribe
end

#recipient_emailObject (readonly)

Returns the value of attribute recipient_email.



40
41
42
# File 'lib/sup/message.rb', line 40

def recipient_email
  @recipient_email
end

#refsObject (readonly)

Returns the value of attribute refs.



40
41
42
# File 'lib/sup/message.rb', line 40

def refs
  @refs
end

#replytoObject (readonly)

Returns the value of attribute replyto.



40
41
42
# File 'lib/sup/message.rb', line 40

def replyto
  @replyto
end

#replytosObject (readonly)

Returns the value of attribute replytos.



40
41
42
# File 'lib/sup/message.rb', line 40

def replytos
  @replytos
end

#sourceObject (readonly)

Returns the value of attribute source.



40
41
42
# File 'lib/sup/message.rb', line 40

def source
  @source
end

#source_infoObject (readonly)

Returns the value of attribute source_info.



40
41
42
# File 'lib/sup/message.rb', line 40

def source_info
  @source_info
end

#subjObject (readonly)

Returns the value of attribute subj.



40
41
42
# File 'lib/sup/message.rb', line 40

def subj
  @subj
end

#toObject (readonly)

Returns the value of attribute to.



40
41
42
# File 'lib/sup/message.rb', line 40

def to
  @to
end

Class Method Details

.normalize_subj(s) ⇒ Object



26
# File 'lib/sup/message.rb', line 26

def normalize_subj s; s.gsub(RE_PATTERN, ""); end

.reify_subj(s) ⇒ Object



28
# File 'lib/sup/message.rb', line 28

def reify_subj s; subj_is_reply?(s) ? s : "Re: " + s; end

.subj_is_reply?(s) ⇒ Boolean



27
# File 'lib/sup/message.rb', line 27

def subj_is_reply? s; s =~ RE_PATTERN; end

Instance Method Details

#add_label(t) ⇒ Object



135
136
137
138
139
# File 'lib/sup/message.rb', line 135

def add_label t
  return if @labels.member? t
  @labels.push t
  @dirty = true
end

#contentObject



227
228
229
230
231
232
233
234
235
236
237
# File 'lib/sup/message.rb', line 227

def content
  load_from_source!
  [
    from && "#{from.name} #{from.email}",
    to.map { |p| "#{p.name} #{p.email}" },
    cc.map { |p| "#{p.name} #{p.email}" },
    bcc.map { |p| "#{p.name} #{p.email}" },
    chunks.select { |c| c.is_a? Chunk::Text }.map { |c| c.lines },
    Message.normalize_subj(subj),
  ].flatten.compact.join " "
end

#draft_filenameObject



122
123
124
125
# File 'lib/sup/message.rb', line 122

def draft_filename
  raise "not a draft" unless is_draft?
  @source.fn_for_offset @source_info
end

#each_raw_message_line(&b) ⇒ Object

much faster than raw_message



223
224
225
# File 'lib/sup/message.rb', line 223

def each_raw_message_line &b
  with_source_errors_handled { @source.each_raw_message_line(@source_info, &b) }
end

#error_message(msg) ⇒ Object



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/sup/message.rb', line 183

def error_message msg
  "\#@snippet...\n\n***********************************************************************\n An error occurred while loading this message. It is possible that\n the source has changed, or (in the case of remote sources) is down.\n You can check the log for errors, though hopefully an error window\n should have popped up at some point.\n\n The message location was:\n \#@source#\#@source_info\n***********************************************************************\n\nThe error message was:\n\#{msg}\n"
end

#has_label?(t) ⇒ Boolean



134
# File 'lib/sup/message.rb', line 134

def has_label? t; @labels.member? t; end

#is_draft?Boolean



121
# File 'lib/sup/message.rb', line 121

def is_draft?; @source.is_a? DraftLoader; end

#is_list_message?Boolean



120
# File 'lib/sup/message.rb', line 120

def is_list_message?; !@list_address.nil?; end

#load_from_source!Object

this is called when the message body needs to actually be loaded.



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/sup/message.rb', line 156

def load_from_source!
  @chunks ||=
    if @source.has_errors?
      [Chunk::Text.new(error_message(@source.error.message.split("\n")))]
    else
      begin
        ## we need to re-read the header because it contains information
        ## that we don't store in the index. actually i think it's just
        ## the mailing list address (if any), so this is kinda overkill.
        ## i could just store that in the index, but i think there might
        ## be other things like that in the future, and i'd rather not
        ## bloat the index.
        ## actually, it's also the differentiation between to/cc/bcc,
        ## so i will keep this.
        parse_header @source.load_header(@source_info)
        message_to_chunks @source.load_message(@source_info)
      rescue SourceError, SocketError, MessageFormatError => e
        Redwood::log "problem getting messages from #{@source}: #{e.message}"
        ## we need force_to_top here otherwise this window will cover
        ## up the error message one
        @source.error ||= e
        Redwood::report_broken_sources :force_to_top => true
        [Chunk::Text.new(error_message(e.message))]
      end
    end
end

#quotable_body_linesObject



239
240
241
# File 'lib/sup/message.rb', line 239

def quotable_body_lines
  chunks.find_all { |c| c.quotable? }.map { |c| c.lines }.flatten
end

#quotable_header_linesObject



243
244
245
246
247
248
249
250
# File 'lib/sup/message.rb', line 243

def quotable_header_lines
  ["From: #{@from.full_address}"] +
    (@to.empty? ? [] : ["To: " + @to.map { |p| p.full_address }.join(", ")]) +
    (@cc.empty? ? [] : ["Cc: " + @cc.map { |p| p.full_address }.join(", ")]) +
    (@bcc.empty? ? [] : ["Bcc: " + @bcc.map { |p| p.full_address }.join(", ")]) +
    ["Date: #{@date.rfc822}",
     "Subject: #{@subj}"]
end

#raw_headerObject



214
215
216
# File 'lib/sup/message.rb', line 214

def raw_header
  with_source_errors_handled { @source.raw_header @source_info }
end

#raw_messageObject



218
219
220
# File 'lib/sup/message.rb', line 218

def raw_message
  with_source_errors_handled { @source.raw_message @source_info }
end

#recipientsObject



146
147
148
# File 'lib/sup/message.rb', line 146

def recipients
  @to + @cc + @bcc
end

#remove_label(t) ⇒ Object



140
141
142
143
144
# File 'lib/sup/message.rb', line 140

def remove_label t
  return unless @labels.member? t
  @labels.delete t
  @dirty = true
end

#sanitize_message_id(mid) ⇒ Object



127
# File 'lib/sup/message.rb', line 127

def sanitize_message_id mid; mid.gsub(/\s/, "") end

#save(index) ⇒ Object



129
130
131
132
# File 'lib/sup/message.rb', line 129

def save index
  index.sync_message self if @dirty
  @dirty = false
end

#snippetObject



119
# File 'lib/sup/message.rb', line 119

def snippet; @snippet || chunks && @snippet; end

#with_source_errors_handledObject

wrap any source methods that might throw sourceerrors



203
204
205
206
207
208
209
210
211
212
# File 'lib/sup/message.rb', line 203

def with_source_errors_handled
  begin
    yield
  rescue SourceError => e
    Redwood::log "problem getting messages from #{@source}: #{e.message}"
    @source.error ||= e
    Redwood::report_broken_sources :force_to_top => true
    error_message e.message
  end
end