commit 3d4b1f0e36a9633c686324b0fe0b3bd12f21a58a
parent 8d38421db6b09a1549505ec20dc4401491f1807a
Author: Eric Weikl <eric.weikl@gmx.net>
Date: Sun, 2 Jun 2013 14:01:18 +0200
Merge branch 'sup-heliotrope/develop' into maildir-sync
Diffstat:
19 files changed, 248 insertions(+), 50 deletions(-)
diff --git a/.travis.yml b/.travis.yml
@@ -3,7 +3,7 @@ language: ruby
rvm:
- 2.0.0
- 1.9.3
- - 1.8.7
+ - 1.9.2
before_install:
- sudo apt-get update -qq
diff --git a/bin/sup-psych-ify-config-files b/bin/sup-psych-ify-config-files
@@ -0,0 +1,16 @@
+#!/usr/bin/env ruby
+
+$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
+
+require "sup"
+require "fileutils"
+
+Redwood.start
+
+fn = Redwood::SOURCE_FN
+FileUtils.cp fn, "#{fn}.syck_bak"
+
+Redwood::SourceManager.load_sources fn
+Redwood::SourceManager.save_sources fn, true
+
+Redwood.finish
diff --git a/lib/sup.rb b/lib/sup.rb
@@ -1,11 +1,6 @@
require 'rubygems'
-
-require 'syck'
require 'yaml'
-if YAML.const_defined? :ENGINE
- YAML::ENGINE.yamler = 'syck'
-end
-
+YAML::ENGINE.yamler = 'psych'
require 'zlib'
require 'thread'
require 'fileutils'
@@ -28,18 +23,23 @@ end
class Module
def yaml_properties *props
props = props.map { |p| p.to_s }
- vars = props.map { |p| "@#{p}" }
- klass = self
- path = klass.name.gsub(/::/, "/")
- klass.instance_eval do
- define_method(:to_yaml_properties) { vars }
- define_method(:to_yaml_type) { "!#{Redwood::YAML_DOMAIN},#{Redwood::YAML_DATE}/#{path}" }
+ path = name.gsub(/::/, "/")
+ yaml_tag "!#{Redwood::YAML_DOMAIN},#{Redwood::YAML_DATE}/#{path}"
+
+ define_method :init_with do |coder|
+ initialize(*coder.map.values_at(*props))
end
- YAML.add_domain_type("#{Redwood::YAML_DOMAIN},#{Redwood::YAML_DATE}", path) do |type, val|
- klass.new(*props.map { |p| val[p] })
+ define_method :encode_with do |coder|
+ coder.map = props.inject({}) do |hash, key|
+ hash[key] = instance_variable_get("@#{key}")
+ hash
+ end
end
+
+ # Legacy
+ Psych.load_tags["!#{Redwood::LEGACY_YAML_DOMAIN},#{Redwood::YAML_DATE}/#{path}"] = self
end
end
@@ -59,7 +59,8 @@ module Redwood
LOG_FN = File.join(BASE_DIR, "log")
SYNC_OK_FN = File.join(BASE_DIR, "sync-back-ok")
- YAML_DOMAIN = "masanjin.net"
+ YAML_DOMAIN = "supmua.org"
+ LEGACY_YAML_DOMAIN = "masanjin.net"
YAML_DATE = "2006-10-01"
## record exceptions thrown in threads nicely
@@ -347,6 +348,7 @@ require "sup/logger/singleton"
## determine encoding and character set
$encoding = Locale.current.charset
$encoding = "UTF-8" if $encoding == "utf8"
+$encoding = "UTF-8" if $encoding == "UTF8"
if $encoding
debug "using character set encoding #{$encoding.inspect}"
else
@@ -354,6 +356,16 @@ else
$encoding = "UTF-8"
end
+# test encoding
+teststr = "test"
+teststr.encode('UTF-8')
+begin
+ teststr.encode($encoding)
+rescue Encoding::ConverterNotFoundError
+ warn "locale encoding is invalid, defaulting to utf-8"
+ $encoding = "UTF-8"
+end
+
require "sup/buffer"
require "sup/keymap"
require "sup/mode"
diff --git a/lib/sup/buffer.rb b/lib/sup/buffer.rb
@@ -709,7 +709,7 @@ EOS
end
Ncurses.mutex.lock unless opts[:sync] == false
- Ncurses.attrset Colormap.color_for(:none)
+ Ncurses.attrset Colormap.color_for(:text_color)
adj = @asking ? 2 : 1
m.each_with_index do |s, i|
Ncurses.mvaddstr Ncurses.rows - i - adj, 0, s + (" " * [Ncurses.cols - s.length, 0].max)
diff --git a/lib/sup/colormap.rb b/lib/sup/colormap.rb
@@ -26,6 +26,7 @@ class Colormap
@@instance = nil
DEFAULT_COLORS = {
+ :text => { :fg => "white", :bg => "black" },
:status => { :fg => "white", :bg => "blue", :attrs => ["bold"] },
:index_old => { :fg => "white", :bg => "default" },
:index_new => { :fg => "white", :bg => "default", :attrs => ["bold"] },
diff --git a/lib/sup/draft.rb b/lib/sup/draft.rb
@@ -32,14 +32,17 @@ class DraftLoader < Source
attr_accessor :dir
yaml_properties
- def initialize
- dir = Redwood::DRAFT_DIR
+ def initialize dir=Redwood::DRAFT_DIR
Dir.mkdir dir unless File.exists? dir
super DraftManager.source_name, true, false
@dir = dir
@cur_offset = 0
end
+ def properly_initialized?
+ !!(@dir && @cur_offset)
+ end
+
def id; DraftManager.source_id; end
def to_s; DraftManager.source_name; end
def uri; DraftManager.source_name; end
diff --git a/lib/sup/hook.rb b/lib/sup/hook.rb
@@ -19,6 +19,14 @@ class HookManager
end
end
+ def flash s
+ if BufferManager.instantiated?
+ BufferManager.flash s
+ else
+ log s
+ end
+ end
+
def log s
info "hook[#@__name]: #{s}"
end
diff --git a/lib/sup/horizontal_selector.rb b/lib/sup/horizontal_selector.rb
@@ -1,6 +1,8 @@
module Redwood
class HorizontalSelector
+ class UnknownValue < StandardError; end
+
attr_accessor :label, :changed_by_user
def initialize label, vals, labels, base_color=:horizontal_selector_unselected_color, selected_color=:horizontal_selector_selected_color
@@ -13,7 +15,14 @@ class HorizontalSelector
@changed_by_user = false
end
- def set_to val; @selection = @vals.index(val) end
+ def set_to val
+ raise UnknownValue, val.inspect unless can_set_to? val
+ @selection = @vals.index(val)
+ end
+
+ def can_set_to? val
+ @vals.include? val
+ end
def val; @vals[@selection] end
diff --git a/lib/sup/message_chunks.rb b/lib/sup/message_chunks.rb
@@ -130,7 +130,7 @@ EOS
end
end
- def color; :none end
+ def color; :text_color end
def patina_color; :attachment_color end
def patina_text
if expandable?
@@ -191,7 +191,7 @@ EOS
def quotable?; true end
def expandable?; false end
def viewable?; false end
- def color; :none end
+ def color; :text_color end
end
class Quote
diff --git a/lib/sup/modes/completion_mode.rb b/lib/sup/modes/completion_mode.rb
@@ -38,11 +38,11 @@ private
suffix = s[(@prefix_len + 1) .. -1]
char = s[@prefix_len].chr
- @lines.last += [[:none, sprintf("%#{max_length - suffix.length - 1}s", prefix)],
+ @lines.last += [[:text_color, sprintf("%#{max_length - suffix.length - 1}s", prefix)],
[:completion_character_color, char],
- [:none, suffix + INTERSTITIAL]]
+ [:text_color, suffix + INTERSTITIAL]]
else
- @lines.last += [[:none, sprintf("%#{max_length}s#{INTERSTITIAL}", s)]]
+ @lines.last += [[:text_color, sprintf("%#{max_length}s#{INTERSTITIAL}", s)]]
end
else
@lines << "" if i % num_per == 0
diff --git a/lib/sup/modes/edit_message_mode.rb b/lib/sup/modes/edit_message_mode.rb
@@ -131,13 +131,18 @@ EOS
HorizontalSelector.new "Account:", AccountManager.user_emails + [nil], user_emails_copy + ["Customized"]
if @header["From"] =~ /<?(\S+@(\S+?))>?$/
- @account_selector.set_to $1
- @account_user = ""
+ # TODO: this is ugly. might implement an AccountSelector and handle
+ # special cases more transparently.
+ account_from = @account_selector.can_set_to?($1) ? $1 : nil
+ @account_selector.set_to account_from
else
@account_selector.set_to nil
- @account_user = @header["From"]
end
+ # A single source of truth might better than duplicating this in both
+ # @account_user and @account_selector.
+ @account_user = @header["From"]
+
add_selector @account_selector
end
diff --git a/lib/sup/modes/inbox_mode.rb b/lib/sup/modes/inbox_mode.rb
@@ -1,4 +1,4 @@
-require 'sup'
+require "sup/modes/thread_index_mode"
module Redwood
diff --git a/lib/sup/modes/line_cursor_mode.rb b/lib/sup/modes/line_cursor_mode.rb
@@ -47,9 +47,9 @@ protected
def draw_line ln, opts={}
if ln == @curpos
- super ln, :highlight => true, :debug => opts[:debug]
+ super ln, :highlight => true, :debug => opts[:debug], :color => :text_color
else
- super
+ super ln, :color => :text_color
end
end
diff --git a/lib/sup/modes/scroll_mode.rb b/lib/sup/modes/scroll_mode.rb
@@ -43,12 +43,12 @@ class ScrollMode < Mode
def draw
ensure_mode_validity
- (@topline ... @botline).each { |ln| draw_line ln }
+ (@topline ... @botline).each { |ln| draw_line ln, :color => :text_color }
((@botline - @topline) ... buffer.content_height).each do |ln|
if @twiddles
buffer.write ln, 0, "~", :color => :twiddle_color
else
- buffer.write ln, 0, ""
+ buffer.write ln, 0, "", :color => :text_color
end
end
@status = "lines #{@topline + 1}:#{@botline}/#{lines}"
@@ -208,7 +208,7 @@ protected
# return
end
- def matching_text_array s, regex, oldcolor=:none
+ def matching_text_array s, regex, oldcolor=:text_color
s.split(regex).map do |text|
next if text.empty?
if text =~ regex
@@ -244,7 +244,7 @@ protected
end
def draw_line_from_string ln, s, opts
- buffer.write ln - @topline, 0, s[@leftcol .. -1], :highlight => opts[:highlight]
+ buffer.write ln - @topline, 0, s[@leftcol .. -1], :highlight => opts[:highlight], :color => opts[:color]
end
end
diff --git a/lib/sup/poll.rb b/lib/sup/poll.rb
@@ -22,7 +22,11 @@ Variables:
num: the total number of new messages added in this poll
num_inbox: the number of new messages added in this poll which
appear in the inbox (i.e. were not auto-archived).
+ num_total: the total number of messages
+ num_inbox_total: the total number of new messages in the inbox.
num_inbox_total_unread: the total number of unread messages in the inbox
+ num_updated: the total number of updated messages
+ num_deleted: the total number of deleted messages
from_and_subj: an array of (from email address, subject) pairs
from_and_subj_inbox: an array of (from email address, subject) pairs for
only those messages appearing in the inbox
@@ -43,10 +47,13 @@ EOS
def poll_with_sources
@mode ||= PollMode.new
- HookManager.run "before-poll"
- BufferManager.flash "Polling for new messages..."
- flash_msg = ""
+ if HookManager.enabled? "before-poll"
+ HookManager.run("before-poll")
+ else
+ BufferManager.flash "Polling for new messages..."
+ end
+
num, numi, numu, numd, from_and_subj, from_and_subj_inbox, loaded_labels = @mode.poll
clear_running_totals if @should_clear_running_totals
@running_totals[:num] += num
@@ -55,18 +62,28 @@ EOS
@running_totals[:numd] += numd
@running_totals[:loaded_labels] += loaded_labels || []
- flash_msg += "Loaded #{@running_totals[:num].pluralize 'new message'}, #{@running_totals[:numi]} to inbox. " if @running_totals[:num] > 0
- flash_msg += "Updated #{@running_totals[:numu].pluralize 'message'}. " if @running_totals[:numu] > 0
- flash_msg += "Deleted #{@running_totals[:numd].pluralize 'message'}. " if @running_totals[:numd] > 0
- flash_msg += "Labels: #{@running_totals[:loaded_labels].map{|l| l.to_s}.join(', ')}." if @running_totals[:loaded_labels].size > 0
- if flash_msg == ""
- BufferManager.flash "No new messages."
+
+ if HookManager.enabled? "after-poll"
+ hook_args = { :num => num, :num_inbox => numi,
+ :num_total => @running_totals[:num], :num_inbox_total => @running_totals[:numi],
+ :num_updated => @running_totals[:numu],
+ :num_deleted => @running_totals[:numd],
+ :from_and_subj => from_and_subj, :from_and_subj_inbox => from_and_subj_inbox,
+ :num_inbox_total_unread => lambda { Index.num_results_for :labels => [:inbox, :unread] } }
+
+ HookManager.run("after-poll", hook_args)
else
- BufferManager.flash flash_msg
+ if @running_totals[:num] > 0
+ flash_msg = "Loaded #{@running_totals[:num].pluralize 'new message'}, #{@running_totals[:numi]} to inbox. " if @running_totals[:num] > 0
+ flash_msg += "Updated #{@running_totals[:numu].pluralize 'message'}. " if @running_totals[:numu] > 0
+ flash_msg += "Deleted #{@running_totals[:numd].pluralize 'message'}. " if @running_totals[:numd] > 0
+ flash_msg += "Labels: #{@running_totals[:loaded_labels].map{|l| l.to_s}.join(', ')}." if @running_totals[:loaded_labels].size > 0
+ BufferManager.flash flash_msg
+ else
+ BufferManager.flash "No new messages."
+ end
end
- HookManager.run "after-poll", :num => num, :num_inbox => numi, :from_and_subj => from_and_subj, :from_and_subj_inbox => from_and_subj_inbox, :num_inbox_total_unread => lambda { Index.num_results_for :labels => [:inbox, :unread] }
-
end
def poll
diff --git a/lib/sup/source.rb b/lib/sup/source.rb
@@ -217,9 +217,9 @@ class SourceManager
end
end
- def save_sources fn=Redwood::SOURCE_FN
+ def save_sources fn=Redwood::SOURCE_FN, force=false
@source_mutex.synchronize do
- if @sources_dirty
+ if @sources_dirty || force
Redwood::save_yaml_obj sources, fn, false, true
end
@sources_dirty = false
diff --git a/sup.gemspec b/sup.gemspec
@@ -5,7 +5,8 @@ require 'sup/version'
# Files
SUP_EXECUTABLES = %w(sup sup-add sup-config sup-dump sup-import-dump
- sup-recover-sources sup-sync sup-sync-back sup-tweak-labels)
+ sup-recover-sources sup-sync sup-sync-back sup-tweak-labels
+ sup-psych-ify-config-files)
SUP_EXTRA_FILES = %w(CONTRIBUTORS README.md LICENSE History.txt ReleaseNotes)
SUP_FILES =
SUP_EXTRA_FILES +
@@ -33,9 +34,15 @@ module Redwood
* Automatically tracking recent contacts
DESC
s.license = 'GPL-2'
+ # TODO: might want to add index migrating script here, too
+ s.post_install_message = <<-EOF
+SUP: Please run `sup-psych-ify-config-files` to migrate from 0.13 to 0.14
+ EOF
s.files = SUP_FILES
s.executables = SUP_EXECUTABLES
+ s.required_ruby_version = '>= 1.9.2'
+
s.add_dependency "xapian-full-alaveteli", "~> 1.2"
s.add_dependency "ncursesw-sup", "~> 1.3", ">= 1.3.1"
s.add_dependency "rmail", ">= 0.17"
diff --git a/test/test_yaml_migration.rb b/test/test_yaml_migration.rb
@@ -0,0 +1,80 @@
+require "test_helper"
+
+require "sup"
+require "psych"
+
+describe "Sup's YAML util" do
+ describe "Module#yaml_properties" do
+ def build_class_with_name name, &b
+ Class.new do
+ meta_cls = class << self; self; end
+ meta_cls.send(:define_method, :name) { name }
+ class_exec(&b) unless b.nil?
+ end
+ end
+
+ after do
+ Psych.load_tags = {}
+ Psych.dump_tags = {}
+ end
+
+ it "defines YAML tag for class" do
+ cls = build_class_with_name 'Cls' do
+ yaml_properties
+ end
+
+ expected_yaml_tag = "!supmua.org,2006-10-01/Cls"
+
+ Psych.load_tags[expected_yaml_tag].must_equal cls
+ Psych.dump_tags[cls].must_equal expected_yaml_tag
+
+ end
+
+ it "Loads legacy YAML format as well" do
+ cls = build_class_with_name 'Cls' do
+ yaml_properties :id
+ attr_accessor :id
+ def initialize id
+ @id = id
+ end
+ end
+
+ Psych.load_tags["!masanjin.net,2006-10-01/Cls"].must_equal cls
+
+ yaml = <<EOF
+--- !masanjin.net,2006-10-01/Cls
+id: ID
+EOF
+ loaded = YAML.load(yaml)
+
+ loaded.id.must_equal 'ID'
+ loaded.must_be_kind_of cls
+ end
+
+ it "Dumps & loads w/ state re-initialized" do
+ cls = build_class_with_name 'Cls' do
+ yaml_properties :id
+ attr_accessor :id
+ attr_reader :flag
+
+ def initialize id
+ @id = id
+ @flag = true
+ end
+ end
+
+ instance = cls.new 'ID'
+
+ dumped = YAML.dump(instance)
+ loaded = YAML.load(dumped)
+
+ dumped.must_equal <<-EOF
+--- !supmua.org,2006-10-01/Cls
+id: ID
+ EOF
+
+ loaded.id.must_equal 'ID'
+ assert loaded.flag
+ end
+ end
+end
diff --git a/test/unit/test_horizontal_selector.rb b/test/unit/test_horizontal_selector.rb
@@ -0,0 +1,40 @@
+require "test_helper"
+
+require "sup/horizontal_selector"
+
+describe Redwood::HorizontalSelector do
+ let(:values) { %w[foo@example.com bar@example.com] }
+ let(:strange_value) { "strange@example.com" }
+
+ before do
+ @selector = Redwood::HorizontalSelector.new(
+ 'Acc:', values, [])
+ end
+
+ it "init w/ the first value selected" do
+ first_value = values.first
+ @selector.val.must_equal first_value
+ end
+
+ it "stores value for selection" do
+ second_value = values[1]
+ @selector.set_to second_value
+ @selector.val.must_equal second_value
+ end
+
+ describe "for unknown value" do
+ it "cannot select unknown value" do
+ @selector.wont_be :can_set_to?, strange_value
+ end
+
+ it "refuses selecting unknown value" do
+ old_value = @selector.val
+
+ assert_raises Redwood::HorizontalSelector::UnknownValue do
+ @selector.set_to strange_value
+ end
+
+ @selector.val.must_equal old_value
+ end
+ end
+end