mailhandler 1.0.38 → 1.0.39

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.
Files changed (38) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +26 -0
  3. data/Gemfile +2 -0
  4. data/lib/mailhandler.rb +2 -10
  5. data/lib/mailhandler/errors.rb +1 -0
  6. data/lib/mailhandler/receiver.rb +14 -6
  7. data/lib/mailhandler/receiving/base.rb +28 -22
  8. data/lib/mailhandler/receiving/filelist/base.rb +17 -6
  9. data/lib/mailhandler/receiving/filelist/filter/base.rb +8 -3
  10. data/lib/mailhandler/receiving/filelist/filter/email.rb +3 -0
  11. data/lib/mailhandler/receiving/folder.rb +8 -3
  12. data/lib/mailhandler/receiving/imap.rb +53 -51
  13. data/lib/mailhandler/receiving/mail.rb +6 -13
  14. data/lib/mailhandler/receiving/notification/console.rb +2 -1
  15. data/lib/mailhandler/receiving/notification/email.rb +7 -4
  16. data/lib/mailhandler/receiving/notification/email/content.rb +1 -4
  17. data/lib/mailhandler/receiving/notification/email/states.rb +23 -20
  18. data/lib/mailhandler/receiving/observer.rb +2 -1
  19. data/lib/mailhandler/sender.rb +1 -1
  20. data/lib/mailhandler/sending/api.rb +3 -3
  21. data/lib/mailhandler/sending/api_batch.rb +4 -1
  22. data/lib/mailhandler/sending/base.rb +4 -1
  23. data/lib/mailhandler/sending/smtp.rb +0 -1
  24. data/lib/mailhandler/version.rb +1 -1
  25. data/mailhandler.gemspec +1 -2
  26. data/spec/unit/mailhandler/receiver_spec.rb +23 -15
  27. data/spec/unit/mailhandler/receiving/base_spec.rb +8 -7
  28. data/spec/unit/mailhandler/receiving/folder_spec.rb +42 -29
  29. data/spec/unit/mailhandler/receiving/imap_spec.rb +4 -5
  30. data/spec/unit/mailhandler/receiving/notification/console_spec.rb +5 -5
  31. data/spec/unit/mailhandler/receiving/notification/email/content_spec.rb +22 -18
  32. data/spec/unit/mailhandler/receiving/notification/email_spec.rb +12 -12
  33. data/spec/unit/mailhandler/sender_spec.rb +7 -3
  34. data/spec/unit/mailhandler/sending/sender_api_batch_spec.rb +9 -7
  35. data/spec/unit/mailhandler/sending/sender_api_spec.rb +4 -4
  36. data/spec/unit/mailhandler/sending/sender_smtp_spec.rb +7 -7
  37. data/spec/unit/mailhandler_spec.rb +10 -9
  38. metadata +6 -6
@@ -1,8 +1,9 @@
1
1
  module Mail
2
+ # IMAP class patch to better manage connection and search of emails
2
3
  class IMAP
3
4
  attr_accessor :imap_connection
4
5
 
5
- def find_emails(options = {}, &block)
6
+ def find_emails(options = {}, &block) # rubocop:disable all
6
7
  options = validate_options(options)
7
8
  options[:read_only] ? imap_connection.examine(options[:mailbox]) : imap_connection.select(options[:mailbox])
8
9
  uids = imap_connection.uid_search(options[:keys])
@@ -13,7 +14,6 @@ module Mail
13
14
  (options[:what].to_sym != :last && options[:order].to_sym == :desc)
14
15
 
15
16
  if block_given?
16
-
17
17
  uids.each do |uid|
18
18
  uid = options[:uid].to_i unless options[:uid].nil?
19
19
  fetchdata = imap_connection.uid_fetch(uid, ['RFC822'])[0]
@@ -26,14 +26,12 @@ module Mail
26
26
  yield new_message
27
27
  end
28
28
 
29
- imap_connection.uid_store(uid, '+FLAGS', [Net::IMAP::DELETED]) if options[:delete_after_find] && new_message.is_marked_for_delete?
29
+ imap_connection.uid_store(uid, '+FLAGS', [Net::IMAP::DELETED]) if options[:delete_after_find] &&
30
+ new_message.is_marked_for_delete?
30
31
  break unless options[:uid].nil?
31
32
  end
32
-
33
33
  imap_connection.expunge if options[:delete_after_find]
34
-
35
34
  else
36
-
37
35
  emails = []
38
36
 
39
37
  uids.each do |uid|
@@ -46,12 +44,11 @@ module Mail
46
44
 
47
45
  imap_connection.expunge if options[:delete_after_find]
48
46
  emails.size == 1 && options[:count] == 1 ? emails.first : emails
49
-
50
47
  end
51
48
  end
52
49
 
53
50
  # Start an IMAP session
54
- def connect(_config = Mail::Configuration.instance)
51
+ def connect(_config = Mail::Configuration.instance) # rubocop:disable all
55
52
  @imap_connection = Net::IMAP.new(settings[:address], settings[:port], settings[:enable_ssl], nil, false)
56
53
 
57
54
  if settings[:authentication].nil?
@@ -64,11 +61,7 @@ module Mail
64
61
  end
65
62
 
66
63
  def disconnect
67
- if defined?(imap_connection) && imap_connection && !imap_connection.disconnected?
68
-
69
- imap_connection.disconnect
70
-
71
- end
64
+ imap_connection.disconnect if defined?(imap_connection) && imap_connection && !imap_connection.disconnected?
72
65
  end
73
66
  end
74
67
  end
@@ -1,6 +1,7 @@
1
1
  module MailHandler
2
2
  module Receiving
3
3
  module Notification
4
+ # notification in form of console output
4
5
  class Console
5
6
  def notify(search)
6
7
  output_delay Time.now - search.started_at
@@ -15,7 +16,7 @@ module MailHandler
15
16
  # print to screen delay length
16
17
  def output_delay(delay)
17
18
  delay_seconds = delay.to_i
18
- output(delay_seconds) if [0, 1].include? (delay_seconds % Seconds::TO_SHOW)
19
+ output(delay_seconds) if [0, 1].include?(delay_seconds % Seconds::TO_SHOW)
19
20
  end
20
21
 
21
22
  # print to screen delay length
@@ -5,6 +5,7 @@ require_relative '../../errors'
5
5
  module MailHandler
6
6
  module Receiving
7
7
  module Notification
8
+ # notification in form of sent email
8
9
  class Email
9
10
  attr_accessor :sender,
10
11
  :from,
@@ -21,7 +22,7 @@ module MailHandler
21
22
  @from = from
22
23
  @contacts = to
23
24
  init_state
24
- set_content_handler(EmailContent.new)
25
+ change_content_handler(EmailContent.new)
25
26
  end
26
27
 
27
28
  def notify(search)
@@ -30,7 +31,7 @@ module MailHandler
30
31
  @current_state.notify(search)
31
32
  end
32
33
 
33
- def set_state(state)
34
+ def change_state(state)
34
35
  @current_state = state
35
36
  end
36
37
 
@@ -42,7 +43,7 @@ module MailHandler
42
43
 
43
44
  # Allow users to specify their own content classes.
44
45
  # Class must match by methods to the interface of MailHandler::Receiving::Notification::EmailContent
45
- def set_content_handler(content_handler)
46
+ def change_content_handler(content_handler)
46
47
  @content_handler = content_handler
47
48
  end
48
49
 
@@ -55,7 +56,9 @@ module MailHandler
55
56
  EMAIL_TYPES = %i[delayed received].freeze
56
57
 
57
58
  def verify_email_type(type)
58
- raise MailHandler::TypeError, "Incorrect type: #{type}, allowed types: #{EMAIL_TYPES}." unless EMAIL_TYPES.include? type
59
+ return if EMAIL_TYPES.include?(type)
60
+
61
+ raise MailHandler::TypeError, "Incorrect type: #{type}, allowed types: #{EMAIL_TYPES}."
59
62
  end
60
63
  end
61
64
  end
@@ -3,6 +3,7 @@ require 'mail'
3
3
  module MailHandler
4
4
  module Receiving
5
5
  module Notification
6
+ # email notification sent
6
7
  class EmailContent
7
8
  # @param [Symbol] type - notification type
8
9
  # @param [Hash] options - search options used for searching for an email
@@ -16,19 +17,15 @@ module MailHandler
16
17
  delay = (delay.to_f / 60).round(2)
17
18
 
18
19
  case type
19
-
20
20
  when :received
21
-
22
21
  mail.subject = "Received - delay was #{delay} minutes"
23
22
  mail.body = "Received - delay was #{delay} minutes - search by #{options}"
24
23
 
25
24
  when :delayed
26
-
27
25
  mail.subject = "Over #{delay} minutes delay"
28
26
  mail.body = "Over #{delay} minutes delay - search by #{options}"
29
27
 
30
28
  else
31
-
32
29
  raise StandardError, "Incorrect type: #{type}"
33
30
 
34
31
  end
@@ -4,6 +4,7 @@ require_relative '../../../errors'
4
4
  module MailHandler
5
5
  module Receiving
6
6
  module Notification
7
+ # base state
7
8
  class DelayState
8
9
  attr_accessor :context,
9
10
  :notified
@@ -23,52 +24,54 @@ module MailHandler
23
24
  protected
24
25
 
25
26
  def send_notification_email(type, search)
26
- unless notified
27
+ return if notified
27
28
 
28
- context.send_email(type, search)
29
- notification_fired
29
+ context.send_email(type, search)
30
+ notification_fired
31
+ end
30
32
 
31
- end
33
+ def change_notification_state(search, state)
34
+ context.change_state(state)
35
+ context.notify(search)
32
36
  end
33
37
  end
34
38
 
39
+ # there was no delay
35
40
  class NoDelay < DelayState
36
41
  def notify(search)
37
- if Time.now - search.started_at >= context.min_time_to_notify
42
+ return unless Time.now - search.started_at >= context.min_time_to_notify
38
43
 
39
- context.set_state(Delay.new(context))
40
- context.notify(search)
41
-
42
- end
44
+ change_notification_state(search, Delay.new(context))
43
45
  end
44
46
  end
45
47
 
48
+ # delay happened
46
49
  class Delay < DelayState
47
50
  def notify(search)
48
51
  if search.result
49
-
50
- context.set_state(Received.new(context))
51
- context.notify(search)
52
-
53
- elsif Time.now - search.started_at >= context.max_time_to_notify
54
-
55
- context.set_state(MaxDelay.new(context))
56
- context.notify(search)
57
-
52
+ change_notification_state(search, Received.new(context))
53
+ elsif max_time_to_notify?(search)
54
+ change_notification_state(search, MaxDelay.new(context))
58
55
  else
59
-
60
56
  send_notification_email(:delayed, search)
61
-
62
57
  end
63
58
  end
59
+
60
+ private
61
+
62
+ def max_time_to_notify?(search)
63
+ Time.now - search.started_at >= context.max_time_to_notify
64
+ end
64
65
  end
65
66
 
67
+ # maximum delay checked happened
66
68
  class MaxDelay < DelayState
67
69
  def notify(search)
68
70
  send_notification_email(:delayed, search)
69
71
  end
70
72
  end
71
73
 
74
+ # no more delays will be fired
72
75
  class Received < DelayState
73
76
  def notify(search)
74
77
  send_notification_email(:received, search)
@@ -1,11 +1,12 @@
1
1
  module MailHandler
2
2
  module Receiving
3
+ # observer handler
3
4
  module Observer
4
5
  def init_observer
5
6
  @observers = []
6
7
  end
7
8
 
8
- def get_observers()
9
+ def observers
9
10
  @observers
10
11
  end
11
12
 
@@ -2,8 +2,8 @@ require_relative 'sending/smtp'
2
2
  require_relative 'sending/api'
3
3
  require_relative 'sending/api_batch'
4
4
 
5
- # Class for sending email, and storing details about the sending.
6
5
  module MailHandler
6
+ # Class for sending email, and storing details about the sending.
7
7
  class Sender
8
8
  attr_accessor :dispatcher,
9
9
  :sending
@@ -4,6 +4,7 @@ require_relative 'base.rb'
4
4
 
5
5
  module MailHandler
6
6
  module Sending
7
+ # sending email by Postmark API
7
8
  class PostmarkAPISender < Sender
8
9
  attr_accessor :host,
9
10
  :api_token,
@@ -35,11 +36,10 @@ module MailHandler
35
36
  def setup_sending_client
36
37
  # clearing cache so valid host is accepted, and not the cached one
37
38
  Postmark::HttpClient.instance_variable_set('@http', nil)
38
- Postmark::ApiClient.new(api_token, http_open_timeout: http_open_timeout, http_read_timeout: http_read_timeout, host: host, secure: @use_ssl)
39
+ Postmark::ApiClient.new(api_token, http_open_timeout: http_open_timeout, http_read_timeout: http_read_timeout,
40
+ host: host, secure: @use_ssl)
39
41
  end
40
42
 
41
- protected
42
-
43
43
  DEFAULT_HOST = 'api.postmarkapp.com'.freeze
44
44
  end
45
45
  end
@@ -3,6 +3,7 @@ require_relative '../errors'
3
3
 
4
4
  module MailHandler
5
5
  module Sending
6
+ # sending batch email by Postmark API
6
7
  class PostmarkBatchAPISender < PostmarkAPISender
7
8
  def initialize(api_token = nil)
8
9
  super(api_token)
@@ -17,7 +18,9 @@ module MailHandler
17
18
  protected
18
19
 
19
20
  def verify_email(emails)
20
- raise MailHandler::TypeError, 'Invalid type error, only Array of Mail::Message object types for sending allowed' unless emails.is_a?(Array) && emails.all? { |e| e.is_a? allowed_email_type }
21
+ return if emails.is_a?(Array) && emails.all? { |e| e.is_a? allowed_email_type }
22
+
23
+ raise MailHandler::TypeError, 'Invalid type error, only Array of Mail::Message object types for sending allowed'
21
24
  end
22
25
  end
23
26
  end
@@ -2,6 +2,7 @@ require_relative '../errors'
2
2
 
3
3
  module MailHandler
4
4
  module Sending
5
+ # email sending handler class
5
6
  class Sender
6
7
  attr_reader :type
7
8
 
@@ -12,7 +13,9 @@ module MailHandler
12
13
  protected
13
14
 
14
15
  def verify_email(email)
15
- raise MailHandler::TypeError, "Invalid type error, only #{allowed_email_type} object type for sending allowed." unless email.is_a?(allowed_email_type)
16
+ return if email.is_a?(allowed_email_type)
17
+
18
+ raise MailHandler::TypeError, "Invalid type error, only #{allowed_email_type} object type for sending allowed."
16
19
  end
17
20
 
18
21
  private
@@ -1,4 +1,3 @@
1
-
2
1
  require 'net/imap'
3
2
  require_relative 'base'
4
3
 
@@ -1,3 +1,3 @@
1
1
  module MailHandler
2
- VERSION = '1.0.38'.freeze
2
+ VERSION = '1.0.39'.freeze
3
3
  end
@@ -1,4 +1,3 @@
1
-
2
1
  lib = File.expand_path('lib', __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'mailhandler/version'
@@ -13,7 +12,7 @@ Gem::Specification.new do |s|
13
12
  s.email = ['ibalosh@gmail.com', 'igor@wildbit.com']
14
13
 
15
14
  s.summary = 'Postmark email receiving and sending handler.'
16
- s.description = 'Use this gem to send emails through SMTP and Postmark API. Check if email arrived by imap or folder check.'
15
+ s.description = 'Use this gem to send emails through SMTP and Postmark API and check if email arrived.'
17
16
 
18
17
  s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
19
18
  s.test_files = `git ls-files -- {spec}/*`.split("\n")
@@ -2,21 +2,26 @@ require 'spec_helper'
2
2
 
3
3
  describe MailHandler::Receiver do
4
4
  context 'valid receiver' do
5
+ subject(:receiver) { described_class.new(checker) }
6
+
5
7
  let(:default_search_option) { { by_subject: 'test' } }
6
8
  let(:receiving_duration) { 5 }
7
9
  let(:found_email) { Mail.new { subject 'test email' } }
8
10
  let(:checker) do
9
- checker = double('Checker')
11
+ checker = instance_double('Checker')
12
+
13
+ allow(checker).to receive(:find) do
14
+ sleep receiving_duration
15
+ true
16
+ end
10
17
 
11
- allow(checker).to receive(:find) { sleep receiving_duration; true }
12
- allow(checker).to receive(:search_result) { true }
18
+ allow(checker).to receive(:search_result).and_return(true)
13
19
  allow(checker).to receive(:found_emails) { [found_email] }
14
- allow(checker).to receive(:reset_found_emails) { [] }
15
- allow(checker).to receive(:start) { nil }
16
- allow(checker).to receive(:stop) { nil }
20
+ allow(checker).to receive(:reset_found_emails).and_return([])
21
+ allow(checker).to receive(:start).and_return(nil)
22
+ allow(checker).to receive(:stop).and_return(nil)
17
23
  checker
18
24
  end
19
- subject(:receiver) { MailHandler::Receiver.new(checker) }
20
25
 
21
26
  context 'att readers' do
22
27
  it { is_expected.to respond_to(:checker) }
@@ -31,7 +36,7 @@ describe MailHandler::Receiver do
31
36
  end
32
37
 
33
38
  it 'create' do
34
- is_expected.to be_kind_of MailHandler::Receiver
39
+ expect(receiver).to be_kind_of described_class
35
40
  end
36
41
 
37
42
  it '.find_email' do
@@ -54,14 +59,17 @@ describe MailHandler::Receiver do
54
59
 
55
60
  context '.search' do
56
61
  let(:checker) do
57
- checker = double('Checker')
62
+ checker = instance_double('Checker')
58
63
 
59
- allow(checker).to receive(:find) { sleep 1; false }
60
- allow(checker).to receive(:search_result) { false }
61
- allow(checker).to receive(:found_emails) { [] }
62
- allow(checker).to receive(:reset_found_emails) { [] }
63
- allow(checker).to receive(:start) { nil }
64
- allow(checker).to receive(:stop) { nil }
64
+ allow(checker).to receive(:find) do
65
+ sleep 1
66
+ false
67
+ end
68
+ allow(checker).to receive(:search_result).and_return(false)
69
+ allow(checker).to receive(:found_emails).and_return([])
70
+ allow(checker).to receive(:reset_found_emails).and_return([])
71
+ allow(checker).to receive(:start).and_return(nil)
72
+ allow(checker).to receive(:stop).and_return(nil)
65
73
  checker
66
74
  end
67
75
 
@@ -1,25 +1,26 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe MailHandler::Receiving::Checker do
4
- subject { MailHandler::Receiving::Checker.new }
4
+ subject(:receiving_checker) { described_class.new }
5
5
 
6
6
  it '.create' do
7
- is_expected.to be_kind_of MailHandler::Receiving::Checker
7
+ expect(receiving_checker).to be_kind_of described_class
8
8
  end
9
9
 
10
10
  it 'init details' do
11
11
  aggregate_failures 'receiving details' do
12
- expect(subject.search_options).to eq(count: 50, archive: false)
13
- expect(subject.found_emails).to eq []
14
- expect(subject.search_result).to be false
12
+ expect(receiving_checker.search_options).to eq(count: 50, archive: false)
13
+ expect(receiving_checker.found_emails).to eq []
14
+ expect(receiving_checker.search_result).to be false
15
15
  end
16
16
  end
17
17
 
18
18
  it '.find' do
19
- expect { subject.find(by_subject: 'test') }.to raise_error(MailHandler::InterfaceError, 'Find interface not implemented.')
19
+ expect { receiving_checker.find(by_subject: 'test') }
20
+ .to raise_error(MailHandler::InterfaceError, 'Find interface not implemented.')
20
21
  end
21
22
 
22
23
  it '.search_result' do
23
- expect(subject.search_result).to be false
24
+ expect(receiving_checker.search_result).to be false
24
25
  end
25
26
  end