commit 3068986cc96d0a6f79cba15639f20405d2d82708
parent 8818a4e2e8a4bf95df1d5d7ed5689bb5a34abb48
Author: Whyme Lyu <callme5long@gmail.com>
Date: Wed, 22 May 2013 06:25:30 -0700
Merge pull request #63 from 5long/batch-label-operation
Fix batch label operation in console
Diffstat:
11 files changed, 126 insertions(+), 7 deletions(-)
diff --git a/lib/sup.rb b/lib/sup.rb
@@ -309,9 +309,7 @@ require "sup/hook"
require "sup/time"
## everything we need to get logging working
-require "sup/logger"
-Redwood::Logger.init.add_sink $stderr
-include Redwood::LogsStuff
+require "sup/logger/singleton"
## determine encoding and character set
$encoding = Locale.current.charset
diff --git a/lib/sup/hook.rb b/lib/sup/hook.rb
@@ -1,3 +1,5 @@
+require "sup/util"
+
module Redwood
class HookManager
diff --git a/lib/sup/index.rb b/lib/sup/index.rb
@@ -6,6 +6,11 @@ require 'fileutils'
require 'monitor'
require 'chronic'
+require "sup/interactive_lock"
+require "sup/hook"
+require "sup/logger/singleton"
+
+
if ([Xapian.major_version, Xapian.minor_version, Xapian.revision] <=> [1,2,1]) < 0
fail "Xapian version 1.2.1 or higher required"
end
@@ -254,6 +259,11 @@ EOS
end
end
+ # Search messages. Returns an Enumerator.
+ def find_messages query_expr
+ enum_for :each_message, parse_query(query_expr)
+ end
+
# wrap all future changes inside a transaction so they're done atomically
def begin_transaction
synchronize { @xapian.begin_transaction }
diff --git a/lib/sup/logger.rb b/lib/sup/logger.rb
@@ -1,4 +1,4 @@
-require "sup"
+require "sup/util"
require 'stringio'
require 'thread'
diff --git a/lib/sup/logger/singleton.rb b/lib/sup/logger/singleton.rb
@@ -0,0 +1,10 @@
+# TODO: this is ugly. It's better to have a application singleton passed
+# down to lower level components instead of including logging methods in
+# class `Object'
+#
+# For now this is what we have to do.
+require "sup/logger"
+Redwood::Logger.init.add_sink $stderr
+class Object
+ include Redwood::LogsStuff
+end
diff --git a/lib/sup/modes/console_mode.rb b/lib/sup/modes/console_mode.rb
@@ -1,10 +1,13 @@
require 'pp'
+require "sup/service/label_service"
+
module Redwood
class Console
def initialize mode
@mode = mode
+ @label_service = LabelService.new
end
def query(query)
@@ -12,19 +15,27 @@ class Console
end
def add_labels(query, *labels)
- query(query).each { |m| m.labels += labels; m.save Index }
+ count = @label_service.add_labels(query, *labels)
+ print_buffer_dirty_msg count
end
def remove_labels(query, *labels)
- query(query).each { |m| m.labels -= labels; m.save Index }
+ count = @label_service.remove_labels(query, *labels)
+ print_buffer_dirty_msg count
+ end
+
+ def print_buffer_dirty_msg msg_count
+ puts "Scanned #{msg_count} messages."
+ puts "You might want to refresh open buffers with `@` key."
end
+ private :print_buffer_dirty_msg
def xapian; Index.instance.instance_variable_get :@xapian; end
def loglevel; Redwood::Logger.level; end
def set_loglevel(level); Redwood::Logger.level = level; end
- def special_methods; methods - Object.methods end
+ def special_methods; public_methods - Object.methods end
def puts x; @mode << "#{x.to_s.rstrip}\n" end
def p x; puts x.inspect end
diff --git a/lib/sup/service/label_service.rb b/lib/sup/service/label_service.rb
@@ -0,0 +1,45 @@
+require "sup/index"
+
+module Redwood
+ # Provides label tweaking service to the user.
+ # Working as the backend of ConsoleMode.
+ #
+ # Should become the backend of bin/sup-tweak-labels in the future.
+ class LabelService
+ # @param index [Redwood::Index]
+ def initialize index=Index.instance
+ @index = index
+ end
+
+ def add_labels query, *labels
+ run_on_each_message(query) do |m|
+ labels.each {|l| m.add_label l }
+ end
+ end
+
+ def remove_labels query, *labels
+ run_on_each_message(query) do |m|
+ labels.each {|l| m.remove_label l }
+ end
+ end
+
+
+ private
+ def run_on_each_message query, &operation
+ count = 0
+
+ find_messages(query).each do |m|
+ operation.call(m)
+ @index.update_message_state m
+ count += 1
+ end
+
+ @index.save_index
+ count
+ end
+
+ def find_messages query
+ @index.find_messages(query)
+ end
+ end
+end
diff --git a/sup.gemspec b/sup.gemspec
@@ -49,5 +49,6 @@ DESC
s.add_development_dependency "bundler", "~> 1.3"
s.add_development_dependency "rake"
s.add_development_dependency "minitest", "~> 4"
+ s.add_development_dependency "rr", "~> 1.0"
end
end
diff --git a/test/integration/test_label_service.rb b/test/integration/test_label_service.rb
@@ -0,0 +1,18 @@
+require "test_helper"
+
+require "sup/service/label_service"
+
+require "tmpdir"
+
+describe Redwood::LabelService do
+ let(:tmpdir) { Dir.mktmpdir }
+ after do
+ require "fileutils"
+ FileUtils.remove_entry_secure @tmpdir unless @tmpdir.nil?
+ end
+
+ describe "#add_labels" do
+ # Integration tests are hard to write at this moment :(
+ it "add labels to all messages matching the query"
+ end
+end
diff --git a/test/test_helper.rb b/test/test_helper.rb
@@ -1 +1,6 @@
require 'minitest/autorun'
+require "rr"
+
+class Minitest::Unit::TestCase
+ include ::RR::Adapters::MiniTest
+end
diff --git a/test/unit/service/test_label_service.rb b/test/unit/service/test_label_service.rb
@@ -0,0 +1,19 @@
+require "test_helper"
+
+require "sup/service/label_service"
+
+describe Redwood::LabelService do
+ describe "#add_labels" do
+ it "add labels to all messages matching the query" do
+ q = 'is:starred'
+ label = 'superstarred'
+ message = mock!.add_label(label).subject
+ index = mock!.find_messages(q){ [message] }.subject
+ mock(index).update_message_state(message)
+ mock(index).save_index
+
+ service = Redwood::LabelService.new(index)
+ service.add_labels q, label
+ end
+ end
+end