mailhandler 1.0.38 → 1.0.39

Sign up to get free protection for your applications and to get access to all the features.
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