]> git.cworth.org Git - sup/commitdiff
Merge branch 'various-api-refactors' into next
authorWilliam Morgan <wmorgan-sup@masanjin.net>
Wed, 19 Aug 2009 18:34:34 +0000 (14:34 -0400)
committerWilliam Morgan <wmorgan-sup@masanjin.net>
Wed, 19 Aug 2009 18:34:34 +0000 (14:34 -0400)
1  2 
lib/sup.rb
lib/sup/imap.rb
lib/sup/maildir.rb
lib/sup/source.rb
lib/sup/util.rb

diff --combined lib/sup.rb
index deae3d4c7474fa5910da56ec186bfe1b9fe585e9,1afc065b60825e4620fd0926cda7281e4836c6ad..12513f7df7397dd1c9a6a25c507e56f7c5f44ba6
@@@ -85,38 -85,51 +85,50 @@@ module Redwoo
    module_function :reporting_thread, :record_exception, :exceptions
  
  ## one-stop shop for yamliciousness
-   def save_yaml_obj object, fn, safe=false
+   def save_yaml_obj o, fn, safe=false
+     o = if o.is_a?(Array)
+       o.map { |x| (x.respond_to?(:before_marshal) && x.before_marshal) || x }
+     else
+       o.respond_to?(:before_marshal) && o.before_marshal
+     end
      if safe
        safe_fn = "#{File.dirname fn}/safe_#{File.basename fn}"
        mode = File.stat(fn).mode if File.exists? fn
-       File.open(safe_fn, "w", mode) { |f| f.puts object.to_yaml }
+       File.open(safe_fn, "w", mode) { |f| f.puts o.to_yaml }
        FileUtils.mv safe_fn, fn
      else
-       File.open(fn, "w") { |f| f.puts object.to_yaml }
+       File.open(fn, "w") { |f| f.puts o.to_yaml }
      end
    end
  
    def load_yaml_obj fn, compress=false
-     if File.exists? fn
+     o = if File.exists? fn
        if compress
          Zlib::GzipReader.open(fn) { |f| YAML::load f }
        else
          YAML::load_file fn
        end
      end
+     if o.is_a?(Array)
+       o.each { |x| x.after_unmarshal! if x.respond_to?(:after_unmarshal!) }
+     else
+       o.after_unmarshal! if o.respond_to?(:after_unmarshal!)
+     end
+     o
    end
  
    def start
 -    Redwood::SentManager.new $config[:sent_source] || 'sup://sent'
 -    Redwood::ContactManager.new Redwood::CONTACT_FN
 -    Redwood::LabelManager.new Redwood::LABEL_FN
 -    Redwood::AccountManager.new $config[:accounts]
 -    Redwood::DraftManager.new Redwood::DRAFT_DIR
 -    Redwood::UpdateManager.new
 -    Redwood::PollManager.new
 -    Redwood::SuicideManager.new Redwood::SUICIDE_FN
 -    Redwood::CryptoManager.new
 -    Redwood::UndoManager.new
 -    Redwood::SourceManager.new
 +    Redwood::SentManager.init $config[:sent_source] || 'sup://sent'
 +    Redwood::ContactManager.init Redwood::CONTACT_FN
 +    Redwood::LabelManager.init Redwood::LABEL_FN
 +    Redwood::AccountManager.init $config[:accounts]
 +    Redwood::DraftManager.init Redwood::DRAFT_DIR
 +    Redwood::UpdateManager.init
 +    Redwood::PollManager.init
 +    Redwood::CryptoManager.init
 +    Redwood::UndoManager.init
 +    Redwood::SourceManager.init
    end
  
    def finish
@@@ -229,29 -242,33 +241,29 @@@ require "sup/hook
  ## we have to initialize this guy first, because other classes must
  ## reference it in order to register hooks, and they do that at parse
  ## time.
 -Redwood::HookManager.new Redwood::HOOK_DIR
 +Redwood::HookManager.init Redwood::HOOK_DIR
  
  ## everything we need to get logging working
 -require "sup/buffer"
 -require "sup/keymap"
 -require "sup/mode"
 -require "sup/modes/scroll-mode"
 -require "sup/modes/text-mode"
 -require "sup/modes/log-mode"
  require "sup/logger"
 -module Redwood
 -  def log s; Logger.log s; end
 -  module_function :log
 -end
 +Redwood::Logger.init.add_sink $stderr
 +include Redwood::LogsStuff
  
  ## determine encoding and character set
    $encoding = Locale.current.charset
    if $encoding
 -    Redwood::log "using character set encoding #{$encoding.inspect}"
 +    debug "using character set encoding #{$encoding.inspect}"
    else
 -    Redwood::log "warning: can't find character set by using locale, defaulting to utf-8"
 +    warn "can't find character set by using locale, defaulting to utf-8"
      $encoding = "UTF-8"
    end
  
 -## now everything else (which can feel free to call Redwood::log at load time)
 +require "sup/buffer"
 +require "sup/keymap"
 +require "sup/mode"
 +require "sup/modes/scroll-mode"
 +require "sup/modes/text-mode"
 +require "sup/modes/log-mode"
  require "sup/update"
 -require "sup/suicide"
  require "sup/message-chunks"
  require "sup/message"
  require "sup/source"
@@@ -261,7 -278,6 +273,7 @@@ require "sup/imap
  require "sup/person"
  require "sup/account"
  require "sup/thread"
 +require "sup/interactive-lock"
  require "sup/index"
  require "sup/textfield"
  require "sup/colormap"
@@@ -292,7 -308,6 +304,7 @@@ require "sup/modes/buffer-list-mode
  require "sup/modes/poll-mode"
  require "sup/modes/file-browser-mode"
  require "sup/modes/completion-mode"
 +require "sup/modes/console-mode"
  require "sup/sent"
  
  $:.each do |base|
diff --combined lib/sup/imap.rb
index c2904fb8a4a8774921f0b6fe2ea66508b8c89ad9,b048ba9ea18f9a2e538b1df698db03169e25f0a8..3cf6489c59ad62d6dc6cae83cb2ad0dae419062a
@@@ -48,6 -48,7 +48,7 @@@ require 'set
  module Redwood
  
  class IMAP < Source
+   include SerializeLabelsNicely
    SCAN_INTERVAL = 60 # seconds
  
    ## upon these errors we'll try to rereconnect a few times
      return if last_id == @ids.length
  
      range = (@ids.length + 1) .. last_id
 -    Redwood::log "fetching IMAP headers #{range}"
 +    debug "fetching IMAP headers #{range}"
      fetch(range, ['RFC822.SIZE', 'INTERNALDATE', 'FLAGS']).each do |v|
        id = make_id v
        @ids << id
        @imap_state[id] = { :id => v.seqno, :flags => v.attr["FLAGS"] }
      end
 -    Redwood::log "done fetching IMAP headers"
 +    debug "done fetching IMAP headers"
    end
    synchronized :scan_mailbox
  
@@@ -225,7 -226,7 +226,7 @@@ privat
      if good_results.empty?
        raise FatalSourceError, "no IMAP response for #{ids} containing all fields #{fields.join(', ')} (got #{results.size} results)"
      elsif good_results.size < results.size
 -      Redwood::log "Your IMAP server sucks. It sent #{results.size} results for a request for #{good_results.size} messages. What are you using, Binc?"
 +      warn "Your IMAP server sucks. It sent #{results.size} results for a request for #{good_results.size} messages. What are you using, Binc?"
      end
  
      good_results
            raise Net::IMAP::NoResponseError unless @imap.capability().member? "AUTH=CRAM-MD5"
            @imap.authenticate 'CRAM-MD5', @username, @password
          rescue Net::IMAP::BadResponseError, Net::IMAP::NoResponseError => e
 -          Redwood::log "CRAM-MD5 authentication failed: #{e.class}. Trying LOGIN auth..."
 +          debug "CRAM-MD5 authentication failed: #{e.class}. Trying LOGIN auth..."
            begin
              raise Net::IMAP::NoResponseError unless @imap.capability().member? "AUTH=LOGIN"
              @imap.authenticate 'LOGIN', @username, @password
            rescue Net::IMAP::BadResponseError, Net::IMAP::NoResponseError => e
 -            Redwood::log "LOGIN authentication failed: #{e.class}. Trying plain-text LOGIN..."
 +            debug "LOGIN authentication failed: #{e.class}. Trying plain-text LOGIN..."
              @imap.login @username, @password
            end
          end
  
    def say s
      @say_id = BufferManager.say s, @say_id if BufferManager.instantiated?
 -    Redwood::log s
 +    debug s
    end
  
    def shutup
        rescue *RECOVERABLE_ERRORS => e
          if (retries += 1) <= 3
            @imap = nil
 -          Redwood::log "got #{e.class.name}: #{e.message.inspect}"
 +          warn "got #{e.class.name}: #{e.message.inspect}"
            sleep 2
            retry
          end
diff --combined lib/sup/maildir.rb
index 5e3ae30f0b896c11494e573a439e06076a94fc8a,db35dad0f52bab455eb47e2ef027eb63cf8d54b1..2c33e3bdc06b08f68b1d4b3caf564021eb6d71b8
@@@ -9,6 -9,7 +9,7 @@@ module Redwoo
  ## pathnames on disk.
  
  class Maildir < Source
+   include SerializeLabelsNicely
    SCAN_INTERVAL = 30 # seconds
    MYHOSTNAME = Socket.gethostname
  
  
      initial_poll = @ids.empty?
  
 -    Redwood::log "scanning maildir #@dir..."
 +    debug "scanning maildir #@dir..."
      begin
        @mtimes.each_key do |d|
        subdir = File.join(@dir, d)
            @ids_to_fns[id] = fn
          end
        else
 -        Redwood::log "no poll on #{d}.  mtime on indicates no new messages."
 +        debug "no poll on #{d}.  mtime on indicates no new messages."
        end
        end
        @ids = @dir_ids.values.flatten.uniq.sort!
        raise FatalSourceError, "Problem scanning Maildir directories: #{e.message}."
      end
      
 -    Redwood::log "done scanning maildir"
 +    debug "done scanning maildir"
      @last_scan = Time.now
    end
    synchronized :scan_mailbox
@@@ -212,7 -213,7 +213,7 @@@ privat
  
    def maildir_data msg
      fn = File.basename @ids_to_fns[msg]
 -    fn =~ %r{^([^:,]+):([12]),([DFPRST]*)$}
 +    fn =~ %r{^([^:]+):([12]),([DFPRST]*)$}
      [($1 || fn), ($2 || "2"), ($3 || "")]
    end
  
diff --combined lib/sup/source.rb
index 8154591f6adce27c071e16d11784f8dd77b05888,a557bd857c815232b07601f98d713fa9ae26ff2c..78386ff3fd19b0f74c75820827346df036f84c61
@@@ -139,7 -139,7 +139,7 @@@ class Sourc
        header[k] = begin
          Rfc2047.decode_to $encoding, v
        rescue Errno::EINVAL, Iconv::InvalidEncoding, Iconv::IllegalSequence => e
 -        #Redwood::log "warning: error decoding RFC 2047 header (#{e.class.name}): #{e.message}"
 +        #debug "warning: error decoding RFC 2047 header (#{e.class.name}): #{e.message}"
          v
        end
      end
@@@ -161,6 -161,21 +161,21 @@@ protecte
    end
  end
  
+ ## if you have a @labels instance variable, include this
+ ## to serialize them nicely as an array, rather than as a
+ ## nasty set.
+ module SerializeLabelsNicely
+   def before_marshal # can return an object
+     c = clone
+     c.instance_eval { @labels = @labels.to_a.map { |l| l.to_s } }
+     c
+   end
+   def after_unmarshal!
+     @labels = Set.new(@labels.map { |s| s.to_sym })
+   end
+ end
  class SourceManager
    include Singleton
  
      @sources = {}
      @sources_dirty = false
      @source_mutex = Monitor.new
 -    self.class.i_am_the_instance self
    end
  
    def [](id)
            File.chmod 0600, fn
            FileUtils.mv fn, bakfn, :force => true unless File.exists?(bakfn) && File.size(fn) == 0
          end
-         Redwood::save_yaml_obj sources.sort_by { |s| s.id.to_i }, fn, true
+         Redwood::save_yaml_obj sources, fn, true
          File.chmod 0600, fn
        end
        @sources_dirty = false
diff --combined lib/sup/util.rb
index 518866beff61e308319ccde80042cdb9c442d8a0,adf44b7ccbd2b46f056a47fdf04dfa9a650275c5..aa3ee764ac59b276d7f4ae758d4b119e141832c2
@@@ -25,7 -25,6 +25,7 @@@ class Lockfil
    def lockinfo_on_disk
      h = load_lock_id IO.read(path)
      h['mtime'] = File.mtime path
 +    h['path'] = path
      h
    end
  
@@@ -92,7 -91,7 +92,7 @@@ en
  
  class Range
    ## only valid for integer ranges (unless I guess it's exclusive)
-   def size 
+   def size
      last - first + (exclude_end? ? 0 : 1)
    end
  end
@@@ -496,20 -495,19 +496,20 @@@ class Tim
    end
  end
  
 -## simple singleton module. far less complete and insane than the ruby
 -## standard library one, but automatically forwards methods calls and
 -## allows for constructors that take arguments.
 +## simple singleton module. far less complete and insane than the ruby standard
 +## library one, but it automatically forwards methods calls and allows for
 +## constructors that take arguments.
  ##
 -## You must have #initialize call "self.class.i_am_the_instance self"
 -## at some point or everything will fail horribly.
 +## classes that inherit this can define initialize. however, you cannot call
 +## .new on the class. To get the instance of the class, call .instance;
 +## to create the instance, call init.
  module Singleton
    module ClassMethods
      def instance; @instance; end
      def instantiated?; defined?(@instance) && !@instance.nil?; end
      def deinstantiate!; @instance = nil; end
      def method_missing meth, *a, &b
 -      raise "no instance defined!" unless defined? @instance
 +      raise "no #{name} instance defined in method call to #{meth}!" unless defined? @instance
  
        ## if we've been deinstantiated, just drop all calls. this is
        ## useful because threads that might be active during the
  
        @instance.send meth, *a, &b
      end
 -    def i_am_the_instance o
 +    def init *args
        raise "there can be only one! (instance)" if defined? @instance
 -      @instance = o
 +      @instance = new(*args)
      end
    end
  
    def self.included klass
 +    klass.private_class_method :allocate, :new
      klass.extend ClassMethods
    end
  end
@@@ -654,7 -651,7 +654,7 @@@ class Icon
      begin
        Iconv.iconv(target + "//IGNORE", charset, text + " ").join[0 .. -2]
      rescue Errno::EINVAL, Iconv::InvalidEncoding, Iconv::InvalidCharacter, Iconv::IllegalSequence => e
 -      Redwood::log "warning: error (#{e.class.name}) decoding text from #{charset} to #{target}: #{text[0 ... 20]}"
 +      info "couldn't transcode text from #{charset} to #{target} (\"#{text[0 ... 20]}\"...)"
        text
      end
    end