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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: aa5320fb5d5625deccf6cdcf0376f5b7e2ec5adb
4
- data.tar.gz: 46b58bc289f75deda7e8deb31e1baa9770fa4807
2
+ SHA256:
3
+ metadata.gz: 7110dbb8ad938a31bdb9b4364e2cca1938aa1c9db573fc71acc00381946104a3
4
+ data.tar.gz: 60f583b519a3794cbca7c0efabc396808754557875c345d763d0fb28c9e182f7
5
5
  SHA512:
6
- metadata.gz: 2b8b6c4594b2a8c797e70d647a92e34f18d261a1b6e35904d8058506d296300664f506e16bd40bf0f36e8923f3ab7af1bac9416b90a83120d7fff23593c3e47d
7
- data.tar.gz: 63e2a18ba075c7203d8b92371646f67d14e67b53fcb2fccb35c07852dc6602f92b7abf6c84798ab20cd6b6ce3bb99b3f462e9661dcfd394c10b53bc842ef58f2
6
+ metadata.gz: 4a2af60b8ace6be532020704039cd57e38cc9a01af02fd0677936c96fc36fdc427c6836b958d91ad2786d0701880464702c563f8f44e032b4827b714193af6c7
7
+ data.tar.gz: 268ce1771328c25865e6c3059faf4411326f139170956a3be3a476330037ec712597fa5308390b93dc02769af047b0e339cccbfc8beaba0810b1d59cdfa4a5ef
@@ -0,0 +1,26 @@
1
+ require: rubocop-rspec
2
+
3
+ # increase line length since we are checking test files
4
+ Metrics/LineLength:
5
+ Max: 120
6
+
7
+ Metrics/MethodLength:
8
+ Max: 15
9
+
10
+ Metrics/ClassLength:
11
+ Max: 120
12
+
13
+ RSpec/ContextWording:
14
+ Enabled: false
15
+
16
+ RSpec/NestedGroups:
17
+ Max: 7
18
+
19
+ Metrics/BlockLength:
20
+ Max: 200
21
+
22
+ RSpec/FilePath:
23
+ Enabled: false
24
+
25
+ RSpec/ExampleLength:
26
+ Max: 10
data/Gemfile CHANGED
@@ -5,3 +5,5 @@ gem 'mail'
5
5
  gem 'postmark'
6
6
  gem 'pry'
7
7
  gem 'rspec'
8
+ gem 'rubocop'
9
+ gem 'rubocop-rspec'
@@ -71,11 +71,9 @@ module MailHandler
71
71
  # @param [Receiving::Object] receiver
72
72
  # @param [Array<Receiving::Notification::Class>] notifications
73
73
  def add_receiving_notifications(receiver, notifications)
74
- if (notifications - NOTIFICATION_TYPES.keys).empty?
74
+ return unless (notifications - NOTIFICATION_TYPES.keys).empty?
75
75
 
76
- notifications.each { |n| receiver.add_observer(NOTIFICATION_TYPES[n].new) }
77
-
78
- end
76
+ notifications.each { |n| receiver.add_observer(NOTIFICATION_TYPES[n].new) }
79
77
  end
80
78
 
81
79
  def verify_type(type, types)
@@ -83,25 +81,19 @@ module MailHandler
83
81
  end
84
82
 
85
83
  CHECKER_TYPES = {
86
-
87
84
  folder: Receiving::FolderChecker,
88
85
  imap: Receiving::IMAPChecker
89
-
90
86
  }.freeze
91
87
 
92
88
  SENDER_TYPES = {
93
-
94
89
  postmark_api: Sending::PostmarkAPISender,
95
90
  postmark_batch_api: Sending::PostmarkBatchAPISender,
96
91
  smtp: Sending::SMTPSender
97
-
98
92
  }.freeze
99
93
 
100
94
  NOTIFICATION_TYPES = {
101
-
102
95
  console: Receiving::Notification::Console,
103
96
  email: Receiving::Notification::Email
104
-
105
97
  }.freeze
106
98
  end
107
99
  end
@@ -1,4 +1,5 @@
1
1
  module MailHandler
2
+ # base error
2
3
  class Error < StandardError
3
4
  def initialize(message = nil)
4
5
  super(message)
@@ -4,6 +4,7 @@ require_relative 'receiving/observer'
4
4
  require_relative 'receiving/mail.rb'
5
5
 
6
6
  module MailHandler
7
+ # handling receiving email
7
8
  class Receiver
8
9
  include Receiving::Observer
9
10
 
@@ -40,13 +41,9 @@ module MailHandler
40
41
  checker.start
41
42
 
42
43
  until search_time_expired?
44
+ break if single_search(options)
43
45
 
44
- received = checker.find(options)
45
- update_search_details
46
- notify_observers(search)
47
- break if received
48
46
  sleep search_frequency
49
-
50
47
  end
51
48
 
52
49
  notify_observers(search)
@@ -57,6 +54,13 @@ module MailHandler
57
54
 
58
55
  private
59
56
 
57
+ def single_search(options)
58
+ received = checker.find(options)
59
+ update_search_details
60
+ notify_observers(search)
61
+ received
62
+ end
63
+
60
64
  def init_search_details(options)
61
65
  @search = Search.new
62
66
  @search.options = options
@@ -68,8 +72,12 @@ module MailHandler
68
72
  search.finished_at = Time.now
69
73
  search.duration = search.finished_at - search.started_at
70
74
  search.result = checker.search_result
75
+ update_search_email_details
76
+ end
77
+
78
+ def update_search_email_details
71
79
  search.emails = checker.found_emails
72
- search.email = search.emails.first
80
+ search.email = checker.found_emails.first
73
81
  end
74
82
 
75
83
  def search_time_expired?
@@ -33,10 +33,7 @@ module MailHandler
33
33
  @found_emails = []
34
34
  end
35
35
 
36
- private
37
-
38
36
  AVAILABLE_SEARCH_OPTIONS = %i[
39
-
40
37
  by_subject
41
38
  by_content
42
39
  since
@@ -59,36 +56,45 @@ module MailHandler
59
56
  end
60
57
 
61
58
  def validate_option_values(options)
62
- if options[:since]
63
-
64
- raise MailHandler::Error, "Incorrect option options[:since]=#{options[:since]}." unless options[:since].is_a?(Time)
65
-
66
- end
67
-
68
- unless options[:count].nil?
59
+ validate_since_option(options)
60
+ validate_count_option(options)
61
+ validate_archive_option(options)
62
+ validate_recipient_option(options)
63
+ end
69
64
 
70
- count = options[:count]
71
- raise MailHandler::Error, "Incorrect option options[:count]=#{options[:count]}." if (count < 0) || (count > 2000)
65
+ def validate_recipient_option(options)
66
+ return if options[:by_recipient].nil?
72
67
 
73
- end
68
+ error_message = "Incorrect option options[:by_recipient]=#{options[:by_recipient]}."
69
+ raise MailHandler::Error, error_message unless options[:by_recipient].is_a?(Hash)
70
+ end
74
71
 
75
- if options[:archive]
72
+ def validate_archive_option(options)
73
+ return if options[:archive].nil?
76
74
 
77
- raise MailHandler::Error, "Incorrect option options[:archive]=#{options[:archive]}." unless (options[:archive] == true) || (options[:archive] == false)
75
+ error_message = "Incorrect option options[:archive]=#{options[:archive]}."
76
+ raise MailHandler::Error, error_message unless [true, false].include?(options[:archive])
77
+ end
78
78
 
79
- end
79
+ def validate_since_option(options)
80
+ return if options[:since].nil?
80
81
 
81
- if options[:by_recipient]
82
+ error_message = "Incorrect option options[:since]=#{options[:since]}."
83
+ raise MailHandler::Error, error_message unless options[:since].is_a?(Time)
84
+ end
82
85
 
83
- raise MailHandler::Error, "Incorrect option options[:by_recipient]=#{options[:by_recipient]}." unless options[:by_recipient].is_a?(Hash)
86
+ def validate_count_option(options)
87
+ return if options[:count].nil?
84
88
 
85
- end
89
+ count = options[:count]
90
+ error_message = "Incorrect option options[:count]=#{options[:count]}."
91
+ raise MailHandler::Error, error_message if (count < 0) || (count > 2000)
86
92
  end
87
93
 
88
94
  def validate_used_options(options)
89
- unless (options.keys - available_search_options).empty?
90
- raise MailHandler::Error, "#{(options.keys - available_search_options)} - Incorrect search option values, options are #{available_search_options}."
91
- end
95
+ error_message = "#{(options.keys - available_search_options)} - Incorrect search option values,"\
96
+ " options are #{available_search_options}."
97
+ raise MailHandler::Error, error_message unless (options.keys - available_search_options).empty?
92
98
  end
93
99
 
94
100
  def set_base_search_options
@@ -2,9 +2,10 @@ require 'fileutils'
2
2
 
3
3
  # Base filtering class, which is used for reading list of all files based on passed pattern.
4
4
  # Patterns to be used can be checked here: http://ruby-doc.org/core-1.9.3/Dir.html
5
-
6
5
  module MailHandler
6
+ # namespace
7
7
  module Receiving
8
+ # namespace
8
9
  module FileHandling
9
10
  # if file exists, execute file operation, otherwise return default return value when it doesn't
10
11
  def access_file(file, default_return = false)
@@ -14,6 +15,7 @@ module MailHandler
14
15
  yield
15
16
  rescue StandardError => e
16
17
  raise e if File.exist? file
18
+
17
19
  default_return
18
20
  end
19
21
 
@@ -25,6 +27,7 @@ module MailHandler
25
27
  end
26
28
  end
27
29
 
30
+ # base filelist
28
31
  class FileList
29
32
  include FileHandling
30
33
 
@@ -37,25 +40,33 @@ module MailHandler
37
40
  j = 0
38
41
 
39
42
  while swapped
40
-
41
43
  swapped = false
42
44
  j += 1
43
45
 
44
46
  (files.size - j).times do |i|
45
- file1 = access_file(files[i], false) { File.new(files[i]).ctime }
46
- file2 = access_file(files[i + 1], false) { File.new(files[i + 1]).ctime }
47
+ next unless swap_files?(files[i], files[i + 1])
47
48
 
48
- next unless file1 && file2 && file1 < file2
49
49
  tmp = files[i]
50
50
  files[i] = files[i + 1]
51
51
  files[i + 1] = tmp
52
52
  swapped = true
53
53
  end
54
-
55
54
  end
56
55
 
57
56
  files
58
57
  end
58
+
59
+ private
60
+
61
+ def swap_files?(current_file, next_file)
62
+ file1 = get_file(current_file)
63
+ file2 = get_file(next_file)
64
+ file1 && file2 && file1 < file2
65
+ end
66
+
67
+ def get_file(file)
68
+ access_file(file, false) { File.new(file).ctime }
69
+ end
59
70
  end
60
71
  end
61
72
  end
@@ -3,8 +3,11 @@ require_relative '../../../errors'
3
3
 
4
4
  module MailHandler
5
5
  module Receiving
6
+ # namespace
6
7
  class FileList
8
+ # namespace
7
9
  module Filter
10
+ # base filter for files
8
11
  class Base
9
12
  attr_accessor :files, :fast_check
10
13
 
@@ -36,7 +39,7 @@ module MailHandler
36
39
  end
37
40
 
38
41
  module ByDate
39
-
42
+ # filter files by date
40
43
  class BaseDate < Base
41
44
  def initialize(files, date)
42
45
  super(files)
@@ -44,19 +47,21 @@ module MailHandler
44
47
  end
45
48
  end
46
49
 
50
+ # since date filter
47
51
  class Since < BaseDate
48
52
  private
49
53
 
50
54
  def meets_expectation?(file)
51
- File.exist?(file)? (File.ctime file) > @date : false
55
+ File.exist?(file) ? (File.ctime file) > @date : false
52
56
  end
53
57
  end
54
58
 
59
+ # before date filter
55
60
  class Before < Base
56
61
  private
57
62
 
58
63
  def meets_expectation?(file)
59
- File.exist?(file)? (File.ctime file) < @date : false
64
+ File.exist?(file) ? (File.ctime file) < @date : false
60
65
  end
61
66
  end
62
67
  end
@@ -33,6 +33,7 @@ module MailHandler
33
33
  end
34
34
  end
35
35
 
36
+ # filter by email content
36
37
  class ByEmailContent < Email
37
38
  def initialize(files, content)
38
39
  super(files)
@@ -60,6 +61,7 @@ module MailHandler
60
61
  end
61
62
  end
62
63
 
64
+ # filter by email subject
63
65
  class ByEmailSubject < ByEmailContent
64
66
  private
65
67
 
@@ -72,6 +74,7 @@ module MailHandler
72
74
  end
73
75
  end
74
76
 
77
+ # filter by email recipient
75
78
  class ByEmailRecipient < Email
76
79
  def initialize(files, recipient)
77
80
  super(files)
@@ -7,6 +7,7 @@ require_relative 'filelist/filter/email.rb'
7
7
 
8
8
  module MailHandler
9
9
  module Receiving
10
+ # folder checking base class
10
11
  class FolderChecker < Checker
11
12
  include FileHandling
12
13
 
@@ -76,7 +77,7 @@ module MailHandler
76
77
  end
77
78
 
78
79
  def move_files(files)
79
- files.each { |file| (inbox_folder == archive_folder) ? delete_file(file) : archive_file(file) }
80
+ files.each { |file| inbox_folder == archive_folder ? delete_file(file) : archive_file(file) }
80
81
  end
81
82
 
82
83
  def parse_email_from_files(files, count)
@@ -94,7 +95,10 @@ module MailHandler
94
95
  end
95
96
 
96
97
  def archive_file(file)
97
- access_file(file) { FileUtils.mv("#{inbox_folder}/#{File.basename(file)}", "#{archive_folder}/#{File.basename(file)}") }
98
+ access_file(file) do
99
+ FileUtils.mv("#{inbox_folder}/#{File.basename(file)}",
100
+ "#{archive_folder}/#{File.basename(file)}")
101
+ end
98
102
  end
99
103
 
100
104
  def delete_file(file)
@@ -103,7 +107,8 @@ module MailHandler
103
107
 
104
108
  def verify_mailbox_folders
105
109
  raise MailHandler::Error, 'Folder variables are not set.' if inbox_folder.nil? || archive_folder.nil?
106
- raise MailHandler::FileError, 'Mailbox folders do not exist.' unless File.directory?(inbox_folder) && File.directory?(archive_folder)
110
+ raise MailHandler::FileError, 'Mailbox folders do not exist.' unless File.directory?(inbox_folder) &&
111
+ File.directory?(archive_folder)
107
112
  end
108
113
  end
109
114
  end
@@ -4,6 +4,7 @@ require_relative '../errors'
4
4
 
5
5
  module MailHandler
6
6
  module Receiving
7
+ # in charge of retrieving email by IMAP
7
8
  class IMAPChecker < Checker
8
9
  attr_accessor :address,
9
10
  :port,
@@ -30,14 +31,16 @@ module MailHandler
30
31
  end
31
32
 
32
33
  def start
33
- unless manual_connection_manage
34
- init_retriever
35
- connect
36
- end
34
+ return if manual_connection_manage
35
+
36
+ init_retriever
37
+ connect
37
38
  end
38
39
 
39
40
  def stop
40
- disconnect unless manual_connection_manage
41
+ return if manual_connection_manage
42
+
43
+ disconnect
41
44
  end
42
45
 
43
46
  def connect
@@ -45,21 +48,21 @@ module MailHandler
45
48
  end
46
49
 
47
50
  def disconnect
48
- mailer.disconnect unless mailer.imap_connection.disconnected?
51
+ return if mailer.imap_connection.disconnected?
52
+
53
+ mailer.disconnect
49
54
  end
50
55
 
51
56
  # delegate retrieval details to Mail library
57
+ # set imap settings if they are not set
52
58
  def init_retriever
53
- # set imap settings if they are not set
54
- unless retriever_set?
55
-
56
- imap_settings = retriever_settings
59
+ return if retriever_set?
57
60
 
58
- Mail.defaults do
59
- retriever_method :imap,
60
- imap_settings
61
- end
61
+ imap_settings = retriever_settings
62
62
 
63
+ Mail.defaults do
64
+ retriever_method :imap,
65
+ imap_settings
63
66
  end
64
67
  end
65
68
 
@@ -81,7 +84,6 @@ module MailHandler
81
84
  # archive - Boolean
82
85
  # by_recipient - Hash, accepts a hash like: :to => 'igor@example.com'
83
86
  AVAILABLE_SEARCH_OPTIONS = %i[
84
-
85
87
  by_subject
86
88
  by_content
87
89
  since
@@ -100,10 +102,8 @@ module MailHandler
100
102
 
101
103
  def retriever_settings
102
104
  {
103
- address: address,
104
- port: port,
105
- user_name: username,
106
- password: password,
105
+ address: address, port: port,
106
+ user_name: username, password: password,
107
107
  authentication: authentication,
108
108
  enable_ssl: use_ssl
109
109
  }
@@ -114,58 +114,60 @@ module MailHandler
114
114
  end
115
115
 
116
116
  def imap_search(retry_count, options)
117
- result = mailer.find_emails(what: :last, count: search_options[:count], order: :desc, keys: imap_filter_keys(options), delete_after_find: options[:archive])
118
- result.is_a?(Array)? result : [result]
117
+ result = mailer.find_emails(what: :last,
118
+ count: search_options[:count],
119
+ order: :desc,
120
+ keys: imap_filter_keys(options),
121
+ delete_after_find: options[:archive])
122
+ result.is_a?(Array) ? result : [result]
119
123
 
120
124
  # Silently ignore IMAP search errors, [RETRY_ON_ERROR_COUNT] times
121
125
  rescue Net::IMAP::ResponseError, EOFError, NoMethodError => e
122
- if (retry_count -= 1) >= 0
123
-
124
- puts e
126
+ if (retry_count -= 1) >= 0 # rubocop:disable all
125
127
  reconnect
126
128
  retry
127
-
128
129
  else
129
-
130
130
  raise e
131
-
132
131
  end
133
132
  end
134
133
 
135
134
  def imap_filter_keys(options)
136
135
  keys = []
136
+ options.each { |key, value| keys += retrieve_filter_setting(key, value) }
137
+ keys.empty? ? nil : keys
138
+ end
137
139
 
138
- options.keys.each do |filter_option|
139
- case filter_option
140
-
141
- when :by_recipient
142
-
143
- keys << options[:by_recipient].keys.first.to_s.upcase << options[:by_recipient].values.first
144
-
145
- when :by_subject
146
-
147
- keys << 'SUBJECT' << options[:by_subject].to_s
148
-
149
- when :by_content
150
-
151
- keys << 'BODY' << options[:by_content].to_s
152
-
153
- when :since
140
+ def retrieve_filter_setting(key, value)
141
+ case key
142
+ when :by_recipient
143
+ imap_recipient_search_pair(value)
154
144
 
155
- keys << 'SINCE' << Net::IMAP.format_date(options[:since])
145
+ when :by_subject
146
+ imap_string_search_pair('SUBJECT', value)
156
147
 
157
- when :before
148
+ when :by_content
149
+ imap_string_search_pair('BODY', value)
158
150
 
159
- keys << 'BEFORE' << Net::IMAP.format_date(options[:before])
151
+ when :since
152
+ imap_date_search_pair('SINCE', value)
160
153
 
161
- else
154
+ when :before
155
+ imap_date_search_pair('BEFORE', value)
156
+ else
157
+ []
158
+ end
159
+ end
162
160
 
163
- # do nothing
161
+ def imap_recipient_search_pair(value)
162
+ [value.keys.first.to_s.upcase, value.values.first]
163
+ end
164
164
 
165
- end
166
- end
165
+ def imap_string_search_pair(name, value)
166
+ [name, value.to_s]
167
+ end
167
168
 
168
- keys.empty? ? nil : keys
169
+ def imap_date_search_pair(name, value)
170
+ [name, Net::IMAP.format_date(value)]
169
171
  end
170
172
  end
171
173
  end