gmail 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +27 -27
  4. data/.rspec +1 -1
  5. data/.rubocop.yml +53 -13
  6. data/.rubocop_todo.yml +299 -239
  7. data/.travis.yml +19 -19
  8. data/CHANGELOG.md +152 -145
  9. data/Gemfile +5 -3
  10. data/LICENSE +21 -21
  11. data/README.md +380 -355
  12. data/Rakefile +44 -46
  13. data/gmail.gemspec +32 -34
  14. data/lib/gmail.rb +72 -78
  15. data/lib/gmail/client.rb +35 -34
  16. data/lib/gmail/client/base.rb +244 -229
  17. data/lib/gmail/client/plain.rb +24 -24
  18. data/lib/gmail/client/xoauth.rb +67 -68
  19. data/lib/gmail/client/xoauth2.rb +39 -39
  20. data/lib/gmail/imap_extensions.rb +156 -159
  21. data/lib/gmail/labels.rb +80 -79
  22. data/lib/gmail/mailbox.rb +178 -175
  23. data/lib/gmail/message.rb +212 -207
  24. data/lib/gmail/version.rb +3 -3
  25. data/spec/account.yml.example +1 -1
  26. data/spec/account.yml.obfus +2 -2
  27. data/spec/gmail/client/base_spec.rb +5 -5
  28. data/spec/gmail/client/plain_spec.rb +169 -169
  29. data/spec/gmail/client/xoauth2_spec.rb +186 -186
  30. data/spec/gmail/client/xoauth_spec.rb +5 -5
  31. data/spec/gmail/client_spec.rb +5 -5
  32. data/spec/gmail/imap_extensions_spec.rb +47 -47
  33. data/spec/gmail/labels_spec.rb +27 -27
  34. data/spec/gmail/mailbox_spec.rb +84 -84
  35. data/spec/gmail/message_spec.rb +181 -181
  36. data/spec/gmail_spec.rb +40 -39
  37. data/spec/recordings/gmail/_new_connects_with_client_and_give_it_context_when_block_given.yml +61 -28
  38. data/spec/recordings/gmail/_new_connects_with_gmail_service_and_return_valid_connection_object.yml +43 -28
  39. data/spec/recordings/gmail/_new_does_not_raise_error_when_couldn_t_connect_with_given_account.yml +21 -13
  40. data/spec/recordings/gmail/_new_raises_error_when_couldn_t_connect_with_given_account.yml +21 -13
  41. data/spec/recordings/gmail_client_plain/instance/delivers_inline_composed_email.yml +61 -42
  42. data/spec/recordings/gmail_client_plain/instance/does_not_log_in_when_given_gmail_account_is_invalid.yml +21 -13
  43. data/spec/recordings/gmail_client_plain/instance/does_not_raise_error_even_though_gmail_account_is_invalid.yml +21 -13
  44. data/spec/recordings/gmail_client_plain/instance/labels/checks_if_there_is_given_label_defined.yml +409 -196
  45. data/spec/recordings/gmail_client_plain/instance/labels/creates_given_label.yml +280 -151
  46. data/spec/recordings/gmail_client_plain/instance/labels/removes_existing_label.yml +271 -146
  47. data/spec/recordings/gmail_client_plain/instance/labels/returns_list_of_all_available_labels.yml +227 -113
  48. data/spec/recordings/gmail_client_plain/instance/properly_logs_in_to_valid_gmail_account.yml +61 -42
  49. data/spec/recordings/gmail_client_plain/instance/properly_logs_out_from_gmail.yml +61 -42
  50. data/spec/recordings/gmail_client_plain/instance/properly_switches_to_given_mailbox.yml +164 -109
  51. data/spec/recordings/gmail_client_plain/instance/properly_switches_to_given_mailbox_using_block_style.yml +164 -109
  52. data/spec/recordings/gmail_client_plain/instance/raises_error_when_given_gmail_account_is_invalid_and_errors_enabled.yml +21 -13
  53. data/spec/recordings/gmail_client_xo_auth2/instance/does_not_log_in_when_given_gmail_account_is_invalid.yml +24 -13
  54. data/spec/recordings/gmail_client_xo_auth2/instance/labels/checks_if_there_is_given_label_defined.yml +39 -27
  55. data/spec/recordings/gmail_client_xo_auth2/instance/labels/creates_given_label.yml +52 -39
  56. data/spec/recordings/gmail_client_xo_auth2/instance/labels/removes_existing_label.yml +52 -39
  57. data/spec/recordings/gmail_client_xo_auth2/instance/labels/returns_list_of_all_available_labels.yml +39 -27
  58. data/spec/recordings/gmail_client_xo_auth2/instance/properly_logs_in_to_valid_gmail_account.yml +26 -15
  59. data/spec/recordings/gmail_client_xo_auth2/instance/properly_logs_out_from_gmail.yml +26 -15
  60. data/spec/recordings/gmail_client_xo_auth2/instance/properly_switches_to_given_mailbox.yml +63 -40
  61. data/spec/recordings/gmail_client_xo_auth2/instance/properly_switches_to_given_mailbox_using_block_style.yml +63 -40
  62. data/spec/recordings/gmail_client_xo_auth2/instance/raises_error_when_given_gmail_account_is_invalid_and_errors_enabled.yml +24 -13
  63. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/all/localizes_into_the_appropriate_label.yml +229 -116
  64. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/and_the_mailbox_does_not_exist/returns_the_mailbox_name_as_a_string.yml +229 -110
  65. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/drafts/localizes_into_the_appropriate_label.yml +229 -116
  66. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/flagged/localizes_into_the_appropriate_label.yml +229 -116
  67. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/important/localizes_into_the_appropriate_label.yml +229 -116
  68. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/inbox/localizes_into_the_appropriate_label.yml +61 -42
  69. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/junk/localizes_into_the_appropriate_label.yml +229 -116
  70. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/sent/localizes_into_the_appropriate_label.yml +229 -116
  71. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/trash/localizes_into_the_appropriate_label.yml +229 -116
  72. data/spec/recordings/gmail_mailbox/instance/counts_all_emails.yml +595 -277
  73. data/spec/recordings/gmail_mailbox/instance/finds_messages.yml +1049 -586
  74. data/spec/recordings/gmail_mailbox/instance/waits_once.yml +191 -136
  75. data/spec/recordings/gmail_mailbox/instance/waits_repeatedly.yml +217 -141
  76. data/spec/recordings/gmail_mailbox/instance/waits_with_29-minute_re-issue.yml +188 -136
  77. data/spec/recordings/gmail_mailbox/instance/waits_with_an_unblocked_connection.yml +644 -207
  78. data/spec/recordings/gmail_mailbox/on_initialize/sets_client_and_name.yml +61 -42
  79. data/spec/recordings/gmail_mailbox/on_initialize/works_in_inbox_by_default.yml +61 -42
  80. data/spec/recordings/gmail_message/initialize/sets_prefetch_attrs.yml +1068 -578
  81. data/spec/recordings/gmail_message/initialize/sets_uid_and_mailbox.yml +1068 -580
  82. data/spec/recordings/gmail_message/instance_methods/deletes_itself.yml +1084 -637
  83. data/spec/recordings/gmail_message/instance_methods/marks_itself_read.yml +1137 -682
  84. data/spec/recordings/gmail_message/instance_methods/marks_itself_unread.yml +1153 -686
  85. data/spec/recordings/gmail_message/instance_methods/moves_from_one_tag_to_other.yml +1447 -862
  86. data/spec/recordings/gmail_message/instance_methods/removes_a_given_label.yml +1288 -776
  87. data/spec/recordings/gmail_message/instance_methods/removes_a_given_label_with_old_method.yml +1288 -776
  88. data/spec/recordings/gmail_message/instance_methods/sets_given_label.yml +1165 -690
  89. data/spec/recordings/gmail_message/instance_methods/sets_given_label_with_old_method.yml +1157 -691
  90. data/spec/spec_helper.rb +56 -53
  91. data/spec/support/imap_mock.rb +181 -181
  92. data/spec/support/obfuscation.rb +49 -52
  93. metadata +21 -90
  94. data/spec/recordings/gmail_client_plain/instance/_connection_automatically_logs_in_to_gmail_account_when_it_s_called.yml +0 -42
@@ -1,79 +1,80 @@
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
-
62
- # Localizes a specific label flag into a label name
63
-
64
- # Accepts standard mailbox flags returned by LIST's special-use extension:
65
- # :Inbox, :All, :Drafts, :Sent, :Trash, :Important, :Junk, :Flagged
66
- # and their string equivalents. Capitalization agnostic.
67
- def localize(label)
68
- type = label.to_sym.capitalize
69
- if [:All, :Drafts, :Sent, :Trash, :Important, :Junk, :Flagged].include? type
70
- @mailboxes ||= connection.list("", "*")
71
- @mailboxes.select { |box| box.attr.include? type }.collect(&:name).compact.uniq.first || label.to_s
72
- elsif type == :Inbox
73
- 'INBOX'
74
- else
75
- label
76
- end
77
- end
78
- end # Labels
79
- end # Gmail
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
+ @list << label
28
+ return if label.attr.include? :Hasnochildren
29
+ conn.list("#{label.name}/", "%").each { |l| sublabels_or_label(l) }
30
+ end
31
+
32
+ def each(*args, &block)
33
+ all.each(*args, &block)
34
+ end
35
+
36
+ # Returns +true+ when given label defined.
37
+ def exists?(label)
38
+ all.include?(label)
39
+ end
40
+ alias :exist? :exists?
41
+
42
+ # Creates given label in your account.
43
+ def create(label)
44
+ !!conn.create(Net::IMAP.encode_utf7(label))
45
+ rescue StandardError
46
+ false
47
+ end
48
+ alias :new :create
49
+ alias :add :create
50
+
51
+ # Deletes given label from your account.
52
+ def delete(label)
53
+ !!conn.delete(Net::IMAP.encode_utf7(label))
54
+ rescue StandardError
55
+ false
56
+ end
57
+ alias :remove :delete
58
+
59
+ def inspect
60
+ "#<Gmail::Labels#{'0x%04x' % (object_id << 1)}>"
61
+ end
62
+
63
+ # Localizes a specific label flag into a label name
64
+
65
+ # Accepts standard mailbox flags returned by LIST's special-use extension:
66
+ # :Inbox, :All, :Drafts, :Sent, :Trash, :Important, :Junk, :Flagged
67
+ # and their string equivalents. Capitalization agnostic.
68
+ def localize(label)
69
+ type = label.to_sym.capitalize
70
+ if [:All, :Drafts, :Sent, :Trash, :Important, :Junk, :Flagged].include? type
71
+ @mailboxes ||= connection.list("", "*")
72
+ @mailboxes.select { |box| box.attr.include? type }.collect(&:name).compact.uniq.first || label.to_s
73
+ elsif type == :Inbox
74
+ 'INBOX'
75
+ else
76
+ label
77
+ end
78
+ end
79
+ end # Labels
80
+ end # Gmail
@@ -1,175 +1,178 @@
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 :encoded_name
21
-
22
- def initialize(gmail, name = "INBOX")
23
- @name = Net::IMAP.decode_utf7(name)
24
- @encoded_name = Net::IMAP.encode_utf7(name)
25
- @gmail = gmail
26
- end
27
-
28
- def fetch_uids(*args)
29
- args << :all if args.size == 0
30
-
31
- if args.first.is_a?(Symbol)
32
- search = MAILBOX_ALIASES[args.shift].dup
33
- opts = args.first.is_a?(Hash) ? args.first : {}
34
-
35
- opts[:after] and search.concat ['SINCE', opts[:after].to_imap_date]
36
- opts[:before] and search.concat ['BEFORE', opts[:before].to_imap_date]
37
- opts[:on] and search.concat ['ON', opts[:on].to_imap_date]
38
- opts[:from] and search.concat ['FROM', opts[:from]]
39
- opts[:to] and search.concat ['TO', opts[:to]]
40
- opts[:subject] and search.concat ['SUBJECT', opts[:subject]]
41
- opts[:label] and search.concat ['LABEL', opts[:label]]
42
- opts[:attachment] and search.concat ['HAS', 'attachment']
43
- opts[:search] and search.concat ['BODY', opts[:search]]
44
- opts[:body] and search.concat ['BODY', opts[:body]]
45
- opts[:uid] and search.concat ['UID', opts[:uid]]
46
- opts[:gm] and search.concat ['X-GM-RAW', opts[:gm]]
47
- opts[:query] and search.concat opts[:query]
48
-
49
- @gmail.mailbox(name) {
50
- @gmail.conn.uid_search(search)
51
- }
52
- elsif args.first.is_a?(Hash)
53
- fetch_uids(:all, args.first)
54
- else
55
- raise ArgumentError, "Invalid search criteria"
56
- end
57
- end
58
-
59
- # Returns list of emails which meets given criteria.
60
- #
61
- # ==== Examples
62
- #
63
- # gmail.inbox.emails(:all)
64
- # gmail.inbox.emails(:unread, :from => "friend@gmail.com")
65
- # gmail.inbox.emails(:all, :after => Time.now-(20*24*3600))
66
- # gmail.mailbox("Test").emails(:read)
67
- #
68
- # gmail.mailbox("Test") do |box|
69
- # box.emails(:read)
70
- # box.emails(:unread) do |email|
71
- # ... do something with each email...
72
- # end
73
- # end
74
- def emails(*args, &block)
75
- fetch_uids(*args).collect do |uid|
76
- message = Message.new(self, uid)
77
- yield(message) if block_given?
78
- message
79
- end
80
- end
81
- alias :search :emails
82
- alias :find :emails
83
-
84
- def emails_in_batches(*args, &block)
85
- messages = Array.new
86
-
87
- uids = fetch_uids(*args)
88
- if uids && uids.any?
89
- uids.each_slice(100) do |slice|
90
- @gmail.conn.uid_fetch(slice, Message::PREFETCH_ATTRS).each do |data|
91
- message = Message.new(self, nil, data)
92
- yield(message) if block_given?
93
- messages << message
94
- end
95
- end
96
- end
97
-
98
- messages
99
- end
100
- alias :search_in_batches :emails_in_batches
101
- alias :find_in_batches :emails_in_batches
102
-
103
- # This is a convenience method that really probably shouldn't need to exist,
104
- # but it does make code more readable, if seriously all you want is the count
105
- # of messages.
106
- #
107
- # ==== Examples
108
- #
109
- # gmail.inbox.count(:all)
110
- # gmail.inbox.count(:unread, :from => "friend@gmail.com")
111
- # gmail.mailbox("Test").count(:all, :after => Time.now-(20*24*3600))
112
- def count(*args)
113
- emails(*args).size
114
- end
115
-
116
- # This permanently removes messages which are marked as deleted
117
- def expunge
118
- @gmail.mailbox(name) { @gmail.conn.expunge }
119
- end
120
-
121
- def wait(options = {}, &block)
122
- loop do
123
- wait_once(options, &block)
124
- end
125
- end
126
-
127
- def wait_once(options = {})
128
- options[:idle_timeout] ||= 29 * 60
129
-
130
- response = nil
131
- loop do
132
- complete_cond = @gmail.conn.new_cond
133
- complete_now = false
134
-
135
- @gmail.conn.idle do |resp|
136
- if resp.kind_of?(Net::IMAP::ContinuationRequest) && resp.data.text == 'idling'
137
- Thread.new do
138
- @gmail.conn.synchronize do
139
- complete_cond.wait(options[:idle_timeout]) unless complete_now
140
- @gmail.conn.idle_done
141
- end
142
- end
143
- elsif resp.kind_of?(Net::IMAP::UntaggedResponse) && resp.name == 'EXISTS'
144
- response = resp
145
-
146
- @gmail.conn.synchronize do
147
- complete_now = true
148
- complete_cond.signal
149
- end
150
- end
151
- end
152
-
153
- break if response
154
- end
155
-
156
- yield response if block_given?
157
-
158
- nil
159
- end
160
-
161
- def inspect
162
- "#<Gmail::Mailbox#{'0x%04x' % (object_id << 1)} name=#{name}>"
163
- end
164
-
165
- def to_s
166
- name
167
- end
168
-
169
- MAILBOX_ALIASES.each_key { |mailbox|
170
- define_method(mailbox) do |*args, &block|
171
- emails(mailbox, *args, &block)
172
- end
173
- }
174
- end # Message
175
- 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
+ }.freeze
18
+
19
+ attr_reader :name
20
+ attr_reader :encoded_name
21
+
22
+ def initialize(gmail, name = "INBOX")
23
+ @name = Net::IMAP.decode_utf7(name)
24
+ @encoded_name = Net::IMAP.encode_utf7(name)
25
+ @gmail = gmail
26
+ end
27
+
28
+ def fetch_uids(*args)
29
+ args << :all if args.empty?
30
+
31
+ if args.first.is_a?(Symbol)
32
+ search = MAILBOX_ALIASES[args.shift].dup
33
+ opts = args.first.is_a?(Hash) ? args.first : {}
34
+
35
+ opts[:after] and search.concat ['SINCE', Net::IMAP.format_date(opts[:after])]
36
+ opts[:before] and search.concat ['BEFORE', Net::IMAP.format_date(opts[:before])]
37
+ opts[:on] and search.concat ['ON', opts[:on].to_imap_date]
38
+ opts[:from] and search.concat ['FROM', opts[:from]]
39
+ opts[:to] and search.concat ['TO', opts[:to]]
40
+ opts[:subject] and search.concat ['SUBJECT', opts[:subject]]
41
+ opts[:label] and search.concat ['LABEL', opts[:label]]
42
+ opts[:attachment] and search.concat ['HAS', 'attachment']
43
+ opts[:search] and search.concat ['BODY', opts[:search]]
44
+ opts[:body] and search.concat ['BODY', opts[:body]]
45
+ opts[:uid] and search.concat ['UID', opts[:uid]]
46
+ opts[:gm] and search.concat ['X-GM-RAW', opts[:gm]]
47
+ opts[:message_id] and search.concat ['X-GM-MSGID', opts[:message_id].to_s]
48
+ opts[:query] and search.concat opts[:query]
49
+
50
+ @gmail.mailbox(name) do
51
+ @gmail.conn.uid_search(search)
52
+ end
53
+ elsif args.first.is_a?(Hash)
54
+ fetch_uids(:all, args.first)
55
+ else
56
+ raise ArgumentError, "Invalid search criteria"
57
+ end
58
+ end
59
+
60
+ # Returns list of emails which meets given criteria.
61
+ #
62
+ # ==== Examples
63
+ #
64
+ # gmail.inbox.emails(:all)
65
+ # gmail.inbox.emails(:unread, :from => "friend@gmail.com")
66
+ # gmail.inbox.emails(:all, :after => Time.now-(20*24*3600))
67
+ # gmail.mailbox("Test").emails(:read)
68
+ #
69
+ # gmail.mailbox("Test") do |box|
70
+ # box.emails(:read)
71
+ # box.emails(:unread) do |email|
72
+ # ... do something with each email...
73
+ # end
74
+ # end
75
+ def emails(*args, &block)
76
+ fetch_uids(*args).collect do |uid|
77
+ message = Message.new(self, uid)
78
+ yield(message) if block_given?
79
+ message
80
+ end
81
+ end
82
+ alias :search :emails
83
+ alias :find :emails
84
+
85
+ def emails_in_batches(*args, &block)
86
+ return [] unless uids.is_a?(Array) && uids.any?
87
+
88
+ uids.each_slice(100).flat_map do |slice|
89
+ find_for_slice(slice, &block)
90
+ end
91
+ end
92
+
93
+ alias :search_in_batches :emails_in_batches
94
+ alias :find_in_batches :emails_in_batches
95
+
96
+ # This is a convenience method that really probably shouldn't need to exist,
97
+ # but it does make code more readable, if seriously all you want is the count
98
+ # of messages.
99
+ #
100
+ # ==== Examples
101
+ #
102
+ # gmail.inbox.count(:all)
103
+ # gmail.inbox.count(:unread, :from => "friend@gmail.com")
104
+ # gmail.mailbox("Test").count(:all, :after => Time.now-(20*24*3600))
105
+ def count(*args)
106
+ emails(*args).size
107
+ end
108
+
109
+ # This permanently removes messages which are marked as deleted
110
+ def expunge
111
+ @gmail.mailbox(name) { @gmail.conn.expunge }
112
+ end
113
+
114
+ def wait(options = {}, &block)
115
+ loop do
116
+ wait_once(options, &block)
117
+ end
118
+ end
119
+
120
+ def wait_once(options = {})
121
+ options[:idle_timeout] ||= 29 * 60
122
+
123
+ response = nil
124
+ loop do
125
+ complete_cond = @gmail.conn.new_cond
126
+ complete_now = false
127
+
128
+ @gmail.conn.idle do |resp|
129
+ if resp.is_a?(Net::IMAP::ContinuationRequest) && resp.data.text == 'idling'
130
+ Thread.new do
131
+ @gmail.conn.synchronize do
132
+ complete_cond.wait(options[:idle_timeout]) unless complete_now
133
+ @gmail.conn.idle_done
134
+ end
135
+ end
136
+ elsif resp.is_a?(Net::IMAP::UntaggedResponse) && resp.name == 'EXISTS'
137
+ response = resp
138
+
139
+ @gmail.conn.synchronize do
140
+ complete_now = true
141
+ complete_cond.signal
142
+ end
143
+ end
144
+ end
145
+
146
+ break if response
147
+ end
148
+
149
+ yield response if block_given?
150
+
151
+ nil
152
+ end
153
+
154
+ def inspect
155
+ "#<Gmail::Mailbox#{'0x%04x' % (object_id << 1)} name=#{name}>"
156
+ end
157
+
158
+ def to_s
159
+ name
160
+ end
161
+
162
+ MAILBOX_ALIASES.each_key do |mailbox|
163
+ define_method(mailbox) do |*args, &block|
164
+ emails(mailbox, *args, &block)
165
+ end
166
+ end
167
+
168
+ private
169
+
170
+ def find_for_slice(slice, &block)
171
+ @gmail.conn.uid_fetch(slice, Message::PREFETCH_ATTRS).map do |data|
172
+ message = Message.new(self, nil, data)
173
+ yield(message) if block_given?
174
+ message
175
+ end
176
+ end
177
+ end # Message
178
+ end # Gmail