imap-backup 1.0.10 → 1.0.11
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/.travis.yml +0 -2
- data/imap-backup.gemspec +3 -16
- data/lib/email/mboxrd/message.rb +12 -9
- data/lib/imap/backup/account/connection.rb +8 -2
- data/lib/imap/backup/account/folder.rb +9 -6
- data/lib/imap/backup/configuration/account.rb +4 -0
- data/lib/imap/backup/configuration/asker.rb +4 -0
- data/lib/imap/backup/configuration/folder_chooser.rb +60 -35
- data/lib/imap/backup/configuration/list.rb +23 -13
- data/lib/imap/backup/configuration/setup.rb +39 -24
- data/lib/imap/backup/configuration/store.rb +20 -17
- data/lib/imap/backup/downloader.rb +6 -3
- data/lib/imap/backup/serializer/mbox.rb +1 -1
- data/lib/imap/backup/version.rb +1 -1
- data/spec/unit/account/connection_spec.rb +4 -2
- data/spec/unit/configuration/account_spec.rb +21 -1
- data/spec/unit/configuration/asker_spec.rb +10 -2
- data/spec/unit/configuration/folder_chooser_spec.rb +1 -1
- data/spec/unit/configuration/list_spec.rb +20 -27
- data/spec/unit/configuration/setup_spec.rb +9 -7
- data/spec/unit/configuration/store_spec.rb +30 -37
- data/spec/unit/downloader_spec.rb +13 -20
- data/spec/unit/serializer/directory_spec.rb +3 -3
- metadata +4 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 664cab74407b44814015417f5488f9ebf194554a
|
4
|
+
data.tar.gz: 64acb39ad63cf6cf194e813c31c70edcb41afeba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 86a3fac151e45fc4d061e26a8569de2d89fb2b37063b11ae7374ee54400ce67eaa4e385ab37f9daef33fdd011305634cf37f50f908590829f037a7d8ca10fbdb
|
7
|
+
data.tar.gz: 292a8b80a095a74091e8b981783f92730a1044c429a761b983770585ead60c5bcd35928193767fdf5daefdb7a1e2f67fbbcfc67df5b7c41e1d9b920d19244f69
|
data/.travis.yml
CHANGED
data/imap-backup.gemspec
CHANGED
@@ -16,23 +16,10 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.require_paths = ['lib']
|
17
17
|
gem.version = Imap::Backup::VERSION
|
18
18
|
|
19
|
-
|
20
|
-
gem.add_runtime_dependency 'rake', '< 10.2.0'
|
21
|
-
else
|
22
|
-
gem.add_runtime_dependency 'rake'
|
23
|
-
end
|
19
|
+
gem.add_runtime_dependency 'rake'
|
24
20
|
gem.add_runtime_dependency 'highline'
|
25
21
|
gem.add_runtime_dependency 'mail'
|
26
|
-
if RUBY_VERSION < '1.9'
|
27
|
-
gem.add_runtime_dependency 'json'
|
28
|
-
end
|
29
22
|
|
30
|
-
gem.add_development_dependency '
|
31
|
-
gem.add_development_dependency '
|
32
|
-
gem.add_development_dependency 'rspec', '>= 2.12.0'
|
33
|
-
if RUBY_VERSION < '1.9'
|
34
|
-
gem.add_development_dependency 'rcov'
|
35
|
-
else
|
36
|
-
gem.add_development_dependency 'simplecov'
|
37
|
-
end
|
23
|
+
gem.add_development_dependency 'rspec', '>= 3.0.0'
|
24
|
+
gem.add_development_dependency 'simplecov'
|
38
25
|
end
|
data/lib/email/mboxrd/message.rb
CHANGED
@@ -4,29 +4,32 @@ module Email; end
|
|
4
4
|
|
5
5
|
module Email::Mboxrd
|
6
6
|
class Message
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
attr_reader :supplied_body
|
8
|
+
|
9
|
+
def initialize(supplied_body)
|
10
|
+
@supplied_body = supplied_body.clone
|
11
|
+
@supplied_body.force_encoding('binary') if RUBY_VERSION >= '1.9.0'
|
10
12
|
end
|
11
13
|
|
12
14
|
def to_s
|
13
|
-
'From ' + from + "\n" +
|
15
|
+
'From ' + from + "\n" + mboxrd_body + "\n"
|
14
16
|
end
|
15
17
|
|
16
18
|
private
|
17
19
|
|
18
20
|
def parsed
|
19
|
-
@parsed ||= Mail.new(
|
21
|
+
@parsed ||= Mail.new(supplied_body)
|
20
22
|
end
|
21
23
|
|
22
24
|
def from
|
23
25
|
parsed.from[0] + ' ' + asctime
|
24
26
|
end
|
25
27
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
28
|
+
def mboxrd_body
|
29
|
+
return @mboxrd_body if @mboxrd_body
|
30
|
+
@mboxrd_body = supplied_body.gsub(/\n(>*From)/, "\n>\\1")
|
31
|
+
@mboxrd_body += "\n" unless @mboxrd_body.end_with?("\n")
|
32
|
+
@mboxrd_body
|
30
33
|
end
|
31
34
|
|
32
35
|
def asctime
|
@@ -4,12 +4,14 @@ module Imap::Backup
|
|
4
4
|
module Account; end
|
5
5
|
|
6
6
|
class Account::Connection
|
7
|
-
attr_reader :username
|
7
|
+
attr_reader :username
|
8
|
+
attr_reader :local_path
|
9
|
+
attr_reader :backup_folders
|
8
10
|
|
9
11
|
def initialize(options)
|
10
12
|
@username, @password = options[:username], options[:password]
|
11
13
|
@local_path, @backup_folders = options[:local_path], options[:folders]
|
12
|
-
@server = options[:server]
|
14
|
+
@server = options[:server]
|
13
15
|
end
|
14
16
|
|
15
17
|
def folders
|
@@ -38,6 +40,10 @@ module Imap::Backup
|
|
38
40
|
imap.disconnect
|
39
41
|
end
|
40
42
|
|
43
|
+
def server
|
44
|
+
@server ||= host_for(username)
|
45
|
+
end
|
46
|
+
|
41
47
|
def imap
|
42
48
|
return @imap unless @imap.nil?
|
43
49
|
options = options_for(server)
|
@@ -6,25 +6,28 @@ module Imap::Backup
|
|
6
6
|
class Account::Folder
|
7
7
|
REQUESTED_ATTRIBUTES = ['RFC822', 'FLAGS', 'INTERNALDATE']
|
8
8
|
|
9
|
+
attr_reader :connection
|
10
|
+
attr_reader :folder
|
11
|
+
|
9
12
|
def initialize(connection, folder)
|
10
13
|
@connection, @folder = connection, folder
|
11
14
|
end
|
12
15
|
|
13
16
|
def uids
|
14
|
-
|
15
|
-
|
17
|
+
connection.imap.examine(folder)
|
18
|
+
connection.imap.uid_search(['ALL']).sort
|
16
19
|
rescue Net::IMAP::NoResponseError => e
|
17
|
-
Imap::Backup.logger.warn "Folder '#{
|
20
|
+
Imap::Backup.logger.warn "Folder '#{folder}' does not exist"
|
18
21
|
[]
|
19
22
|
end
|
20
23
|
|
21
24
|
def fetch(uid)
|
22
|
-
|
23
|
-
message =
|
25
|
+
connection.imap.examine(folder)
|
26
|
+
message = connection.imap.uid_fetch([uid.to_i], REQUESTED_ATTRIBUTES)[0][1]
|
24
27
|
message['RFC822'].force_encoding('utf-8') if RUBY_VERSION > '1.9'
|
25
28
|
message
|
26
29
|
rescue Net::IMAP::NoResponseError => e
|
27
|
-
Imap::Backup.logger.warn "Folder '#{
|
30
|
+
Imap::Backup.logger.warn "Folder '#{folder}' does not exist"
|
28
31
|
nil
|
29
32
|
end
|
30
33
|
end
|
@@ -6,6 +6,10 @@ module Imap::Backup
|
|
6
6
|
class Configuration::Asker < Struct.new(:highline)
|
7
7
|
EMAIL_MATCHER = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i
|
8
8
|
|
9
|
+
def initialize(highline)
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
9
13
|
def email(default = '')
|
10
14
|
highline.ask('email address: ') do |q|
|
11
15
|
q.default = default
|
@@ -4,54 +4,79 @@ module Imap::Backup
|
|
4
4
|
module Configuration; end
|
5
5
|
|
6
6
|
class Configuration::FolderChooser
|
7
|
+
attr_reader :account
|
8
|
+
|
7
9
|
def initialize(account)
|
8
10
|
@account = account
|
9
11
|
end
|
10
12
|
|
11
13
|
def run
|
12
|
-
|
13
|
-
@connection = Account::Connection.new(@account)
|
14
|
-
rescue => e
|
14
|
+
if connection.nil?
|
15
15
|
Imap::Backup.logger.warn 'Connection failed'
|
16
|
-
|
16
|
+
highline.ask 'Press a key '
|
17
17
|
return
|
18
18
|
end
|
19
|
-
|
20
|
-
if
|
19
|
+
|
20
|
+
if folders.nil?
|
21
21
|
Imap::Backup.logger.warn 'Unable to get folder list'
|
22
|
-
|
22
|
+
highline.ask 'Press a key '
|
23
23
|
return
|
24
24
|
end
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
25
|
+
|
26
|
+
catch :done do
|
27
|
+
loop do
|
28
|
+
system('clear')
|
29
|
+
show_menu
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def show_menu
|
37
|
+
highline.choose do |menu|
|
38
|
+
menu.header = 'Add/remove folders'
|
39
|
+
menu.index = :number
|
40
|
+
add_folders menu
|
41
|
+
menu.choice('return to the account menu') { throw :done }
|
42
|
+
menu.hidden('quit') { throw :done }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_folders(menu)
|
47
|
+
folders.each do |folder|
|
48
|
+
name = folder.name
|
49
|
+
mark = is_selected?(name) ? '+' : '-'
|
50
|
+
menu.choice("#{mark} #{name}") do
|
51
|
+
toggle_selection name
|
53
52
|
end
|
54
53
|
end
|
55
54
|
end
|
55
|
+
|
56
|
+
def is_selected?(folder_name)
|
57
|
+
account[:folders].find { |f| f[:name] == folder_name }
|
58
|
+
end
|
59
|
+
|
60
|
+
def toggle_selection(folder_name)
|
61
|
+
if is_selected?(folder_name)
|
62
|
+
account[:folders].reject! { |f| f[:name] == folder_name }
|
63
|
+
else
|
64
|
+
account[:folders] << { :name => folder_name }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def connection
|
69
|
+
@connection ||= Account::Connection.new(account)
|
70
|
+
rescue => e
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
|
74
|
+
def folders
|
75
|
+
@folders ||= connection.folders
|
76
|
+
end
|
77
|
+
|
78
|
+
def highline
|
79
|
+
Configuration::Setup.highline
|
80
|
+
end
|
56
81
|
end
|
57
82
|
end
|
@@ -4,27 +4,37 @@ module Imap::Backup
|
|
4
4
|
module Configuration; end
|
5
5
|
|
6
6
|
class Configuration::List
|
7
|
-
attr_reader :
|
7
|
+
attr_reader :required_accounts
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
|
11
|
-
raise ConfigurationNotFound.new("Configuration file '#{Configuration::Store.default_pathname}' not found")
|
12
|
-
end
|
13
|
-
@config = Configuration::Store.new
|
14
|
-
|
15
|
-
if accounts.nil?
|
16
|
-
@accounts = @config.data[:accounts]
|
17
|
-
else
|
18
|
-
@accounts = @config.data[:accounts].select{ |account| accounts.include?(account[:username]) }
|
19
|
-
end
|
9
|
+
def initialize(required_accounts = nil)
|
10
|
+
@required_accounts = required_accounts
|
20
11
|
end
|
21
12
|
|
22
13
|
def each_connection
|
23
|
-
|
14
|
+
accounts.each do |account|
|
24
15
|
connection = Account::Connection.new(account)
|
25
16
|
yield connection
|
26
17
|
connection.disconnect
|
27
18
|
end
|
28
19
|
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def config
|
24
|
+
return @config if @config
|
25
|
+
if not Configuration::Store.exist?
|
26
|
+
raise ConfigurationNotFound.new("Configuration file '#{Configuration::Store.default_pathname}' not found")
|
27
|
+
end
|
28
|
+
@config = Configuration::Store.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def accounts
|
32
|
+
return @accounts if @accounts
|
33
|
+
if required_accounts.nil?
|
34
|
+
@accounts = config.data[:accounts]
|
35
|
+
else
|
36
|
+
@accounts = config.data[:accounts].select{ |account| required_accounts.include?(account[:username]) }
|
37
|
+
end
|
38
|
+
end
|
29
39
|
end
|
30
40
|
end
|
@@ -13,32 +13,48 @@ module Imap::Backup
|
|
13
13
|
|
14
14
|
def run
|
15
15
|
setup_logging
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
config.data[:accounts].each do |account|
|
21
|
-
menu.choice("#{account[:username]}") do
|
22
|
-
edit_account account[:username]
|
23
|
-
end
|
24
|
-
end
|
25
|
-
menu.choice('add account') do
|
26
|
-
username = Configuration::Asker.email
|
27
|
-
edit_account username
|
28
|
-
end
|
29
|
-
menu.choice('save and exit') do
|
30
|
-
config.save
|
31
|
-
return
|
32
|
-
end
|
33
|
-
menu.choice(:quit) do
|
34
|
-
return
|
35
|
-
end
|
16
|
+
catch :done do
|
17
|
+
loop do
|
18
|
+
system('clear')
|
19
|
+
show_menu
|
36
20
|
end
|
37
21
|
end
|
38
22
|
end
|
39
23
|
|
40
24
|
private
|
41
25
|
|
26
|
+
def show_menu
|
27
|
+
self.class.highline.choose do |menu|
|
28
|
+
menu.header = 'Choose an action'
|
29
|
+
account_items menu
|
30
|
+
add_account_item menu
|
31
|
+
menu.choice('add account') do
|
32
|
+
username = Configuration::Asker.email
|
33
|
+
edit_account username
|
34
|
+
end
|
35
|
+
menu.choice('save and exit') do
|
36
|
+
config.save
|
37
|
+
throw :done
|
38
|
+
end
|
39
|
+
menu.choice(:quit) { throw :done }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def account_items(menu)
|
44
|
+
config.data[:accounts].each do |account|
|
45
|
+
menu.choice("#{account[:username]}") do
|
46
|
+
edit_account account[:username]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def add_account_item(menu)
|
52
|
+
menu.choice('add account') do
|
53
|
+
username = Configuration::Asker.email
|
54
|
+
edit_account username
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
42
58
|
def config
|
43
59
|
@config ||= Configuration::Store.new
|
44
60
|
end
|
@@ -52,21 +68,20 @@ module Imap::Backup
|
|
52
68
|
end
|
53
69
|
end
|
54
70
|
|
55
|
-
def
|
71
|
+
def default_account_config(username)
|
56
72
|
account = {
|
57
73
|
:username => username,
|
58
74
|
:password => '',
|
59
75
|
:local_path => File.join(config.path, username.gsub('@', '_')),
|
60
76
|
:folders => []
|
61
77
|
}
|
62
|
-
config.data[:accounts] << account
|
63
|
-
account
|
64
78
|
end
|
65
79
|
|
66
80
|
def edit_account(username)
|
67
81
|
account = config.data[:accounts].find { |a| a[:username] == username }
|
68
82
|
if account.nil?
|
69
|
-
account =
|
83
|
+
account = default_account_config(username)
|
84
|
+
config.data[:accounts] << account
|
70
85
|
end
|
71
86
|
Configuration::Account.new(config, account, Configuration::Setup.highline).run
|
72
87
|
end
|
@@ -8,8 +8,7 @@ module Imap::Backup
|
|
8
8
|
class Configuration::Store
|
9
9
|
CONFIGURATION_DIRECTORY = File.expand_path('~/.imap-backup')
|
10
10
|
|
11
|
-
attr_reader :
|
12
|
-
attr_reader :path
|
11
|
+
attr_reader :pathname
|
13
12
|
|
14
13
|
def self.default_pathname
|
15
14
|
File.join(CONFIGURATION_DIRECTORY, 'config.json')
|
@@ -21,21 +20,19 @@ module Imap::Backup
|
|
21
20
|
|
22
21
|
def initialize(pathname = self.class.default_pathname)
|
23
22
|
@pathname = pathname
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
Utils.check_permissions @pathname, 0600
|
29
|
-
@data = JSON.parse(File.read(@pathname), :symbolize_names => true)
|
30
|
-
else
|
31
|
-
@data = {:accounts => []}
|
32
|
-
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def path
|
26
|
+
File.dirname(pathname)
|
33
27
|
end
|
34
28
|
|
35
29
|
def save
|
30
|
+
if File.directory?(path)
|
31
|
+
Utils.check_permissions path, 0700
|
32
|
+
end
|
36
33
|
mkdir_private path
|
37
|
-
File.open(
|
38
|
-
FileUtils.chmod 0600,
|
34
|
+
File.open(pathname, 'w') { |f| f.write(JSON.pretty_generate(data)) }
|
35
|
+
FileUtils.chmod 0600, pathname
|
39
36
|
@data[:accounts].each do |account|
|
40
37
|
mkdir_private account[:local_path]
|
41
38
|
account[:folders].each do |f|
|
@@ -49,12 +46,18 @@ module Imap::Backup
|
|
49
46
|
end
|
50
47
|
end
|
51
48
|
|
52
|
-
def path
|
53
|
-
File.dirname(@pathname)
|
54
|
-
end
|
55
|
-
|
56
49
|
private
|
57
50
|
|
51
|
+
def data
|
52
|
+
return @data if @data
|
53
|
+
if File.exist?(pathname)
|
54
|
+
Utils.check_permissions pathname, 0600
|
55
|
+
@data = JSON.parse(File.read(pathname), :symbolize_names => true)
|
56
|
+
else
|
57
|
+
@data = {:accounts => []}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
58
61
|
def mkdir_private(path)
|
59
62
|
if ! File.directory?(path)
|
60
63
|
FileUtils.mkdir path
|
@@ -4,16 +4,19 @@ require 'json'
|
|
4
4
|
|
5
5
|
module Imap::Backup
|
6
6
|
class Downloader
|
7
|
+
attr_reader :folder
|
8
|
+
attr_reader :serializer
|
9
|
+
|
7
10
|
def initialize(folder, serializer)
|
8
11
|
@folder, @serializer = folder, serializer
|
9
12
|
end
|
10
13
|
|
11
14
|
def run
|
12
|
-
uids =
|
15
|
+
uids = folder.uids - serializer.uids
|
13
16
|
uids.each do |uid|
|
14
|
-
message =
|
17
|
+
message = folder.fetch(uid)
|
15
18
|
next if message.nil?
|
16
|
-
|
19
|
+
serializer.save(uid, message)
|
17
20
|
end
|
18
21
|
end
|
19
22
|
end
|
@@ -37,7 +37,7 @@ module Imap::Backup
|
|
37
37
|
imap = File.open(imap_pathname, 'ab')
|
38
38
|
mbox.write mboxrd_message.to_s
|
39
39
|
imap.write uid + "\n"
|
40
|
-
rescue
|
40
|
+
rescue => e
|
41
41
|
Imap::Backup.logger.warn "Failed to save message #{uid}:\n#{body}. #{e}"
|
42
42
|
ensure
|
43
43
|
mbox.close if mbox
|
data/lib/imap/backup/version.rb
CHANGED
@@ -43,7 +43,9 @@ describe Imap::Backup::Account::Connection do
|
|
43
43
|
[:local_path, 'local_path'],
|
44
44
|
[:backup_folders, [folder_config]],
|
45
45
|
].each do |attr, expected|
|
46
|
-
|
46
|
+
it "expects #{attr}" do
|
47
|
+
expect(subject.send(attr)).to eq(expected)
|
48
|
+
end
|
47
49
|
end
|
48
50
|
|
49
51
|
context 'server' do
|
@@ -118,7 +120,7 @@ describe Imap::Backup::Account::Connection do
|
|
118
120
|
before { subject.disconnect }
|
119
121
|
|
120
122
|
it 'disconnects from the server' do
|
121
|
-
expect(imap).to have_received(:disconnect)
|
123
|
+
expect(imap).to have_received(:disconnect)
|
122
124
|
end
|
123
125
|
|
124
126
|
include_examples 'connects to IMAP'
|
@@ -19,6 +19,26 @@ describe Imap::Backup::Configuration::Account do
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
+
context '#initialize' do
|
23
|
+
it 'requires 3 parameters' do
|
24
|
+
expect do
|
25
|
+
described_class.new('foo')
|
26
|
+
end.to raise_error(ArgumentError, /1 for 3/)
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:store) { 'store' }
|
30
|
+
let(:account) { 'account' }
|
31
|
+
let(:highline) { 'highline' }
|
32
|
+
|
33
|
+
subject { described_class.new(store, account, highline) }
|
34
|
+
|
35
|
+
[:store, :account, :highline].each do |param|
|
36
|
+
it "expects #{param}" do
|
37
|
+
expect(subject.send(param)).to eq(send(param))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
22
42
|
context '#run' do
|
23
43
|
let(:highline) { double('Highline') }
|
24
44
|
let(:menu) { MockHighlineMenu.new }
|
@@ -225,7 +245,7 @@ describe Imap::Backup::Configuration::Account do
|
|
225
245
|
end
|
226
246
|
|
227
247
|
it 'validates that the path is not used by other backups' do
|
228
|
-
expect(@validator.call(other_existing_path)).to
|
248
|
+
expect(@validator.call(other_existing_path)).to be_falsey
|
229
249
|
end
|
230
250
|
end
|
231
251
|
|
@@ -45,7 +45,15 @@ module Imap::Backup
|
|
45
45
|
end
|
46
46
|
|
47
47
|
context '#initialize' do
|
48
|
-
|
48
|
+
it 'requires 1 parameter' do
|
49
|
+
expect do
|
50
|
+
described_class.new
|
51
|
+
end.to raise_error(ArgumentError, /0 for 1/)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'expects a higline' do
|
55
|
+
expect(subject.highline).to eq(highline)
|
56
|
+
end
|
49
57
|
end
|
50
58
|
|
51
59
|
context '#email' do
|
@@ -56,7 +64,7 @@ module Imap::Backup
|
|
56
64
|
@result = subject.email
|
57
65
|
end
|
58
66
|
|
59
|
-
it 'asks for an email'
|
67
|
+
it 'asks for an email' do
|
60
68
|
expect(highline).to have_received(:ask).with(/email/)
|
61
69
|
end
|
62
70
|
|
@@ -12,26 +12,39 @@ describe Imap::Backup::Configuration::List do
|
|
12
12
|
double('Imap::Backup::Configuration::Store', :data => {:accounts => accounts})
|
13
13
|
end
|
14
14
|
let(:exists) { true }
|
15
|
+
let(:connection1) { double('Imap::Backup::Account::Connection', :disconnect => nil) }
|
16
|
+
let(:connection2) { double('Imap::Backup::Account::Connection', :disconnect => nil) }
|
15
17
|
|
16
18
|
before do
|
17
19
|
allow(Imap::Backup::Configuration::Store).to receive(:new).and_return(store)
|
18
20
|
allow(Imap::Backup::Configuration::Store).to receive(:exist?).and_return(exists)
|
21
|
+
allow(Imap::Backup::Account::Connection).to receive(:new).with(accounts[0]).and_return(connection1)
|
22
|
+
allow(Imap::Backup::Account::Connection).to receive(:new).with(accounts[1]).and_return(connection2)
|
19
23
|
end
|
20
24
|
|
21
25
|
subject { described_class.new }
|
22
26
|
|
23
27
|
context '#initialize' do
|
28
|
+
end
|
29
|
+
|
30
|
+
context '#each_connection' do
|
31
|
+
specify "calls the block with each account's connection" do
|
32
|
+
connections = []
|
33
|
+
|
34
|
+
subject.each_connection { |a| connections << a }
|
35
|
+
|
36
|
+
expect(connections).to eq([connection1, connection2])
|
37
|
+
end
|
38
|
+
|
24
39
|
context 'with account parameter' do
|
25
40
|
subject { described_class.new(['a2@example.com']) }
|
26
41
|
|
27
42
|
it 'should only create requested accounts' do
|
28
|
-
|
29
|
-
|
30
|
-
|
43
|
+
connections = []
|
44
|
+
|
45
|
+
subject.each_connection { |a| connections << a }
|
31
46
|
|
32
|
-
|
33
|
-
it 'selects all accounts' do
|
34
|
-
expect(subject.accounts).to eq(accounts)
|
47
|
+
expect(connections).to eq([connection2])
|
35
48
|
end
|
36
49
|
end
|
37
50
|
|
@@ -40,29 +53,9 @@ describe Imap::Backup::Configuration::List do
|
|
40
53
|
|
41
54
|
it 'fails' do
|
42
55
|
expect {
|
43
|
-
|
56
|
+
subject.each_connection {}
|
44
57
|
}.to raise_error(Imap::Backup::ConfigurationNotFound, /not found/)
|
45
58
|
end
|
46
59
|
end
|
47
60
|
end
|
48
|
-
|
49
|
-
context 'instance methods' do
|
50
|
-
let(:connection1) { double('Imap::Backup::Account::Connection', :disconnect => nil) }
|
51
|
-
let(:connection2) { double('Imap::Backup::Account::Connection', :disconnect => nil) }
|
52
|
-
|
53
|
-
before do
|
54
|
-
allow(Imap::Backup::Account::Connection).to receive(:new).with(accounts[0]).and_return(connection1)
|
55
|
-
allow(Imap::Backup::Account::Connection).to receive(:new).with(accounts[1]).and_return(connection2)
|
56
|
-
end
|
57
|
-
|
58
|
-
context '#each_connection' do
|
59
|
-
specify "calls the block with each account's connection" do
|
60
|
-
connections = []
|
61
|
-
|
62
|
-
subject.each_connection { |a| connections << a }
|
63
|
-
|
64
|
-
expect(connections).to eq([connection1, connection2])
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
61
|
end
|
@@ -20,7 +20,8 @@ describe Imap::Backup::Configuration::Setup do
|
|
20
20
|
double(
|
21
21
|
'Imap::Backup::Configuration::Store',
|
22
22
|
:data => data,
|
23
|
-
:path => '/base/path'
|
23
|
+
:path => '/base/path',
|
24
|
+
:save => nil
|
24
25
|
)
|
25
26
|
end
|
26
27
|
|
@@ -45,15 +46,15 @@ describe Imap::Backup::Configuration::Setup do
|
|
45
46
|
end
|
46
47
|
|
47
48
|
it 'clears the screen' do
|
48
|
-
subject.should_receive(:system).with('clear')
|
49
|
-
|
50
49
|
subject.run
|
50
|
+
|
51
|
+
expect(subject).to have_received(:system).with('clear')
|
51
52
|
end
|
52
53
|
|
53
54
|
it 'should list accounts' do
|
54
55
|
subject.run
|
55
56
|
|
56
|
-
@output.string.
|
57
|
+
expect(@output.string).to match /account@example.com/
|
57
58
|
end
|
58
59
|
|
59
60
|
context 'adding accounts' do
|
@@ -80,14 +81,15 @@ describe Imap::Backup::Configuration::Setup do
|
|
80
81
|
end
|
81
82
|
|
82
83
|
it 'should save the configuration' do
|
83
|
-
@input.
|
84
|
-
store.should_receive(:save).with()
|
84
|
+
allow(@input).to receive(:gets).and_return("save\n")
|
85
85
|
|
86
86
|
subject.run
|
87
|
+
|
88
|
+
expect(store).to have_received(:save)
|
87
89
|
end
|
88
90
|
|
89
91
|
it 'should exit' do
|
90
|
-
@input.
|
92
|
+
allow(@input).to receive(:gets).and_return("quit\n")
|
91
93
|
|
92
94
|
subject.run
|
93
95
|
end
|
@@ -3,9 +3,9 @@ require 'spec_helper'
|
|
3
3
|
require 'json'
|
4
4
|
|
5
5
|
describe Imap::Backup::Configuration::Store do
|
6
|
-
let(:file_path) { '/base/path/config.json' }
|
7
|
-
let(:file_exists) { true }
|
8
6
|
let(:directory) { '/base/path' }
|
7
|
+
let(:file_path) { File.join(directory, '/config.json') }
|
8
|
+
let(:file_exists) { true }
|
9
9
|
let(:directory_exists) { true }
|
10
10
|
let(:data) { {:the => :config} }
|
11
11
|
let(:configuration) { data.to_json }
|
@@ -31,41 +31,9 @@ describe Imap::Backup::Configuration::Store do
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
context '#
|
35
|
-
|
36
|
-
|
37
|
-
end
|
38
|
-
|
39
|
-
context 'loading' do
|
40
|
-
subject { described_class.new }
|
41
|
-
|
42
|
-
it 'sets data' do
|
43
|
-
expect(subject.data).to eq(data)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
context 'if the configuration file is missing' do
|
48
|
-
let(:file_exists) { false }
|
49
|
-
|
50
|
-
it "doesn't fail" do
|
51
|
-
expect do
|
52
|
-
described_class.new
|
53
|
-
end.to_not raise_error
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
context 'if the config file permissions are too lax' do
|
58
|
-
let(:file_exists) { true }
|
59
|
-
|
60
|
-
before do
|
61
|
-
allow(Imap::Backup::Utils).to receive(:check_permissions).with(file_path, 0600).and_raise('Error')
|
62
|
-
end
|
63
|
-
|
64
|
-
it 'fails' do
|
65
|
-
expect do
|
66
|
-
described_class.new
|
67
|
-
end.to raise_error(RuntimeError, 'Error')
|
68
|
-
end
|
34
|
+
context '#path' do
|
35
|
+
it 'is the directory containing the configuration file' do
|
36
|
+
expect(subject.path).to eq(directory)
|
69
37
|
end
|
70
38
|
end
|
71
39
|
|
@@ -79,6 +47,7 @@ describe Imap::Backup::Configuration::Store do
|
|
79
47
|
allow(FileUtils).to receive(:chmod)
|
80
48
|
allow(Imap::Backup::Utils).to receive(:stat).with(directory).and_return(0700)
|
81
49
|
allow(Imap::Backup::Utils).to receive(:stat).with(file_path).and_return(0600)
|
50
|
+
allow(Imap::Backup::Utils).to receive(:check_permissions).and_return(nil)
|
82
51
|
allow(File).to receive(:open).with(file_path, 'w') { |&b| b.call file }
|
83
52
|
allow(JSON).to receive(:pretty_generate).and_return('JSON output')
|
84
53
|
end
|
@@ -103,6 +72,30 @@ describe Imap::Backup::Configuration::Store do
|
|
103
72
|
expect(FileUtils).to have_received(:chmod).with(0600, file_path)
|
104
73
|
end
|
105
74
|
|
75
|
+
context 'if the configuration file is missing' do
|
76
|
+
let(:file_exists) { false }
|
77
|
+
|
78
|
+
it "doesn't fail" do
|
79
|
+
expect do
|
80
|
+
subject.save
|
81
|
+
end.to_not raise_error
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'if the config file permissions are too lax' do
|
86
|
+
let(:file_exists) { true }
|
87
|
+
|
88
|
+
before do
|
89
|
+
allow(Imap::Backup::Utils).to receive(:check_permissions).with(file_path, 0600).and_raise('Error')
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'fails' do
|
93
|
+
expect do
|
94
|
+
subject.save
|
95
|
+
end.to raise_error(RuntimeError, 'Error')
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
106
99
|
context 'saving accounts' do
|
107
100
|
let(:folders) { [{ :name => 'A folder' }] }
|
108
101
|
let(:data) do
|
@@ -18,7 +18,7 @@ describe Imap::Backup::Downloader do
|
|
18
18
|
:prepare => nil,
|
19
19
|
:exist? => true,
|
20
20
|
:uids => [],
|
21
|
-
:save => nil
|
21
|
+
:save => nil,
|
22
22
|
)
|
23
23
|
end
|
24
24
|
|
@@ -29,7 +29,7 @@ describe Imap::Backup::Downloader do
|
|
29
29
|
context '#run' do
|
30
30
|
context 'with folder' do
|
31
31
|
it 'should list messages' do
|
32
|
-
folder.
|
32
|
+
allow(folder).to receive(:uids).and_return([])
|
33
33
|
|
34
34
|
subject.run
|
35
35
|
end
|
@@ -39,19 +39,12 @@ describe Imap::Backup::Downloader do
|
|
39
39
|
allow(folder).to receive(:uids).and_return(['123', '999', '1234'])
|
40
40
|
end
|
41
41
|
|
42
|
-
it 'should skip messages that are downloaded' do
|
43
|
-
allow(File).to receive(:exist?).and_return(true)
|
44
|
-
|
45
|
-
serializer.should_not_receive(:fetch)
|
46
|
-
|
47
|
-
subject.run
|
48
|
-
end
|
49
|
-
|
50
42
|
it 'skips failed fetches' do
|
51
|
-
folder.
|
52
|
-
serializer.should_not_receive(:save).with('999', anything)
|
43
|
+
allow(folder).to receive(:fetch).with('999').and_return(nil)
|
53
44
|
|
54
45
|
subject.run
|
46
|
+
|
47
|
+
expect(serializer).to_not have_received(:save).with('999', anything)
|
55
48
|
end
|
56
49
|
|
57
50
|
context 'to download' do
|
@@ -65,18 +58,18 @@ describe Imap::Backup::Downloader do
|
|
65
58
|
end
|
66
59
|
end
|
67
60
|
|
68
|
-
it '
|
69
|
-
folder.should_receive(:fetch).with('999')
|
70
|
-
folder.should_receive(:fetch).with('1234')
|
71
|
-
|
61
|
+
it 'requests messages' do
|
72
62
|
subject.run
|
73
|
-
end
|
74
63
|
|
75
|
-
|
76
|
-
|
77
|
-
|
64
|
+
expect(folder).to have_received(:fetch).with('999')
|
65
|
+
expect(folder).to have_received(:fetch).with('1234')
|
66
|
+
end
|
78
67
|
|
68
|
+
it 'saves messages' do
|
79
69
|
subject.run
|
70
|
+
|
71
|
+
expect(serializer).to have_received(:save).with('999', message)
|
72
|
+
expect(serializer).to have_received(:save).with('1234', message)
|
80
73
|
end
|
81
74
|
end
|
82
75
|
end
|
@@ -24,14 +24,14 @@ describe Imap::Backup::Serializer::Directory do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
it 'returns the backed-up uids' do
|
27
|
-
subject.uids.
|
27
|
+
expect(subject.uids).to eq([1, 123])
|
28
28
|
end
|
29
29
|
|
30
30
|
context 'if the directory does not exist' do
|
31
31
|
let(:folder_exists) { false }
|
32
32
|
|
33
33
|
it 'returns an empty array' do
|
34
|
-
subject.uids.
|
34
|
+
expect(subject.uids).to eq([])
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
@@ -40,7 +40,7 @@ describe Imap::Backup::Serializer::Directory do
|
|
40
40
|
it 'checks if the file exists' do
|
41
41
|
allow(File).to receive(:exist?).with(%r{/base/path/my_folder/0+123.json}).and_return(true)
|
42
42
|
|
43
|
-
subject.exist?(123).
|
43
|
+
expect(subject.exist?(123)).to be_truthy
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: imap-backup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe Yates
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -52,48 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: pry
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: pry-doc
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
55
|
- !ruby/object:Gem::Dependency
|
84
56
|
name: rspec
|
85
57
|
requirement: !ruby/object:Gem::Requirement
|
86
58
|
requirements:
|
87
59
|
- - ">="
|
88
60
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
61
|
+
version: 3.0.0
|
90
62
|
type: :development
|
91
63
|
prerelease: false
|
92
64
|
version_requirements: !ruby/object:Gem::Requirement
|
93
65
|
requirements:
|
94
66
|
- - ">="
|
95
67
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
68
|
+
version: 3.0.0
|
97
69
|
- !ruby/object:Gem::Dependency
|
98
70
|
name: simplecov
|
99
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -203,4 +175,3 @@ test_files:
|
|
203
175
|
- spec/unit/serializer/directory_spec.rb
|
204
176
|
- spec/unit/serializer/mbox_spec.rb
|
205
177
|
- spec/unit/utils_spec.rb
|
206
|
-
has_rdoc:
|