mailpeek 1.0.2 → 1.0.4

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: '0235196fb58aaf81253349db5403378590dbf0b359a0f553fda33db73dac5072'
4
- data.tar.gz: b3646014f52e01d097d60a46dd5ae1cb2468e306af777f3d71bf9106d1b94786
3
+ metadata.gz: 4514f84574556dabbc17ae49a42be6689297ba535aa4abfdd4ef57151af05ae8
4
+ data.tar.gz: 2d2958d8a2cf0d050335fb58d93057256f3c3a74d53d704e9424e7b6deb6c54b
5
5
  SHA512:
6
- metadata.gz: 22786fdb89e523070567fee50898956592bed0ffdfddee8f4da0ffd7d7d4efe8cf6e9ace3bf3a9a5e88ca3028181fb2233241a09eb500ed1ec7d28a1a68b6dc6
7
- data.tar.gz: 39fb2a5a99003630057871273c1925da8d059c5364eb5345ae9b151131da32c2399be8b176854f72e8a3e5fb9a51f259ac58ed0302e78317e4b0ef7b581cbdd1
6
+ metadata.gz: 66ca6a516758ad3140d74d769cd0b62df634ab9dbd76d461bad401f01a49ccb87b226926000ba4b3d86cbb325fb5d419b7bb5996836363fe61b474451b50e0fc
7
+ data.tar.gz: 5fe63727c42d669e4b84ae4ff87e986f685aec216402b63060f4e9432c62c254c4b433bc2403a6dc1bc95e027bee2ffef15c8f659ebcfcd31a9352a50a85d417
data/.gitignore CHANGED
@@ -13,3 +13,4 @@ spec/dummy/log/*.log
13
13
  spec/dummy/tmp/*
14
14
  !spec/dummy/tmp/mailpeek/*
15
15
  spec/dummy/.sass-cache
16
+ .ruby-version
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
- Lint/AmbiguousBlockAssociation:
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/README.md CHANGED
@@ -7,7 +7,7 @@ Mailpeek provides a web interface to view emails sent out when developing in Rai
7
7
  Add to your Gemfile:
8
8
 
9
9
  ```
10
- gem 'mailpeek', group: :development
10
+ gem 'mailpeek', group: %i[development test]
11
11
  ```
12
12
 
13
13
  ## Setup
@@ -18,8 +18,24 @@ In your `config/environments/development.rb` file:
18
18
  config.action_mailer.delivery_method = :mailpeek
19
19
  ```
20
20
 
21
- Then in your `routes.rb` file add this line:
21
+ Then add a file called `config/initializers/mailpeek.rb` with this:
22
+
23
+ ```
24
+ Mailpeek.configure do |config|
25
+ config.location = Rails.root.join('tmp/mailpeek')
26
+ end
27
+ ```
28
+
29
+ Finally in your `routes.rb` file add this line:
22
30
 
23
31
  ```
24
32
  mount Mailpeek::Web => '/mailpeek' if Rails.env.development?
25
33
  ```
34
+
35
+ ## Use
36
+
37
+ Once an email is sent in a Rails project, either via `deliver_now` or `deliver_later`. You can then view the email by visiting:
38
+
39
+ ```
40
+ http://localhost:3000/mailpeek
41
+ ```
@@ -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 = Rails.root.join('tmp', 'mailpeek')
9
+ @location = Dir.mktmpdir('mailpeek')
12
10
  @limit = 50
13
11
  end
14
12
  end
@@ -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: Delivery
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, 'A location option is required when using Mailpeek'
17
+ raise InvalidOption, 'The Mailpeek location option is required'
21
18
  end
22
19
 
23
- # rubocop:disable Style/GuardClause
24
- if options[:limit].nil?
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
- basepath = File.join(settings[:location], Time.now.to_i.to_s)
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
- return true unless mail.attachments.any?
36
+ add_attachments(mail, basepath) if mail.attachments.any?
45
37
 
46
- add_attachments(mail, basepath)
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 |f|
64
- f.write mail.to_s
54
+ File.open(File.join(basepath, 'mail'), 'w') do |file|
55
+ file.write(mail.to_s)
65
56
  end
66
57
  end
67
58
 
@@ -1,13 +1,20 @@
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
8
6
  attr_reader(
9
- :id, :position, :mail, :html, :text, :attachments, :to, :from, :subject,
10
- :message_id, :date
7
+ :attachments,
8
+ :date,
9
+ :from,
10
+ :html,
11
+ :id,
12
+ :mail,
13
+ :message_id,
14
+ :position,
15
+ :subject,
16
+ :text,
17
+ :to,
11
18
  )
12
19
 
13
20
  def initialize(timestamp, mail)
@@ -21,11 +28,7 @@ module Mailpeek
21
28
  @date = mail.date
22
29
  @attachments = []
23
30
 
24
- if mail.multipart?
25
- parse_parts
26
- else
27
- parse_body
28
- end
31
+ mail.multipart? ? parse_parts : parse_body
29
32
  end
30
33
 
31
34
  def match?(query)
@@ -33,9 +36,7 @@ module Mailpeek
33
36
  end
34
37
 
35
38
  def destroy
36
- location = Mailpeek.configuration.location
37
-
38
- FileUtils.rm_rf("#{location}/#{id}")
39
+ FileUtils.rm_rf("#{Mailpeek.configuration.location}/#{id}")
39
40
  end
40
41
 
41
42
  def read
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mailpeek
4
- VERSION = '1.0.2'
4
+ VERSION = '1.0.4'
5
5
  end
@@ -18,7 +18,7 @@ module Mailpeek
18
18
  end
19
19
 
20
20
  def redirect(location)
21
- throw :halt, [302, { 'Location' => "#{request.base_url}#{location}" }, []]
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
- # rubocop:disable Style/ClassVars
93
- @@files ||= {}
94
- # rubocop:enable Style/ClassVars
92
+ # @@files ||= {}
95
93
  end
96
94
 
97
95
  private
@@ -37,10 +37,6 @@ module Mailpeek
37
37
  @query_string ||= request.query_string
38
38
  end
39
39
 
40
- def current_path
41
- @current_path ||= request.path_info.gsub(%r{^\/}, '')
42
- end
43
-
44
40
  def h(text)
45
41
  ::Rack::Utils.escape_html(text)
46
42
  rescue ArgumentError => e
data/lib/mailpeek/web.rb CHANGED
@@ -10,7 +10,7 @@ require 'mailpeek/web/application'
10
10
  require 'rack/protection'
11
11
 
12
12
  require 'rack/builder'
13
- require 'rack/file'
13
+ require 'rack/files'
14
14
 
15
15
  # Public: Mailpeek
16
16
  module Mailpeek
@@ -114,7 +114,7 @@ module Mailpeek
114
114
  ::Rack::Builder.new do
115
115
  %w[stylesheets javascripts images].each do |asset_dir|
116
116
  map "/#{asset_dir}" do
117
- run ::Rack::File.new(
117
+ run ::Rack::Files.new(
118
118
  "#{ASSETS}/#{asset_dir}",
119
119
  'Cache-Control' => 'public, max-age=86400'
120
120
  )
data/lib/mailpeek.rb CHANGED
@@ -8,8 +8,9 @@ require 'mailpeek/web'
8
8
  require 'mailpeek/version'
9
9
 
10
10
  require 'json'
11
+ require 'tmpdir'
11
12
 
12
- # Public: Mailpeek
13
+ # Public: Mailpeek Base Class
13
14
  module Mailpeek
14
15
  def self.configuration
15
16
  @configuration ||= Configuration.new
data/mailpeek.gemspec CHANGED
@@ -19,14 +19,18 @@ Gem::Specification.new do |s|
19
19
 
20
20
  s.files = `git ls-files`.split("\n")
21
21
 
22
- # s.test_files = Dir['spec/**/*']
22
+ s.test_files = Dir['spec/**/*']
23
23
 
24
- s.required_ruby_version = '>= 2.3.0'
24
+ s.required_ruby_version = '>= 3.0.0'
25
25
 
26
- s.add_dependency 'mail', '~> 2.7'
27
- s.add_dependency 'rack', '~> 2.0'
28
- s.add_dependency 'rack-protection', '~> 2.0'
26
+ s.add_dependency 'mail', '~> 2'
27
+ s.add_dependency 'rack', '~> 3'
28
+ s.add_dependency 'rack-protection', '~> 4'
29
29
 
30
- s.add_development_dependency 'rubocop', '~> 0.79'
31
- s.add_development_dependency 'rubocop-performance', '~> 1.5'
30
+ s.add_development_dependency 'faker'
31
+ s.add_development_dependency 'rspec'
32
+ s.add_development_dependency 'rubocop'
33
+ s.add_development_dependency 'rubocop-performance'
34
+ s.add_development_dependency 'rubocop-rspec'
35
+ s.add_development_dependency 'simplecov'
32
36
  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
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Mailpeek do
4
+ describe '.emails' do
5
+ it 'returns all emails' do
6
+ expect(described_class.emails.size).to eq 4
7
+ end
8
+ end
9
+
10
+ describe '.unread' do
11
+ it 'returns count of unread emails' do
12
+ expect(described_class.unread).to eq 3
13
+ end
14
+ end
15
+
16
+ describe '.email' do
17
+ let(:id) { 'html_and_text' }
18
+
19
+ it 'returns email by id' do
20
+ expect(described_class.email(id).id).to eq id
21
+ end
22
+ end
23
+
24
+ describe '.prep_folder' do
25
+ it 'creates folder to store emails, if does not exist' do
26
+ allow(File).to receive(:directory?).and_return(false)
27
+ allow(FileUtils).to receive(:mkdir_p).and_return(true)
28
+
29
+ described_class.prep_folder
30
+
31
+ location = described_class.configuration.location
32
+
33
+ expect(FileUtils).to have_received(:mkdir_p).with(location).once
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'simplecov'
4
+
5
+ SimpleCov.start do
6
+ # add_group 'Group', 'lib/mailpeek'
7
+ end
8
+
9
+ require 'mailpeek'
10
+ require 'faker'
11
+
12
+ Dir[File.dirname(__FILE__) + '/support/**/*.rb'].sort.each { |f| require f }
13
+
14
+ RSpec.configure do |config|
15
+ config.before do
16
+ Mailpeek.configure do |c|
17
+ c.location = File.dirname(__FILE__) + '/support/emails'
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ Date: Fri, 31 Jan 2020 11:06:52 -0700
2
+ From: trudi@rutherford.com
3
+ To: erika@schmidt.biz
4
+ Message-ID: <5e346cbc64b6a_112ab1096aef5c4633b@f14030d40a1c.mail>
5
+ Subject: Eligendi nihil reprehenderit exercitationem.
6
+ Mime-Version: 1.0
7
+ Content-Type: text/html;
8
+ charset=UTF-8
9
+ Content-Transfer-Encoding: 7bit
10
+
11
+ <html>
12
+ <body>
13
+ <h3>Sequi</h3>
14
+
15
+ <p>Consequatur dolorem quia. Dolore architecto rerum. Et rerum exercitationem.<br>
16
+ 0. In.</p>
17
+
18
+ </body>
19
+ </html>