truemail 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4060f7dac2add0bb0d3db39eead01f018eea7e7b63777610786481196910ace3
4
- data.tar.gz: 37d590352efefcd159e9ed57a9ffe67a06ca4be8bdc5fb15f3d7791b89f3ef61
3
+ metadata.gz: 8dbdc2b39f8f74cdf2965ab1bde2fd613647247827b64c322250046f1a9a22d5
4
+ data.tar.gz: f1acdd0da6664afa9e03e4031c45d957fbc765378dbc38553f4bae5f4f9569bf
5
5
  SHA512:
6
- metadata.gz: 3071c58d0624ffd0a9bcc17e7859c75be4b0d63429315ad446b96d49f4a430dbfab95fb467780ed3efe1fadc55e5d9f0f796c8647197bff7f58cf67db903a45d
7
- data.tar.gz: 8f451a87f8b7c39c3fe38ef2fe8d720ded9d06ceed7cf381c17edb51b3dbca13fce6b3e9d9143032ea38257da1e5f172ef1af54ae93d774b6d7da0f135eaefd3
6
+ metadata.gz: 772bba708fc85b90f2bc3785e40959f067a1ca10bce27c16016ef1f089928f480eb008ec53d18832ce110ae3b5a4c2043ea8c09095af1ce9b92ede14818975d8
7
+ data.tar.gz: 8d915247fa460b109c18f0e8bd359135c7e4ed738cd5265aa4cec76894910e6199c6e2370d0271674f45597e832501589dfc85e564ffc69846e20f050e20d9ce
data/.reek.yml CHANGED
@@ -11,6 +11,8 @@ detectors:
11
11
  exclude:
12
12
  - Truemail::Validate::Smtp::Request#run
13
13
  - Truemail::Validate::Smtp#run
14
+ - Truemail::Validate::Mx#hosts_from_cname_records
15
+ - Truemail::Configuration#initialize
14
16
 
15
17
  TooManyInstanceVariables:
16
18
  exclude:
@@ -19,11 +21,13 @@ detectors:
19
21
  Attribute:
20
22
  exclude:
21
23
  - Truemail::Configuration#smtp_safe_check
24
+ - Truemail::Validate::ResolverExecutionWrapper#attempts
22
25
 
23
26
  UtilityFunction:
24
27
  exclude:
25
28
  - Truemail::Validate::Smtp::Request#compose_from
26
29
  - Truemail::Validator#select_validation_type
30
+ - Truemail::Validate::Mx#mx_records
27
31
 
28
32
  ControlParameter:
29
33
  exclude:
data/.rubocop.yml CHANGED
@@ -35,3 +35,6 @@ RSpec/ContextWording:
35
35
 
36
36
  RSpec/AnyInstance:
37
37
  Enabled: false
38
+
39
+ RSpec/MessageSpies:
40
+ Enabled: false
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- truemail (0.1.3)
4
+ truemail (0.1.4)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -4,6 +4,12 @@
4
4
 
5
5
  The Truemail gem helps you validate emails by regex pattern, presence of domain mx-records, and real existence of email account on a current email server.
6
6
 
7
+ ## Features
8
+
9
+ - Configurable validator, validate only what you need
10
+ - Zero runtime dependencies
11
+ - 100% test coverage
12
+
7
13
  ## Installation
8
14
 
9
15
  Add this line to your application's Gemfile:
@@ -58,6 +64,9 @@ Truemail.configure do |config|
58
64
  # Optional parameter. A SMTP server response timeout is equal to 2 ms by default.
59
65
  config.response_timeout = 1
60
66
 
67
+ # Optional parameter. Total of timeout retry. It is equal to 1 by default.
68
+ config.retry_count = 2
69
+
61
70
  # Optional parameter. You can predefine which type of validation will be used for domains.
62
71
  # Available validation types: :regex, :mx, :smtp
63
72
  # This configuration will be used over current or default validation type parameter
@@ -82,6 +91,7 @@ Truemail.configuration
82
91
  @connection_timeout=1,
83
92
  @email_pattern=/regex_pattern/,
84
93
  @response_timeout=1,
94
+ @retry_count=2,
85
95
  @validation_type_by_domain={},
86
96
  @verifier_domain="somedomain.com",
87
97
  @verifier_email="verifier@example.com"
@@ -95,12 +105,15 @@ Truemail.configuration.connection_timeout = 3
95
105
  => 3
96
106
  Truemail.configuration.response_timeout = 4
97
107
  => 4
108
+ Truemail.configuration.retry_count = 1
109
+ => 1
98
110
 
99
111
  Truemail.configuration
100
112
  => #<Truemail::Configuration:0x000055590cb17b40
101
113
  @connection_timeout=3,
102
114
  @email_pattern=/regex_pattern/,
103
115
  @response_timeout=4,
116
+ @retry_count=1,
104
117
  @validation_type_by_domain={},
105
118
  @verifier_domain="somedomain.com",
106
119
  @verifier_email="verifier@example.com",
@@ -122,7 +135,7 @@ Truemail.configuration
122
135
 
123
136
  #### Regex validation
124
137
 
125
- Validation with regex pattern is the first validation level.
138
+ Validation with regex pattern is the first validation level. By default this validation not performs strictly following RFC 5322 standart, so you can override Truemail default regex pattern if you want.
126
139
 
127
140
  Example of usage:
128
141
 
@@ -174,7 +187,7 @@ Truemail.validate('email@example.com', with: :regex)
174
187
 
175
188
  #### MX validation
176
189
 
177
- Validation by MX records is second validation level. It uses Regex validation before launch. When regex validation has completed successfully then runs itself.
190
+ Validation by MX records is the second validation level. It uses Regex validation before running itself. When regex validation has completed successfully then runs itself. Truemail MX validation performs strictly following the [RFC 5321](https://tools.ietf.org/html/rfc5321#section-5) standard.
178
191
 
179
192
  Example of usage:
180
193
 
@@ -201,7 +214,7 @@ Truemail.validate('email@example.com', with: :mx)
201
214
 
202
215
  #### SMTP validation
203
216
 
204
- SMTP validation is a final, third validation level. This type of validation tries to check real existence of email account on a current email server. This validation runs a chain of previous validations, and if they're complete, successfully runs itself.
217
+ SMTP validation is a final, third validation level. This type of validation tries to check real existence of email account on a current email server. This validation runs a chain of previous validations and if they're complete successfully then runs itself.
205
218
 
206
219
  ```code
207
220
  [Regex validation] -> [MX validation] -> [SMTP validation]
@@ -354,11 +367,39 @@ Truemail.validate('email@example.com')
354
367
  @validation_type=:smtp>
355
368
  ```
356
369
 
370
+ ### Truemail helpers
371
+
372
+ #### .valid?
373
+
374
+ You can use the ```.valid?``` helper for quick validation of email address. It returns a boolean:
375
+
376
+ ```ruby
377
+ # It is shortcut for Truemail.validate('email@example.com').result.valid?
378
+ Truemail.valid?('email@example.com')
379
+ => true
380
+ ```
381
+
382
+ ### Test environment
383
+
384
+ You can stub out that validation for your test environment. Just add RSpec before action:
385
+
386
+ ```ruby
387
+ # spec_helper.rb
388
+
389
+ RSpec.configure do |config|
390
+ config.before { allow(Truemail).to receive(:valid?).and_return(true) }
391
+ # or
392
+ config.before { allow(Truemail).to receive(:validate).and_return(true) }
393
+ # or
394
+ config.before { allow(Truemail).to receive_message_chain(:validate, :result, :valid?).and_return(true) }
395
+ end
396
+ ```
397
+
398
+ ---
357
399
  ## ToDo
358
400
 
359
401
  1. Gem compatibility with Ruby 2.3
360
402
  2. Fail validations logger
361
- 3. The ability to use a proxy
362
403
 
363
404
  ## Contributing
364
405
 
data/lib/truemail.rb CHANGED
@@ -32,5 +32,9 @@ module Truemail
32
32
  raise ConfigurationError, NOT_CONFIGURED unless configuration
33
33
  Truemail::Validator.new(email, **options).run
34
34
  end
35
+
36
+ def valid?(email, **options)
37
+ validate(email, **options).result.valid?
38
+ end
35
39
  end
36
40
  end
@@ -4,20 +4,23 @@ module Truemail
4
4
  class Configuration
5
5
  DEFAULT_CONNECTION_TIMEOUT = 2
6
6
  DEFAULT_RESPONSE_TIMEOUT = 2
7
+ DEFAULT_RETRY_COUNT = 1
7
8
 
8
9
  attr_reader :email_pattern,
9
10
  :verifier_email,
10
11
  :verifier_domain,
11
12
  :connection_timeout,
12
13
  :response_timeout,
14
+ :retry_count,
13
15
  :validation_type_by_domain
14
16
 
15
17
  attr_accessor :smtp_safe_check
16
18
 
17
19
  def initialize
18
20
  @email_pattern = Truemail::RegexConstant::REGEX_EMAIL_PATTERN
19
- @connection_timeout = DEFAULT_CONNECTION_TIMEOUT
20
- @response_timeout = DEFAULT_RESPONSE_TIMEOUT
21
+ @connection_timeout = Truemail::Configuration::DEFAULT_CONNECTION_TIMEOUT
22
+ @response_timeout = Truemail::Configuration::DEFAULT_RESPONSE_TIMEOUT
23
+ @retry_count = Truemail::Configuration::DEFAULT_RETRY_COUNT
21
24
  @validation_type_by_domain = {}
22
25
  @smtp_safe_check = false
23
26
  end
@@ -38,7 +41,7 @@ module Truemail
38
41
  @verifier_domain = domain.downcase
39
42
  end
40
43
 
41
- %i[connection_timeout response_timeout].each do |method|
44
+ %i[connection_timeout response_timeout retry_count].each do |method|
42
45
  define_method("#{method}=") do |argument|
43
46
  raise ArgumentError.new(argument, __method__) unless argument.is_a?(Integer) && argument.positive?
44
47
  instance_variable_set(:"@#{method}", argument)
data/lib/truemail/core.rb CHANGED
@@ -19,6 +19,7 @@ module Truemail
19
19
  module Validate
20
20
  require 'truemail/validate/base'
21
21
  require 'truemail/validate/regex'
22
+ require 'truemail/validate/resolver_execution_wrapper'
22
23
  require 'truemail/validate/mx'
23
24
  require 'truemail/validate/smtp'
24
25
  require 'truemail/validate/smtp/response'
@@ -5,21 +5,62 @@ module Truemail
5
5
  class Mx < Truemail::Validate::Base
6
6
  require 'resolv'
7
7
 
8
- ERROR = 'mx records not found'
8
+ ERROR = 'target host(s) not found'
9
9
 
10
10
  def run
11
11
  return false unless Truemail::Validate::Regex.check(result)
12
12
  result.domain = result.email[Truemail::RegexConstant::REGEX_DOMAIN_FROM_EMAIL, 1]
13
- return true if success(!result.mail_servers.push(*mx_records).empty?)
13
+ return true if success(mx_lookup)
14
14
  add_error(Truemail::Validate::Mx::ERROR)
15
15
  false
16
16
  end
17
17
 
18
18
  private
19
19
 
20
- def mx_records
21
- mx_records = Resolv::DNS.open { |dns| dns.getresources(result.domain, Resolv::DNS::Resource::IN::MX) }
22
- mx_records.sort_by(&:preference).map { |mx_record| mx_record.exchange.to_s }
20
+ def host_extractor_methods
21
+ %i[hosts_from_mx_records? hosts_from_cname_records? host_from_a_record?]
22
+ end
23
+
24
+ def mx_lookup
25
+ host_extractor_methods.any? do |method|
26
+ Truemail::Validate::ResolverExecutionWrapper.call { send(method) }
27
+ end
28
+ end
29
+
30
+ def fetch_target_hosts(hosts)
31
+ result.mail_servers.push(*hosts)
32
+ end
33
+
34
+ def mx_records(domain)
35
+ Resolv::DNS.new.getresources(domain, Resolv::DNS::Resource::IN::MX).sort_by(&:preference).map do |mx_record|
36
+ Resolv.getaddress(mx_record.exchange.to_s)
37
+ end
38
+ end
39
+
40
+ def mail_servers_found?
41
+ !result.mail_servers.empty?
42
+ end
43
+
44
+ def hosts_from_mx_records?
45
+ fetch_target_hosts(mx_records(result.domain))
46
+ mail_servers_found?
47
+ end
48
+
49
+ def hosts_from_cname_records?
50
+ cname_records = Resolv::DNS.new.getresources(result.domain, Resolv::DNS::Resource::IN::CNAME)
51
+ return if cname_records.empty?
52
+ cname_records.each do |cname_record|
53
+ host = Resolv.getaddress(cname_record.name.to_s)
54
+ hostname = Resolv.getname(host)
55
+ found_hosts = mx_records(hostname)
56
+ fetch_target_hosts(found_hosts.empty? ? [host] : found_hosts)
57
+ end
58
+ mail_servers_found?
59
+ end
60
+
61
+ def host_from_a_record?
62
+ fetch_target_hosts([Resolv.getaddress(result.domain)])
63
+ mail_servers_found?
23
64
  end
24
65
  end
25
66
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Truemail
4
+ module Validate
5
+ class ResolverExecutionWrapper
6
+ attr_accessor :attempts
7
+
8
+ def self.call(&block)
9
+ new.call(&block)
10
+ end
11
+
12
+ def initialize
13
+ @attempts = Truemail.configuration.retry_count
14
+ end
15
+
16
+ def call(&block)
17
+ Timeout.timeout(Truemail.configuration.connection_timeout, &block)
18
+ rescue Resolv::ResolvError
19
+ false
20
+ rescue Timeout::Error
21
+ retry unless (self.attempts -= 1).zero?
22
+ false
23
+ end
24
+ end
25
+ end
26
+ end
@@ -18,7 +18,7 @@ module Truemail
18
18
  establish_smtp_connection
19
19
  return true if success(success_response?)
20
20
  result.smtp_debug = smtp_results
21
- return true if success(not_includes_user_not_found_errors)
21
+ return true if success(not_includes_user_not_found_errors?)
22
22
  add_error(Truemail::Validate::Smtp::ERROR)
23
23
  false
24
24
  end
@@ -45,7 +45,7 @@ module Truemail
45
45
  smtp_results.map(&:response).any?(&:rcptto)
46
46
  end
47
47
 
48
- def not_includes_user_not_found_errors
48
+ def not_includes_user_not_found_errors?
49
49
  return unless Truemail.configuration.smtp_safe_check
50
50
  result.smtp_debug.map(&:response).map(&:errors).all? do |errors|
51
51
  next true unless errors.key?(:rcptto)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Truemail
4
- VERSION = '0.1.3'
4
+ VERSION = '0.1.4'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: truemail
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladislav Trotsenko
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-03-27 00:00:00.000000000 Z
11
+ date: 2019-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -195,6 +195,7 @@ files:
195
195
  - lib/truemail/validate/base.rb
196
196
  - lib/truemail/validate/mx.rb
197
197
  - lib/truemail/validate/regex.rb
198
+ - lib/truemail/validate/resolver_execution_wrapper.rb
198
199
  - lib/truemail/validate/smtp.rb
199
200
  - lib/truemail/validate/smtp/request.rb
200
201
  - lib/truemail/validate/smtp/response.rb