gmail 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +26 -25
- data/.rspec +2 -0
- data/CHANGELOG.md +82 -82
- data/Gemfile +3 -0
- data/LICENSE +21 -21
- data/README.md +271 -251
- data/Rakefile +35 -45
- data/TODO.md +12 -7
- data/gmail.gemspec +34 -23
- data/lib/gmail.rb +65 -82
- data/lib/gmail/client.rb +30 -22
- data/lib/gmail/client/base.rb +225 -220
- data/lib/gmail/client/imap_extensions.rb +54 -0
- data/lib/gmail/client/plain.rb +20 -18
- data/lib/gmail/client/xoauth.rb +51 -49
- data/lib/gmail/labels.rb +62 -48
- data/lib/gmail/mailbox.rb +117 -117
- data/lib/gmail/message.rb +166 -164
- data/lib/gmail/version.rb +3 -12
- data/spec/account.yml.example +1 -1
- data/spec/client_spec.rb +178 -173
- data/spec/gmail_spec.rb +39 -38
- data/spec/mailbox_spec.rb +47 -49
- data/spec/message_spec.rb +51 -51
- data/spec/spec_helper.rb +28 -28
- metadata +109 -110
@@ -0,0 +1,54 @@
|
|
1
|
+
# Taken from https://github.com/oxos/gmail-oauth-thread-stats/blob/master/gmail_imap_extensions_compatibility.rb
|
2
|
+
|
3
|
+
module GmailImapExtensions
|
4
|
+
|
5
|
+
def self.patch_net_imap_response_parser(klass = Net::IMAP::ResponseParser)
|
6
|
+
klass.class_eval do
|
7
|
+
def msg_att
|
8
|
+
match(Net::IMAP::ResponseParser::T_LPAR)
|
9
|
+
attr = {}
|
10
|
+
while true
|
11
|
+
token = lookahead
|
12
|
+
case token.symbol
|
13
|
+
when Net::IMAP::ResponseParser::T_RPAR
|
14
|
+
shift_token
|
15
|
+
break
|
16
|
+
when Net::IMAP::ResponseParser::T_SPACE
|
17
|
+
shift_token
|
18
|
+
token = lookahead
|
19
|
+
end
|
20
|
+
case token.value
|
21
|
+
when /\A(?:ENVELOPE)\z/ni
|
22
|
+
name, val = envelope_data
|
23
|
+
when /\A(?:FLAGS)\z/ni
|
24
|
+
name, val = flags_data
|
25
|
+
when /\A(?:INTERNALDATE)\z/ni
|
26
|
+
name, val = internaldate_data
|
27
|
+
when /\A(?:RFC822(?:\.HEADER|\.TEXT)?)\z/ni
|
28
|
+
name, val = rfc822_text
|
29
|
+
when /\A(?:RFC822\.SIZE)\z/ni
|
30
|
+
name, val = rfc822_size
|
31
|
+
when /\A(?:BODY(?:STRUCTURE)?)\z/ni
|
32
|
+
name, val = body_data
|
33
|
+
when /\A(?:UID)\z/ni
|
34
|
+
name, val = uid_data
|
35
|
+
|
36
|
+
# Gmail extension additions.
|
37
|
+
# Cargo-Cult code warning: # I have no idea why the regexp - just copying a pattern
|
38
|
+
when /\A(?:X-GM-LABELS)\z/ni
|
39
|
+
name, val = flags_data
|
40
|
+
when /\A(?:X-GM-MSGID)\z/ni
|
41
|
+
name, val = uid_data
|
42
|
+
when /\A(?:X-GM-THRID)\z/ni
|
43
|
+
name, val = uid_data
|
44
|
+
else
|
45
|
+
parse_error("unknown attribute `%s'", token.value)
|
46
|
+
end
|
47
|
+
attr[name] = val
|
48
|
+
end
|
49
|
+
return attr
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/lib/gmail/client/plain.rb
CHANGED
@@ -1,18 +1,20 @@
|
|
1
|
-
module Gmail
|
2
|
-
module Client
|
3
|
-
class Plain < Base
|
4
|
-
attr_reader :password
|
5
|
-
|
6
|
-
def initialize(username, password, options={})
|
7
|
-
@password = password
|
8
|
-
super(username, options)
|
9
|
-
end
|
10
|
-
|
11
|
-
def login(raise_errors=false)
|
12
|
-
@imap and @logged_in = (login = @imap.login(username, password)) && login.name == 'OK'
|
13
|
-
rescue Net::IMAP::NoResponseError
|
14
|
-
raise_errors and raise AuthorizationError, "Couldn't login to given GMail account: #{username}"
|
15
|
-
end
|
16
|
-
end # Plain
|
17
|
-
|
18
|
-
|
1
|
+
module Gmail
|
2
|
+
module Client
|
3
|
+
class Plain < Base
|
4
|
+
attr_reader :password
|
5
|
+
|
6
|
+
def initialize(username, password, options={})
|
7
|
+
@password = password
|
8
|
+
super(username, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def login(raise_errors=false)
|
12
|
+
@imap and @logged_in = (login = @imap.login(username, password)) && login.name == 'OK'
|
13
|
+
rescue Net::IMAP::NoResponseError
|
14
|
+
raise_errors and raise AuthorizationError, "Couldn't login to given GMail account: #{username}"
|
15
|
+
end
|
16
|
+
end # Plain
|
17
|
+
|
18
|
+
register :plain, Plain
|
19
|
+
end # Client
|
20
|
+
end # Gmail
|
data/lib/gmail/client/xoauth.rb
CHANGED
@@ -1,49 +1,51 @@
|
|
1
|
-
require 'gmail_xoauth'
|
2
|
-
|
3
|
-
module Gmail
|
4
|
-
module Client
|
5
|
-
class XOAuth < Base
|
6
|
-
attr_reader :token
|
7
|
-
attr_reader :secret
|
8
|
-
attr_reader :consumer_key
|
9
|
-
attr_reader :consumer_secret
|
10
|
-
|
11
|
-
def initialize(username, options={})
|
12
|
-
@token = options.delete(:token)
|
13
|
-
@secret = options.delete(:secret)
|
14
|
-
@consumer_key = options.delete(:consumer_key)
|
15
|
-
@consumer_secret = options.delete(:consumer_secret)
|
16
|
-
|
17
|
-
super(username, options)
|
18
|
-
end
|
19
|
-
|
20
|
-
def login(raise_errors=false)
|
21
|
-
@imap and @logged_in = (login = @imap.authenticate('XOAUTH', username,
|
22
|
-
:consumer_key => consumer_key,
|
23
|
-
:consumer_secret => consumer_secret,
|
24
|
-
:token => token,
|
25
|
-
:token_secret => secret
|
26
|
-
)) && login.name == 'OK'
|
27
|
-
rescue
|
28
|
-
raise_errors and raise AuthorizationError, "Couldn't login to given GMail account: #{username}"
|
29
|
-
end
|
30
|
-
|
31
|
-
def smtp_settings
|
32
|
-
[:smtp, {
|
33
|
-
:address => GMAIL_SMTP_HOST,
|
34
|
-
:port => GMAIL_SMTP_PORT,
|
35
|
-
:domain => mail_domain,
|
36
|
-
:user_name => username,
|
37
|
-
:password =>
|
38
|
-
:consumer_key => consumer_key,
|
39
|
-
:consumer_secret => consumer_secret,
|
40
|
-
:token => token,
|
41
|
-
:token_secret =>
|
42
|
-
},
|
43
|
-
:authentication => :xoauth,
|
44
|
-
:enable_starttls_auto => true
|
45
|
-
}]
|
46
|
-
end
|
47
|
-
end # XOAuth
|
48
|
-
|
49
|
-
|
1
|
+
require 'gmail_xoauth'
|
2
|
+
|
3
|
+
module Gmail
|
4
|
+
module Client
|
5
|
+
class XOAuth < Base
|
6
|
+
attr_reader :token
|
7
|
+
attr_reader :secret
|
8
|
+
attr_reader :consumer_key
|
9
|
+
attr_reader :consumer_secret
|
10
|
+
|
11
|
+
def initialize(username, options={})
|
12
|
+
@token = options.delete(:token)
|
13
|
+
@secret = options.delete(:secret)
|
14
|
+
@consumer_key = options.delete(:consumer_key)
|
15
|
+
@consumer_secret = options.delete(:consumer_secret)
|
16
|
+
|
17
|
+
super(username, options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def login(raise_errors=false)
|
21
|
+
@imap and @logged_in = (login = @imap.authenticate('XOAUTH', username,
|
22
|
+
:consumer_key => consumer_key,
|
23
|
+
:consumer_secret => consumer_secret,
|
24
|
+
:token => token,
|
25
|
+
:token_secret => secret
|
26
|
+
)) && login.name == 'OK'
|
27
|
+
rescue
|
28
|
+
raise_errors and raise AuthorizationError, "Couldn't login to given GMail account: #{username}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def smtp_settings
|
32
|
+
[:smtp, {
|
33
|
+
:address => GMAIL_SMTP_HOST,
|
34
|
+
:port => GMAIL_SMTP_PORT,
|
35
|
+
:domain => mail_domain,
|
36
|
+
:user_name => username,
|
37
|
+
:password => {
|
38
|
+
:consumer_key => consumer_key,
|
39
|
+
:consumer_secret => consumer_secret,
|
40
|
+
:token => token,
|
41
|
+
:token_secret => secret
|
42
|
+
},
|
43
|
+
:authentication => :xoauth,
|
44
|
+
:enable_starttls_auto => true
|
45
|
+
}]
|
46
|
+
end
|
47
|
+
end # XOAuth
|
48
|
+
|
49
|
+
register :xoauth, XOAuth
|
50
|
+
end # Client
|
51
|
+
end # Gmail
|
data/lib/gmail/labels.rb
CHANGED
@@ -1,48 +1,62 @@
|
|
1
|
-
module Gmail
|
2
|
-
class Labels
|
3
|
-
include Enumerable
|
4
|
-
attr_reader :connection
|
5
|
-
alias :conn :connection
|
6
|
-
|
7
|
-
def initialize(connection)
|
8
|
-
@connection = connection
|
9
|
-
end
|
10
|
-
|
11
|
-
# Get list of all defined labels.
|
12
|
-
def all
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
end
|
1
|
+
module Gmail
|
2
|
+
class Labels
|
3
|
+
include Enumerable
|
4
|
+
attr_reader :connection
|
5
|
+
alias :conn :connection
|
6
|
+
|
7
|
+
def initialize(connection)
|
8
|
+
@connection = connection
|
9
|
+
end
|
10
|
+
|
11
|
+
# Get list of all defined labels.
|
12
|
+
def all
|
13
|
+
@list = []
|
14
|
+
|
15
|
+
## check each item in list for subfolders
|
16
|
+
conn.list("", "%").each {|l| sublabels_or_label(l)}
|
17
|
+
|
18
|
+
@list.inject([]) do |labels,label|
|
19
|
+
label[:name].each_line {|l| labels << Net::IMAP.decode_utf7(l) }
|
20
|
+
labels
|
21
|
+
end
|
22
|
+
end
|
23
|
+
alias :list :all
|
24
|
+
alias :to_a :all
|
25
|
+
|
26
|
+
def sublabels_or_label(label)
|
27
|
+
if label.attr.include? :Hasnochildren
|
28
|
+
@list << label
|
29
|
+
else
|
30
|
+
@list << label
|
31
|
+
conn.list("#{label.name}/", "%").each {|l| sublabels_or_label(l)}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def each(*args, &block)
|
36
|
+
all.each(*args, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns +true+ when given label defined.
|
40
|
+
def exists?(label)
|
41
|
+
all.include?(label)
|
42
|
+
end
|
43
|
+
alias :exist? :exists?
|
44
|
+
|
45
|
+
# Creates given label in your account.
|
46
|
+
def create(label)
|
47
|
+
!!conn.create(Net::IMAP.encode_utf7(label)) rescue false
|
48
|
+
end
|
49
|
+
alias :new :create
|
50
|
+
alias :add :create
|
51
|
+
|
52
|
+
# Deletes given label from your account.
|
53
|
+
def delete(label)
|
54
|
+
!!conn.delete(Net::IMAP.encode_utf7(label)) rescue false
|
55
|
+
end
|
56
|
+
alias :remove :delete
|
57
|
+
|
58
|
+
def inspect
|
59
|
+
"#<Gmail::Labels#{'0x%04x' % (object_id << 1)}>"
|
60
|
+
end
|
61
|
+
end # Labels
|
62
|
+
end # Gmail
|
data/lib/gmail/mailbox.rb
CHANGED
@@ -1,117 +1,117 @@
|
|
1
|
-
module Gmail
|
2
|
-
class Mailbox
|
3
|
-
MAILBOX_ALIASES = {
|
4
|
-
:all => ['ALL'],
|
5
|
-
:seen => ['SEEN'],
|
6
|
-
:unseen => ['UNSEEN'],
|
7
|
-
:read => ['SEEN'],
|
8
|
-
:unread => ['UNSEEN'],
|
9
|
-
:flagged => ['FLAGGED'],
|
10
|
-
:unflagged => ['UNFLAGGED'],
|
11
|
-
:starred => ['FLAGGED'],
|
12
|
-
:unstarred => ['UNFLAGGED'],
|
13
|
-
:deleted => ['DELETED'],
|
14
|
-
:undeleted => ['UNDELETED'],
|
15
|
-
:draft => ['DRAFT'],
|
16
|
-
:undrafted => ['UNDRAFT']
|
17
|
-
}
|
18
|
-
|
19
|
-
attr_reader :name
|
20
|
-
attr_reader :external_name
|
21
|
-
|
22
|
-
def initialize(gmail, name="INBOX")
|
23
|
-
@name = name
|
24
|
-
@external_name = Net::IMAP.decode_utf7(name)
|
25
|
-
@gmail = gmail
|
26
|
-
end
|
27
|
-
|
28
|
-
# Returns list of emails which meets given criteria.
|
29
|
-
#
|
30
|
-
# ==== Examples
|
31
|
-
#
|
32
|
-
# gmail.inbox.emails(:all)
|
33
|
-
# gmail.inbox.emails(:unread, :from => "friend@gmail.com")
|
34
|
-
# gmail.inbox.emails(:all, :after => Time.now-(20*24*3600))
|
35
|
-
# gmail.mailbox("Test").emails(:read)
|
36
|
-
#
|
37
|
-
# gmail.mailbox("Test") do |box|
|
38
|
-
# box.emails(:read)
|
39
|
-
# box.emails(:unread) do |email|
|
40
|
-
# ... do something with each email...
|
41
|
-
# end
|
42
|
-
# end
|
43
|
-
def emails(*args, &block)
|
44
|
-
args << :all if args.size == 0
|
45
|
-
|
46
|
-
if args.first.is_a?(Symbol)
|
47
|
-
search = MAILBOX_ALIASES[args.shift].dup
|
48
|
-
opts = args.first.is_a?(Hash) ? args.first : {}
|
49
|
-
|
50
|
-
opts[:after] and search.concat ['SINCE', opts[:after].to_imap_date]
|
51
|
-
opts[:before] and search.concat ['BEFORE', opts[:before].to_imap_date]
|
52
|
-
opts[:on] and search.concat ['ON', opts[:on].to_imap_date]
|
53
|
-
opts[:from] and search.concat ['FROM', opts[:from]]
|
54
|
-
opts[:to] and search.concat ['TO', opts[:to]]
|
55
|
-
opts[:subject] and search.concat ['SUBJECT', opts[:subject]]
|
56
|
-
opts[:label] and search.concat ['LABEL', opts[:label]]
|
57
|
-
opts[:attachment] and search.concat ['HAS', 'attachment']
|
58
|
-
opts[:search] and search.concat ['BODY', opts[:search]]
|
59
|
-
opts[:body] and search.concat ['BODY', opts[:body]]
|
60
|
-
opts[:query] and search.concat opts[:query]
|
61
|
-
|
62
|
-
@gmail.mailbox(name) do
|
63
|
-
@gmail.conn.uid_search(search).collect do |uid|
|
64
|
-
message = (messages[uid] ||= Message.new(self, uid))
|
65
|
-
block.call(message) if block_given?
|
66
|
-
message
|
67
|
-
end
|
68
|
-
end
|
69
|
-
elsif args.first.is_a?(Hash)
|
70
|
-
emails(:all, args.first)
|
71
|
-
else
|
72
|
-
raise ArgumentError, "Invalid search criteria"
|
73
|
-
end
|
74
|
-
end
|
75
|
-
alias :mails :emails
|
76
|
-
alias :search :emails
|
77
|
-
alias :find :emails
|
78
|
-
alias :filter :emails
|
79
|
-
|
80
|
-
# This is a convenience method that really probably shouldn't need to exist,
|
81
|
-
# but it does make code more readable, if seriously all you want is the count
|
82
|
-
# of messages.
|
83
|
-
#
|
84
|
-
# ==== Examples
|
85
|
-
#
|
86
|
-
# gmail.inbox.count(:all)
|
87
|
-
# gmail.inbox.count(:unread, :from => "friend@gmail.com")
|
88
|
-
# gmail.mailbox("Test").count(:all, :after => Time.now-(20*24*3600))
|
89
|
-
def count(*args)
|
90
|
-
emails(*args).size
|
91
|
-
end
|
92
|
-
|
93
|
-
# This permanently removes messages which are marked as deleted
|
94
|
-
def expunge
|
95
|
-
@gmail.mailbox(name) { @gmail.conn.expunge }
|
96
|
-
end
|
97
|
-
|
98
|
-
# Cached messages.
|
99
|
-
def messages
|
100
|
-
@messages ||= {}
|
101
|
-
end
|
102
|
-
|
103
|
-
def inspect
|
104
|
-
"#<Gmail::Mailbox#{'0x%04x' % (object_id << 1)} name=#{external_name}>"
|
105
|
-
end
|
106
|
-
|
107
|
-
def to_s
|
108
|
-
name
|
109
|
-
end
|
110
|
-
|
111
|
-
MAILBOX_ALIASES.each_key { |mailbox|
|
112
|
-
define_method(mailbox) do |*args, &block|
|
113
|
-
emails(mailbox, *args, &block)
|
114
|
-
end
|
115
|
-
}
|
116
|
-
end # Message
|
117
|
-
end # Gmail
|
1
|
+
module Gmail
|
2
|
+
class Mailbox
|
3
|
+
MAILBOX_ALIASES = {
|
4
|
+
:all => ['ALL'],
|
5
|
+
:seen => ['SEEN'],
|
6
|
+
:unseen => ['UNSEEN'],
|
7
|
+
:read => ['SEEN'],
|
8
|
+
:unread => ['UNSEEN'],
|
9
|
+
:flagged => ['FLAGGED'],
|
10
|
+
:unflagged => ['UNFLAGGED'],
|
11
|
+
:starred => ['FLAGGED'],
|
12
|
+
:unstarred => ['UNFLAGGED'],
|
13
|
+
:deleted => ['DELETED'],
|
14
|
+
:undeleted => ['UNDELETED'],
|
15
|
+
:draft => ['DRAFT'],
|
16
|
+
:undrafted => ['UNDRAFT']
|
17
|
+
}
|
18
|
+
|
19
|
+
attr_reader :name
|
20
|
+
attr_reader :external_name
|
21
|
+
|
22
|
+
def initialize(gmail, name="INBOX")
|
23
|
+
@name = name
|
24
|
+
@external_name = Net::IMAP.decode_utf7(name)
|
25
|
+
@gmail = gmail
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns list of emails which meets given criteria.
|
29
|
+
#
|
30
|
+
# ==== Examples
|
31
|
+
#
|
32
|
+
# gmail.inbox.emails(:all)
|
33
|
+
# gmail.inbox.emails(:unread, :from => "friend@gmail.com")
|
34
|
+
# gmail.inbox.emails(:all, :after => Time.now-(20*24*3600))
|
35
|
+
# gmail.mailbox("Test").emails(:read)
|
36
|
+
#
|
37
|
+
# gmail.mailbox("Test") do |box|
|
38
|
+
# box.emails(:read)
|
39
|
+
# box.emails(:unread) do |email|
|
40
|
+
# ... do something with each email...
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
def emails(*args, &block)
|
44
|
+
args << :all if args.size == 0
|
45
|
+
|
46
|
+
if args.first.is_a?(Symbol)
|
47
|
+
search = MAILBOX_ALIASES[args.shift].dup
|
48
|
+
opts = args.first.is_a?(Hash) ? args.first : {}
|
49
|
+
|
50
|
+
opts[:after] and search.concat ['SINCE', opts[:after].to_imap_date]
|
51
|
+
opts[:before] and search.concat ['BEFORE', opts[:before].to_imap_date]
|
52
|
+
opts[:on] and search.concat ['ON', opts[:on].to_imap_date]
|
53
|
+
opts[:from] and search.concat ['FROM', opts[:from]]
|
54
|
+
opts[:to] and search.concat ['TO', opts[:to]]
|
55
|
+
opts[:subject] and search.concat ['SUBJECT', opts[:subject]]
|
56
|
+
opts[:label] and search.concat ['LABEL', opts[:label]]
|
57
|
+
opts[:attachment] and search.concat ['HAS', 'attachment']
|
58
|
+
opts[:search] and search.concat ['BODY', opts[:search]]
|
59
|
+
opts[:body] and search.concat ['BODY', opts[:body]]
|
60
|
+
opts[:query] and search.concat opts[:query]
|
61
|
+
|
62
|
+
@gmail.mailbox(name) do
|
63
|
+
@gmail.conn.uid_search(search).collect do |uid|
|
64
|
+
message = (messages[uid] ||= Message.new(self, uid))
|
65
|
+
block.call(message) if block_given?
|
66
|
+
message
|
67
|
+
end
|
68
|
+
end
|
69
|
+
elsif args.first.is_a?(Hash)
|
70
|
+
emails(:all, args.first)
|
71
|
+
else
|
72
|
+
raise ArgumentError, "Invalid search criteria"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
alias :mails :emails
|
76
|
+
alias :search :emails
|
77
|
+
alias :find :emails
|
78
|
+
alias :filter :emails
|
79
|
+
|
80
|
+
# This is a convenience method that really probably shouldn't need to exist,
|
81
|
+
# but it does make code more readable, if seriously all you want is the count
|
82
|
+
# of messages.
|
83
|
+
#
|
84
|
+
# ==== Examples
|
85
|
+
#
|
86
|
+
# gmail.inbox.count(:all)
|
87
|
+
# gmail.inbox.count(:unread, :from => "friend@gmail.com")
|
88
|
+
# gmail.mailbox("Test").count(:all, :after => Time.now-(20*24*3600))
|
89
|
+
def count(*args)
|
90
|
+
emails(*args).size
|
91
|
+
end
|
92
|
+
|
93
|
+
# This permanently removes messages which are marked as deleted
|
94
|
+
def expunge
|
95
|
+
@gmail.mailbox(name) { @gmail.conn.expunge }
|
96
|
+
end
|
97
|
+
|
98
|
+
# Cached messages.
|
99
|
+
def messages
|
100
|
+
@messages ||= {}
|
101
|
+
end
|
102
|
+
|
103
|
+
def inspect
|
104
|
+
"#<Gmail::Mailbox#{'0x%04x' % (object_id << 1)} name=#{external_name}>"
|
105
|
+
end
|
106
|
+
|
107
|
+
def to_s
|
108
|
+
name
|
109
|
+
end
|
110
|
+
|
111
|
+
MAILBOX_ALIASES.each_key { |mailbox|
|
112
|
+
define_method(mailbox) do |*args, &block|
|
113
|
+
emails(mailbox, *args, &block)
|
114
|
+
end
|
115
|
+
}
|
116
|
+
end # Message
|
117
|
+
end # Gmail
|