lib/gmail/message.rb in gmail-0.4.2 vs lib/gmail/message.rb in gmail-0.5.0

- old
+ new

@@ -1,135 +1,169 @@ module Gmail class Message + PREFETCH_ATTRS = ["UID", "ENVELOPE", "BODY.PEEK[]", "FLAGS", "X-GM-LABELS", "X-GM-MSGID", "X-GM-THRID"] + # Raised when given label doesn't exists. - class NoLabelError < Exception; end - - attr_reader :uid - - def initialize(mailbox, uid) + class NoLabelError < Exception; end + + def initialize(mailbox, uid, _attrs = nil) @uid = uid @mailbox = mailbox - @gmail = mailbox.instance_variable_get("@gmail") if mailbox + @gmail = mailbox.instance_variable_get("@gmail") if mailbox # UGLY + @_attrs = _attrs end - - def labels - @gmail.conn.uid_fetch(uid, "X-GM-LABELS")[0].attr["X-GM-LABELS"] - end - + def uid - @uid ||= @gmail.conn.uid_search(['HEADER', 'Message-ID', message_id])[0] + @uid ||= fetch("UID") end - + + def msg_id + @msg_id ||= fetch("X-GM-MSGID") + end + alias_method :message_id, :msg_id + + def thr_id + @thr_id ||= fetch("X-GM-THRID") + end + alias_method :thread_id, :thr_id + + def envelope + @envelope ||= fetch("ENVELOPE") + end + + def message + @message ||= Mail.new(fetch("BODY[]")) + end + alias_method :raw_message, :message + + def flags + @flags ||= fetch("FLAGS") + end + + def labels + @labels ||= fetch("X-GM-LABELS") + end + # Mark message with given flag. def flag(name) - !!@gmail.mailbox(@mailbox.name) { @gmail.conn.uid_store(uid, "+FLAGS", [name]) } + !!@gmail.mailbox(@mailbox.name) do + @gmail.conn.uid_store(uid, "+FLAGS", [name]) + clear_cached_attributes + end end - - # Unmark message. + + # Unmark message. def unflag(name) - !!@gmail.mailbox(@mailbox.name) { @gmail.conn.uid_store(uid, "-FLAGS", [name]) } + !!@gmail.mailbox(@mailbox.name) do + @gmail.conn.uid_store(uid, "-FLAGS", [name]) + clear_cached_attributes + end end - - # Do commonly used operations on message. + + # Do commonly used operations on message. def mark(flag) case flag - when :read then read! - when :unread then unread! - when :deleted then delete! - when :spam then spam! + when :read then read! + when :unread then unread! + when :deleted then delete! + when :spam then spam! else flag(flag) end end - - # Mark this message as a spam. - def spam! - move_to('[Gmail]/Spam') + + # Check whether message is read + def read? + flags.include?(:Seen) end - + # Mark as read. def read! flag(:Seen) end - + # Mark as unread. def unread! unflag(:Seen) end - + + # Check whether message is starred + def starred? + flags.include?(:Flagged) + end + # Mark message with star. def star! - flag('[Gmail]/Starred') + flag(:Flagged) end - + # Remove message from list of starred. def unstar! - unflag('[Gmail]/Starred') + unflag(:Flagged) end - - # Move to trash / bin. - def delete! - @mailbox.messages.delete(uid) - flag(:deleted) - # For some, it's called "Trash", for others, it's called "Bin". Support both. - trash = @gmail.labels.exist?('[Gmail]/Bin') ? '[Gmail]/Bin' : '[Gmail]/Trash' - move_to(trash) unless %w[[Gmail]/Spam [Gmail]/Bin [Gmail]/Trash].include?(@mailbox.name) + # Marking as spam is done by adding the `\Spam` label. To undo this, + # you just re-apply the `\Inbox` label (see `#unspam!`) + def spam! + add_label("\\Spam") end - # Archive this message. + # Deleting is done by adding the `\Trash` label. To undo this, + # you just re-apply the `\Inbox` label (see `#undelete!`) + def delete! + add_label("\\Trash") + end + + # Archiving is done by adding the `\Trash` label. To undo this, + # you just re-apply the `\Inbox` label (see `#unarchive!`) def archive! - move_to('[Gmail]/All Mail') + remove_label("\\Inbox") end - - # Move to given box and delete from others. - def move_to(name, from=nil) - label(name, from) - delete! if !%w[[Gmail]/Bin [Gmail]/Trash].include?(name) + + def unarchive! + add_label("\\Inbox") end - alias :move :move_to - - # Move message to given and delete from others. When given mailbox doesn't - # exist then it will be automaticaly created. - def move_to!(name, from=nil) - label!(name, from) && delete! + alias_method :unspam!, :unarchive! + alias_method :undelete!, :unarchive! + + # Move to given box and delete from others. + # Apply a given label and optionally remove one. + # TODO: We should probably deprecate this method. It doesn't really add a lot + # of value, especially since the concept of "moving" a message from one + # label to another doesn't totally make sense in the Gmail world. + def move_to(name, from = nil) + add_label(name) + remove_label(from) if from end - alias :move! :move_to! - - # Mark this message with given label. When given label doesn't exist then - # it will raise <tt>NoLabelError</tt>. - # - # See also <tt>Gmail::Message#label!</tt>. - def label(name, from=nil) - @gmail.mailbox(Net::IMAP.encode_utf7(from || @mailbox.external_name)) { @gmail.conn.uid_copy(uid, Net::IMAP.encode_utf7(name)) } - rescue Net::IMAP::NoResponseError - raise NoLabelError, "Label '#{name}' doesn't exist!" + alias_method :move, :move_to + alias_method :move!, :move_to + alias_method :move_to!, :move_to + + # Use Gmail IMAP Extensions to add a Label to an email + def add_label(name) + @gmail.mailbox(@mailbox.name) do + @gmail.conn.uid_store(uid, "+X-GM-LABELS", [Net::IMAP.encode_utf7(name.to_s)]) + clear_cached_attributes + end end + alias_method :label, :add_label + alias_method :label!, :add_label + alias_method :add_label!, :add_label - # Mark this message with given label. When given label doesn't exist then - # it will be automaticaly created. - # - # See also <tt>Gmail::Message#label</tt>. - def label!(name, from=nil) - label(name, from) - rescue NoLabelError - @gmail.labels.add(Net::IMAP.encode_utf7(name)) - label(name, from) + # Use Gmail IMAP Extensions to remove a Label from an email + def remove_label(name) + @gmail.mailbox(@mailbox.name) do + @gmail.conn.uid_store(uid, "-X-GM-LABELS", [Net::IMAP.encode_utf7(name.to_s)]) + clear_cached_attributes + end end - alias :add_label :label! - alias :add_label! :label! - - # Remove given label from this message. - def remove_label!(name) - move_to('[Gmail]/All Mail', name) - end - alias :delete_label! :remove_label! - + alias_method :remove_label!, :remove_label + def inspect - "#<Gmail::Message#{'0x%04x' % (object_id << 1)} mailbox=#{@mailbox.external_name}#{' uid='+@uid.to_s if @uid}#{' message_id='+@message_id.to_s if @message_id}>" + "#<Gmail::Message#{'0x%04x' % (object_id << 1)} mailbox=#{@mailbox.name}#{' uid=' + @uid.to_s if @uid}#{' message_id=' + @msg_id.to_s if @msg_id}>" end - + def method_missing(meth, *args, &block) # Delegate rest directly to the message. if envelope.respond_to?(meth) envelope.send(meth, *args, &block) elsif message.respond_to?(meth) @@ -147,20 +181,27 @@ else super(meth, *args, &block) end end - def envelope - @envelope ||= @gmail.mailbox(@mailbox.name) { - @gmail.conn.uid_fetch(uid, "ENVELOPE")[0].attr["ENVELOPE"] - } + private + + def clear_cached_attributes + @_attrs = nil + @msg_id = nil + @thr_id = nil + @envelope = nil + @message = nil + @flags = nil + @labels = nil end - - def message - @message ||= Mail.new(@gmail.mailbox(@mailbox.name) { - @gmail.conn.uid_fetch(uid, "RFC822")[0].attr["RFC822"] # RFC822 - }) - end - alias_method :raw_message, :message + def fetch(value) + @_attrs ||= begin + @gmail.mailbox(@mailbox.name) do + @gmail.conn.uid_fetch(uid, PREFETCH_ATTRS)[0] + end + end + @_attrs.attr[value] + end end # Message end # Gmail