imap 0.10.0 → 0.12.0
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 +4 -4
- data/CHANGELOG +44 -26
- data/README.md +122 -16
- data/imap.gemspec +2 -2
- data/lib/Imap/Message.rb +13 -1
- data/lib/Imap/VERSION.rb +1 -1
- data/test/Imap/Message_test.rb +28 -0
- data/test/test_helper.rb +3 -0
- metadata +11 -11
- /data/test/{Imap_test.rb → imap_test.rb} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cf1e1e584ce9151c1729d359ab2ba2ac68b75c3d06a3fd15ef7512defa491c12
|
|
4
|
+
data.tar.gz: 0f07df5b9ae03d1421e5a243655fdfe56237ee2c1a9fc7597cacc3599f6db0a2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 696700ed822fd7d2af94fa66d07dd81dced92892a8437d64c53ac4ed94e27df3a9846374cb148deee2225bcc8250745e394bb151ed2029839bfbbc68a250faf0
|
|
7
|
+
data.tar.gz: 5c3786b4fa23995246292616675a0d2aa394c0827cb2114caee990de8992d971506f44fee5acc533ca17960b7504b239f0fcf52657f5414ad4ec9d2de1b8bf6f
|
data/CHANGELOG
CHANGED
|
@@ -1,7 +1,32 @@
|
|
|
1
1
|
# imap/CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 20261014
|
|
4
|
+
|
|
5
|
+
0.12.0: ~ Imap::Message: + #mailbox, and a select before every fetch and store. A sequence number is valid only inside the SELECT which issued it, so a message whose body was read after another mailbox had been selected read that other mailbox instead, and said nothing about it.
|
|
6
|
+
|
|
7
|
+
1. + Imap::Message#mailbox, taken from the client at construction, so .search and .for hand back messages which know where they came from.
|
|
8
|
+
2. + Imap::Message#select_mailbox: selects back where the client has moved on since.
|
|
9
|
+
3. ~ Imap::Message#fetch_data, #mark_as_seen: select_mailbox first. #body, #source and a flag store all reached the server by sequence number and none of them checked.
|
|
10
|
+
4. ~ Imap::Message#initialize: + mailbox, defaulting to the client's current one, so a message built by hand is guarded too.
|
|
11
|
+
5. ~ test/Imap_test.rb --> test/imap_test.rb, the file it tests being lib/imap.rb. git mv declines a destination differing only in case, so it went by way of a third name.
|
|
12
|
+
6. ~ CHANGELOG: 0.4.4's second item said test_all.rb "required './Imap_test' for a file named imap_test.rb". It required './Imap_test' for test/Imap_test.rb, which matched, and the clause is struck. What remains of that item, that test_all.rb never loaded test/Imap/VERSION_test.rb, holds.
|
|
13
|
+
7. + 4 tests: that a message knows its mailbox, that a fetch selects back, that a flag store selects back, and that neither selects where the client is already there. The two which select back fail with the guard removed.
|
|
14
|
+
8. ~ Imap::VERSION: /0.11.0/0.12.0/
|
|
15
|
+
|
|
16
|
+
|
|
3
17
|
## 20260919
|
|
4
18
|
|
|
19
|
+
0.11.0: ~ README.md: rewritten for the surface as it now stands; ~ the gemspecs: /['minitest', '~> 5.0']/'minitest'/, /['rake', '~> 13.0']/'rake'/. The README documented 0.4, five minors back, and the minitest line permitted only versions the suite no longer runs on.
|
|
20
|
+
|
|
21
|
+
1. ~ README.md: + the searching, or, not, all_of, header and UnknownSearchKey; + the message accessors, all of them off the one fetch; + .for, .decode, #mailboxes, #source, LoginFailed, and subclassing. Every example run against the suite's mock rather than written from memory.
|
|
22
|
+
2. ~ README.md: + that the gem is published under both names and that one installs one of them.
|
|
23
|
+
3. ~ the gemspecs: /['minitest', '~> 5.0']/'minitest'/. The suite has run on minitest 6 since 0.4.3, which is where it gained require 'minitest/mock'; the line permitted 5.x alone.
|
|
24
|
+
4. ~ the gemspecs: /['rake', '~> 13.0']/'rake'/, nothing being known to break outside it.
|
|
25
|
+
5. ~ CHANGELOG: - the names of other programs and gems throughout. A reader has this repository and no other, so a comparison against software they cannot see is not evidence to them.
|
|
26
|
+
6. ~ CHANGELOG: - the 6 items which recorded no change, the numbering closed up behind them.
|
|
27
|
+
7. ~ Imap::VERSION: /0.10.0/0.11.0/
|
|
28
|
+
|
|
29
|
+
|
|
5
30
|
0.10.0: ~ Imap.setup: + Imap::LoginFailed; ~ the gemspecs: /'~> 0.4'/'>= 0.4.5'/ upon net-imap. Imap.setup ignored the false which Imap#login returns when the server rejects the password, and handed back a client which was connected but not logged in.
|
|
6
31
|
|
|
7
32
|
1. + Imap::LoginFailed
|
|
@@ -17,14 +42,13 @@
|
|
|
17
42
|
|
|
18
43
|
1. + Imap::Search#all_of: all_of: [{text: 'invoice'}, {text: 'overdue'}] for TEXT invoice TEXT overdue. IMAP has no AND keyword, keys simply sitting side by side, but a hash cannot hold the same key twice, so two terms upon the one key could not be asked for at all: {text: 'a', text: 'b'} keeps b and drops a without a word. Each element is a criteria hash of its own, so or and not nest inside it.
|
|
19
44
|
2. ~ Imap::Search#to_imap_search_keys: + ALL_OF and + ALL, read before the operator tests beside OR and NOT, being none of the three. ALL sat in ALL_SEARCH_KEYS and in neither operator list, so {all: true} raised UnknownSearchKey. It is the key meaning every message, which is precisely what a search with no criteria has to send, so the hash could not express the simplest search there is.
|
|
20
|
-
3.
|
|
21
|
-
4.
|
|
22
|
-
5. ~ Imap::VERSION: /0.8.0/0.9.0/
|
|
45
|
+
3. + 5 tests: that all_of ands a list, that it is how the one key carries several terms where a hash cannot, that or and not nest within it, that it sits beside the plain keys, and that {all: true} answers ['ALL'].
|
|
46
|
+
4. ~ Imap::VERSION: /0.8.0/0.9.0/
|
|
23
47
|
|
|
24
48
|
|
|
25
49
|
0.8.0: + Imap::Message.for, the fetch without the search, and a class a program can subclass.
|
|
26
50
|
|
|
27
|
-
1. + Imap::Message.for(imap_client, message_ids): the batched fetch on its own, .search now being the search and then this. A program which builds its own search keys had no way in
|
|
51
|
+
1. + Imap::Message.for(imap_client, message_ids): the batched fetch on its own, .search now being the search and then this. A program which builds its own search keys had no way in, .search coupling the two, and had to reimplement the fetch beside it.
|
|
28
52
|
2. ~ Imap::Message.for: builds with new rather than Imap::Message.new, so that a subclass is handed back its own kind. A program which wants a message of its own, with a mailbox or a decoded body upon it, subclasses rather than starts again.
|
|
29
53
|
3. + 4 tests: that .for builds for ids searched elsewhere, that it fetches once, that it is empty where nothing was found, and that a subclass gets itself back.
|
|
30
54
|
4. ~ Imap::VERSION: /0.7.0/0.8.0/
|
|
@@ -58,29 +82,24 @@
|
|
|
58
82
|
1. + Imap::Search#any_of: or: [{from: 'a@x'}, {to: 'b@y'}]. OR takes two keys and no more, so three or more nest, OR OR a b c, as the server reads them. Each side is a criteria hash of its own and may nest further.
|
|
59
83
|
2. + Imap::Search#none_of: not: {subject: 'Payday'}. NOT takes one key, so several are negated apiece, NOT a NOT b.
|
|
60
84
|
3. ~ Imap::Search#to_imap_search_keys: OR and NOT read before the operator tests, being neither general nor boolean.
|
|
61
|
-
4.
|
|
62
|
-
5.
|
|
63
|
-
6. ~ Imap::VERSION: /0.4.9/0.5.0/
|
|
85
|
+
4. + tests for each, and for OR mixed with the plain keys.
|
|
86
|
+
5. ~ Imap::VERSION: /0.4.9/0.5.0/
|
|
64
87
|
|
|
65
88
|
|
|
66
89
|
0.4.9: + imap.rb.gemspec, so that the gem resolves under both names.
|
|
67
90
|
|
|
68
91
|
1. + imap.rb.gemspec: the same code published as imap.rb, which is how gems are named here: 21 carry the suffix and imap is one of the few which do not. rubygems has no notion of an alias, so the two are separate gems which must be kept at the same version by hand.
|
|
69
|
-
2.
|
|
70
|
-
3.
|
|
71
|
-
4. gem-publish builds and pushes each in turn, gem build and gem push taking one file apiece and gem build *.gemspec building only whichever the shell lists first.
|
|
72
|
-
5. ~ Gemfile: gemspec name: 'imap', bundler refusing to choose between two gemspecs and stopping with "There are multiple gemspecs at ...". Every pair here which uses gemspec names one: webdav, measurand and eodhd each name the elder of their two, and namo, which carried both from its first release, names the bare one. imap is the elder here.
|
|
73
|
-
6. ~ Imap::VERSION: /0.4.8/0.4.9/
|
|
92
|
+
2. ~ Gemfile: gemspec name: 'imap', bundler refusing to choose between two gemspecs and stopping with "There are multiple gemspecs at ...". imap is the elder of the two names.
|
|
93
|
+
3. ~ Imap::VERSION: /0.4.8/0.4.9/
|
|
74
94
|
|
|
75
95
|
|
|
76
96
|
0.4.8: Rewrite the CHANGELOG in the standard form.
|
|
77
97
|
|
|
78
98
|
1. ~ CHANGELOG: ## before each date rather than ## [version] - date, the version and its summary beneath, a blank line between that and the first item, and the items numbered rather than dashed. No item is reworded, dropped or reordered.
|
|
79
99
|
2. ~ CHANGELOG: the dates in basic form, 20260910 for 2026-09-10, as every other gem here writes them.
|
|
80
|
-
3. ~ CHANGELOG: + the title, # imap/CHANGELOG
|
|
100
|
+
3. ~ CHANGELOG: + the title, # imap/CHANGELOG.
|
|
81
101
|
4. ~ CHANGELOG: the five releases of 20260917 under one date heading, which is how the form carries more than one in a day.
|
|
82
|
-
5.
|
|
83
|
-
6. ~ Imap::VERSION: /0.4.7/0.4.8/
|
|
102
|
+
5. ~ Imap::VERSION: /0.4.7/0.4.8/
|
|
84
103
|
|
|
85
104
|
0.4.7: ~ Imap::Search: the chaining interface, which the header has documented and which has never run
|
|
86
105
|
|
|
@@ -103,24 +122,24 @@
|
|
|
103
122
|
|
|
104
123
|
0.4.5: + Gemfile; ~ imap.gemspec: dependencies=, - spec.date, + metadata
|
|
105
124
|
|
|
106
|
-
1. + Gemfile: source and gemspec,
|
|
107
|
-
2. ~ imap.gemspec: + 'Gemfile' to spec.files
|
|
108
|
-
3. ~ imap.gemspec: dependencies= and development_dependencies= through the Gem::Specification extension
|
|
109
|
-
4. ~ imap.gemspec: - spec.date = '2025-12-20', nine months and two versions stale
|
|
110
|
-
5. ~ imap.gemspec: + metadata, the four uris
|
|
125
|
+
1. + Gemfile: source and gemspec, so that the dependencies are declared once in the gemspec rather than restated here to drift.
|
|
126
|
+
2. ~ imap.gemspec: + 'Gemfile' to spec.files.
|
|
127
|
+
3. ~ imap.gemspec: dependencies= and development_dependencies= through the Gem::Specification extension. The constraints survive: the splat takes a bare string or a [name, constraint] pair alike.
|
|
128
|
+
4. ~ imap.gemspec: - spec.date = '2025-12-20', nine months and two versions stale. RubyGems answers 1980-01-02 in its place, the reproducible-build epoch.
|
|
129
|
+
5. ~ imap.gemspec: + metadata, the four uris, so that the RubyGems page links to the issues, the changelog, the source and the README.
|
|
111
130
|
6. ~ Imap::VERSION: /0.4.4/0.4.5/
|
|
112
131
|
|
|
113
132
|
0.4.4: + Rakefile, - test/test_all.rb
|
|
114
133
|
|
|
115
134
|
1. + Rakefile: Rake::TestTask over FileList['test/**/*_test.rb'], with libs for lib and test, and warning = true. The minimal form nine of the gems here use rather than the fuller one five use: the :spec alias and the :version task belong to the minority, and a :version task reading VERSION.rb would not have caught 0.4.1, where require 'imap' left Imap::VERSION undefined while VERSION.rb sat there defining it. rake has been a development dependency since 0.4.0 with nothing here to run.
|
|
116
|
-
2. - test/test_all.rb: a second entry point which the Rakefile's FileList makes redundant, and which had drifted from the files on disk
|
|
135
|
+
2. - test/test_all.rb: a second entry point which the Rakefile's FileList makes redundant, and which had drifted from the files on disk: it never loaded test/Imap/VERSION_test.rb, added in 0.4.2, so it ran 42 tests where there are 44. A glob cannot drift.
|
|
117
136
|
3. ~ test/Imap/Message_test.rb: must_match(/Mock body/) for must_match /Mock body/, the ambiguous / which warning = true reports and which is the only warning the suite raises.
|
|
118
|
-
4. ~ imap.gemspec: + 'Rakefile' to spec.files
|
|
137
|
+
4. ~ imap.gemspec: + 'Rakefile' to spec.files.
|
|
119
138
|
5. ~ Imap::VERSION: /0.4.3/0.4.4/
|
|
120
139
|
|
|
121
140
|
0.4.3: ~ test/test_helper.rb: + require 'minitest/mock', which minitest 6 no longer brings
|
|
122
141
|
|
|
123
|
-
1. ~ test/test_helper.rb: + `require 'minitest/mock'`. 35 of the 42 tests call stub and not one of them could: the suite answered "42 runs, 7 assertions, 0 failures, 35 errors". minitest 5's autorun required minitest/mock and shipped it, so the suite ran until minitest 6 dropped both the require and the file
|
|
142
|
+
1. ~ test/test_helper.rb: + `require 'minitest/mock'`. 35 of the 42 tests call stub and not one of them could: the suite answered "42 runs, 7 assertions, 0 failures, 35 errors". minitest 5's autorun required minitest/mock and shipped it, so the suite ran until minitest 6 dropped both the require and the file.
|
|
124
143
|
2. ~ imap.gemspec: + minitest-mock as a development dependency, which minitest ~> 5.0 carried and minitest 6 does not.
|
|
125
144
|
3. ~ Imap::VERSION: /0.4.2/0.4.3/
|
|
126
145
|
|
|
@@ -130,9 +149,8 @@
|
|
|
130
149
|
0.4.2: ~ lib/imap.rb: + require_relative './Imap/VERSION'; + test/Imap/VERSION_test.rb
|
|
131
150
|
|
|
132
151
|
1. ~ lib/imap.rb: + `require_relative './Imap/VERSION'`, last among the requires. The file had been present and unreached, so `require 'imap'` left Imap::VERSION undefined and the gemspec was the only thing loading it.
|
|
133
|
-
2. + test/Imap/VERSION_test.rb: that Imap::VERSION is a string and that it is three numbers separated by dots.
|
|
134
|
-
3.
|
|
135
|
-
4. ~ Imap::VERSION: /0.4.1/0.4.2/
|
|
152
|
+
2. + test/Imap/VERSION_test.rb: that Imap::VERSION is a string and that it is three numbers separated by dots. Less the example comparing against the changelog, which was unreachable here while the file was misspelt.
|
|
153
|
+
3. ~ Imap::VERSION: /0.4.1/0.4.2/
|
|
136
154
|
|
|
137
155
|
|
|
138
156
|
## 20260822
|
data/README.md
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# imap
|
|
2
2
|
|
|
3
|
-
A Ruby wrapper around Net::IMAP with
|
|
3
|
+
A Ruby wrapper around Net::IMAP with a hash-based search interface and messages which answer questions rather than handing back structs.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
7
|
+
- Hash-based search criteria, including OR, NOT and repeated keys
|
|
8
|
+
- Message accessors for dates, flags, size, addresses and attachments
|
|
9
|
+
- RFC 2047 decoding, which Net::IMAP does not do
|
|
10
|
+
- Attachment filenames read out of the BODYSTRUCTURE, which IMAP cannot search upon
|
|
11
|
+
- One fetch for a whole set of messages rather than one per attribute
|
|
12
|
+
- BODY.PEEK throughout, so that reading a message does not mark it read
|
|
9
13
|
- No Rails dependency - works anywhere
|
|
10
14
|
- Zero external dependencies beyond net-imap
|
|
11
15
|
|
|
@@ -29,38 +33,140 @@ Or install it yourself as:
|
|
|
29
33
|
$ gem install imap
|
|
30
34
|
```
|
|
31
35
|
|
|
36
|
+
The gem is published under both `imap` and `imap.rb`. They are the same code; install one.
|
|
37
|
+
|
|
32
38
|
## Usage
|
|
39
|
+
|
|
40
|
+
### Connecting
|
|
41
|
+
|
|
33
42
|
```ruby
|
|
34
43
|
require 'imap'
|
|
35
44
|
|
|
36
|
-
# Connect and authenticate
|
|
37
45
|
imap_client = Imap.setup(
|
|
38
|
-
server: 'imap.
|
|
39
|
-
username: '
|
|
46
|
+
server: 'imap.example.com',
|
|
47
|
+
username: 'someone@example.com',
|
|
40
48
|
password: 'bigsecret',
|
|
41
49
|
mailbox: 'INBOX' # optional/default
|
|
42
50
|
)
|
|
51
|
+
```
|
|
43
52
|
|
|
53
|
+
`Imap.setup` raises `Imap::LoginFailed` where the server rejects the password, or where only one of the username and the password is given. `Imap#login` answers true or false for a caller which would rather handle it itself.
|
|
54
|
+
|
|
55
|
+
`Imap#mailboxes` lists every mailbox which can be selected:
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
imap_client.mailboxes # => ['INBOX', 'INBOX.Archive', ...]
|
|
59
|
+
imap_client.mailbox = 'INBOX.Archive'
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Searching
|
|
63
|
+
|
|
64
|
+
Criteria are a hash, keyed by the IMAP search keys in lower case:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
44
67
|
messages = imap_client.search(
|
|
45
68
|
from: 'noreply@example.com',
|
|
46
69
|
subject: 'Payday Loans',
|
|
47
|
-
since: '18-
|
|
70
|
+
since: '18-Dec-2025',
|
|
48
71
|
seen: false
|
|
49
72
|
)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`or:` takes a list of criteria hashes. IMAP's OR takes two keys, so three or more nest for you:
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
imap_client.search(or: [{from: 'a@x'}, {to: 'b@y'}, {cc: 'c@z'}])
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`not:` negates each key it carries:
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
imap_client.search(not: {subject: 'Payday'})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`all_of:` is how one key carries several terms, a hash holding it only once:
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
imap_client.search(all_of: [{text: 'invoice'}, {text: 'overdue'}])
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`header:` takes the field and the value:
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
imap_client.search(header: ['List-Id', 'ruby-talk'])
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
An unrecognised key raises `Imap::Search::UnknownSearchKey` rather than widening the search silently.
|
|
50
100
|
|
|
51
|
-
|
|
101
|
+
Where the search keys are built elsewhere, `Imap::Message.for` does the fetch on its own:
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
Imap::Message.for(imap_client, imap_client.imap.search(['ALL']))
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Messages
|
|
108
|
+
|
|
109
|
+
A search fetches UID, FLAGS, INTERNALDATE, RFC822.SIZE, ENVELOPE and BODYSTRUCTURE for the whole set in one command, so none of these costs a round trip:
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
52
112
|
messages.each do |message|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
113
|
+
message.uid
|
|
114
|
+
message.flags # => [:Seen]
|
|
115
|
+
message.seen?
|
|
116
|
+
message.size # bytes
|
|
117
|
+
message.received_at # Time, from INTERNALDATE
|
|
118
|
+
message.sent_at # Time, from the Date header
|
|
119
|
+
message.subject # encoded words decoded
|
|
120
|
+
message.from # => 'Café <billing@example.com>'
|
|
121
|
+
message.from_address # => 'billing@example.com'
|
|
122
|
+
message.from_domain # => 'example.com'
|
|
123
|
+
message.to # => ['user@example.com']
|
|
124
|
+
message.cc
|
|
125
|
+
message.attachment?
|
|
126
|
+
message.attachment_filenames # => ['résumé.pdf']
|
|
58
127
|
end
|
|
128
|
+
```
|
|
59
129
|
|
|
60
|
-
|
|
61
|
-
|
|
130
|
+
The body is fetched only when it is asked for:
|
|
131
|
+
|
|
132
|
+
```ruby
|
|
133
|
+
message.body # BODY[TEXT], undecoded
|
|
134
|
+
message.source # the whole message, headers and all
|
|
135
|
+
message.urls # the URLs found in the body
|
|
136
|
+
message.mark_as_read
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`#body` is the raw text part. Choosing a part out of a multipart message and undoing its transfer encoding is MIME work rather than IMAP work, so hand `#source` to the `mail` gem where that is wanted:
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
mail = Mail.read_from_string(message.source)
|
|
143
|
+
```
|
|
62
144
|
|
|
63
|
-
|
|
145
|
+
### Decoding
|
|
146
|
+
|
|
147
|
+
`Imap::Message.decode` reads RFC 2047 encoded words, which headers carry wherever a subject or a name is not ASCII:
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
Imap::Message.decode('=?UTF-8?B?UsOpc3Vtw6k=?=') # => 'Résumé'
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Extending
|
|
154
|
+
|
|
155
|
+
`Imap::Message.search` and `.for` build with `new`, so a subclass is handed back its own kind:
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
class Message < Imap::Message
|
|
159
|
+
def body
|
|
160
|
+
@body ||= Mail.read_from_string(source).text_part.decoded
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
Message.for(imap_client, message_ids).first.class # => Message
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Finishing
|
|
168
|
+
|
|
169
|
+
```ruby
|
|
64
170
|
imap_client.bye
|
|
65
171
|
```
|
|
66
172
|
|
data/imap.gemspec
CHANGED
data/lib/Imap/Message.rb
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
# 10. Imap::Message.new(message_id, imap_client).received_at
|
|
15
15
|
# 11. Imap::Message.new(message_id, imap_client).attachment_filenames
|
|
16
16
|
# 12. Imap::Message.new(message_id, imap_client).source
|
|
17
|
+
# 13. Imap::Message.new(message_id, imap_client).mailbox
|
|
17
18
|
|
|
18
19
|
require 'time'
|
|
19
20
|
|
|
@@ -71,6 +72,7 @@ class Imap
|
|
|
71
72
|
|
|
72
73
|
attr_accessor :imap_client
|
|
73
74
|
attr_accessor :message_id
|
|
75
|
+
attr_reader :mailbox
|
|
74
76
|
|
|
75
77
|
def uid
|
|
76
78
|
data.uid
|
|
@@ -148,16 +150,25 @@ class Imap
|
|
|
148
150
|
end
|
|
149
151
|
|
|
150
152
|
def mark_as_seen
|
|
153
|
+
select_mailbox
|
|
151
154
|
imap_client.imap.store(@message_id, '+FLAGS', [:Seen])
|
|
152
155
|
end
|
|
153
156
|
alias_method :mark_as_read, :mark_as_seen
|
|
154
157
|
|
|
155
158
|
private
|
|
156
159
|
|
|
157
|
-
def initialize(message_id = nil, imap_client = nil, data = nil)
|
|
160
|
+
def initialize(message_id = nil, imap_client = nil, data = nil, mailbox = nil)
|
|
158
161
|
@message_id = message_id
|
|
159
162
|
@imap_client = imap_client
|
|
160
163
|
@data = data
|
|
164
|
+
@mailbox = mailbox || (imap_client && imap_client.mailbox)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# A sequence number means nothing outside the mailbox which issued it, so
|
|
168
|
+
# anything fetched after another mailbox was selected has to select back.
|
|
169
|
+
def select_mailbox
|
|
170
|
+
return unless mailbox && imap_client && imap_client.mailbox != mailbox
|
|
171
|
+
imap_client.mailbox = mailbox
|
|
161
172
|
end
|
|
162
173
|
|
|
163
174
|
# The Net::IMAP::FetchData itself rather than its attr hash, since it reads
|
|
@@ -218,6 +229,7 @@ class Imap
|
|
|
218
229
|
end
|
|
219
230
|
|
|
220
231
|
def fetch_data(*fetch_attributes)
|
|
232
|
+
select_mailbox
|
|
221
233
|
@fetch_data = imap_client.imap.fetch(message_id, [*fetch_attributes])
|
|
222
234
|
end
|
|
223
235
|
end
|
data/lib/Imap/VERSION.rb
CHANGED
data/test/Imap/Message_test.rb
CHANGED
|
@@ -237,6 +237,34 @@ describe Imap::Message do
|
|
|
237
237
|
end
|
|
238
238
|
end
|
|
239
239
|
|
|
240
|
+
describe '#mailbox' do
|
|
241
|
+
it 'knows the mailbox it was built in' do
|
|
242
|
+
_(Imap::Message.for(client, [1]).first.mailbox).must_equal 'INBOX'
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
it 'selects back before fetching, where the client has moved on since' do
|
|
246
|
+
message = Imap::Message.for(client, [1]).first
|
|
247
|
+
client.mailbox = 'Archive/2026'
|
|
248
|
+
message.body
|
|
249
|
+
_(client.mailbox).must_equal 'INBOX'
|
|
250
|
+
_(client.imap.selects.last).must_equal 'INBOX'
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
it 'selects back before storing a flag' do
|
|
254
|
+
message = Imap::Message.for(client, [1]).first
|
|
255
|
+
client.mailbox = 'Archive/2026'
|
|
256
|
+
message.mark_as_seen
|
|
257
|
+
_(client.imap.selects.last).must_equal 'INBOX'
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
it 'does not select where the client is already there' do
|
|
261
|
+
message = Imap::Message.for(client, [1]).first
|
|
262
|
+
before = (client.imap.selects || []).length
|
|
263
|
+
message.body
|
|
264
|
+
_((client.imap.selects || []).length).must_equal before
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
240
268
|
describe '#urls' do
|
|
241
269
|
it 'extracts http URLs from body' do
|
|
242
270
|
imap_message.stub(:body, 'Check out http://example.com and https://test.com') do
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: imap
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- thoran
|
|
@@ -27,16 +27,16 @@ dependencies:
|
|
|
27
27
|
name: minitest
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
|
-
- - "
|
|
30
|
+
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '
|
|
32
|
+
version: '0'
|
|
33
33
|
type: :development
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
|
-
- - "
|
|
37
|
+
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '
|
|
39
|
+
version: '0'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: minitest-mock
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -55,16 +55,16 @@ dependencies:
|
|
|
55
55
|
name: rake
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
|
-
- - "
|
|
58
|
+
- - ">="
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '
|
|
60
|
+
version: '0'
|
|
61
61
|
type: :development
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
|
-
- - "
|
|
65
|
+
- - ">="
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '
|
|
67
|
+
version: '0'
|
|
68
68
|
description: A Ruby wrapper around Net::IMAP with an elegant DSL for searching, reading,
|
|
69
69
|
and managing email.
|
|
70
70
|
email: code@thoran.com
|
|
@@ -85,7 +85,7 @@ files:
|
|
|
85
85
|
- test/Imap/Message_test.rb
|
|
86
86
|
- test/Imap/Search_test.rb
|
|
87
87
|
- test/Imap/VERSION_test.rb
|
|
88
|
-
- test/
|
|
88
|
+
- test/imap_test.rb
|
|
89
89
|
- test/test_helper.rb
|
|
90
90
|
homepage: https://github.com/thoran/imap
|
|
91
91
|
licenses:
|
|
@@ -109,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
110
|
version: '0'
|
|
111
111
|
requirements: []
|
|
112
|
-
rubygems_version: 4.0.
|
|
112
|
+
rubygems_version: 4.0.22
|
|
113
113
|
specification_version: 4
|
|
114
114
|
summary: IMAP for Ruby made easy.
|
|
115
115
|
test_files: []
|
|
File without changes
|