mailpeek 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -8
- data/Guardfile +10 -0
- data/lib/mailpeek.rb +2 -1
- data/lib/mailpeek/configuration.rb +1 -3
- data/lib/mailpeek/delivery.rb +13 -22
- data/lib/mailpeek/email.rb +2 -10
- data/lib/mailpeek/version.rb +1 -1
- data/lib/mailpeek/web/action.rb +2 -4
- data/mailpeek.gemspec +6 -1
- data/spec/mailpeek/delivery_spec.rb +127 -0
- data/spec/mailpeek/email_spec.rb +152 -0
- data/spec/mailpeek/mailpeek_spec.rb +36 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/emails/html/mail +19 -0
- data/spec/support/emails/html_and_text/.read +0 -0
- data/spec/support/emails/html_and_text/mail +36 -0
- data/spec/support/emails/html_text_and_attachment/attachments/20130704030733_zebra-melon.pdf +318 -1
- data/spec/support/emails/html_text_and_attachment/mail +2260 -0
- data/spec/support/emails/text/mail +15 -0
- metadata +93 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8175b6629889a19c09ac2b9ea39791481afe4b14c07cdec0d8b63fd4c1529e3d
|
4
|
+
data.tar.gz: 5e2885db884fb3ca70db10b436c966d776a2fe1e6ac57f1b678469c27d703cf8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8582f7df76eb9123e3a9eef08006bd1b0676bb9ad56edd93b3b4fa9862f6e41548b04e0386dae477f04438781541f77dbfc734131689cf17722d5bbf8ee69e54
|
7
|
+
data.tar.gz: bc6cc33ceec866f1affe7f85408bc86a4cd955a960ee6bdc825af31dc095da00365a4a5db978e118e3deb3567b7199b91c6debee1b2c4216704c95117107d731
|
data/.rubocop.yml
CHANGED
@@ -1,24 +1,22 @@
|
|
1
1
|
require:
|
2
2
|
- rubocop-performance
|
3
|
+
- rubocop-rspec
|
3
4
|
|
4
5
|
AllCops:
|
5
6
|
Exclude:
|
6
7
|
- '**/bin/**/*'
|
8
|
+
Layout/LineLength:
|
9
|
+
Max: 80
|
10
|
+
Lint/AmbiguousBlockAssociation:
|
11
|
+
Enabled: false
|
7
12
|
Metrics/AbcSize:
|
8
13
|
Max: 30
|
9
|
-
Style/BlockDelimiters:
|
10
|
-
Enabled: false
|
11
14
|
Metrics/BlockLength:
|
12
15
|
Exclude:
|
13
16
|
- '**/spec/**/*'
|
14
17
|
- '**/lib/**/*.rake'
|
15
18
|
Metrics/ClassLength:
|
16
19
|
Max: 150
|
17
|
-
Style/Lambda:
|
18
|
-
Exclude:
|
19
|
-
- 'app/models/**/*'
|
20
|
-
Layout/LineLength:
|
21
|
-
Max: 80
|
22
20
|
Metrics/MethodLength:
|
23
21
|
Max: 15
|
24
22
|
Metrics/ModuleLength:
|
@@ -27,10 +25,13 @@ Naming/FileName:
|
|
27
25
|
Exclude:
|
28
26
|
- '**/Guardfile'
|
29
27
|
- '**/Capfile'
|
30
|
-
|
28
|
+
Style/BlockDelimiters:
|
31
29
|
Enabled: false
|
32
30
|
Style/Documentation:
|
33
31
|
Exclude:
|
34
32
|
- '**/db/migrate/*'
|
35
33
|
Style/EvalWithLocation:
|
36
34
|
Enabled: false
|
35
|
+
Style/Lambda:
|
36
|
+
Exclude:
|
37
|
+
- 'app/models/**/*'
|
data/Guardfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
guard :rubocop, all_on_start: true, cli: ['-D'] do
|
4
|
+
watch(/.+\.rb$/)
|
5
|
+
watch(/.+\.rake$/)
|
6
|
+
watch(/.+\.jbuilder$/)
|
7
|
+
watch(/Rakefile$/)
|
8
|
+
watch(/Gemfile$/)
|
9
|
+
watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
|
10
|
+
end
|
data/lib/mailpeek.rb
CHANGED
@@ -1,14 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'mail/check_delivery_params'
|
4
|
-
|
5
3
|
module Mailpeek
|
6
4
|
# Public: Stores config info for Mailpeek
|
7
5
|
class Configuration
|
8
6
|
attr_accessor :location, :limit
|
9
7
|
|
10
8
|
def initialize
|
11
|
-
@location =
|
9
|
+
@location = Dir.mktmpdir('mailpeek')
|
12
10
|
@limit = 50
|
13
11
|
end
|
14
12
|
end
|
data/lib/mailpeek/delivery.rb
CHANGED
@@ -1,12 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'mail/check_delivery_params'
|
4
|
-
|
5
3
|
module Mailpeek
|
6
|
-
# Public:
|
4
|
+
# Public: Handles consuming emails, checking configuration, creating
|
5
|
+
# directories, cleaning up emails and saving attachments
|
7
6
|
class Delivery
|
8
|
-
include Mail::CheckDeliveryParams if defined?(Mail::CheckDeliveryParams)
|
9
|
-
|
10
7
|
class InvalidOption < StandardError; end
|
11
8
|
|
12
9
|
attr_accessor :settings
|
@@ -17,40 +14,34 @@ module Mailpeek
|
|
17
14
|
options[:limit] = options[:limit].to_i
|
18
15
|
|
19
16
|
if options[:location].nil?
|
20
|
-
raise InvalidOption, '
|
17
|
+
raise InvalidOption, 'The Mailpeek location option is required'
|
21
18
|
end
|
22
19
|
|
23
|
-
|
24
|
-
|
25
|
-
raise InvalidOption, 'A limit option is required when using Mailpeek'
|
26
|
-
elsif options[:limit] <= 0
|
27
|
-
raise InvalidOption, 'A limit option is an invalid number'
|
20
|
+
if options[:limit] <= 0
|
21
|
+
raise InvalidOption, 'The Mailpeek limit option is an invalid number'
|
28
22
|
end
|
29
23
|
|
30
|
-
# rubocop:enable Style/GuardClause
|
31
|
-
|
32
24
|
self.settings = options
|
33
25
|
end
|
34
26
|
|
35
27
|
def deliver!(mail)
|
36
|
-
check_delivery_params(mail) if respond_to?(:check_delivery_params)
|
37
|
-
|
38
28
|
clean_up
|
39
29
|
|
40
|
-
|
30
|
+
timestamp = Time.now.to_i.to_s
|
31
|
+
|
32
|
+
basepath = File.join(settings[:location], timestamp)
|
41
33
|
|
42
34
|
save_email(mail, basepath)
|
43
35
|
|
44
|
-
|
36
|
+
add_attachments(mail, basepath) if mail.attachments.any?
|
45
37
|
|
46
|
-
|
38
|
+
timestamp
|
47
39
|
end
|
48
40
|
|
49
41
|
private
|
50
42
|
|
43
|
+
# remove oldest email if over `limit` settings
|
51
44
|
def clean_up
|
52
|
-
# remove oldest email if over `limit` settings
|
53
|
-
|
54
45
|
directory = File.join(settings[:location], '*')
|
55
46
|
return if Dir[directory].length < settings[:limit]
|
56
47
|
|
@@ -60,8 +51,8 @@ module Mailpeek
|
|
60
51
|
def save_email(mail, basepath)
|
61
52
|
FileUtils.mkdir_p(basepath)
|
62
53
|
|
63
|
-
File.open(File.join(basepath, 'mail'), 'w') do |
|
64
|
-
|
54
|
+
File.open(File.join(basepath, 'mail'), 'w') do |file|
|
55
|
+
file.write(mail.to_s)
|
65
56
|
end
|
66
57
|
end
|
67
58
|
|
data/lib/mailpeek/email.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'mail/check_delivery_params'
|
4
|
-
|
5
3
|
module Mailpeek
|
6
4
|
# Public: Wrapper class for mail object
|
7
5
|
class Email
|
@@ -21,11 +19,7 @@ module Mailpeek
|
|
21
19
|
@date = mail.date
|
22
20
|
@attachments = []
|
23
21
|
|
24
|
-
|
25
|
-
parse_parts
|
26
|
-
else
|
27
|
-
parse_body
|
28
|
-
end
|
22
|
+
mail.multipart? ? parse_parts : parse_body
|
29
23
|
end
|
30
24
|
|
31
25
|
def match?(query)
|
@@ -33,9 +27,7 @@ module Mailpeek
|
|
33
27
|
end
|
34
28
|
|
35
29
|
def destroy
|
36
|
-
|
37
|
-
|
38
|
-
FileUtils.rm_rf("#{location}/#{id}")
|
30
|
+
FileUtils.rm_rf("#{Mailpeek.configuration.location}/#{id}")
|
39
31
|
end
|
40
32
|
|
41
33
|
def read
|
data/lib/mailpeek/version.rb
CHANGED
data/lib/mailpeek/web/action.rb
CHANGED
@@ -18,7 +18,7 @@ module Mailpeek
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def redirect(location)
|
21
|
-
|
21
|
+
halt([302, { 'Location' => "#{request.base_url}#{location}" }, []])
|
22
22
|
end
|
23
23
|
|
24
24
|
def params
|
@@ -89,9 +89,7 @@ module Mailpeek
|
|
89
89
|
@env = env
|
90
90
|
@block = block
|
91
91
|
|
92
|
-
#
|
93
|
-
@@files ||= {}
|
94
|
-
# rubocop:enable Style/ClassVars
|
92
|
+
# @@files ||= {}
|
95
93
|
end
|
96
94
|
|
97
95
|
private
|
data/mailpeek.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
|
20
20
|
s.files = `git ls-files`.split("\n")
|
21
21
|
|
22
|
-
|
22
|
+
s.test_files = Dir['spec/**/*']
|
23
23
|
|
24
24
|
s.required_ruby_version = '>= 2.3.0'
|
25
25
|
|
@@ -27,6 +27,11 @@ Gem::Specification.new do |s|
|
|
27
27
|
s.add_dependency 'rack', '~> 2.0'
|
28
28
|
s.add_dependency 'rack-protection', '~> 2.0'
|
29
29
|
|
30
|
+
s.add_development_dependency 'faker', '~> 2'
|
31
|
+
s.add_development_dependency 'guard-rubocop', '~> 1.3'
|
32
|
+
s.add_development_dependency 'rspec', '~> 3'
|
30
33
|
s.add_development_dependency 'rubocop', '~> 0.79'
|
31
34
|
s.add_development_dependency 'rubocop-performance', '~> 1.5'
|
35
|
+
s.add_development_dependency 'rubocop-rspec', '~> 1.37'
|
36
|
+
s.add_development_dependency 'simplecov', '~> 0.17'
|
32
37
|
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Mailpeek::Delivery do
|
4
|
+
subject(:delivery) { described_class.new(options) }
|
5
|
+
|
6
|
+
let(:options) { {} }
|
7
|
+
|
8
|
+
describe '#new' do
|
9
|
+
let(:configuration) { Mailpeek::Configuration.new }
|
10
|
+
|
11
|
+
it 'sets settings to default configuration' do
|
12
|
+
settings = {
|
13
|
+
limit: Mailpeek.configuration.limit,
|
14
|
+
location: Mailpeek.configuration.location
|
15
|
+
}
|
16
|
+
|
17
|
+
expect(delivery.settings).to eq settings
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'without location option' do
|
21
|
+
before do
|
22
|
+
configuration.location = nil
|
23
|
+
|
24
|
+
allow(Mailpeek).to receive(:configuration).and_return(configuration)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'raises InvalidOption' do
|
28
|
+
expect { delivery }.to raise_error(Mailpeek::Delivery::InvalidOption)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'without limit option' do
|
33
|
+
before do
|
34
|
+
configuration.limit = nil
|
35
|
+
|
36
|
+
allow(Mailpeek).to receive(:configuration).and_return(configuration)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'raises InvalidOption' do
|
40
|
+
expect { delivery }.to raise_error(Mailpeek::Delivery::InvalidOption)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'with limit set to non-number' do
|
45
|
+
before do
|
46
|
+
configuration.limit = 'invalid'
|
47
|
+
|
48
|
+
allow(Mailpeek).to receive(:configuration).and_return(configuration)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'raises InvalidOption' do
|
52
|
+
expect { delivery }.to raise_error(Mailpeek::Delivery::InvalidOption)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#deliver!' do
|
58
|
+
let(:path) { "./spec/support/emails/#{id}/mail" }
|
59
|
+
let(:mail) { ::Mail.read(path) }
|
60
|
+
let(:timestamp) { delivery.deliver!(mail) }
|
61
|
+
let(:basepath) { File.join(Mailpeek.configuration.location, timestamp) }
|
62
|
+
let(:file) { instance_double('File') }
|
63
|
+
|
64
|
+
before do
|
65
|
+
mail # declare first so our File stubs don't get in the way
|
66
|
+
|
67
|
+
allow(FileUtils).to receive(:mkdir_p).and_return(true)
|
68
|
+
|
69
|
+
allow(file).to receive(:write).and_return(file)
|
70
|
+
allow(File).to receive(:open).and_yield(file)
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'with email without attachments' do
|
74
|
+
let(:id) { 'html_and_text' }
|
75
|
+
|
76
|
+
it 'returns a timestamp' do
|
77
|
+
expect(timestamp).to be_truthy
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'creates a directory' do
|
81
|
+
timestamp
|
82
|
+
|
83
|
+
expect(FileUtils).to have_received(:mkdir_p).with(basepath).once
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'opens a file' do
|
87
|
+
timestamp
|
88
|
+
|
89
|
+
expect(File).to(
|
90
|
+
have_received(:open).with(File.join(basepath, 'mail'), 'w').once
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'saves a file' do
|
95
|
+
timestamp
|
96
|
+
|
97
|
+
expect(file).to have_received(:write).once
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'with attachment email' do
|
102
|
+
let(:id) { 'html_text_and_attachment' }
|
103
|
+
|
104
|
+
it 'returns a timestamp' do
|
105
|
+
expect(timestamp).to be_truthy
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'creates 2 directories' do
|
109
|
+
timestamp
|
110
|
+
|
111
|
+
expect(FileUtils).to have_received(:mkdir_p).twice
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'opens 2 files' do
|
115
|
+
timestamp
|
116
|
+
|
117
|
+
expect(File).to have_received(:open).twice
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'saves 2 files' do
|
121
|
+
timestamp
|
122
|
+
|
123
|
+
expect(file).to have_received(:write).twice
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Mailpeek::Email do
|
4
|
+
subject(:email) { described_class.new(id, ::Mail.read(path)) }
|
5
|
+
|
6
|
+
let(:path) { "./spec/support/emails/#{id}/mail" }
|
7
|
+
let(:id) { 'html_and_text' }
|
8
|
+
|
9
|
+
describe '#new' do
|
10
|
+
context 'with html and text email' do
|
11
|
+
it 'sets html' do
|
12
|
+
expect(email.html.nil?).to eq false
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'sets text' do
|
16
|
+
expect(email.text.nil?).to eq false
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'has 0 attachments' do
|
20
|
+
expect(email.attachments.empty?).to eq true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'with text email' do
|
25
|
+
let(:id) { 'text' }
|
26
|
+
|
27
|
+
it 'does not set html' do
|
28
|
+
expect(email.html.nil?).to eq true
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'sets text' do
|
32
|
+
expect(email.text.nil?).to eq false
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'has 0 attachments' do
|
36
|
+
expect(email.attachments.empty?).to eq true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'with html email' do
|
41
|
+
let(:id) { 'html' }
|
42
|
+
|
43
|
+
it 'sets html' do
|
44
|
+
expect(email.html.nil?).to eq false
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'does not set text' do
|
48
|
+
expect(email.text.nil?).to eq true
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'has 0 attachments' do
|
52
|
+
expect(email.attachments.empty?).to eq true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'with html, text and attachment email' do
|
57
|
+
let(:id) { 'html_text_and_attachment' }
|
58
|
+
|
59
|
+
it 'sets html' do
|
60
|
+
expect(email.html.nil?).to eq false
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'sets text' do
|
64
|
+
expect(email.text.nil?).to eq false
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'has attachments' do
|
68
|
+
expect(email.attachments.empty?).to eq false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#match?' do
|
74
|
+
let(:results) { email.match?(query) }
|
75
|
+
|
76
|
+
context 'with valid query' do
|
77
|
+
let(:query) { 'Itaque' }
|
78
|
+
|
79
|
+
it 'returns true' do
|
80
|
+
expect(results).to be_truthy
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'with invalid query' do
|
85
|
+
let(:query) { 'Invalid' }
|
86
|
+
|
87
|
+
it 'returns true' do
|
88
|
+
expect(results).to be_falsey
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '#destroy' do
|
94
|
+
before do
|
95
|
+
allow(FileUtils).to receive(:rm_rf).and_return(true)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'returns true' do
|
99
|
+
expect(email.destroy).to eq true
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'removes directory' do
|
103
|
+
email.destroy
|
104
|
+
|
105
|
+
with = "#{Mailpeek.configuration.location}/#{id}"
|
106
|
+
|
107
|
+
expect(FileUtils).to have_received(:rm_rf).with(with).once
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe '#read' do
|
112
|
+
context 'with read email' do
|
113
|
+
it 'returns true' do
|
114
|
+
expect(email.read).to eq true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'with unread email' do
|
119
|
+
let(:id) { 'text' }
|
120
|
+
|
121
|
+
it 'returns false' do
|
122
|
+
expect(email.read).to eq false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe '#read=' do
|
128
|
+
context 'with read email' do
|
129
|
+
after do
|
130
|
+
email.read = true
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'unreads file' do
|
134
|
+
email.read = false
|
135
|
+
expect(email.read).to eq false
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context 'with unread email' do
|
140
|
+
let(:id) { 'html' }
|
141
|
+
|
142
|
+
after do
|
143
|
+
email.read = false
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'unreads file' do
|
147
|
+
email.read = true
|
148
|
+
expect(email.read).to eq true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|