Skip to content

Commit

Permalink
Change Gmail::MailController, need performance improvement.
Browse files Browse the repository at this point in the history
  • Loading branch information
Tuan Duc Nguyen committed Feb 19, 2011
1 parent b921696 commit cfcb38d
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 87 deletions.
34 changes: 28 additions & 6 deletions lib/gmail/mailbox.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,33 @@ class Mailbox
:undrafted => ['UNDRAFT']
}

attr_reader :name, :imap_name, :controller
def initialize(controller, name="INBOX")
@name = name
@imap_name = Net::IMAP.encode_utf7(name)
attr_reader :controller, :name, :imap_path, :delim, :parent
def initialize(controller, name="INBOX", delim="/", parent=nil)
@controller = controller
@name = name.split(delim).last.strip
@imap_path = name
@delim = delim
@parent = parent
end

# Return array of children mailboxes.
def children
@children ||= controller.load_mailboxes(self).values
end

# Return array of descendants.
def descendants
children.inject([]) {|l, c| (l << c) + c.descendants}
end

# Enumerate for children.
def each_child(*args, &block)
children.each(*args, &block)
end

# Enumerate for descendants.
def each_descendant(*args, &block)
descendants.each(*args, &block)
end

# Returns list of emails which meets given criteria.
Expand Down Expand Up @@ -88,7 +110,7 @@ def count(*args)

# This permanently removes messages which are marked as deleted
def expunge
controller.switch_to_mailbox(name) { controller.expunge }
controller.switch_to_mailbox(imap_path) { controller.expunge }
end

# Cached messages.
Expand All @@ -97,7 +119,7 @@ def messages
end

def inspect
"#<Gmail::Mailbox#{'0x%04x' % (object_id << 1)} name=#{name}>"
"#<Gmail::Mailbox#{'0x%04x' % (object_id << 1)} name=#{imap_path}>"
end

def to_s
Expand Down
66 changes: 43 additions & 23 deletions lib/gmail/mailbox_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,53 @@ class MailboxController
def initialize(client)
@client = client
@mailbox_mutex = Mutex.new
@mailboxes = {}
load_mailboxes
end

# Get list of all defined labels.
# Array of first level mailboxes.
def mailboxes
@mailboxes.values
end

# Array of all mailboxes.
def all
labels = client.imap.list("", "%") # search first level labels.
gmail_mailboxes = labels.select {|l| l.attr.include?(:Haschildren)}
gmail_mailboxes.each {|l| labels += client.imap.list("#{l.name}#{l.delim}", "%").to_a}

labels.map {|l| Net::IMAP.decode_utf7(l.name)}
mailboxes.inject([]) do |list, mailbox|
(list << mailbox) + mailbox.descendants
end
end
alias :list :all
alias :to_a :all

def each(*args, &block)
all.each(*args, &block)
end

# Returns +true+ when given label defined.
def exists?(label)
all.include?(label)
# Return +true+ when given mailbox defined.
def exist?(name)
not all.detect {|mailbox| mailbox.name == name or mailbox.imap_path == name}.nil?
end
alias :exist? :exists?
alias :exists? :exist?

# Creates given label in your account.
def create(label)
!!client.imap.create(Net::IMAP.encode_utf7(label)) rescue false
# Create mailbox with given path in your account.
def create(path)
client.imap.create(Net::IMAP.encode_utf7(path))
load_mailboxes
return true
rescue
return false
end
alias :add :create

# Deletes given label from your account.
def delete(label)
!!client.imap.delete(Net::IMAP.encode_utf7(label)) rescue false
# Delete mailbox with given imap path from your account.
def delete(path)
client.imap.delete(Net::IMAP.encode_utf7(path))
deleted = @mailboxes.delete(path)
deleted.each_descendant {|d| @mailboxes.delete(d.imap_path)}
return true
rescue
return false
end
alias :remove :delete

# Cached mailboxes.
def mailboxes
@mailboxes ||= {}
end

# Returns a mailbox object for the given name.
# Creates it if not exists.
def mailbox(name="INBOX")
Expand Down Expand Up @@ -89,7 +96,20 @@ def inspect
"#<Gmail::MailboxController#{'0x%04x' % (object_id << 1)}>"
end

def load_mailboxes(mailbox=nil)
mailboxes = {}
path = mailbox.nil? ? "" : (mailbox.imap_path + mailbox.delim)

client.imap.list(Net::IMAP.encode_utf7(path), "%").to_a.each do |m|
mailboxes[Net::IMAP.decode_utf7(m.name)] = Mailbox.new(self, m.name, m.delim, mailbox)
end

@mailboxes.merge!(mailboxes)
mailboxes
end

private

def _switch_to_mailbox(mailbox)
client.imap.select(Net::IMAP.encode_utf7(mailbox)) if mailbox
@current_mailbox = mailbox
Expand Down
108 changes: 50 additions & 58 deletions spec/mailbox_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,78 +2,70 @@

describe "Gmail mailbox controller" do
context "instance" do
subject {
client = Gmail::Client.new(*TEST_ACCOUNT)
client.connect
client.mailbox_controller
}
subject { Gmail.connect(*TEST_ACCOUNT).mailbox_controller }

it "should get list of all available labels" do
mailbox_controller = subject
mailbox_controller.all.should include("INBOX")
it "should get list of all available mailboxes" do
labels = subject.all.map {|m| m.imap_path}
labels.should include("INBOX")
end

it "should be able to check if there is given label defined" do
mailbox_controller = subject
mailbox_controller.exists?("INBOX").should be_true
mailbox_controller.exists?("FOOBAR").should be_false
it "should be able to check if a given mailbox defined" do
subject.exists?("INBOX").should be_true
subject.exists?("FOOBAR").should be_false
end

it "should be able to create given label" do
mailbox_controller = subject
mailbox_controller.create("MYLABEL")
mailbox_controller.exists?("MYLABEL").should be_true
mailbox_controller.create("MYLABEL").should be_false
mailbox_controller.delete("MYLABEL")
subject.create("MYLABEL")
subject.exists?("MYLABEL").should be_true
subject.create("MYLABEL").should be_false
subject.delete("MYLABEL")
end

it "should be able to remove existing label" do
mailbox_controller = subject
mailbox_controller.create("MYLABEL")
mailbox_controller.delete("MYLABEL").should be_true
mailbox_controller.exists?("MYLABEL").should be_false
mailbox_controller.delete("MYLABEL").should be_false
subject.create("MYLABEL")
subject.delete("MYLABEL").should be_true
subject.exists?("MYLABEL").should be_false
subject.delete("MYLABEL").should be_false
end

it "should be able to create label with non-ascii characters" do
mailbox_controller = subject
name = Net::IMAP.decode_utf7("TEST &APYA5AD8-") # TEST äöü
mailbox_controller.create(name)
mailbox_controller.delete(name).should be_true
mailbox_controller.exists?(name).should be_false
mailbox_controller.delete(name).should be_false
# subject.create(name)
# subject.delete(name).should be_true
# subject.exists?(name).should be_false
# subject.delete(name).should be_false
end
end

context "mailboxes" do
subject { Gmail.connect(*TEST_ACCOUNT).mailbox_controller }

%w[mailbox mailbox!].each do |method|
it "##{method} should return INBOX if no name was given" do
mailbox = subject.send(method)
mailbox.should be_kind_of(Gmail::Mailbox)
mailbox.name.should == "INBOX"
end

it "##{method} should return a mailbox with given name" do
mailbox = subject.send(method, "TEST")
mailbox.should be_kind_of(Gmail::Mailbox)
mailbox.name.should == "TEST"
end

it "##{method} should return a mailbox with given name using block style" do
subject.send(method, "TEST") do |mailbox|
mailbox.should be_kind_of(Gmail::Mailbox)
mailbox.name.should == "TEST"
end
end
end

it "#mailbox! should raise an error for not existing name" do
lambda {
mailbox = subject.mailbox!("FOO")
mailbox.should_not be_kind_of(Gmail::Mailbox)
}.should raise_error(KeyError)
end
end
# context "mailboxes" do
# subject { Gmail.connect(*TEST_ACCOUNT).mailbox_controller }
#
# %w[mailbox mailbox!].each do |method|
# it "##{method} should return INBOX if no name was given" do
# mailbox = subject.send(method)
# mailbox.should be_kind_of(Gmail::Mailbox)
# mailbox.name.should == "INBOX"
# end
#
# it "##{method} should return a mailbox with given name" do
# mailbox = subject.send(method, "TEST")
# mailbox.should be_kind_of(Gmail::Mailbox)
# mailbox.name.should == "TEST"
# end
#
# it "##{method} should return a mailbox with given name using block style" do
# subject.send(method, "TEST") do |mailbox|
# mailbox.should be_kind_of(Gmail::Mailbox)
# mailbox.name.should == "TEST"
# end
# end
# end
#
# it "#mailbox! should raise an error for not existing name" do
# lambda {
# mailbox = subject.mailbox!("FOO")
# mailbox.should_not be_kind_of(Gmail::Mailbox)
# }.should raise_error(KeyError)
# end
# end
end

0 comments on commit cfcb38d

Please sign in to comment.