email_validator 2.0.1 → 2.2.3

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: ede74c324c144cfd01a3f13a1782ecb45af661a794ac8361d9193939a1973174
4
- data.tar.gz: 4a8dad3331c014a855e03fbcf40f2cdfdb4e0cc6f216775d579b571f8276cc0e
3
+ metadata.gz: 46bd723714c6aa3a844e584e986e5989920b6d3f212a44d800183d118c561cb7
4
+ data.tar.gz: bb58e5bf862c4be796e3ce59727dcf3fd2e28267e7c6077014f01d25dc841580
5
5
  SHA512:
6
- metadata.gz: 108a2f115956ceb9b3f53e9dac871589efc9169e99789cc0c7a5339fbcaa063e9b47f6872b9b9836fa19b53d6e42fade76fa33961b8896bf382cf54ff9aad53f
7
- data.tar.gz: 7cde7e0065099b6b5739338190dc6712dd5d8781892f5d645a13f24b5f4c1f328d563fb324a74782fcae307a4786bfb025acd4a33250b65c8f6c9ab028626bcc
6
+ metadata.gz: 2ece91a44bd82bd79c5484a17729996731787148b6813af0c4e41804715242a9cbfb4c0d661b08e0c904753dd59f23560d11aed7bf9044986802445e36f69438
7
+ data.tar.gz: 1b91bf21ce112f86f9b97138573d20fe14b67f075935136c3b19a620f155d50c5d5cf5ff9566e4c101429b8d7ce80ccb80709a2a15eeb07f4caeaac34f2b86b0
data/CHANGELOG.md ADDED
@@ -0,0 +1,106 @@
1
+ # CHANGELOG
2
+
3
+ This file is used to list changes made in `email_validator`.
4
+
5
+ All notable changes to this project will be documented in this file.
6
+ This project adheres to [Semantic Versioning](http://semver.org/).
7
+
8
+ ## 2.2.3 (2021-04-05)
9
+
10
+ * [karlwilbur] - Fix regexp for numeric domains (fixes [#72](https://github.com/K-and-R/email_validator/issues/72))
11
+ - [delphaber] - Add checks for numeric-only domains in tests (should be considered valid)
12
+ - [karlwilbur] - Fix specs for numeric-only domains labels (should be considered valid)
13
+ - [karlwilbur] - Add checks for numeric-only TLDs in tests (should be considered invalid)
14
+ - [karlwilbur] - Add tests to ensure that `regexp` returns expected value
15
+ - [karlwilbur] - Add checks for double dash in domain (should be considered invalid)
16
+ - [karlwilbur] - Add `EmailValidator::Error` class, raise `EmailValidator::Error` when invalid `mode`
17
+
18
+ ## 2.2.2 (2020-12-10)
19
+
20
+ * [karlwilbur] - Fix includes for `:rfc` and `:strict` modes from `Gemfile`
21
+
22
+ ## 2.2.1 (2020-12-10)
23
+
24
+ * [karlwilbur] - Modify regexp to:
25
+ - allow numeric-only hosts [#68]
26
+ - allow mailbox-only addresses in `:rfc` mode
27
+ - enforce the 255-char domain limit (not in `:loose` mode unless using `:domain`)
28
+
29
+ ## 2.2.0 (2020-12-09)
30
+
31
+ * [karlwilbur] - Rename `:strict` -> `:rfc`; `:moderate` -> `:strict`
32
+
33
+ ## 2.1.0 (2020-12-09)
34
+
35
+ * [karlwilbur] - Add linters and commit hooks to validate code prior to commits
36
+ * [karlwilbur] - Add `:mode` config option; values `:loose`, `:moderate`, `:strict`; default to `:loose`
37
+ * [karlwilbur] - Merge in changes from <https://github.com/karlwilbur/email_validator> fork
38
+
39
+ ## 1.9.0.pre (2020-10-14)
40
+
41
+ * [karlwilbur] - Add `require_fqdn` option, require FQDN by default
42
+ * [karlwilbur] - Add support for IPv4 and IPv6 address hosts
43
+ * [karlwilbur] - Add Rubocop, `.editorconfig`; code cleanup/linting
44
+
45
+ ## 1.8.0 (2019-06-14)
46
+
47
+ * [karlwilbur] - Refactor class methods for readability
48
+ * [karlwilbur] - `gemspec` meta updates
49
+ * [karlwilbur] - Use POSIX classes for better performance
50
+ * [karlwilbur] - Refactored tests to check specical characters one at a time
51
+ * [karlwilbur] - Refactored validation regex to be more techincally correct
52
+ * [karlwilbur] - Add this `CHANGELOG`
53
+
54
+ ## 1.7.0 (2019-04-20)
55
+
56
+ * [karlwilbur] - Added test coverage badge to README
57
+ * [karlwilbur] - Added I18n directive to remove warning message in testing
58
+ * [karlwilbur] - Added RFC-2822 reference
59
+ * [karlwilbur] - Ignore local rspec config file
60
+ * [karlwilbur] - Check for invalid double dots in strict mode
61
+ * [karlwilbur] - Updated spec_helper to remove Code Climate Test Reporter; it is to be run separately now
62
+ * [karlwilbur] - Allow leading/trailing whitespace in normal, not strict
63
+ * [karlwilbur] - Added `invalid?` as inverse of `valid?`
64
+ * [karlwilbur] - Add the ability to limit to a domain
65
+ * [karlwilbur] - Removed CodeShip badge
66
+ * [karlwilbur] - Make the dot in the domain part non-conditional
67
+ * [karlwilbur] - Fix domain label pattern to allow numbers per rfc5321
68
+
69
+ ## 1.6.0 (2015-06-14)
70
+
71
+ * [karlwilbur] - Fixed validation to be closer to RFC-5321
72
+ * [karlwilbur] - Updated specs to use Rspec 3 syntax
73
+ * [karlwilbur] - Added unicode suport to validation regexp
74
+ * [karlwilbur] - Added class access to regexp, and `valid?` calss method
75
+ * [karlwilbur] - Simplified code using new methods
76
+ * [karlwilbur] - Added CodeClimate and SimpleCov
77
+ * [karlwilbur] - Updated version and contact info
78
+
79
+ *** Forked from <https://github.com/balexand/email_validator>
80
+
81
+ ## 2.0.1 (2019-03-09)
82
+
83
+ * Add email value to error details [f1sherman #50]
84
+ * CI doesn't test Ruby versions that no longer receive updates [f1sherman #51]
85
+
86
+ ## 2.0.0 (2019-03-02)
87
+
88
+ * Looser validation [#49]
89
+
90
+ ## 1.6.0 (2015-05-12)
91
+
92
+ * Unicode characters support [i7an #24]
93
+
94
+ ## 1.5.0 (2014-12-08)
95
+
96
+ * Add a class method for simpler validation [TiteiKo and cluesque #19]
97
+ * RSpec 3.0 syntax [strivedi183 #17]
98
+ * Create Changes.md
99
+
100
+ ---
101
+
102
+ Check the [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax)
103
+ for help with Markdown.
104
+
105
+ The [Github Flavored Markdown page](http://github.github.com/github-flavored-markdown/)
106
+ describes the differences between markdown on github and standard markdown.
data/README.md CHANGED
@@ -1,6 +1,29 @@
1
- [![Build Status](https://secure.travis-ci.org/balexand/email_validator.png)](http://travis-ci.org/balexand/email_validator)
1
+ # EmailValidator
2
2
 
3
- ## Usage
3
+ [![Build Status](https://travis-ci.com/K-and-R/email_validator.svg?branch=master)](http://travis-ci.com/K-and-R/email_validator)
4
+ [![Code Climate](https://codeclimate.com/github/K-and-R/email_validator/badges/gpa.svg)](https://codeclimate.com/github/K-and-R/email_validator)
5
+ [![Test Coverage](https://codeclimate.com/github/K-and-R/email_validator/badges/coverage.svg)](https://codeclimate.com/github/K-and-R/email_validator/coverage)
6
+
7
+ An email validator for Rails 3+.
8
+
9
+ Supports RFC-2822-compliant and RFC-5321-compliant email validation using RFC-3696 validation.
10
+
11
+ Formerly found at: <https://github.com/balexand/email_validator>
12
+
13
+ ## Validation philosophy
14
+
15
+ The default validation provided by this gem (the `:loose` configuration option)
16
+ is extremely loose. It just checks that there's an `@` with something before and
17
+ after it without any whitespace. See [this article by David Gilbertson](https://hackernoon.com/the-100-correct-way-to-validate-email-addresses-7c4818f24643)
18
+ for an explanation of why.
19
+
20
+ We understand that many use cases require an increased level of validation. This
21
+ is supported by using the `:strict` validation mode. Additionally, the `:rfc`
22
+ RFC-compliant mode will consider technically valid emails address as valid which
23
+ may not be wanted, such as the valid `user` or `user@somehost` addresses. These
24
+ would be valid in `:rfc` mode but not valid in `:loose` or `:strict`.
25
+
26
+ ## Installation
4
27
 
5
28
  Add to your Gemfile:
6
29
 
@@ -10,47 +33,185 @@ gem 'email_validator'
10
33
 
11
34
  Run:
12
35
 
13
- ```
36
+ ```bash
14
37
  bundle install
15
38
  ```
16
39
 
17
- Then add the following to your model:
40
+ ## Usage
41
+
42
+ Add the following to your model:
18
43
 
19
44
  ```ruby
20
45
  validates :my_email_attribute, email: true
21
46
  ```
22
47
 
48
+ You may wish to allow domains without a FQDN, like `user@somehost`. While this
49
+ is technically a valid address, it is uncommon to consider such address valid.
50
+ We will consider them valid by default with the `:loose` checking. Disallowed
51
+ by setting `require_fqdn: true` or by enabling `:strict` checking:
52
+
53
+ ```ruby
54
+ validates :my_email_attribute, email: {mode: :strict, require_fqdn: true}
55
+ ```
56
+
57
+ You can also limit to a single domain (e.g: you have separate `User` and
58
+ `AdminUser` models and want to ensure that `AdminUser` emails are on a specific
59
+ domain):
60
+
61
+ ```ruby
62
+ validates :my_email_attribute, email: {domain: 'example.com'}
63
+ ```
64
+
65
+ ## Configuration
66
+
67
+ Default configuration can be overridden by setting options in `config/initializers/email_validator.rb`:
68
+
69
+ ```ruby
70
+ if defined?(EmailValidator)
71
+ # To completly override the defaults
72
+ EmailValidator.default_options = {
73
+ allow_nil: false,
74
+ domain: nil,
75
+ require_fqdn: nil,
76
+ mode: :loose
77
+ }
78
+
79
+ # or just a few options
80
+ EmailValidator.default_options.merge!({ domain: 'mydomain.com' })
81
+ end
82
+ ```
83
+
84
+ ### Loose mode
85
+
86
+ This it the default validation mode of this gem. It is intentionally extremely
87
+ loose (see the [Validation Philosophy section](#validation_philosophy) above. It
88
+ just checks that there's an `@` with something before and after it without any
89
+ whitespace.
90
+
91
+ ### Strict mode
92
+
93
+ Enabling `:strict` checking will check for a "normal" email format that would
94
+ be expected in most common everyday usage. Strict mode basically checks for a
95
+ properly sized and formatted mailbox label, a single "@" symbol, and a properly
96
+ sized and formatted FQDN. Enabling `:strict` mode will also enable `:require_fqdn`
97
+ configuration option.
98
+
99
+ Strict mode can be enabled globally by requiring `email_validator/strict` in
100
+ your `Gemfile`, by setting the option in `config/initializers/email_validator.rb`,
101
+ or by specifying the option in a specific `validates` call.
102
+
103
+ * `Gemfile`:
104
+
105
+ ```ruby
106
+ gem 'email_validator', require: 'email_validator/strict'
107
+ ```
108
+
109
+ * `config/initializers/email_validator.rb`:
110
+
111
+ ```ruby
112
+ if defined?(EmailValidator)
113
+ EmailValidator.default_options[:mode] = :strict
114
+ end
115
+ ```
116
+
117
+ * `validates` call:
118
+
119
+ ```ruby
120
+ validates :my_email_attribute, email: {mode: :strict}
121
+ ```
122
+
123
+ ### RFC mode
124
+
125
+ In order to have RFC-compliant validation (according to [http://www.remote.org/jochen/mail/info/chars.html](https://web.archive.org/web/20150508102948/http://www.remote.org/jochen/mail/info/chars.html)),
126
+ enable `:rfc` mode.
127
+
128
+ You can do this globally by requiring `email_validator/rfc` in your `Gemfile`,
129
+ by setting the options in `config/initializers/email_validator.rb`, or you can do
130
+ this in a specific `validates` call.
131
+
132
+ * `Gemfile`:
133
+
134
+ ```ruby
135
+ gem 'email_validator', require: 'email_validator/rfc'
136
+ ```
137
+
138
+ * `config/initializers/email_validator.rb`:
139
+
140
+ ```ruby
141
+ if defined?(EmailValidator)
142
+ EmailValidator.default_options[:mode] = :rfc
143
+ end
144
+ ```
145
+
146
+ * `validates` call:
147
+
148
+ ```ruby
149
+ validates :my_email_attribute, email: {mode: :rfc}
150
+ ```
151
+
23
152
  ## Validation outside a model
24
153
 
25
- If you'd like to validate an email outside of a model then here are some class methods that you can use:
154
+ If you need to validate an email outside a model, you can get the regexp:
155
+
156
+ ### Loose/default mode
26
157
 
27
158
  ```ruby
28
- EmailValidator.regexp # returns the regex
29
- EmailValidator.valid?('narf@example.com') # => true
30
- EmailValidator.invalid?('narf@example.com') # => false
159
+ EmailValidator.valid?('narf@example.com') # boolean
31
160
  ```
32
161
 
33
- ## Validation philosophy
162
+ ### Requiring a FQDN
163
+
164
+ ```ruby
165
+ EmailValidator.valid?('narf@somehost') # boolean false
166
+ EmailValidator.invalid?('narf@somehost', require_fqdn: false) # boolean true
167
+ ```
34
168
 
35
- The validation provided by this gem is loose. It just checks that there's an `@` with something before and after it. See [this article by David Gilbertson](https://hackernoon.com/the-100-correct-way-to-validate-email-addresses-7c4818f24643) for an explanation of why.
169
+ _NB: Enabling strict mode (`mode: :strict`) enables `require_fqdn`
170
+ (`require_fqdn: true`), overridding any `require_fqdn: false` while
171
+ `mode: :strict` is set._
36
172
 
37
- ## Trimming whitespace
173
+ ### Requiring a specific domain
174
+
175
+ ```ruby
176
+ EmailValidator.valid?('narf@example.com', domain: 'foo.com') # boolean false
177
+ EmailValidator.invalid?('narf@example.com', domain: 'foo.com') # boolean true
178
+ ```
38
179
 
39
- Your users may accidentally submit leading or trailing whitespace when submitting a form. You may want to automatically trim this. This is not the job of a validator gem but it's trivial to implement yourself by adding a setter in your model:
180
+ ### Strict mode
40
181
 
41
182
  ```ruby
42
- def email=(e)
43
- e = e.strip if e
44
- super
45
- end
183
+ EmailValidator.regexp(mode: :strict) # returns the regex
184
+ EmailValidator.valid?('narf@example.com', mode: :strict) # boolean
185
+ ```
186
+
187
+ ### RFC mode
188
+
189
+ ```ruby
190
+ EmailValidator.regexp(mode: :rfc) # returns the regex
191
+ EmailValidator.valid?('narf@example.com', mode: :rfc) # boolean
46
192
  ```
47
193
 
48
- You may also want to convert emails to lowercase in the same way.
194
+ ## Thread safety
195
+
196
+ This gem is thread safe, with one caveat: `EmailValidator.default_options` must
197
+ be configured before use in a multi-threaded environment. If you configure
198
+ `default_options` in a Rails initializer file, then you're good to go since
199
+ initializers are run before worker threads are spawned.
49
200
 
50
201
  ## Alternative gems
51
202
 
52
- Do you prefer a different email validation gem? If so, open an issue with a brief explanation of how it differs from this gem. I'll add a link to it in this README.
203
+ Do you prefer a different email validation gem? If so, open an issue with a brief
204
+ explanation of how it differs from this gem. I'll add a link to it in this README.
53
205
 
54
- ## Thread safety
206
+ * [`email_address`](https://github.com/afair/email_address) (<https://github.com/K-and-R/email_validator/issues/58>)
207
+ * [`email_verifier`](https://github.com/kamilc/email_verifier) (<https://github.com/K-and-R/email_validator/issues/65>)
208
+
209
+ ## Maintainers
210
+
211
+ All thanks is given to [Brian Alexander (balexand)](https://github.com/balexand)
212
+ for is initial work on this gem.
213
+
214
+ Currently maintained by:
55
215
 
56
- This gem is thread safe.
216
+ * Karl Wilbur (<https://github.com/karlwilbur>)
217
+ * K&R Software (<https://github.com/K-and-R>)
@@ -1,29 +1,164 @@
1
- # frozen_string_literal: true
2
- require 'active_model'
1
+ # Based on work from http://thelucid.com/2010/01/08/sexy-validation-in-edge-rails-rails-3/
3
2
 
3
+ # EmailValidator class
4
4
  class EmailValidator < ActiveModel::EachValidator
5
- def self.regexp(options = {})
6
- if options[:strict_mode]
7
- ActiveSupport::Deprecation.warn(
8
- 'Strict mode has been deprecated in email_validator 2.0. To fix this warning, '\
9
- 'remove `strict_mode: true` from your validation call.'
10
- )
11
- end
5
+ # rubocop:disable Style/ClassVars
6
+ @@default_options = {
7
+ :allow_nil => false,
8
+ :domain => nil,
9
+ :require_fqdn => nil,
10
+ :mode => :loose
11
+ }
12
+ # rubocop:enable Style/ClassVars
12
13
 
13
- /[^\s]@[^\s]/
14
+ # EmailValidator::Error class
15
+ class Error < StandardError
16
+ def initialize(msg = 'EmailValidator error')
17
+ super
18
+ end
14
19
  end
15
20
 
16
- def self.valid?(value, options = nil)
17
- !invalid?(value)
18
- end
21
+ class << self
22
+ def default_options
23
+ @@default_options
24
+ end
25
+
26
+ def valid?(value, options = {})
27
+ options = parse_options(options)
28
+ return true if value.nil? && options[:allow_nil] == true
29
+ return false if value.nil?
30
+ !!(value =~ regexp(options))
31
+ end
32
+
33
+ def invalid?(value, options = {})
34
+ !valid?(value, options)
35
+ end
36
+
37
+ # Refs:
38
+ # https://tools.ietf.org/html/rfc2822 : 3.2. Lexical Tokens, 3.4.1. Addr-spec specification
39
+ # https://tools.ietf.org/html/rfc5321 : 4.1.2. Command Argument Syntax
40
+ def regexp(options = {})
41
+ options = parse_options(options)
42
+ case options[:mode]
43
+ when :loose
44
+ loose_regexp(options)
45
+ when :rfc
46
+ rfc_regexp(options)
47
+ when :strict
48
+ options[:require_fqdn] = true
49
+ strict_regexp(options)
50
+ else
51
+ fail EmailValidator::Error, "Validation mode '#{options[:mode]}' is not supported by EmailValidator"
52
+ end
53
+ end
54
+
55
+ protected
56
+
57
+ def loose_regexp(options = {})
58
+ return /\A[^\s]+@[^\s]+\z/ if options[:domain].nil?
59
+ /\A[^\s]+@#{domain_part_pattern(options)}\z/
60
+ end
61
+
62
+ def strict_regexp(options = {})
63
+ /\A(?>#{local_part_pattern})@#{domain_part_pattern(options)}\z/i
64
+ end
65
+
66
+ def rfc_regexp(options = {})
67
+ /\A(?>#{local_part_pattern})(?:@#{domain_part_pattern(options)})?\z/i
68
+ end
69
+
70
+ def alpha
71
+ '[[:alpha:]]'
72
+ end
73
+
74
+ def alnum
75
+ '[[:alnum:]]'
76
+ end
77
+
78
+ def alnumhy
79
+ "(?:#{alnum}|-)"
80
+ end
81
+
82
+ def ipv4
83
+ '\d{1,3}(?:\.\d{1,3}){3}'
84
+ end
85
+
86
+ def ipv6
87
+ # only supporting full IPv6 addresses right now
88
+ 'IPv6:[[:xdigit:]]{1,4}(?::[[:xdigit:]]{1,4}){7}'
89
+ end
90
+
91
+ def address_literal
92
+ "\\[(?:#{ipv4}|#{ipv6})\\]"
93
+ end
94
+
95
+ def host_label_pattern
96
+ "#{label_is_correct_length}" \
97
+ "#{label_contains_no_more_than_one_consecutive_hyphen}" \
98
+ "#{alnum}(?:#{alnumhy}{,61}#{alnum})?"
99
+ end
100
+
101
+ # splitting this up into separate regex pattern for performance; let's not
102
+ # try the "contains" pattern unless we have to
103
+ def domain_label_pattern
104
+ "#{host_label_pattern}\\.#{tld_label_pattern}"
105
+ end
106
+
107
+ # While, techincally, TLDs can be numeric-only, this is not allowed by ICANN
108
+ # Ref: ICANN Application Guidebook for new TLDs (June 2012)
109
+ # says the following starting at page 64:
110
+ #
111
+ # > The ASCII label must consist entirely of letters (alphabetic characters a-z)
112
+ #
113
+ # -- https://newgtlds.icann.org/en/applicants/agb/guidebook-full-04jun12-en.pdf
114
+ def tld_label_pattern
115
+ "#{alpha}{1,64}"
116
+ end
117
+
118
+ def label_is_correct_length
119
+ '(?=[^.]{1,63}(?:\.|$))'
120
+ end
121
+
122
+ def domain_part_is_correct_length
123
+ '(?=.{1,255}$)'
124
+ end
19
125
 
20
- def self.invalid?(value, options = nil)
21
- !(value =~ regexp)
126
+ def label_contains_no_more_than_one_consecutive_hyphen
127
+ '(?!.*?--.*$)'
128
+ end
129
+
130
+ def atom_char
131
+ # The `atext` spec
132
+ # We are looking at this without whitespace; no whitespace support here
133
+ "[-#{alpha}#{alnum}+_!\"'#$%^&*{}/=?`|~]"
134
+ end
135
+
136
+ def local_part_pattern
137
+ # the `dot-atom-text` spec, but with a 64 character limit
138
+ "#{atom_char}(?:\\.?#{atom_char}){,63}"
139
+ end
140
+
141
+ def domain_part_pattern(options)
142
+ return options[:domain].sub(/\./, '\.') if options[:domain].present?
143
+ return fqdn_pattern if options[:require_fqdn]
144
+ "#{domain_part_is_correct_length}(?:#{address_literal}|(?:#{host_label_pattern}\\.)*#{tld_label_pattern})"
145
+ end
146
+
147
+ def fqdn_pattern
148
+ "(?=.{1,255}$)(?:#{host_label_pattern}\\.)*#{domain_label_pattern}"
149
+ end
150
+
151
+ private
152
+
153
+ def parse_options(options)
154
+ # `:strict` mode enables `:require_fqdn`, unless it is already explicitly disabled
155
+ options[:require_fqdn] = true if options[:require_fqdn].nil? && options[:mode] == :strict
156
+ default_options.merge(options)
157
+ end
22
158
  end
23
159
 
24
160
  def validate_each(record, attribute, value)
25
- if self.class.invalid?(value)
26
- record.errors.add(attribute, :invalid, options.slice(:message).merge(value: value))
27
- end
161
+ options = @@default_options.merge(self.options)
162
+ record.errors.add(attribute, options[:message] || :invalid) unless self.class.valid?(value, options)
28
163
  end
29
164
  end