email_validator 2.0.0 → 2.2.2

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: da5f6cc330adef4786045236ce8a34a49ad638a5b42c7447e7585711e0dccca4
4
- data.tar.gz: 04eff8f220eaafdb748f8ba0bc1f8890f450b0620731b5c3a10c9125954b6e7b
3
+ metadata.gz: 9f5010b8d56592e8febeda1e96b6fa35a27e2dae75dab6277fbade5f35b74b18
4
+ data.tar.gz: 50bbb4a9c6abea2db5c2e887ba67a88739afccdb445c56be74c063b10993cef3
5
5
  SHA512:
6
- metadata.gz: 9b39cbd7c9d87a3274f6d4b570a7a774ef0a232e5171f561a384e2c287d3242d832e91fb0240437484b5a500da98ee9715a20a5a15247075fbfd4100d596b0b2
7
- data.tar.gz: 8907894a088e8a2c53c53eb346331abc561643b2316bd4f5132f8961bc9cb98e08dc3ea4cb930d3a038ad8199922c429047b314e75ff96f034f506936ae1621b
6
+ metadata.gz: b520404b2777c72ac87eaccaaacb073b340ebca043d104d531d8d78c74fd413d2f0e1c5ef6b7d08529a23d2aad913c7e1d15ab16989ea5777b5f41d37539365b
7
+ data.tar.gz: 100eb11eaff2bd530d7a6c554655d93649a45aa6a5e5718a31cfa0c994f8e5341b57964d01c7f256ce4e8e927c3ba8e5610694cd293dda2d3241a2f2c187e1b9
@@ -0,0 +1,96 @@
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.2 (2020-12-10)
9
+
10
+ * [karlwilbur] - Fix includes for `:rfc` and `:strict` modes from `Gemfile`
11
+
12
+ ## 2.2.1 (2020-12-10)
13
+
14
+ * [karlwilbur] - Modify regexp to:
15
+ - allow numeric-only hosts [#68]
16
+ - allow mailbox-only addresses in `:rfc` mode
17
+ - enforce the 255-char domain limit (not in `:loose` mode unless using `:domain`)
18
+
19
+ ## 2.2.0 (2020-12-09)
20
+
21
+ * [karlwilbur] - Rename `:strict` -> `:rfc`; `:moderate` -> `:strict`
22
+
23
+ ## 2.1.0 (2020-12-09)
24
+
25
+ * [karlwilbur] - Add linters and commit hooks to validate code prior to commits
26
+ * [karlwilbur] - Add `:mode` config option; values `:loose`, `:moderate`, `:strict`; default to `:loose`
27
+ * [karlwilbur] - Merge in changes from <https://github.com/karlwilbur/email_validator> fork
28
+
29
+ ## 1.9.0.pre (2020-10-14)
30
+
31
+ * [karlwilbur] - Add `require_fqdn` option, require FQDN by default
32
+ * [karlwilbur] - Add support for IPv4 and IPv6 address hosts
33
+ * [karlwilbur] - Add Rubocop, `.editorconfig`; code cleanup/linting
34
+
35
+ ## 1.8.0 (2019-06-14)
36
+
37
+ * [karlwilbur] - Refactor class methods for readability
38
+ * [karlwilbur] - `gemspec` meta updates
39
+ * [karlwilbur] - Use POSIX classes for better performance
40
+ * [karlwilbur] - Refactored tests to check specical characters one at a time
41
+ * [karlwilbur] - Refactored validation regex to be more techincally correct
42
+ * [karlwilbur] - Add this `CHANGELOG`
43
+
44
+ ## 1.7.0 (2019-04-20)
45
+
46
+ * [karlwilbur] - Added test coverage badge to README
47
+ * [karlwilbur] - Added I18n directive to remove warning message in testing
48
+ * [karlwilbur] - Added RFC-2822 reference
49
+ * [karlwilbur] - Ignore local rspec config file
50
+ * [karlwilbur] - Check for invalid double dots in strict mode
51
+ * [karlwilbur] - Updated spec_helper to remove Code Climate Test Reporter; it is to be run separately now
52
+ * [karlwilbur] - Allow leading/trailing whitespace in normal, not strict
53
+ * [karlwilbur] - Added `invalid?` as inverse of `valid?`
54
+ * [karlwilbur] - Add the ability to limit to a domain
55
+ * [karlwilbur] - Removed CodeShip badge
56
+ * [karlwilbur] - Make the dot in the domain part non-conditional
57
+ * [karlwilbur] - Fix domain label pattern to allow numbers per rfc5321
58
+
59
+ ## 1.6.0 (2015-06-14)
60
+
61
+ * [karlwilbur] - Fixed validation to be closer to RFC-5321
62
+ * [karlwilbur] - Updated specs to use Rspec 3 syntax
63
+ * [karlwilbur] - Added unicode suport to validation regexp
64
+ * [karlwilbur] - Added class access to regexp, and `valid?` calss method
65
+ * [karlwilbur] - Simplified code using new methods
66
+ * [karlwilbur] - Added CodeClimate and SimpleCov
67
+ * [karlwilbur] - Updated version and contact info
68
+
69
+ *** Forked from <https://github.com/balexand/email_validator>
70
+
71
+ ## 2.0.1 (2019-03-09)
72
+
73
+ * Add email value to error details [f1sherman #50]
74
+ * CI doesn't test Ruby versions that no longer receive updates [f1sherman #51]
75
+
76
+ ## 2.0.0 (2019-03-02)
77
+
78
+ * Looser validation [#49]
79
+
80
+ ## 1.6.0 (2015-05-12)
81
+
82
+ * Unicode characters support [i7an #24]
83
+
84
+ ## 1.5.0 (2014-12-08)
85
+
86
+ * Add a class method for simpler validation [TiteiKo and cluesque #19]
87
+ * RSpec 3.0 syntax [strivedi183 #17]
88
+ * Create Changes.md
89
+
90
+ ---
91
+
92
+ Check the [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax)
93
+ for help with Markdown.
94
+
95
+ The [Github Flavored Markdown page](http://github.github.com/github-flavored-markdown/)
96
+ 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,181 @@ 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: this might help if, for example, you
58
+ have separate `User` and `AdminUser` models and want to ensure that `AdminUser`
59
+ emails are on a specific 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
34
163
 
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.
164
+ ```ruby
165
+ EmailValidator.valid?('narf@somehost') # boolean false
166
+ EmailValidator.invalid?('narf@somehost', require_fqdn: false) # boolean true
167
+ ```
36
168
 
37
- ## Trimming whitespace
169
+ ### Requiring a specific domain
170
+
171
+ ```ruby
172
+ EmailValidator.valid?('narf@example.com', domain: 'foo.com') # boolean false
173
+ EmailValidator.invalid?('narf@example.com', domain: 'foo.com') # boolean true
174
+ ```
38
175
 
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:
176
+ ### Strict mode
40
177
 
41
178
  ```ruby
42
- def email=(e)
43
- e = e.strip if e
44
- super
45
- end
179
+ EmailValidator.regexp(mode: :strict) # returns the regex
180
+ EmailValidator.valid?('narf@example.com', mode: :strict) # boolean
181
+ ```
182
+
183
+ ### RFC mode
184
+
185
+ ```ruby
186
+ EmailValidator.regexp(mode: :rfc) # returns the regex
187
+ EmailValidator.valid?('narf@example.com', mode: :rfc) # boolean
46
188
  ```
47
189
 
48
- You may also want to convert emails to lowercase in the same way.
190
+ ## Thread safety
191
+
192
+ This gem is thread safe, with one caveat: `EmailValidator.default_options` must
193
+ be configured before use in a multi-threaded environment. If you configure
194
+ `default_options` in a Rails initializer file, then you're good to go since
195
+ initializers are run before worker threads are spawned.
49
196
 
50
197
  ## Alternative gems
51
198
 
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.
199
+ Do you prefer a different email validation gem? If so, open an issue with a brief
200
+ explanation of how it differs from this gem. I'll add a link to it in this README.
53
201
 
54
- ## Thread safety
202
+ * [`email_address`](https://github.com/afair/email_address) (<https://github.com/K-and-R/email_validator/issues/58>)
203
+ * [`email_verifier`](https://github.com/kamilc/email_verifier) (<https://github.com/K-and-R/email_validator/issues/65>)
204
+
205
+ ## Maintainers
206
+
207
+ All thanks is given to [Brian Alexander (balexand)](https://github.com/balexand)
208
+ for is initial work on this gem.
209
+
210
+ Currently maintained by:
55
211
 
56
- This gem is thread safe.
212
+ * Karl Wilbur (<https://github.com/karlwilbur>)
213
+ * K&R Software (<https://github.com/K-and-R>)
@@ -1,29 +1,145 @@
1
- # frozen_string_literal: true
2
- require 'active_model'
3
-
1
+ # Based on work from http://thelucid.com/2010/01/08/sexy-validation-in-edge-rails-rails-3/
4
2
  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
- )
3
+ # rubocop:disable Style/ClassVars
4
+ @@default_options = {
5
+ :allow_nil => false,
6
+ :domain => nil,
7
+ :require_fqdn => nil,
8
+ :mode => :loose
9
+ }
10
+ # rubocop:enable Style/ClassVars
11
+
12
+ class << self
13
+ def default_options
14
+ @@default_options
11
15
  end
12
16
 
13
- /[^\s]@[^\s]/
14
- end
17
+ def valid?(value, options = {})
18
+ options = parse_options(options)
19
+ return true if value.nil? && options[:allow_nil] == true
20
+ return false if value.nil?
21
+ !!(value =~ regexp(options))
22
+ end
15
23
 
16
- def self.valid?(value, options = nil)
17
- !invalid?(value)
18
- end
24
+ def invalid?(value, options = {})
25
+ !valid?(value, options)
26
+ end
27
+
28
+ # Refs:
29
+ # https://tools.ietf.org/html/rfc2822 : 3.2. Lexical Tokens, 3.4.1. Addr-spec specification
30
+ # https://tools.ietf.org/html/rfc5321 : 4.1.2. Command Argument Syntax
31
+ def regexp(options = {})
32
+ options = parse_options(options)
33
+ case options[:mode]
34
+ when :loose
35
+ loose_regexp(options)
36
+ when :rfc
37
+ rfc_regexp(options)
38
+ else
39
+ strict_regexp(options)
40
+ end
41
+ end
42
+
43
+ protected
44
+
45
+ def loose_regexp(options = {})
46
+ return /\A[^\s]+@[^\s]+\z/ if options[:domain].nil?
47
+ /\A[^\s]+@#{domain_part_pattern(options)}\z/
48
+ end
49
+
50
+ def strict_regexp(options = {})
51
+ /\A(?>#{local_part_pattern})@#{domain_part_pattern(options)}\z/i
52
+ end
53
+
54
+ def rfc_regexp(options = {})
55
+ /\A(?>#{local_part_pattern})(?:@#{domain_part_pattern(options)})?\z/i
56
+ end
57
+
58
+ def alpha
59
+ '[[:alpha:]]'
60
+ end
61
+
62
+ def alnum
63
+ '[[:alnum:]]'
64
+ end
65
+
66
+ def alnumhy
67
+ "(?:#{alnum}|-)"
68
+ end
69
+
70
+ def ipv4
71
+ '\d{1,3}(?:\.\d{1,3}){3}'
72
+ end
73
+
74
+ def ipv6
75
+ # only supporting full IPv6 addresses right now
76
+ 'IPv6:[[:xdigit:]]{1,4}(?::[[:xdigit:]]{1,4}){7}'
77
+ end
78
+
79
+ def address_literal
80
+ "\\[(?:#{ipv4}|#{ipv6})\\]"
81
+ end
82
+
83
+ def host_label_pattern
84
+ "#{alnum}(?:#{alnumhy}{,61}#{alnum})?"
85
+ end
86
+
87
+ # splitting this up into separate regex pattern for performance; let's not
88
+ # try the "contains" pattern unless we have to
89
+ def domain_label_pattern
90
+ '(?=[^.]{1,63}(?:\.|$))' \
91
+ '(?:' \
92
+ "#{alpha}" \
93
+ "|#{domain_label_starts_with_a_letter_pattern}" \
94
+ "|#{domain_label_ends_with_a_letter_pattern}" \
95
+ "|#{domain_label_contains_a_letter_pattern}" \
96
+ ')'
97
+ end
98
+
99
+ def domain_label_starts_with_a_letter_pattern
100
+ "#{alpha}#{alnumhy}{,61}#{alnum}"
101
+ end
102
+
103
+ def domain_label_ends_with_a_letter_pattern
104
+ "#{alnum}#{alnumhy}{,61}#{alpha}"
105
+ end
106
+
107
+ def domain_label_contains_a_letter_pattern
108
+ "(?:[[:digit:]])(?:[[:digit:]]|-)*#{alpha}#{alnumhy}*#{alnum}"
109
+ end
110
+
111
+ def atom_char
112
+ # The `atext` spec
113
+ # We are looking at this without whitespace; no whitespace support here
114
+ "[-#{alpha}#{alnum}+_!\"'#$%^&*{}/=?`|~]"
115
+ end
116
+
117
+ def local_part_pattern
118
+ # the `dot-atom-text` spec, but with a 64 character limit
119
+ "#{atom_char}(?:\\.?#{atom_char}){,63}"
120
+ end
121
+
122
+ def domain_part_pattern(options)
123
+ return options[:domain].sub(/\./, '\.') if options[:domain].present?
124
+ return fqdn_pattern if options[:require_fqdn]
125
+ "(?=.{1,255}$)(?:#{address_literal}|(?:#{host_label_pattern}\\.)*#{domain_label_pattern})"
126
+ end
127
+
128
+ def fqdn_pattern
129
+ "(?=.{1,255}$)(?:#{host_label_pattern}\\.)*#{domain_label_pattern}\\.#{domain_label_pattern}"
130
+ end
19
131
 
20
- def self.invalid?(value, options = nil)
21
- !(value =~ regexp)
132
+ private
133
+
134
+ def parse_options(options)
135
+ # `:strict` mode enables `:require_fqdn`, unless it is already explicitly disabled
136
+ options[:require_fqdn] = true if options[:require_fqdn].nil? && options[:mode] == :strict
137
+ default_options.merge(options)
138
+ end
22
139
  end
23
140
 
24
141
  def validate_each(record, attribute, value)
25
- if self.class.invalid?(value)
26
- record.errors.add(attribute, options[:message] || :invalid)
27
- end
142
+ options = @@default_options.merge(self.options)
143
+ record.errors.add(attribute, options[:message] || :invalid) unless self.class.valid?(value, options)
28
144
  end
29
145
  end