From: William Morgan Date: Wed, 19 Aug 2009 18:34:34 +0000 (-0400) Subject: Merge branch 'various-api-refactors' into next X-Git-Url: https://git.cworth.org/git?a=commitdiff_plain;h=3de96fb9b308afe600c7ccfcee75913f039ef4f6;hp=-c;p=sup Merge branch 'various-api-refactors' into next --- 3de96fb9b308afe600c7ccfcee75913f039ef4f6 diff --combined lib/sup.rb index deae3d4,1afc065..12513f7 --- a/lib/sup.rb +++ b/lib/sup.rb @@@ -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 c2904fb,b048ba9..3cf6489 --- a/lib/sup/imap.rb +++ b/lib/sup/imap.rb @@@ -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 @@@ -160,13 -161,13 +161,13 @@@ 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 @@@ -253,12 -254,12 +254,12 @@@ 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 @@@ -275,7 -276,7 +276,7 @@@ def say s @say_id = BufferManager.say s, @say_id if BufferManager.instantiated? - Redwood::log s + debug s end def shutup @@@ -332,7 -333,7 +333,7 @@@ 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 5e3ae30,db35dad..2c33e3b --- a/lib/sup/maildir.rb +++ b/lib/sup/maildir.rb @@@ -9,6 -9,7 +9,7 @@@ module Redwoo ## pathnames on disk. class Maildir < Source + include SerializeLabelsNicely SCAN_INTERVAL = 30 # seconds MYHOSTNAME = Socket.gethostname @@@ -115,7 -116,7 +116,7 @@@ initial_poll = @ids.empty? - Redwood::log "scanning maildir #@dir..." + debug "scanning maildir #@dir..." begin @mtimes.each_key do |d| subdir = File.join(@dir, d) @@@ -134,7 -135,7 +135,7 @@@ @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! @@@ -142,7 -143,7 +143,7 @@@ 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 8154591,a557bd8..78386ff --- a/lib/sup/source.rb +++ b/lib/sup/source.rb @@@ -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 @@@ -168,6 -183,7 +183,6 @@@ @sources = {} @sources_dirty = false @source_mutex = Monitor.new - self.class.i_am_the_instance self end def [](id) @@@ -209,7 -225,7 +224,7 @@@ 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 518866b,adf44b7..aa3ee76 --- a/lib/sup/util.rb +++ b/lib/sup/util.rb @@@ -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 @@@ -519,14 -517,13 +519,14 @@@ @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