lib/imap_guard/guard.rb in imap_guard-0.0.1 vs lib/imap_guard/guard.rb in imap_guard-0.0.2

- old
+ new

@@ -1,71 +1,123 @@ require 'net/imap' -require 'mail' require 'ostruct' +require 'mail' +require 'colored' module IMAPGuard class Guard + attr_reader :settings + def initialize settings - @settings = OpenStruct.new(settings).freeze + self.settings = settings + end - # http://www.ruby-doc.org/stdlib-1.9.3/libdoc/net/imap/rdoc/Net/IMAP.html#method-c-new + # @see http://www.ruby-doc.org/stdlib-1.9.3/libdoc/net/imap/rdoc/Net/IMAP.html#method-c-new + def login @imap = Net::IMAP.new(@settings.host, @settings.port, true, nil, false) @imap.login(@settings.username, @settings.password) + verbose.puts "Logged in successfully" end def select mailbox if @settings.read_only @imap.examine(mailbox) # open in read-only else @imap.select(mailbox) # open in read-write end end - def delete query - unless query.is_a? String - if query.respond_to? :to_s - query = query.to_s - else - raise ArgumentError, "query must provide #to_s" - end - end + # @param mailbox Destination mailbox + def move query, mailbox, &filter + operation = lambda { |message_id| + @imap.copy(message_id, mailbox) unless @settings.read_only + @imap.store(message_id, "+FLAGS", [:Deleted]) + "moved to #{mailbox}".cyan + } + process query, operation, &filter + end - messages = @imap.search query - count = messages.size - puts "Query: #{query.inspect}: #{count} results" + def delete query, &filter + operation = lambda { |message_id| + @imap.store(message_id, "+FLAGS", [:Deleted]) + 'deleted'.red + } + process query, operation, &filter + end - counter = 0 - messages.each do |message_id| - counter += 1 - # puts message_id - mail = nil + def expunge + @imap.expunge + end + + def close + puts "Expunging deleted messages and closing mailbox..." + @imap.close + end + + private + + def process query, operation + message_ids = search query + count = message_ids.size + + message_ids.each_with_index do |message_id, index| + print "Processing UID #{message_id} (#{index + 1}/#{count}): " + + result = true if block_given? - msg = @imap.fetch(message_id, 'RFC822')[0].attr['RFC822'] - mail = Mail.read_from_string msg - result = yield mail - if result - # puts "Given filter matched" - else - # puts "Given filter returned falsy" - next - end + mail = fetch_mail message_id + result = yield(mail) + verbose.print "(given filter result: #{result.inspect}) " end - print "Deleting UID #{message_id} (#{counter}/#{count})" - print " (DRY-RUN)" if @settings.read_only - if mail - puts ": #{mail.subject} (#{mail.body.to_s.length})" + if result + puts operation.call(message_id) else - puts + puts "ignored".yellow end - @imap.store(message_id, "+FLAGS", [:Deleted]) end end - def close - puts "Expunging deleted messages and closing mailbox..." - # p imap.expunge - @imap.close + def fetch_mail message_id + msg = @imap.fetch(message_id, 'RFC822')[0].attr['RFC822'] + Mail.read_from_string msg + end + + def search query + unless [Array, String].any? { |type| query.is_a? type } + raise ArgumentError, "query must be either a string holding the entire search string, or a single-dimension array of search keywords and arguments" + end + + messages = @imap.search query + puts "Query: #{query.inspect}: #{messages.count} results".cyan + + messages + end + + def verbose + @verbose ||= if @settings.verbose + $stdout + else + # anonymous null object + Class.new do + def method_missing(*args, &block) + nil + end + end.new + end + end + + def settings= settings + required = %w(host port username password).map!(&:to_sym) + missing = required - settings.keys + raise ArgumentError, "Missing settings: #{missing}" unless missing.empty? + + optional = %w(read_only verbose).map!(&:to_sym) + unknown = settings.keys - required - optional + raise ArgumentError, "Unknown settings: #{unknown}" unless unknown.empty? + + @settings = OpenStruct.new(settings).freeze + puts "DRY-RUN MODE ENABLED".yellow.bold.reversed if @settings.read_only end end end