email_validator 2.0.1 → 2.1.0

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: ede74c324c144cfd01a3f13a1782ecb45af661a794ac8361d9193939a1973174
4
- data.tar.gz: 4a8dad3331c014a855e03fbcf40f2cdfdb4e0cc6f216775d579b571f8276cc0e
3
+ metadata.gz: bc66e9e16fb1ba1babe7b102865ba9b112eb373392057313c9e7aeffcef2bf23
4
+ data.tar.gz: a4a991392b379b60dbf04105469c28424a8c294d62557095e920cee7d2cf8e27
5
5
  SHA512:
6
- metadata.gz: 108a2f115956ceb9b3f53e9dac871589efc9169e99789cc0c7a5339fbcaa063e9b47f6872b9b9836fa19b53d6e42fade76fa33961b8896bf382cf54ff9aad53f
7
- data.tar.gz: 7cde7e0065099b6b5739338190dc6712dd5d8781892f5d645a13f24b5f4c1f328d563fb324a74782fcae307a4786bfb025acd4a33250b65c8f6c9ab028626bcc
6
+ metadata.gz: 864e0e44385e3252be96087fd57f76b81e26cfdd4bec1a08a880afaf1335efbba609b7b28ef821cb4df63519776ec95a520d6f6f1910d77b28221cacae8cab28
7
+ data.tar.gz: bb48dc83509e3a87d581153aa8f3950431a2daf106c4a2bd5be798925a8a3629e054f4397127c748b5f99eba69b55e0084a7b6e0597343d02f3ed617e057f46c
@@ -0,0 +1,81 @@
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.1.0 (2020-12-09)
9
+
10
+ * [karlwilbur] - Add linters and commit hooks to validate code prior to commits
11
+ * [karlwilbur] - Add `:mode` config option; values `:loose`, `:moderate`, `:strict`; default to `:loose`
12
+ * [karlwilbur] - Merge in changes from <https://github.com/karlwilbur/email_validator> fork
13
+
14
+ ## 1.9.0.pre (2020-10-14)
15
+
16
+ * [karlwilbur] - Add `require_fqdn` option, require FQDN by default
17
+ * [karlwilbur] - Add support for IPv4 and IPv6 address hosts
18
+ * [karlwilbur] - Add Rubocop, `.editorconfig`; code cleanup/linting
19
+
20
+ ## 1.8.0 (2019-06-14)
21
+
22
+ * [karlwilbur] - Refactor class methods for readability
23
+ * [karlwilbur] - `gemspec` meta updates
24
+ * [karlwilbur] - Use POSIX classes for better performance
25
+ * [karlwilbur] - Refactored tests to check specical characters one at a time
26
+ * [karlwilbur] - Refactored validation regex to be more techincally correct
27
+ * [karlwilbur] - Add this `CHANGELOG`
28
+
29
+ ## 1.7.0 (2019-04-20)
30
+
31
+ * [karlwilbur] - Added test coverage badge to README
32
+ * [karlwilbur] - Added I18n directive to remove warning message in testing
33
+ * [karlwilbur] - Added RFC-2822 reference
34
+ * [karlwilbur] - Ignore local rspec config file
35
+ * [karlwilbur] - Check for invalid double dots in strict mode
36
+ * [karlwilbur] - Updated spec_helper to remove Code Climate Test Reporter; it is to be run separately now
37
+ * [karlwilbur] - Allow leading/trailing whitespace in normal, not strict
38
+ * [karlwilbur] - Added `invalid?` as inverse of `valid?`
39
+ * [karlwilbur] - Add the ability to limit to a domain
40
+ * [karlwilbur] - Removed CodeShip badge
41
+ * [karlwilbur] - Make the dot in the domain part non-conditional
42
+ * [karlwilbur] - Fix domain label pattern to allow numbers per rfc5321
43
+
44
+ ## 1.6.0 (2015-06-14)
45
+
46
+ * [karlwilbur] - Fixed validation to be closer to RFC-5321
47
+ * [karlwilbur] - Updated specs to use Rspec 3 syntax
48
+ * [karlwilbur] - Added unicode suport to validation regexp
49
+ * [karlwilbur] - Added class access to regexp, and `valid?` calss method
50
+ * [karlwilbur] - Simplified code using new methods
51
+ * [karlwilbur] - Added CodeClimate and SimpleCov
52
+ * [karlwilbur] - Updated version and contact info
53
+
54
+ *** Forked from <https://github.com/balexand/email_validator>
55
+
56
+ ## 2.0.1 (2019-03-09)
57
+
58
+ * Add email value to error details [f1sherman #50]
59
+ * CI doesn't test Ruby versions that no longer receive updates [f1sherman #51]
60
+
61
+ ## 2.0.0 (2019-03-02)
62
+
63
+ * Looser validation [#49]
64
+
65
+ ## 1.6.0 (2015-05-12)
66
+
67
+ * Unicode characters support [i7an #24]
68
+
69
+ ## 1.5.0 (2014-12-08)
70
+
71
+ * Add a class method for simpler validation [TiteiKo and cluesque #19]
72
+ * RSpec 3.0 syntax [strivedi183 #17]
73
+ * Create Changes.md
74
+
75
+ ---
76
+
77
+ Check the [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax)
78
+ for help with Markdown.
79
+
80
+ The [Github Flavored Markdown page](http://github.github.com/github-flavored-markdown/)
81
+ 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://secure.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/karlwilbur/email_validator/badges/gpa.svg)](https://codeclimate.com/github/karlwilbur/email_validator)
5
+ [![Test Coverage](https://codeclimate.com/github/karlwilbur/email_validator/badges/coverage.svg)](https://codeclimate.com/github/karlwilbur/email_validator/coverage)
6
+
7
+ An email validator for Rails 3+.
8
+
9
+ Supports RFC-2822-compliant and RFC-5321-compliant email 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 maybe use cases require more stricy validation and this is
21
+ supported by using the `:moderate` validation mode. Additionally, the `:strict`
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 `:strict` mode but not valid in `:loose` or `:moderate`.
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 FDQN, 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 `:moderate` checking:
52
+
53
+ ```ruby
54
+ validates :my_email_attribute, email: {mode: :moderate, 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. The `:domain` and `:require_fqdn` option are ignored in `:loose` mode.
90
+
91
+ ### Moderate mode
92
+
93
+ Enabling `:moderate` checking will check for a "normal" email format that would
94
+ be expected in most common everyday usage. Moderate mode basically checks for a
95
+ properly sized and formatted mailbox label, a single "@" symbol, and a properly
96
+ sized and formatted FQDN. Enabling `:moderate` mode will also enable `:require_fqdn`
97
+ configuration option.
98
+
99
+ Moderate mode can be enabled globally by requiring `email_validator/moderate` 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', github: 'karlwilbur/email_validator', require: 'email_validator/moderate'
107
+ ```
108
+
109
+ * `config/initializers/email_validator.rb`:
110
+
111
+ ```ruby
112
+ if defined?(EmailValidator)
113
+ EmailValidator.default_options[:mode] = :moderate
114
+ end
115
+ ```
116
+
117
+ * `validates` call:
118
+
119
+ ```ruby
120
+ validates :my_email_attribute, email: {mode: :moderate}
121
+ ```
122
+
123
+ ### Strict mode
124
+
125
+ In order to have stricter 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 `:strict` mode.
127
+
128
+ You can do this globally by requiring `email_validator/strict` 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', github: 'karlwilbur/email_validator', require: 'email_validator/strict'
136
+ ```
137
+
138
+ * `config/initializers/email_validator.rb`:
139
+
140
+ ```ruby
141
+ if defined?(EmailValidator)
142
+ EmailValidator.default_options[:mode] = :strict
143
+ end
144
+ ```
145
+
146
+ * `validates` call:
147
+
148
+ ```ruby
149
+ validates :my_email_attribute, email: {mode: :strict}
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
+ ### Moderate mode
40
177
 
41
178
  ```ruby
42
- def email=(e)
43
- e = e.strip if e
44
- super
45
- end
179
+ EmailValidator.regexp(mode: :moderate) # returns the regex
180
+ EmailValidator.valid?('narf@example.com', mode: :moderate) # boolean
181
+ ```
182
+
183
+ ### Strict mode
184
+
185
+ ```ruby
186
+ EmailValidator.regexp(mode: :strict) # returns the regex
187
+ EmailValidator.valid?('narf@example.com', mode: :strict) # 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,101 @@
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
+ if options[:mode] == :loose
34
+ return /\A[^\s]+@[^\s]+\z/ if options[:domain].nil?
35
+ return /\A[^\s]+@#{domain_pattern(options)}\z/
36
+ end
37
+ /\A(?>#{local_part_pattern})@#{domain_pattern(options)}\z/i
38
+ end
39
+
40
+ protected
41
+
42
+ def alpha
43
+ '[[:alpha:]]'
44
+ end
45
+
46
+ def alnum
47
+ '[[:alnum:]]'
48
+ end
49
+
50
+ def alnumhy
51
+ "(?:#{alnum}|-)"
52
+ end
53
+
54
+ def ipv4
55
+ '\d{1,3}(?:\.\d{1,3}){3}'
56
+ end
57
+
58
+ def ipv6
59
+ # only supporting full IPv6 addresses right now
60
+ 'IPv6:[[:xdigit:]]{1,4}(?::[[:xdigit:]]{1,4}){7}'
61
+ end
62
+
63
+ def address_literal
64
+ "\\[(?:#{ipv4}|#{ipv6})\\]"
65
+ end
66
+
67
+ def label_pattern
68
+ "#{alpha}(?:#{alnumhy}{,62}#{alnum})?"
69
+ end
70
+
71
+ def atom_char
72
+ # The `atext` spec
73
+ # We are looking at this without whitespace; no whitespace support here
74
+ "[-#{alpha}#{alnum}+_!\"'#$%^&*{}/=?`|~]"
75
+ end
76
+
77
+ def local_part_pattern
78
+ # the `dot-atom-text` spec, but with a 64 character limit
79
+ "#{atom_char}(?:\\.?#{atom_char}){,63}"
80
+ end
81
+
82
+ def domain_pattern(options)
83
+ return options[:domain].sub(/\./, '\.') if options[:domain].present?
84
+ return "(?:#{label_pattern}\\.)+#{label_pattern}" if options[:require_fqdn]
85
+ "(?:#{address_literal}|(?:#{label_pattern}\\.)*#{label_pattern})"
86
+ end
87
+
88
+ private
19
89
 
20
- def self.invalid?(value, options = nil)
21
- !(value =~ regexp)
90
+ def parse_options(options)
91
+ # `:moderate` mode enables `:require_fqdn`, unless it is already explicitly disabled
92
+ options[:require_fqdn] = true if options[:require_fqdn].nil? && options[:mode] == :moderate
93
+ default_options.merge(options)
94
+ end
22
95
  end
23
96
 
24
97
  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
98
+ options = @@default_options.merge(self.options)
99
+ record.errors.add(attribute, options[:message] || :invalid) unless self.class.valid?(value, options)
28
100
  end
29
101
  end
@@ -1,153 +1,872 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
 
4
- class TestUser < TestModel
3
+ class DefaultUser < TestModel
5
4
  validates :email, :email => true
6
5
  end
7
6
 
8
- class TestUserAllowsNil < TestModel
9
- validates :email, :email => {:allow_nil => true}
7
+ class ModerateUser < TestModel
8
+ validates :email, :email => { :mode => :moderate }
10
9
  end
11
10
 
12
- class TestUserAllowsNilFalse < TestModel
13
- validates :email, :email => {:allow_nil => false}
11
+ class StrictUser < TestModel
12
+ validates :email, :email => { :mode => :strict }
14
13
  end
15
14
 
16
- class TestUserWithMessage < TestModel
17
- validates :email_address, :email => {:message => 'is not looking very good!'}
15
+ class AllowNilDefaultUser < TestModel
16
+ validates :email, :email => { :allow_nil => true }
18
17
  end
19
18
 
20
- describe EmailValidator do
19
+ class AllowNilModerateUser < TestModel
20
+ validates :email, :email => { :allow_nil => true, :mode => :moderate }
21
+ end
22
+
23
+ class AllowNilStrictUser < TestModel
24
+ validates :email, :email => { :allow_nil => true, :mode => :strict }
25
+ end
26
+
27
+ class DisallowNilDefaultUser < TestModel
28
+ validates :email, :email => { :allow_nil => false }
29
+ end
30
+
31
+ class DisallowNilModerateUser < TestModel
32
+ validates :email, :email => { :allow_nil => false, :mode => :moderate }
33
+ end
34
+
35
+ class DisallowNilStrictUser < TestModel
36
+ validates :email, :email => { :allow_nil => false, :mode => :strict }
37
+ end
38
+
39
+ class DomainModerateUser < TestModel
40
+ validates :email, :email => { :domain => 'example.com', :mode => :moderate }
41
+ end
42
+
43
+ class DomainStrictUser < TestModel
44
+ validates :email, :email => { :domain => 'example.com', :mode => :strict }
45
+ end
46
+
47
+ class NonFqdnModerateUser < TestModel
48
+ validates :email, :email => { :require_fqdn => false, :mode => :moderate }
49
+ end
50
+
51
+ class NonFqdnStrictUser < TestModel
52
+ validates :email, :email => { :require_fqdn => false, :mode => :strict }
53
+ end
54
+
55
+ class DefaultUserWithMessage < TestModel
56
+ validates :email_address, :email => { :message => 'is not looking very good!' }
57
+ end
58
+
59
+ RSpec.describe EmailValidator do
60
+ describe 'validation' do
61
+ valid_special_chars = {
62
+ :ampersand => '&',
63
+ :asterisk => '*',
64
+ :backtick => '`',
65
+ :braceleft => '{',
66
+ :braceright => '}',
67
+ :caret => '^',
68
+ :dollar => '$',
69
+ :equals => '=',
70
+ :exclaim => '!',
71
+ :hash => '#',
72
+ :hyphen => '-',
73
+ :percent => '%',
74
+ :plus => '+',
75
+ :pipe => '|',
76
+ :question => '?',
77
+ :quotedouble => '"',
78
+ :quotesingle => "'",
79
+ :slash => '/',
80
+ :tilde => '~',
81
+ :underscore => '_'
82
+ }
83
+
84
+ invalid_special_chars = {
85
+ :backslash => '\\',
86
+ :braketleft => '[',
87
+ :braketright => ']',
88
+ :colon => ':',
89
+ :comma => ',',
90
+ :greater => '>',
91
+ :lesser => '<',
92
+ :parenleft => '(',
93
+ :parenright => ')',
94
+ :semicolon => ';'
95
+ }
96
+
97
+ valid_includable = valid_special_chars.merge({ :dot => '.' })
98
+ valid_beginable = valid_special_chars
99
+ valid_endable = valid_special_chars
100
+ invalid_includable = { :at => '@' }
101
+ whitespace = { :newline => "\n", :tab => "\t", :carriage_return => "\r", :space => ' ' }
102
+ moderatly_invalid_includable = invalid_special_chars
103
+ moderatly_invalid_beginable = moderatly_invalid_includable.merge({ :dot => '.' })
104
+ moderatly_invalid_endable = moderatly_invalid_beginable
105
+ domain_invalid_beginable = invalid_special_chars.merge(valid_special_chars)
106
+ domain_invalid_endable = domain_invalid_beginable
107
+ domain_invalid_includable = domain_invalid_beginable.reject { |k, _v| k == :hyphen }
108
+
109
+ # rubocop:disable Layout/BlockEndNewline, Layout/MultilineBlockLayout, Layout/MultilineMethodCallBraceLayout, Style/BlockDelimiters, Style/MultilineBlockChain
110
+ context 'when given the valid email' do
111
+ valid_includable.map { |k, v| [
112
+ "include-#{v}-#{k}@valid-characters-in-local.dev"
113
+ ]}.concat(valid_beginable.map { |k, v| [
114
+ "#{v}start-with-#{k}@valid-characters-in-local.dev"
115
+ ]}).concat(valid_endable.map { |k, v| [
116
+ "end-with-#{k}-#{v}@valid-characters-in-local.dev"
117
+ ]}).concat([
118
+ 'a+b@plus-in-local.com',
119
+ 'a_b@underscore-in-local.com',
120
+ 'user@example.com',
121
+ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@letters-in-local.dev',
122
+ '01234567890@numbers-in-local.dev',
123
+ 'a@single-character-in-local.dev',
124
+ 'one-character-third-level@a.example.com',
125
+ 'single-character-in-sld@x.dev',
126
+ 'local@dash-in-sld.com',
127
+ 'numbers-in-sld@s123.com',
128
+ 'one-letter-sld@x.dev',
129
+ 'uncommon-tld@sld.museum',
130
+ 'uncommon-tld@sld.travel',
131
+ 'uncommon-tld@sld.mobi',
132
+ 'country-code-tld@sld.uk',
133
+ 'country-code-tld@sld.rw',
134
+ 'local@sld.newTLD',
135
+ 'local@sub.domains.com',
136
+ 'aaa@bbb.co.jp',
137
+ 'nigel.worthington@big.co.uk',
138
+ 'f@c.com',
139
+ 'f@s.c',
140
+ 'someuser@somehost.somedomain',
141
+ 'mixed-1234-in-{+^}-local@sld.dev',
142
+ 'partially."quoted"@sld.com',
143
+ 'areallylongnameaasdfasdfasdfasdf@asdfasdfasdfasdfasdf.ab.cd.ef.gh.co.ca'
144
+ ]).flatten.each do |email|
145
+ context 'when using defaults' do
146
+ it "#{email} should be valid" do
147
+ expect(DefaultUser.new(:email => email)).to be_valid
148
+ end
149
+
150
+ it "#{email} should be valid using EmailValidator.valid?" do
151
+ expect(described_class).to be_valid(email)
152
+ end
21
153
 
22
- describe "validation" do
23
- context "given the valid emails" do
154
+ it "#{email} should not be invalid using EmailValidator.invalid?" do
155
+ expect(described_class).not_to be_invalid(email)
156
+ end
157
+
158
+ it "#{email} should match the regexp" do
159
+ expect(!!(email.strip =~ described_class.regexp)).to be(true)
160
+ end
161
+ end
162
+
163
+ context 'when in `:moderate` mode' do
164
+ it "#{email} should be valid" do
165
+ expect(ModerateUser.new(:email => email)).to be_valid
166
+ end
167
+
168
+ it "#{email} should be valid using EmailValidator.valid?" do
169
+ expect(described_class).to be_valid(email, :mode => :moderate)
170
+ end
171
+
172
+ it "#{email} should not be invalid using EmailValidator.valid?" do
173
+ expect(described_class).not_to be_invalid(email, :mode => :moderate)
174
+ end
175
+
176
+ it "#{email} should match the regexp" do
177
+ expect(!!(email.strip =~ described_class.regexp(:mode => :moderate))).to be(true)
178
+ end
179
+ end
180
+
181
+ context 'when in `:strict` mode' do
182
+ it "#{email} should be valid" do
183
+ expect(StrictUser.new(:email => email)).to be_valid
184
+ end
185
+
186
+ it "#{email} should be valid using EmailValidator.valid?" do
187
+ expect(described_class).to be_valid(email, :mode => :strict)
188
+ end
189
+
190
+ it "#{email} should not be invalid using EmailValidator.invalid?" do
191
+ expect(described_class).not_to be_invalid(email, :mode => :strict)
192
+ end
193
+
194
+ it "#{email} should match the regexp" do
195
+ expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(true)
196
+ end
197
+ end
198
+ end
199
+ end
200
+
201
+ context 'when given the valid host-only email' do
24
202
  [
25
- "a+b@plus-in-local.com",
26
- "a_b@underscore-in-local.com",
27
- "user@example.com",
28
- " user@example.com ",
29
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@letters-in-local.org",
30
- "01234567890@numbers-in-local.net",
31
- "a@single-character-in-local.org",
32
- "one-character-third-level@a.example.com",
33
- "single-character-in-sld@x.org",
34
- "local@dash-in-sld.com",
35
- "letters-in-sld@123.com",
36
- "one-letter-sld@x.org",
37
- "uncommon-tld@sld.museum",
38
- "uncommon-tld@sld.travel",
39
- "uncommon-tld@sld.mobi",
40
- "country-code-tld@sld.uk",
41
- "country-code-tld@sld.rw",
42
- "local@sld.newTLD",
43
- "local@sub.domains.com",
44
- "aaa@bbb.co.jp",
45
- "nigel.worthington@big.co.uk",
46
- "f@c.com",
47
- "areallylongnameaasdfasdfasdfasdf@asdfasdfasdfasdfasdf.ab.cd.ef.gh.co.ca",
48
- "ящик@яндекс.рф",
49
- "test@test.testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttes",
50
- "hans,peter@example.com",
51
- "hans(peter@example.com",
52
- "hans)peter@example.com",
53
- "partially.\"quoted\"@sld.com",
54
- "&'*+-./=?^_{}~@other-valid-characters-in-local.net",
55
- "mixed-1234-in-{+^}-local@sld.net",
56
- "user@domain.рф",
57
- "寿司@example.com"
203
+ 'f@s',
204
+ 'user@localhost',
205
+ 'someuser@somehost'
58
206
  ].each do |email|
207
+ context 'when using defaults' do
208
+ it "#{email} should be valid" do
209
+ expect(DefaultUser.new(:email => email)).to be_valid
210
+ end
59
211
 
60
- it "#{email.inspect} should be valid" do
61
- expect(TestUser.new(:email => email)).to be_valid
212
+ it "#{email} should be valid using EmailValidator.valid?" do
213
+ expect(described_class).to be_valid(email)
214
+ end
215
+
216
+ it "#{email} should not be invalid using EmailValidator.invalid?" do
217
+ expect(described_class).not_to be_invalid(email)
218
+ end
219
+
220
+ it "#{email} should match the regexp" do
221
+ expect(!!(email.strip =~ described_class.regexp)).to be(true)
222
+ end
62
223
  end
63
224
 
64
- it "#{email.inspect} should not be invalid" do
65
- expect(TestUser.new(:email => email)).not_to be_invalid
225
+ context 'when in `:moderate` mode' do
226
+ it "#{email} should not be valid" do
227
+ expect(ModerateUser.new(:email => email)).not_to be_valid
228
+ end
229
+
230
+ it "#{email} should not be valid using EmailValidator.valid?" do
231
+ expect(described_class).not_to be_valid(email, :mode => :moderate)
232
+ end
233
+
234
+ it "#{email} should be invalid using EmailValidator.invalid?" do
235
+ expect(described_class).to be_invalid(email, :mode => :moderate)
236
+ end
237
+
238
+ it "#{email} should not match the regexp" do
239
+ expect(!!(email.strip =~ described_class.regexp(:mode => :moderate))).to be(false)
240
+ end
66
241
  end
67
242
 
68
- it "#{email.inspect} should match the regexp" do
69
- expect(email =~ EmailValidator.regexp).to be_truthy
243
+ context 'when in `:strict` mode' do
244
+ it "#{email} should be valid" do
245
+ expect(StrictUser.new(:email => email)).to be_valid
246
+ end
247
+
248
+ it "#{email} should be valid using EmailValidator.valid?" do
249
+ expect(described_class).to be_valid(email, :mode => :strict)
250
+ end
251
+
252
+ it "#{email} should not be invalid using EmailValidator.invalid?" do
253
+ expect(described_class).not_to be_invalid(email, :mode => :strict)
254
+ end
255
+
256
+ it "#{email} should match the regexp" do
257
+ expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(true)
258
+ end
70
259
  end
260
+ end
261
+ end
262
+
263
+ context 'when given the valid IP address email' do
264
+ [
265
+ 'bracketed-IP@[127.0.0.1]',
266
+ 'bracketed-and-labeled-IPv6@[IPv6:abcd:ef01:1234:5678:9abc:def0:1234:5678]'
267
+ ].each do |email|
268
+ context 'when using defaults' do
269
+ it "#{email} should be valid" do
270
+ expect(DefaultUser.new(:email => email)).to be_valid
271
+ end
272
+
273
+ it "#{email} should be valid using EmailValidator.valid?" do
274
+ expect(described_class).to be_valid(email)
275
+ end
276
+
277
+ it "#{email} should not be invalid using EmailValidator.invalid?" do
278
+ expect(described_class).not_to be_invalid(email)
279
+ end
71
280
 
72
- it "#{email.inspect} should pass the class tester" do
73
- expect(EmailValidator.valid?(email)).to be_truthy
281
+ it "#{email} should match the regexp" do
282
+ expect(!!(email.strip =~ described_class.regexp)).to be(true)
283
+ end
74
284
  end
75
285
 
286
+ context 'when in `:moderate` mode' do
287
+ it "#{email} should not be valid" do
288
+ expect(ModerateUser.new(:email => email)).not_to be_valid
289
+ end
290
+
291
+ it "#{email} should not be valid using EmailValidator.valid?" do
292
+ expect(described_class).not_to be_valid(email, :mode => :moderate)
293
+ end
294
+
295
+ it "#{email} should be invalid using EmailValidator.invalid?" do
296
+ expect(described_class).to be_invalid(email, :mode => :moderate)
297
+ end
298
+
299
+ it "#{email} should not match the strict regexp" do
300
+ expect(!!(email.strip =~ described_class.regexp(:mode => :moderate))).to be(false)
301
+ end
302
+ end
303
+
304
+ context 'when in `:strict` mode' do
305
+ it "#{email} should be valid" do
306
+ expect(StrictUser.new(:email => email)).to be_valid
307
+ end
308
+
309
+ it "#{email} should be valid using EmailValidator.valid?" do
310
+ expect(described_class).to be_valid(email, :mode => :strict)
311
+ end
312
+
313
+ it "#{email} should not be invalid using EmailValidator.invalid?" do
314
+ expect(described_class).not_to be_invalid(email, :mode => :strict)
315
+ end
316
+
317
+ it "#{email} should match the strict regexp" do
318
+ expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(true)
319
+ end
320
+ end
76
321
  end
322
+ end
323
+
324
+ context 'when given the invalid email' do
325
+ invalid_includable.map { |k, v| [
326
+ "include-#{v}-#{k}@invalid-characters-in-local.dev"
327
+ ]}.concat(domain_invalid_beginable.map { |k, v| [
328
+ "start-with-#{k}@#{v}invalid-characters-in-domain.dev"
329
+ ]}).concat(domain_invalid_endable.map { |k, v| [
330
+ "end-with-#{k}@invalid-characters-in-domain#{v}.dev"
331
+ ]}).concat(domain_invalid_includable.map { |k, v| [
332
+ "include-#{k}@invalid-characters-#{v}-in-domain.dev"
333
+ ]}).concat([
334
+ 'test@example.com@example.com',
335
+ 'missing-sld@.com',
336
+ 'missing-tld@sld.',
337
+ 'only-numbers-in-domain-label@sub.123.com',
338
+ 'only-numbers-in-domain-label@123.example.com',
339
+ 'unbracketed-IPv6@abcd:ef01:1234:5678:9abc:def0:1234:5678',
340
+ 'unbracketed-and-labled-IPv6@IPv6:abcd:ef01:1234:5678:9abc:def0:1234:5678',
341
+ 'bracketed-and-unlabeled-IPv6@[abcd:ef01:1234:5678:9abc:def0:1234:5678]',
342
+ 'unbracketed-IPv4@127.0.0.1',
343
+ 'invalid-IPv4@127.0.0.1.26',
344
+ 'another-invalid-IPv4@127.0.0.256',
345
+ 'IPv4-and-port@127.0.0.1:25',
346
+ 'host-beginning-with-dot@.example.com',
347
+ 'domain-beginning-with-dash@-example.com',
348
+ 'domain-ending-with-dash@example-.com',
349
+ 'the-local-part-is-invalid-if-it-is-longer-than-sixty-four-characters@sld.dev',
350
+ "user@example.com<script>alert('hello')</script>"
351
+ ]).flatten.each do |email|
352
+ context 'when using defaults' do
353
+ it "#{email} should be valid" do
354
+ expect(DefaultUser.new(:email => email)).to be_valid
355
+ end
356
+
357
+ it "#{email} should be valid using EmailValidator.valid?" do
358
+ expect(described_class).to be_valid(email)
359
+ end
360
+
361
+ it "#{email} should not be invalid using EmailValidator.invalid?" do
362
+ expect(described_class).not_to be_invalid(email)
363
+ end
364
+
365
+ it "#{email} should match the regexp" do
366
+ expect(!!(email.strip =~ described_class.regexp)).to be(true)
367
+ end
368
+ end
369
+
370
+ context 'when in `:moderate` mode' do
371
+ it "#{email} should not be valid" do
372
+ expect(ModerateUser.new(:email => email)).not_to be_valid
373
+ end
374
+
375
+ it "#{email} should not be valid using EmailValidator.valid?" do
376
+ expect(described_class).not_to be_valid(email, :mode => :moderate)
377
+ end
378
+
379
+ it "#{email} should be invalid using EmailValidator.invalid?" do
380
+ expect(described_class).to be_invalid(email, :mode => :moderate)
381
+ end
77
382
 
383
+ it "#{email} should not match the strict regexp" do
384
+ expect(!!(email.strip =~ described_class.regexp(:mode => :moderate))).to be(false)
385
+ end
386
+ end
387
+
388
+ context 'when in `:strict` mode' do
389
+ it "#{email} should not be valid" do
390
+ expect(StrictUser.new(:email => email)).not_to be_valid
391
+ end
392
+
393
+ it "#{email} should not be valid using EmailValidator.valid?" do
394
+ expect(described_class).not_to be_valid(email, :mode => :strict)
395
+ end
396
+
397
+ it "#{email} should be invalid using EmailValidator.invalid?" do
398
+ expect(described_class).to be_invalid(email, :mode => :strict)
399
+ end
400
+
401
+ it "#{email} should not match the strict regexp" do
402
+ expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(false)
403
+ end
404
+ end
405
+ end
406
+ end
407
+
408
+ context 'when given the invalid email with whitespace in parts' do
409
+ whitespace.map { |k, v| [
410
+ "include-#{v}-#{k}@invalid-characters-in-local.dev"
411
+ ]}.concat([
412
+ 'foo @bar.com',
413
+ "foo\t@bar.com",
414
+ "foo\n@bar.com",
415
+ "foo\r@bar.com",
416
+ 'test@ example.com',
417
+ 'user@example .com',
418
+ "user@example\t.com",
419
+ "user@example\n.com",
420
+ "user@example\r.com",
421
+ 'user@exam ple.com',
422
+ "user@exam\tple.com",
423
+ "user@exam\nple.com",
424
+ "user@exam\rple.com",
425
+ 'us er@example.com',
426
+ "us\ter@example.com",
427
+ "us\ner@example.com",
428
+ "us\rer@example.com",
429
+ "user@example.com\n<script>alert('hello')</script>",
430
+ "user@example.com\t<script>alert('hello')</script>",
431
+ "user@example.com\r<script>alert('hello')</script>",
432
+ "user@example.com <script>alert('hello')</script>",
433
+ ' leading-whitespace@example.com',
434
+ 'trailing-whitespace@example.com ',
435
+ ' leading-and-trailing-whitespace@example.com ',
436
+ ' user-with-leading-whitespace-space@example.com',
437
+ "\tuser-with-leading-whitespace-tab@example.com",
438
+ "
439
+ user-with-leading-whitespace-newline@example.com",
440
+ 'domain-with-trailing-whitespace-space@example.com ',
441
+ "domain-with-trailing-whitespace-tab@example.com\t",
442
+ "domain-with-trailing-whitespace-newline@example.com
443
+ "
444
+ ]).flatten.each do |email|
445
+ context 'when using defaults' do
446
+ it "#{email} should not be valid" do
447
+ expect(DefaultUser.new(:email => email)).not_to be_valid
448
+ end
449
+
450
+ it "#{email} should not be valid using EmailValidator.valid?" do
451
+ expect(described_class).not_to be_valid(email)
452
+ end
453
+
454
+ it "#{email} should be invalid using EmailValidator.invalid?" do
455
+ expect(described_class).to be_invalid(email)
456
+ end
457
+
458
+ it "#{email} should not match the regexp" do
459
+ expect(!!(email =~ described_class.regexp)).to be(false)
460
+ end
461
+ end
462
+
463
+ context 'when in `:moderate` mode' do
464
+ it "#{email} should not be valid" do
465
+ expect(ModerateUser.new(:email => email)).not_to be_valid
466
+ end
467
+
468
+ it "#{email} should not be valid using EmailValidator.valid?" do
469
+ expect(described_class).not_to be_valid(email, :mode => :moderate)
470
+ end
471
+
472
+ it "#{email} should be invalid using EmailValidator.invalid?" do
473
+ expect(described_class).to be_invalid(email, :mode => :moderate)
474
+ end
475
+
476
+ it "#{email} should not match the strict regexp" do
477
+ expect(!!(email =~ described_class.regexp(:mode => :moderate))).to be(false)
478
+ end
479
+ end
480
+
481
+ context 'when in `:strict` mode' do
482
+ it "#{email} should not be valid" do
483
+ expect(StrictUser.new(:email => email)).not_to be_valid
484
+ end
485
+
486
+ it "#{email} should not be valid using EmailValidator.valid?" do
487
+ expect(described_class).not_to be_valid(email, :mode => :strict)
488
+ end
489
+
490
+ it "#{email} should be invalid using EmailValidator.invalid?" do
491
+ expect(described_class).to be_invalid(email, :mode => :strict)
492
+ end
493
+
494
+ it "#{email} should not match the strict regexp" do
495
+ expect(!!(email =~ described_class.regexp(:mode => :strict))).to be(false)
496
+ end
497
+ end
498
+ end
78
499
  end
79
500
 
80
- context "given the invalid emails" do
501
+ context 'when given the invalid email with missing parts' do
81
502
  [
82
- "",
83
- "@bar.com",
84
- " @bar.com",
85
- "test@",
86
- "test@ ",
87
- " ",
88
- "missing-at-sign.net",
503
+ '',
504
+ '@bar.com',
505
+ 'test@',
506
+ '@missing-local.dev',
507
+ ' ',
508
+ 'missing-at-sign.dev'
89
509
  ].each do |email|
510
+ context 'when using defaults' do
511
+ it "#{email} should not be valid" do
512
+ expect(DefaultUser.new(:email => email)).not_to be_valid
513
+ end
514
+
515
+ it "#{email} should not be valid using EmailValidator.valid?" do
516
+ expect(described_class).not_to be_valid(email)
517
+ end
518
+
519
+ it "#{email} should be invalid using EmailValidator.invalid?" do
520
+ expect(described_class).to be_invalid(email)
521
+ end
522
+
523
+ it "#{email} should not match the regexp" do
524
+ expect(!!(email.strip =~ described_class.regexp)).to be(false)
525
+ end
526
+ end
527
+
528
+ context 'when in `:moderate` mode' do
529
+ it "#{email} should not be valid" do
530
+ expect(ModerateUser.new(:email => email)).not_to be_valid
531
+ end
90
532
 
91
- it "#{email.inspect} should not be valid" do
92
- expect(TestUser.new(:email => email)).not_to be_valid
533
+ it "#{email} should not be valid using EmailValidator.valid?" do
534
+ expect(described_class).not_to be_valid(email, :mode => :moderate)
535
+ end
536
+
537
+ it "#{email} should be invalid using EmailValidator.invalid?" do
538
+ expect(described_class).to be_invalid(email, :mode => :moderate)
539
+ end
540
+
541
+ it "#{email} should not match the strict regexp" do
542
+ expect(!!(email.strip =~ described_class.regexp(:mode => :moderate))).to be(false)
543
+ end
93
544
  end
94
545
 
95
- it "#{email.inspect} should be invalid" do
96
- expect(TestUser.new(:email => email)).to be_invalid
546
+ context 'when in `:strict` mode' do
547
+ it "#{email} should not be valid" do
548
+ expect(StrictUser.new(:email => email)).not_to be_valid
549
+ end
550
+
551
+ it "#{email} should not be valid using EmailValidator.valid?" do
552
+ expect(described_class).not_to be_valid(email, :mode => :strict)
553
+ end
554
+
555
+ it "#{email} should be invalid using EmailValidator.invalid?" do
556
+ expect(described_class).to be_invalid(email, :mode => :strict)
557
+ end
558
+
559
+ it "#{email} should not match the strict regexp" do
560
+ expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(false)
561
+ end
97
562
  end
563
+ end
564
+ end
565
+
566
+ context 'when given the moderatly invalid email' do
567
+ moderatly_invalid_includable.map { |k, v| [
568
+ "include-#{v}-#{k}@invalid-characters-in-local.dev"
569
+ ]}.concat(moderatly_invalid_beginable.map { |k, v| [
570
+ "#{v}start-with-#{k}@invalid-characters-in-local.dev"
571
+ ]}).concat(moderatly_invalid_endable.map { |k, v| [
572
+ "end-with-#{k}#{v}@invalid-characters-in-local.dev"
573
+ ]}).concat([
574
+ 'user..-with-double-dots@example.com',
575
+ '.user-beginning-with-dot@example.com',
576
+ 'user-ending-with-dot.@example.com'
577
+ ]).flatten.each do |email|
578
+ context 'when using defaults' do
579
+ it "#{email.strip} in a model should be valid" do
580
+ expect(DefaultUser.new(:email => email)).to be_valid
581
+ end
582
+
583
+ it "#{email.strip} should be valid using EmailValidator.valid?" do
584
+ expect(described_class).to be_valid(email)
585
+ end
98
586
 
99
- it "#{email.inspect} should not match the regexp" do
100
- expect(email =~ EmailValidator.regexp).to be_falsy
587
+ it "#{email.strip} should not be invalid using EmailValidator.invalid?" do
588
+ expect(described_class).not_to be_invalid(email)
589
+ end
590
+
591
+ it "#{email.strip} should match the regexp" do
592
+ expect(!!(email =~ described_class.regexp)).to be(true)
593
+ end
101
594
  end
102
595
 
103
- it "#{email.inspect} should fail the class tester" do
104
- expect(EmailValidator.valid?(email)).to be_falsy
596
+ context 'when in `:moderate` mode' do
597
+ it "#{email.strip} in a model should be valid" do
598
+ expect(ModerateUser.new(:email => email)).not_to be_valid
599
+ end
600
+
601
+ it "#{email.strip} should not be valid using EmailValidator.valid?" do
602
+ expect(described_class).not_to be_valid(email, :mode => :moderate)
603
+ end
604
+
605
+ it "#{email.strip} should be invalid using EmailValidator.invalid?" do
606
+ expect(described_class).to be_invalid(email, :mode => :moderate)
607
+ end
608
+
609
+ it "#{email.strip} should not match the strict regexp" do
610
+ expect(!!(email =~ described_class.regexp(:mode => :moderate))).to be(false)
611
+ end
105
612
  end
106
613
 
614
+ context 'when in `:strict` mode' do
615
+ it "#{email.strip} in a model should not be valid" do
616
+ expect(StrictUser.new(:email => email)).not_to be_valid
617
+ end
618
+
619
+ it "#{email.strip} should not be valid using EmailValidator.valid?" do
620
+ expect(described_class).not_to be_valid(email, :mode => :strict)
621
+ end
622
+
623
+ it "#{email.strip} should be invalid using EmailValidator.invalid?" do
624
+ expect(described_class).to be_invalid(email, :mode => :strict)
625
+ end
626
+
627
+ it "#{email.strip} should not match the strict regexp" do
628
+ expect(!!(email =~ described_class.regexp(:mode => :strict))).to be(false)
629
+ end
630
+ end
631
+ end
632
+ end
633
+
634
+ context 'when `require_fqdn` is explicitly disabled' do
635
+ let(:opts) { { :require_fqdn => false } }
636
+
637
+ context 'when given a valid hostname-only email' do
638
+ let(:email) { 'someuser@somehost' }
639
+
640
+ context 'when using defaults' do
641
+ it 'is valid using EmailValidator.valid?' do
642
+ expect(described_class).to be_valid(email, opts)
643
+ end
644
+
645
+ it 'is not invalid using EmailValidator.invalid?' do
646
+ expect(described_class).not_to be_invalid(email, opts)
647
+ end
648
+
649
+ it 'matches the regexp' do
650
+ expect(!!(email =~ described_class.regexp(opts))).to be(true)
651
+ end
652
+ end
653
+
654
+ context 'when in `"moderate` mode' do
655
+ let(:opts) { { :require_fqdn => false, :mode => :moderate } }
656
+
657
+ it 'is valid' do
658
+ expect(NonFqdnModerateUser.new(:email => email)).to be_valid
659
+ end
660
+
661
+ it 'is valid using EmailValidator.valid?' do
662
+ expect(described_class).to be_valid(email, opts)
663
+ end
664
+
665
+ it 'is not invalid using EmailValidator.invalid?' do
666
+ expect(described_class).not_to be_invalid(email, opts)
667
+ end
668
+
669
+ it 'matches the regexp' do
670
+ expect(!!(email =~ described_class.regexp(opts))).to be(true)
671
+ end
672
+ end
673
+
674
+ context 'when in `:strict` mode' do
675
+ let(:opts) { { :require_fqdn => false, :mode => :strict } }
676
+
677
+ it 'is valid in strict mode' do
678
+ expect(NonFqdnStrictUser.new(:email => email)).to be_valid
679
+ end
680
+
681
+ it 'is valid using EmailValidator.valid?' do
682
+ expect(described_class).to be_valid(email, opts)
683
+ end
684
+
685
+ it 'is not invalid using EmailValidator.invalid?' do
686
+ expect(described_class).not_to be_invalid(email, opts)
687
+ end
688
+
689
+ it 'does not match the strict regexp' do
690
+ expect(!!(email =~ described_class.regexp(opts))).to be(true)
691
+ end
692
+ end
693
+ end
694
+
695
+ context 'when given a valid email using an FQDN' do
696
+ let(:email) { 'someuser@somehost.somedomain' }
697
+
698
+ it 'is valid' do
699
+ expect(NonFqdnModerateUser.new(:email => email)).to be_valid
700
+ end
701
+
702
+ # rubocop:disable RSpec/PredicateMatcher
703
+ it 'is valid using EmailValidator.valid?' do
704
+ expect(described_class.valid?(email, opts)).to be(true)
705
+ end
706
+
707
+ it 'is not invalid using EmailValidator.invalid?' do
708
+ expect(described_class.invalid?(email, opts)).to be(false)
709
+ end
710
+ # rubocop:enable RSpec/PredicateMatcher
711
+
712
+ it 'matches the regexp' do
713
+ expect(!!(email =~ described_class.regexp(opts))).to be(true)
714
+ end
715
+
716
+ context 'when in `:strict` mode' do
717
+ let(:opts) { { :require_fqdn => false, :mode => :strict } }
718
+
719
+ # rubocop:disable RSpec/PredicateMatcher
720
+ it 'is valid using EmailValidator.valid?' do
721
+ expect(described_class.valid?(email, opts)).to be(true)
722
+ end
723
+
724
+ it 'is not invalid using EmailValidator.invalid?' do
725
+ expect(described_class.invalid?(email, opts)).to be(false)
726
+ end
727
+ # rubocop:enable RSpec/PredicateMatcher
728
+
729
+ it 'is valid in strict mode' do
730
+ expect(NonFqdnStrictUser.new(:email => email)).to be_valid
731
+ end
732
+
733
+ it 'does not match the strict regexp' do
734
+ expect(!!(email =~ described_class.regexp(opts))).to be(true)
735
+ end
736
+ end
737
+
738
+ context 'when requiring a non-matching domain' do
739
+ let(:domain) { 'example.com' }
740
+ let(:opts) { { :domain => domain } }
741
+
742
+ it 'is not valid' do
743
+ expect(DomainModerateUser.new(:email => email)).not_to be_valid
744
+ end
745
+
746
+ it 'is not valid using EmailValidator.valid?' do
747
+ expect(described_class).not_to be_valid(email, opts)
748
+ end
749
+
750
+ it 'is invalid using EmailValidator.invalid?' do
751
+ expect(described_class).to be_invalid(email, opts)
752
+ end
753
+
754
+ it 'does not matches the regexp' do
755
+ expect(!!(email =~ described_class.regexp(opts))).to be(false)
756
+ end
757
+
758
+ context 'when in `:strict` mode' do
759
+ let(:opts) { { :domain => domain, :require_fqdn => false, :mode => :strict } }
760
+
761
+ it 'is not valid using EmailValidator.valid?' do
762
+ expect(described_class).not_to be_valid(email, opts)
763
+ end
764
+
765
+ it 'is invalid using EmailValidator.invalid?' do
766
+ expect(described_class).to be_invalid(email, opts)
767
+ end
768
+
769
+ it 'is not valid' do
770
+ expect(DomainStrictUser.new(:email => email)).not_to be_valid
771
+ end
772
+
773
+ it 'does not match the regexp' do
774
+ expect(!!(email =~ described_class.regexp(opts))).to be(false)
775
+ end
776
+ end
777
+ end
107
778
  end
108
779
  end
109
780
  end
781
+ # rubocop:enable Layout/BlockEndNewline, Layout/MultilineBlockLayout, Layout/MultilineMethodCallBraceLayout, Style/BlockDelimiters, Style/MultilineBlockChain
782
+
783
+ describe 'error messages' do
784
+ context 'when the message is not defined' do
785
+ let(:model) { DefaultUser.new :email => 'invalidemail@' }
110
786
 
111
- describe "error messages" do
112
- context "when the message is not defined" do
113
- subject { TestUser.new :email => 'invalidemail@' }
114
- before { subject.valid? }
787
+ before { model.valid? }
115
788
 
116
- it "should add the default message" do
117
- expect(subject.errors[:email]).to include "is invalid"
789
+ it 'adds the default message' do
790
+ expect(model.errors[:email]).to include 'is invalid'
118
791
  end
119
792
  end
120
793
 
121
- context "when the message is defined" do
122
- subject { TestUserWithMessage.new :email_address => 'invalidemail@' }
123
- before { subject.valid? }
794
+ context 'when the message is defined' do
795
+ let(:model) { DefaultUserWithMessage.new :email_address => 'invalidemail@' }
796
+
797
+ before { model.valid? }
124
798
 
125
- it "should add the customized message" do
126
- expect(subject.errors[:email_address]).to include "is not looking very good!"
799
+ it 'adds the customized message' do
800
+ expect(model.errors[:email_address]).to include 'is not looking very good!'
127
801
  end
128
802
  end
129
803
  end
130
804
 
131
- describe "error details" do
132
- subject { TestUser.new :email => 'invalidemail@' }
133
- before { subject.valid? }
805
+ describe 'nil email' do
806
+ it 'is not valid when :allow_nil option is missing' do
807
+ expect(DefaultUser.new(:email => nil)).not_to be_valid
808
+ end
809
+
810
+ it 'is valid when :allow_nil options is set to true' do
811
+ expect(AllowNilDefaultUser.new(:email => nil)).to be_valid
812
+ end
134
813
 
135
- it "should add the default message" do
136
- expect(subject.errors.details[:email]).to eq [{ error: :invalid, value: 'invalidemail@' }]
814
+ it 'is not valid when :allow_nil option is set to false' do
815
+ expect(DisallowNilDefaultUser.new(:email => nil)).not_to be_valid
137
816
  end
138
817
  end
139
818
 
140
- describe "nil email" do
141
- it "should not be valid when :allow_nil option is missing" do
142
- expect(TestUser.new(:email => nil)).not_to be_valid
819
+ describe 'limited to a domain' do
820
+ context 'when in `:moderate` mode' do
821
+ it 'is not valid with mismatched domain' do
822
+ expect(DomainModerateUser.new(:email => 'user@not-matching.io')).not_to be_valid
823
+ end
824
+
825
+ it 'is valid with matching domain' do
826
+ expect(DomainModerateUser.new(:email => 'user@example.com')).to be_valid
827
+ end
828
+
829
+ it 'does not interpret the dot as any character' do
830
+ expect(DomainModerateUser.new(:email => 'user@example-com')).not_to be_valid
831
+ end
143
832
  end
144
833
 
145
- it "should be valid when :allow_nil options is set to true" do
146
- expect(TestUserAllowsNil.new(:email => nil)).to be_valid
834
+ context 'when in strict mode' do
835
+ it 'does not interpret the dot as any character' do
836
+ expect(DomainStrictUser.new(:email => 'user@example-com')).not_to be_valid
837
+ end
838
+
839
+ it 'is valid with matching domain' do
840
+ expect(DomainStrictUser.new(:email => 'user@example.com')).to be_valid
841
+ end
842
+
843
+ it 'is not valid with mismatched domain' do
844
+ expect(DomainStrictUser.new(:email => 'user@not-matching.io')).not_to be_valid
845
+ end
846
+ end
847
+ end
848
+
849
+ describe 'default_options' do
850
+ let(:email) { 'includes-whitespace-in-otherwise-valid-email@local' }
851
+
852
+ it 'validates using `:loose` mode' do
853
+ expect(DefaultUser.new(:email => email)).to be_valid
147
854
  end
148
855
 
149
- it "should not be valid when :allow_nil option is set to false" do
150
- expect(TestUserAllowsNilFalse.new(:email => nil)).not_to be_valid
856
+ context 'when `email_validator/moderate` has been required' do
857
+ before { require 'email_validator/moderate' }
858
+
859
+ it 'validates using `:moderate` mode' do
860
+ expect(DefaultUser.new(:email => email)).not_to be_valid
861
+ end
862
+ end
863
+
864
+ context 'when `email_validator/strict` has been required' do
865
+ before { require 'email_validator/strict' }
866
+
867
+ it 'validate using `:strict` mode' do
868
+ expect(DefaultUser.new(:email => email)).to be_valid
869
+ end
151
870
  end
152
871
  end
153
872
  end