imapcli 2.0.0 → 2.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 251ac4bd70a607d8891eb4035bf69ba10017995436d2af15138e8647ac042530
4
- data.tar.gz: 57072fa35d2eb0e53a85e27464638844fedb83807c8e4f98ad59c502cbd07be3
3
+ metadata.gz: ee39530d31868e3c8f3c90a1ad9957552ee91f51e7ed05c57fb9a3724a297ef7
4
+ data.tar.gz: 3d6484ebcce956fc9425447786daaf6d7bce486e0b787d5be9e817cba4517dab
5
5
  SHA512:
6
- metadata.gz: 5cfc0a536bb6ae0b71e4262cf9ae03d7213c093c10b6c47597a02ee2bac101357423ec0df5a66038d2e6673fcf03973cef1d564015dc56dd3c665bcb31521da4
7
- data.tar.gz: b2f986bd44dfe6e3374b272bf50fd238ebff681665a32ddad07c2930e8842f5132c27df861dd31f13486b167179649b94ea2e788679721f952662a6620170102
6
+ metadata.gz: 0e6fab7ebd5c9e58b432323e7ee974c8880785ccf969139966ac2bc9bc7f1034db51678e95c35d7fb53a7df434790340c03aedd40bf2d1bfb6308b2d6b6d4614
7
+ data.tar.gz: 44801f7d7aa3311683b7e2370b39b9738d4de629e080f32f034b6ae6ac65bda0882dfc15d3731af3929421757a350217f8e4a74ec2127f35d543215cf8aef84e
data/CHANGELOG.md CHANGED
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Version 2.0.1 (2025-03-11)][v2.0.1]
9
+
10
+ ### Fixed
11
+
12
+ - Do not crash with empty IMAP mailboxes.
13
+
8
14
  ## [Version 2.0.0 (2025-03-10)][v2.0.0]
9
15
 
10
16
  ### Added
@@ -66,6 +72,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66
72
  - Fix: Docker image.
67
73
  - Fix: Options error message.
68
74
 
75
+ [v2.0.1]: https://github.com/bovender/imapcli/releases/tag/v2.0.1
69
76
  [v2.0.0]: https://github.com/bovender/imapcli/releases/tag/v2.0.0
70
77
  [v1.0.7]: https://github.com/bovender/imapcli/releases/tag/v1.0.7
71
78
  [v1.0.5]: https://github.com/bovender/imapcli/releases/tag/v1.0.5
data/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- FROM ruby:alpine
1
+ FROM ruby:3.4-alpine
2
2
  LABEL maintainer="bovender@bovender.de"
3
3
  LABEL description="Command-line tool to query IMAP servers, collect stats etc."
4
4
 
@@ -7,11 +7,12 @@ ADD . /imapcli
7
7
  RUN apk add --no-cache git build-base && \
8
8
  gem update --system && \
9
9
  cd imapcli && \
10
- # bundle config set --local without development test && \
10
+ bundle config set --local without development test && \
11
+ bundle config set --local deployment true && \
11
12
  bundle install && \
12
13
  apk del build-base && \
13
14
  rm -rf /var/cache/apk/*
14
15
 
15
16
  WORKDIR /imapcli
16
- ENTRYPOINT ["exe/imapcli"]
17
+ ENTRYPOINT ["bundle", "exec", "exe/imapcli"]
17
18
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- imapcli (1.0.7)
4
+ imapcli (2.0.0)
5
5
  activesupport (~> 8.0)
6
6
  csv
7
7
  descriptive_statistics (~> 2.5)
@@ -237,4 +237,4 @@ DEPENDENCIES
237
237
  simplecov
238
238
 
239
239
  BUNDLED WITH
240
- 2.5.22
240
+ 2.6.5
data/README.md CHANGED
@@ -15,7 +15,6 @@ IMAP mailbox sizes.
15
15
  - [Terminology](#terminology)
16
16
  - [Usage](#usage)
17
17
  - [Alternative resources](#alternative-resources)
18
- - [State of the project](#state-of-the-project)
19
18
  - [Credits](#credits)
20
19
  - [License](#license)
21
20
 
@@ -292,20 +291,13 @@ following:
292
291
 
293
292
  Pull down e-mails from an IMAP server to your local disk
294
293
 
295
- ## State of the project
296
-
297
- I have not been able to work on this project for quite some time. It still
298
- serves me well when I occasionally need it. Pull requests are of course welcome.
299
-
300
- This project is [semantically versioned](https://semver.org).
301
-
302
294
  ### To do
303
295
 
304
- [x] More human-friendly number formatting (e.g., MiB/GiB as appropriate)
305
- [ ] Output to file
306
- [ ] Deal with server-specific mailbox separator characters (e.g. '.' vs. '/')
307
- [ ] Man page
308
- [ ] More commands?
296
+ - [x] More human-friendly number formatting (e.g., MiB/GiB as appropriate)
297
+ - [ ] Output to file
298
+ - [ ] Deal with server-specific mailbox separator characters (e.g. '.' vs. '/')
299
+ - [ ] Man page
300
+ - [ ] More commands?
309
301
 
310
302
  ## Credits
311
303
 
data/imapcli.gemspec CHANGED
@@ -22,10 +22,10 @@ Gem::Specification.new do |s|
22
22
  s.bindir = 'exe'
23
23
  s.executables = ['imapcli']
24
24
 
25
+ s.add_dependency('activesupport', '~> 8.0')
25
26
  s.add_dependency('csv')
26
27
  s.add_dependency('descriptive_statistics', '~> 2.5')
27
28
  s.add_dependency('dotenv', '~> 3.1')
28
- s.add_dependency('activesupport', '~> 8.0')
29
29
  s.add_dependency('gli', '~> 2.22')
30
30
  s.add_dependency('net-imap')
31
31
  s.add_dependency('tty-progressbar', '~> 0.18')
@@ -138,28 +138,30 @@ module Imapcli
138
138
  end
139
139
  end
140
140
 
141
- def sorted_list(list, options = {}) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
142
- sorted = case options[:sort]&.to_sym
143
- when :count
144
- list.sort_by { |mailbox| mailbox.stats.count }
145
- when :total_size
146
- list.sort_by { |mailbox| mailbox.stats.total_size }
147
- when :median_size
148
- list.sort_by { |mailbox| mailbox.stats.median_size }
149
- when :min_size
150
- list.sort_by { |mailbox| mailbox.stats.min_size }
151
- when :q1_size
152
- list.sort_by { |mailbox| mailbox.stats.q1 }
153
- when :q3_size
154
- list.sort_by { |mailbox| mailbox.stats.q3 }
155
- when :max_size
156
- list.sort_by { |mailbox| mailbox.stats.max_size }
157
- when nil
158
- list
159
- else
160
- raise "invalid sort option: #{options[:sort]}"
161
- end
162
- options[:reverse] ? sorted.reverse : sorted
141
+ def sorted_list(list, options)
142
+ return list if options.nil? || options[:sort].nil?
143
+ sort_property = options[:sort]&.to_sym
144
+ if %i[count total_size min_size q1 median_size q3 max_size].include? sort_property
145
+ sorted = sort_mailboxes(list, sort_property, options[:reverse])
146
+ else
147
+ raise "invalid sort option: #{options[:sort]}"
148
+ end
149
+ end
150
+
151
+ private
152
+
153
+ # Sorts a list of mailboxes according to a given property
154
+ # such as total count of e-mails, total size of all e-mails etc.
155
+ # Some mailboxes may not have this property, e.g., an empty
156
+ # mailbox will have `nil` values for median_size etc.
157
+ # We always sort mailboxes with `nil` values to the bottom.
158
+ def sort_mailboxes(mailboxes, property, reverse)
159
+ # Devide ary into two parts, one part where the property has a value
160
+ # and the other where the property is nil.
161
+ partitions = mailboxes.partition { |mailbox| mailbox.stats.send(property) }
162
+ sorted = partitions[0].sort_by { |mailbox| mailbox.stats.send(property) }
163
+ sorted = sorted.reverse if reverse
164
+ sorted + partitions[1]
163
165
  end
164
166
 
165
167
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Imapcli
4
- VERSION = '2.0.0'
4
+ VERSION = '2.0.1'
5
5
  end
@@ -22,9 +22,11 @@ RSpec.describe Imapcli::Command do
22
22
  Net::IMAP::MailboxList.new(nil, '/', 'Inbox'),
23
23
  Net::IMAP::MailboxList.new(nil, '/', 'Inbox/Foo'),
24
24
  Net::IMAP::MailboxList.new(nil, '/', 'Inbox/Foo/Sub'),
25
+ Net::IMAP::MailboxList.new(nil, '/', 'Inbox/Many/Messages'),
25
26
  Net::IMAP::MailboxList.new(nil, '/', 'Inbox/Bar'),
26
- Net::IMAP::MailboxList.new(nil, '/', 'Inbox/Bar/Sub'),
27
- Net::IMAP::MailboxList.new(nil, '/', 'Inbox/Bar/Sub/Subsub'),
27
+ Net::IMAP::MailboxList.new(nil, '/', 'Inbox/Largest/Messages'),
28
+ Net::IMAP::MailboxList.new(nil, '/', 'Inbox/Smallest/Messages'),
29
+ Net::IMAP::MailboxList.new(nil, '/', 'Inbox/Empty'),
28
30
  ])
29
31
  end
30
32
 
@@ -57,17 +59,19 @@ RSpec.describe Imapcli::Command do
57
59
  allow(client).to receive(:message_sizes) do |mailbox|
58
60
  case mailbox
59
61
  when 'Inbox'
60
- (1..4).map { |i| 1024 * i }
62
+ [200, 300, 400, 500]
61
63
  when 'Inbox/Foo'
62
- (3..10).map { |i| 1024 * i }
63
- when 'Inbox/Foo/Sub'
64
- Array.new(10, 1024)
64
+ [355, 360, 380, 200]
65
+ when 'Inbox/Many/Messages'
66
+ Array.new(10, 100)
65
67
  when 'Inbox/Bar'
66
- (1..2).map { |i| 1024 * i }
67
- when 'Inbox/Bar/Sub'
68
- [1, 1024 * 20]
69
- when 'Inbox/Bar/Sub/Subsub' # rubocop:disable Lint/DuplicateBranch
70
- (1..4).map { |i| 1024 * i }
68
+ [2_000, 3_000, 4_000]
69
+ when 'Inbox/Largest/Messages'
70
+ [2_500_000, 100_000, 200_000]
71
+ when 'Inbox/Smallest/Messages'
72
+ [10, 20, 30]
73
+ when 'Inbox/Empty'
74
+ []
71
75
  else
72
76
  [1024, 2048, 4096, 8192]
73
77
  end
@@ -77,8 +81,8 @@ RSpec.describe Imapcli::Command do
77
81
  it 'for all folders' do
78
82
  output = command.stats
79
83
  expect(output).to be_a Array
80
- expect(output.length).to eq 7
81
- expect(output[0][0]).to eq 'Inbox'
84
+ expect(output.length).to eq 9
85
+ expect(output[0][0]).to eq 'Inbox' # sorted before 'Inbox'
82
86
  expect(output[0][1]).to eq 4
83
87
  end
84
88
 
@@ -87,7 +91,15 @@ RSpec.describe Imapcli::Command do
87
91
  expect(output).to be_a Array
88
92
  expect(output.length).to eq 1
89
93
  expect(output[0][0]).to eq 'Inbox/Foo'
90
- expect(output[0][1]).to eq 8 # depends on message_sizes stub (see above)
94
+ expect(output[0][1]).to eq 4 # depends on message_sizes stub (see above)
95
+ end
96
+
97
+ it 'for an empty folder' do
98
+ output = command.stats('Inbox/Empty')
99
+ expect(output).to be_a Array
100
+ expect(output.length).to eq 1
101
+ expect(output[0][0]).to eq 'Inbox/Empty'
102
+ expect(output[0][1]).to eq 0
91
103
  end
92
104
 
93
105
  it 'for a given folder and subfolders' do
@@ -100,26 +112,26 @@ RSpec.describe Imapcli::Command do
100
112
  it 'sorts by number of messages' do
101
113
  output = command.stats('Inbox', depth: -1, sort: :count, reverse: true)
102
114
  expect(output).to be_a Array
103
- expect(output[0][0]).to eq 'Inbox/Foo/Sub'
115
+ expect(output[0][0]).to eq 'Inbox/Many/Messages'
104
116
  end
105
117
 
106
118
  it 'sorts by total message size' do
107
- output = command.stats('Inbox', depth: -1, sort: :total_size, reverse: true)
119
+ output = command.stats('Inbox', depth: -1, sort: :total_size, reverse: false)
108
120
  expect(output).to be_a Array
109
- expect(output[0][0]).to eq 'Inbox/Foo'
121
+ expect(output[-2][0]).to eq 'Inbox/Largest/Messages'
110
122
  end
111
123
 
112
- it 'sorts by largest message' do
124
+ it 'reverse-sorts largest message first' do
113
125
  output = command.stats('Inbox', depth: -1, sort: :max_size, reverse: true)
114
126
  expect(output).to be_a Array
115
- expect(output[0][0]).to eq 'Inbox/Bar/Sub'
127
+ expect(output[0][0]).to eq 'Inbox/Largest/Messages'
128
+ expect(output[-2][0]).to eq 'Inbox/Empty'
116
129
  end
117
130
 
118
- it 'sorts by smallest message' do
119
- output = command.stats('Inbox', depth: -1, sort: :min_size, reverse: true)
131
+ it 'sorts smallest message first' do
132
+ output = command.stats('Inbox', depth: -1, sort: :min_size, reverse: false)
120
133
  expect(output).to be_a Array
121
- # binding.pry
122
- expect(output[0][0]).to eq 'Inbox/Foo'
134
+ expect(output[0][0]).to eq 'Inbox/Smallest/Messages'
123
135
  end
124
136
  end
125
137
 
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: imapcli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Kraus (bovender)
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-03-10 00:00:00.000000000 Z
11
+ date: 2025-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '8.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '8.0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: csv
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -52,20 +66,6 @@ dependencies:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
68
  version: '3.1'
55
- - !ruby/object:Gem::Dependency
56
- name: activesupport
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '8.0'
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '8.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: gli
71
71
  requirement: !ruby/object:Gem::Requirement