email_address 0.2.3 → 0.2.9

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: ad468bd4a05991b9a99adbeabd6fdb8c40d965fcdeed1559ddeb95ad6d599d13
4
- data.tar.gz: b3b678cf78737eee8ca6a9febb5ff2434edb322b6393383ca7cd50042e66833c
3
+ metadata.gz: f208df87862abc3af2936e9f6780e12409ed4430353125829cdeed515bffe044
4
+ data.tar.gz: 9c5573440c5759d71d172e2a534b14050375a0a66351c01ddafabd6a5db5cb26
5
5
  SHA512:
6
- metadata.gz: b453716b5ffb47f3cc9a03fd153cb38349d686bfa8830f273c9f9704736ac987c36128c788ab84472c5a3c5d483743f675c1126bf621643fd979f0235bd19f11
7
- data.tar.gz: 0e5bdf1545efd28e45da88e600e7e0bf88ddbed11c1e46c516206f5a301b4e7c0a87301daa90141cf91c8403fc1ea8fcb8cd7a725e64b5d9f810428d14115fe8
6
+ metadata.gz: 67fd95a7991cd3c6acd3cc0a0f955de58597435297ccf7a7edd57e92cf9aed9b4cd523655bfb9775cd03f477c4a06d86f7195fb5b7a257fde96dcdc36ca21cca
7
+ data.tar.gz: 437e9173744dccba78cef4301e3a4b107a977017cb58d273896c5ea4c05e41389c3b638819767658d03878f808e09e06b480a33ce0ca7ef446641d98a20475f8
@@ -5,9 +5,9 @@ jobs:
5
5
  runs-on: ubuntu-latest
6
6
  strategy:
7
7
  matrix:
8
- ruby-version: [2.6, 2.7, 3.0, 3.1, jruby]
8
+ ruby-version: [3.2, 3.3, 3.4, 4.0]
9
9
  steps:
10
- - uses: actions/checkout@v2
10
+ - uses: actions/checkout@v3
11
11
  - uses: ruby/setup-ruby@v1
12
12
  with:
13
13
  ruby-version: ${{ matrix.ruby-version }}
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ .ruby-lsp
data/README.md CHANGED
@@ -4,29 +4,66 @@
4
4
  [![CI Build](https://github.com/afair/email_address/actions/workflows/ci.yml/badge.svg)](https://github.com/afair/email_address/actions/workflows/ci.yml)
5
5
  [![Code Climate](https://codeclimate.com/github/afair/email_address/badges/gpa.svg)](https://codeclimate.com/github/afair/email_address)
6
6
 
7
- The `email_address` gem provides a ruby language library for working
8
- with email addresses.
9
-
10
- By default, it validates against conventional usage,
11
- the format preferred for user email addresses.
12
- It can be configured to validate against RFC "Standard" formats,
13
- common email service provider formats, and perform DNS validation.
14
-
15
- Using `email_address` to validate user email addresses results in
16
- fewer "false positives" due to typing errors and gibberish data.
17
- It validates syntax more strictly for popular email providers,
18
- and can deal with gmail's "optional dots" in addresses.
19
-
20
- It provides Active Record (Rails) extensions, including an
21
- address validator and attributes API custom datatypes.
22
-
23
- Requires Ruby 2.0 or later.
7
+ The `email_address` ruby gem is an opinionated validation library for
8
+ email addresses. The [RFC 5322](https://www.rfc-editor.org/rfc/rfc5322#section-3.4)
9
+ address specification defines them as extensions to the email
10
+ header syntax, not as a useful method for creating email transport
11
+ systems with user accounts, mailboxes, and routing.
12
+
13
+ The library follows "real world" email address patterns for end-user addresses.
14
+
15
+ - "Conventional" format (the default) fits most user email accounts
16
+ as created by major email service providers and software.
17
+ Only 7-bit ASCII characters are supported in the local (left) part.
18
+ - "Relaxed" format loosely follows conventional, allowing a looser
19
+ punctuation format.
20
+ - "Standard" format follows the RFC. This is provided for non-user
21
+ addresses, such as uniquely-generated destinations for
22
+ consumption between automated systems.
23
+
24
+ RFC "Standard" Addresses allow syntaxes that most developers do not want:
25
+
26
+ - Mailboxes are case-sensitive.
27
+ - Double-quoted tokens can contain spaces, "@" symbols, and unusual punctuation.
28
+ - Parenthetical comment fields can appear at the beginning or end
29
+ of the local (left) part.
30
+ - Addresses do not have to have fully-qualified domain names
31
+ - The Host part (after the "@") can be an IP Address
32
+
33
+ Additionally, this library respects "address tags", a convention
34
+ not specified by the RFC, with which email providers and software
35
+ append an identifier or route to the mailbox, usually after a "+" symbol.
36
+
37
+ Configuration options include specialized address formats for the largest
38
+ ESP (Email service providers) to validate against their formats.
39
+
40
+ If you have false negatives with "conventional" format, try the
41
+ `local_format: :relaxed` option. To validate to the RFC only, use the
42
+ `local_format: :standard` option. When possible, confirm the address
43
+ with the user if conventional check fails but relaxed succeeds.
44
+
45
+ Remember: the only true way to validate an email address is to successfully
46
+ send email to it. SMTP checks can help, but should only be done politely
47
+ to avoid blacklisting your application. Several (unaffiliated) services
48
+ exist to do this for you.
49
+
50
+ Finally, there are conveniences to handle storage and management of
51
+ address digests for PII removal or sharing addresses without revealing them.
52
+
53
+ The gem requires ruby only, but includes a, optional Ruby on Rails helper for
54
+ those who need to use it with ActiveRecord.
24
55
 
25
56
  Looking for a Javascript version of this library? Check out the
26
57
  [email_address](https://www.npmjs.com/package/email_address) npm module.
27
58
 
28
59
  ## Quick Start
29
60
 
61
+ Install the gem to your project with bundler:
62
+
63
+ bundle add email_address
64
+
65
+ or with the gem command:
66
+
30
67
  To quickly validate email addresses, use the valid? and error helpers.
31
68
  `valid?` returns a boolean, and `error` returns nil if valid, otherwise
32
69
  a basic error message.
@@ -38,19 +75,19 @@ EmailAddress.error "allen@bad-d0main.com" #=> "Invalid Host/Domain Name"
38
75
 
39
76
  `EmailAddress` deeply validates your email addresses. It checks:
40
77
 
41
- * Host name format and DNS setup
42
- * Mailbox format according to "conventional" form. This matches most used user
78
+ - Host name format and DNS setup
79
+ - Mailbox format according to "conventional" form. This matches most used user
43
80
  email accounts, but is a subset of the RFC specification.
44
81
 
45
82
  It does not check:
46
83
 
47
- * The mail server is configured to accept connections
48
- * The mailbox is valid and accepts email.
84
+ - The mail server is configured to accept connections
85
+ - The mailbox is valid and accepts email.
49
86
 
50
87
  By default, MX records are required in DNS. MX or "mail exchanger" records
51
88
  tell where to deliver email for the domain. Many domains run their
52
89
  website on one provider (ISP, Heroku, etc.), and email on a different
53
- provider (such as G Suite). Note that `example.com`, while
90
+ provider (such as G Suite). Note that `example.com`, while
54
91
  a valid domain name, does not have MX records.
55
92
 
56
93
  ```ruby
@@ -71,134 +108,91 @@ The email address specification is complex and often not what you want
71
108
  when working with personal email addresses in applications. This library
72
109
  introduces terms to distinguish types of email addresses.
73
110
 
74
- * *Normal* - The edited form of any input email address. Typically, it
111
+ - _Normal_ - The edited form of any input email address. Typically, it
75
112
  is lower-cased and minor "fixes" can be performed, depending on the
76
113
  configurations and email address provider.
77
114
 
78
- CKENT@DAILYPLANET.NEWS => ckent@dailyplanet.news
115
+ <CKENT@DAILYPLANET.NEWS> => <ckent@dailyplanet.news>
79
116
 
80
- * *Conventional* - Most personal account addresses are in this basic
117
+ - _Conventional_ - Most personal account addresses are in this basic
81
118
  format, one or more "words" separated by a single simple punctuation
82
119
  character. It consists of a mailbox (user name or role account) and
83
120
  an optional address "tag" assigned by the user.
84
121
 
85
- miles.o'brien@ncc-1701-d.ufp
122
+ miles.o'<brien@ncc-1701-d.ufp>
86
123
 
87
- * *Relaxed* - A less strict form of Conventional, same character set,
124
+ - _Relaxed_ - A less strict form of Conventional, same character set,
88
125
  must begin and end with an alpha-numeric character, but order within
89
126
  is not enforced.
90
127
 
91
- aasdf-34-.z@example.com
128
+ <aasdf-34-.z@example.com>
92
129
 
93
- * *Standard* - The RFC-Compliant syntax of an email address. This is
130
+ - _Standard_ - The RFC-Compliant syntax of an email address. This is
94
131
  useful when working with software-generated addresses or handling
95
132
  existing email addresses, but otherwise not useful for personal
96
133
  addresses.
97
134
 
98
- madness!."()<>[]:,;@\\\"!#$%&'*+-/=?^_`{}| ~.a(comment )"@example.org
135
+ madness!."()<>[]:,;@\\\"!#$%&'\*+-/=?^\_`{}| ~.a(comment )"@example.org
99
136
 
100
- * *Base* - A unique mailbox without tags. For gmail, is uses the incoming
137
+ - _Base_ - A unique mailbox without tags. For gmail, is uses the incoming
101
138
  punctation, essential when building an MD5, SHA1, or SHA256 to match services
102
139
  like Gravatar, and email address digest interchange.
103
140
 
104
- * *Canonical* - An unique account address, lower-cased, without the
141
+ - _Canonical_ - An unique account address, lower-cased, without the
105
142
  tag, and with irrelevant characters stripped.
106
143
 
107
- clark.kent+scoops@gmail.com => clarkkent@gmail.com
144
+ <clark.kent+scoops@gmail.com> => <clarkkent@gmail.com>
108
145
 
109
- * *Reference* - The MD5 of the Base format, used to share account
146
+ - _Reference_ - The MD5 of the Base format, used to share account
110
147
  references without exposing the private email address directly.
111
148
 
112
- Clark.Kent+scoops@gmail.com =>
113
- clark.kent@gmail.com => 1429a1dfc797d6e93075fef011c373fb
149
+ <Clark.Kent+scoops@gmail.com> =>
150
+ <clark.kent@gmail.com> => 1429a1dfc797d6e93075fef011c373fb
114
151
 
115
- * *Redacted* - A form of the email address where it is replaced by
152
+ - _Redacted_ - A form of the email address where it is replaced by
116
153
  a SHA1-based version to remove the original address from the
117
154
  database, or to store the address privately, yet still keep it
118
155
  accessible at query time by converting the queried address to
119
156
  the redacted form.
120
157
 
121
- Clark.Kent+scoops@gmail.com => {bea3f3560a757f8142d38d212a931237b218eb5e}@gmail.com
158
+ <Clark.Kent+scoops@gmail.com> => {bea3f3560a757f8142d38d212a931237b218eb5e}@gmail.com
122
159
 
123
- * *Munged* - An obfuscated version of the email address suitable for
160
+ - _Munged_ - An obfuscated version of the email address suitable for
124
161
  publishing on the internet, where email address harvesting
125
162
  could occur.
126
163
 
127
- Clark.Kent+scoops@gmail.com => cl\*\*\*\*\*@gm\*\*\*\*\*
164
+ <Clark.Kent+scoops@gmail.com> => cl\*\*\*\*\*@gm\*\*\*\*\*
128
165
 
129
166
  Other terms:
130
167
 
131
- * *Local* - The left-hand side of the "@", representing the user,
168
+ - _Local_ - The left-hand side of the "@", representing the user,
132
169
  mailbox, or role, and an optional "tag".
133
170
 
134
- mailbox+tag@example.com; Local part: mailbox+tag
171
+ <mailbox+tag@example.com>; Local part: mailbox+tag
135
172
 
136
- * *Mailbox* - The destination user account or role account.
137
- * *Tag* - A parameter added after the mailbox, usually after the
173
+ - _Mailbox_ - The destination user account or role account.
174
+ - _Tag_ - A parameter added after the mailbox, usually after the
138
175
  "+" symbol, set by the user for mail filtering and sub-accounts.
139
176
  Not all mail systems support this.
140
- * *Host* (sometimes called *Domain*) - The right-hand side of the "@"
177
+ - _Host_ (sometimes called _Domain_) - The right-hand side of the "@"
141
178
  indicating the domain or host name server to delivery the email.
142
179
  If missing, "localhost" is assumed, or if not a fully-qualified
143
180
  domain name, it assumed another computer on the same network, but
144
181
  this is increasingly rare.
145
- * *Provider* - The Email Service Provider (ESP) providing the email
182
+ - _Provider_ - The Email Service Provider (ESP) providing the email
146
183
  service. Each provider may have its own email address validation
147
184
  and canonicalization rules.
148
- * *Punycode* - A host name with Unicode characters (International
185
+ - _Punycode_ - A host name with Unicode characters (International
149
186
  Domain Name or IDN) needs conversion to this ASCII-encoded format
150
187
  for DNS lookup.
151
188
 
152
- "HIRO@こんにちは世界.com" => "hiro@xn--28j2a3ar1pp75ovm7c.com"
189
+ "HIRO@こんにちは世界.com" => "<hiro@xn--28j2a3ar1pp75ovm7c.com>"
153
190
 
154
191
  Wikipedia has a great article on
155
192
  [Email Addresses](https://en.wikipedia.org/wiki/Email_address),
156
193
  much more readable than the section within
157
194
  [RFC 5322](https://tools.ietf.org/html/rfc5322#section-3.4)
158
195
 
159
- ## Avoiding the Bad Parts of RFC Specification
160
-
161
- Following the RFC specification sounds like a good idea, until you
162
- learn about all the madness contained therein. This library can
163
- validate the RFC syntax, but this is never useful, especially when
164
- validating user email address submissions. By default, it validates
165
- to the *conventional* format.
166
-
167
- Here are a few parts of the RFC specification you should avoid:
168
-
169
- * Case-sensitive local parts: `First.Last@example.com`
170
- * Spaces and Special Characters: `"():;<>@[\\]`
171
- * Quoting and Escaping Requirements: `"first \"nickname\" last"@example.com`
172
- * Comment Parts: `(comment)mailbox@example.com`
173
- * IP and IPv6 addresses as hosts: `mailbox@[127.0.0.1]`
174
- * Non-ASCII (7-bit) characters in the local part: `Pelé@example.com`
175
- * Validation by voodoo regular expressions
176
- * Gmail allows ".." in addresses since they are not meaningful, but
177
- the standard does not.
178
-
179
- ## Installation With Rails or Bundler
180
-
181
- If you are using Rails or a project with Bundler, add this line to your application's Gemfile:
182
-
183
- gem 'email_address'
184
-
185
- And then execute:
186
-
187
- $ bundle
188
-
189
- ## Installation Without Bundler
190
-
191
- If you are not using Bundler, you need to install the gem yourself.
192
-
193
- $ gem install email_address
194
-
195
- Require the gem inside your script.
196
-
197
- ```ruby
198
- require 'rubygems'
199
- require 'email_address'
200
- ```
201
-
202
196
  ## Usage
203
197
 
204
198
  Use `EmailAddress` to do transformations and validations. You can also
@@ -231,7 +225,7 @@ email.valid? #=> true
231
225
 
232
226
  Here are some other methods that are available.
233
227
 
234
- ```ruby
228
+ ````ruby
235
229
  email.redact #=> "{bea3f3560a757f8142d38d212a931237b218eb5e}@gmail.com"
236
230
  email.sha1 #=> "bea3f3560a757f8142d38d212a931237b218eb5e"
237
231
  email.sha256 #=> "9e2a0270f2d6778e5f647fc9eaf6992705ca183c23d1ed1166586fd54e859f75"
@@ -241,24 +235,13 @@ email.provider #=> :google
241
235
  email.mailbox #=> "clark.kent"
242
236
  email.tag #=> "scoops"
243
237
 
244
- email.host.exchanger.first[:ip] #=> "2a00:1450:400b:c02::1a"
238
+ email.host.exchangers.first[:ip] #=> "2a00:1450:400b:c02::1a"
245
239
  email.host.txt_hash #=> {:v=>"spf1", :redirect=>"\_spf.google.com"}
246
240
 
247
241
  EmailAddress.normal("HIRO@こんにちは世界.com")
248
242
  #=> "hiro@xn--28j2a3ar1pp75ovm7c.com"
249
243
  EmailAddress.normal("hiro@xn--28j2a3ar1pp75ovm7c.com", host_encoding: :unicode)
250
244
  #=> "hiro@こんにちは世界.com"
251
- ```
252
- As of release 0.1.17, exchanger_match is no longer used for host provider
253
- determination, which designated the set of rules for that domain.
254
- Sometimes, as in Google-hosted domains, the address
255
- rules are different, notably the optional dots in mailboxes for gmail.com
256
- accounts do not apply to other private domains hosted at google.
257
-
258
- To access the provider service, you can now call:
259
-
260
- EmailAddress.new("user@hosteddomain.com").host.hosted_provider
261
-
262
245
 
263
246
  #### Rails Validator
264
247
 
@@ -271,7 +254,7 @@ Specify your email address attributes with `field: :user_email`, or
271
254
  class User < ActiveRecord::Base
272
255
  validates_with EmailAddress::ActiveRecordValidator, field: :email
273
256
  end
274
- ```
257
+ ````
275
258
 
276
259
  #### Rails I18n
277
260
 
@@ -359,7 +342,7 @@ by the redacted form.
359
342
  The only true validation is to send a message to the email address and
360
343
  have the user (or process) verify it has been received. Syntax checks
361
344
  help prevent erroneous input. Even sent messages can be silently
362
- dropped, or bounced back after acceptance. Conditions such as a
345
+ dropped, or bounced back after acceptance. Conditions such as a
363
346
  "Mailbox Full" can mean the email address is known, but abandoned.
364
347
 
365
348
  There are different levels of validations you can perform. By default, it will
@@ -389,14 +372,14 @@ e1 < e2 #=> true (Compares using normalized address)
389
372
 
390
373
  Matching addresses by simple patterns:
391
374
 
392
- * Top-Level-Domain: .org
393
- * Domain Name: example.com
394
- * Registration Name: hotmail. (matches any TLD)
395
- * Domain Glob: *.exampl?.com
396
- * Provider Name: google
397
- * Mailbox Name or Glob: user00*@
398
- * Address or Glob: postmaster@domain*.com
399
- * Provider or Registration: msn
375
+ - Top-Level-Domain: .org
376
+ - Domain Name: example.com
377
+ - Registration Name: hotmail. (matches any TLD)
378
+ - Domain Glob: \*.exampl?.com
379
+ - Provider Name: google
380
+ - Mailbox Name or Glob: user00\*@
381
+ - Address or Glob: postmaster@domain\*.com
382
+ - Provider or Registration: msn
400
383
 
401
384
  Usage:
402
385
 
@@ -484,122 +467,8 @@ EmailAddress::Config.error_messages({
484
467
  not_allowed: "Address is not allowed",
485
468
  incomplete_domain: "Domain name is incomplete"}, 'en')
486
469
  ```
487
- Note: Release 0.1.14 fixed setting error messages by locale.
488
- Also, it will accept a ruby "collected" hash as before,
489
-
490
- Full translation support would be ideal though.
491
-
492
- ### Available Configuration Settings
493
-
494
- * sha1_secret -
495
- This application-level secret is appended to the email_address to compute
496
- the SHA1 Digest, making it unique to your application so it can't easily be
497
- discovered by comparing against a known list of email/sha1 pairs.
498
-
499
- * sha256_secret -
500
- This application-level secret is appended to the email_address to compute
501
- the SHA256 Digest, making it unique to your application so it can't easily be
502
- discovered by comparing against a known list of email/sha256 pairs.
503
470
 
504
- * munge_string - "*****", the string to replace into munged addresses.
505
-
506
- For local part configuration:
507
-
508
- * local_downcase: true.
509
- Downcase the local part. You probably want this for uniqueness.
510
- RFC says local part is case insensitive, that's a bad part.
511
-
512
- * local_fix: true.
513
- Make simple fixes when available, remove spaces, condense multiple punctuations
514
-
515
- * local_encoding: :ascii, :unicode,
516
- Enable Unicode in local part. Most mail systems do not yet support this.
517
- You probably want to stay with ASCII for now.
518
-
519
- * local_parse: nil, ->(local) { [mailbox, tag, comment] }
520
- Specify an optional lambda/Proc to parse the local part. It should return an
521
- array (tuple) of mailbox, tag, and comment.
522
-
523
- * local_format:
524
- * :conventional - word ( puncuation{1} word )*
525
- * :relaxed - alphanum ( allowed_characters)* alphanum
526
- * :standard - RFC Compliant email addresses (anything goes!)
527
-
528
- * local_size: 1..64,
529
- A Range specifying the allowed size for mailbox + tags + comment
530
-
531
- * tag_separator: nil, character (+)
532
- Nil, or a character used to split the tag from the mailbox
533
-
534
- For the mailbox (AKA account, role), without the tag
535
- * mailbox_size: 1..64
536
- A Range specifying the allowed size for mailbox
537
-
538
- * mailbox_canonical: nil, ->(mailbox) { mailbox }
539
- An optional lambda/Proc taking a mailbox name, returning a canonical
540
- version of it. (E.G.: gmail removes '.' characters)
541
-
542
- * mailbox_validator: nil, ->(mailbox) { true }
543
- An optional lambda/Proc taking a mailbox name, returning true or false.
544
-
545
- * host_encoding: :punycode, :unicode,
546
- How to treat International Domain Names (IDN). Note that most mail and
547
- DNS systems do not support unicode, so punycode needs to be passed.
548
- :punycode Convert Unicode names to punycode representation
549
- :unicode Keep Unicode names as is.
550
-
551
- * host_validation:
552
- :mx Ensure host is configured with DNS MX records
553
- :a Ensure host is known to DNS (A Record)
554
- :syntax Validate by syntax only, no Network verification
555
- :connect Attempt host connection (Dangerous: Do not use)
556
-
557
- * host_size: 1..253,
558
- A range specifying the size limit of the host part,
559
-
560
- * host_allow_ip: false,
561
- Allow IP address format in host: [127.0.0.1], [IPv6:::1]
562
-
563
- * address_validation: :parts, :smtp, ->(address) { true }
564
- Address validation policy
565
- :parts Validate local and host.
566
- :smtp Validate via SMTP (not implemented, BAD!)
567
- A lambda/Proc taking the address string, returning true or false
568
-
569
- * address_size: 3..254,
570
- A range specifying the size limit of the complete address
571
-
572
- For provider rules to match to domain names and Exchanger hosts
573
- The value is an array of match tokens.
574
- * host_match: %w(.org example.com hotmail. user*@ sub.*.com)
575
- * exchanger_match: %w(google.com 127.0.0.1 10.9.8.0/24 ::1/64)
576
-
577
- ### Namespace conflict resolution
578
-
579
- If your application already uses the `EmailAddress` class name,
580
- it's possible to create an alias prior to loading your code:
581
-
582
- For a Rails application, you can do this in `config/application.rb`
583
- after the `Bundler.require` line, usually:
584
-
585
- ```ruby
586
- Bundler.require(*Rails.groups)
587
- ```
588
-
589
- Add these lines immediately after that point:
590
-
591
- ```ruby
592
- EmailAddressValidator = EmailAddress
593
- Object.send(:remove_const, :EmailAddress)
594
- ```
595
-
596
- Then your application loads with your EmailAddress class. You may
597
- then use this gem with `EmailAddressValidator` or whatever name you
598
- gave it above:
599
-
600
- ```ruby
601
- EmailAddressValidator.valid?("clark.kent@gmail.com") # => true
602
- ```
471
+ Complete settings and methods are found in the config.rb file within.
603
472
 
604
473
  ## Notes
605
474
 
@@ -659,5 +528,5 @@ This project lives at [https://github.com/afair/email_address/](https://github.c
659
528
 
660
529
  #### Authors
661
530
 
662
- * [Allen Fair](https://github.com/afair) ([@allenfair](https://twitter.com/allenfair)):
531
+ - [Allen Fair](https://github.com/afair) ([@allenfair](https://twitter.com/allenfair)):
663
532
  I've worked with email-based applications and email addresses since 1999.
@@ -12,26 +12,29 @@ Gem::Specification.new do |spec|
12
12
  spec.homepage = "https://github.com/afair/email_address"
13
13
  spec.license = "MIT"
14
14
 
15
- spec.required_ruby_version = ">= 2.5", "< 4"
15
+ spec.required_ruby_version = ">= 2.5", "< 5.0"
16
16
  spec.files = `git ls-files`.split($/)
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
18
  spec.require_paths = ["lib"]
20
19
 
21
20
  spec.add_development_dependency "rake"
22
21
  spec.add_development_dependency "minitest", "~> 5.11"
23
22
  spec.add_development_dependency "bundler" # , "~> 1.16.0"
24
23
  if RUBY_PLATFORM == "java"
25
- spec.add_development_dependency "activerecord", "~> 5.2.6"
26
- spec.add_development_dependency "activerecord-jdbcsqlite3-adapter", "~> 52.7"
24
+ spec.add_development_dependency "activerecord", "~> 7.2.0"
25
+ spec.add_development_dependency "activerecord-jdbcsqlite3-adapter", "~> 70.2"
27
26
  else
28
- spec.add_development_dependency "activerecord", "~> 6.1.4"
29
- spec.add_development_dependency "sqlite3"
30
- spec.add_development_dependency "standard", "~> 1.5.0"
27
+ spec.add_development_dependency "activerecord", "~> 8.0.0"
28
+ spec.add_development_dependency "sqlite3", "~> 2.1"
29
+ spec.add_development_dependency "standard"
31
30
  end
32
- # spec.add_development_dependency "net-smtp"
31
+ spec.add_development_dependency "net-smtp"
33
32
  spec.add_development_dependency "simplecov"
34
33
  spec.add_development_dependency "pry"
34
+ spec.add_development_dependency "bigdecimal"
35
+ spec.add_development_dependency "mutex_m"
36
+ spec.add_development_dependency "irb"
35
37
 
38
+ spec.add_dependency "base64"
36
39
  spec.add_dependency "simpleidn"
37
40
  end
@@ -12,8 +12,12 @@ module EmailAddress
12
12
  # * field: email,
13
13
  # * fields: [:email1, :email2]
14
14
  #
15
+ # * code: custom error code (default: :invalid_address)
16
+ # * message: custom error message (default: "Invalid Email Address")
17
+ #
15
18
  # Default field: :email or :email_address (first found)
16
19
  #
20
+ #
17
21
  class ActiveRecordValidator < ActiveModel::Validator
18
22
  def initialize(options = {})
19
23
  @opt = options
@@ -24,22 +28,35 @@ module EmailAddress
24
28
  @opt[:fields].each { |f| validate_email(r, f) }
25
29
  elsif @opt[:field]
26
30
  validate_email(r, @opt[:field])
27
- elsif r.respond_to? :email
28
- validate_email(r, :email)
29
- elsif r.respond_to? :email_address
30
- validate_email(r, :email_address)
31
+ else
32
+ validate_email(r, :email) || validate_email(r, :email_address)
31
33
  end
32
34
  end
33
35
 
34
36
  def validate_email(r, f)
35
- return if r[f].nil?
36
- e = Address.new(r[f])
37
+ v = field_value(r, f)
38
+ return if v.nil?
39
+
40
+ e = Address.new(v)
37
41
  unless e.valid?
42
+ error_code = @opt[:code] || :invalid_address
38
43
  error_message = @opt[:message] ||
39
44
  Config.error_message(:invalid_address, I18n.locale.to_s) ||
40
45
  "Invalid Email Address"
41
- r.errors.add(f, error_message)
46
+ r.errors.add(f, error_code, message: error_message)
47
+ end
48
+ end
49
+
50
+ def field_value(r, f)
51
+ if r.respond_to?(f)
52
+ r.send(f)
53
+ elsif r[f]
54
+ r[f]
55
+ else
56
+ nil
42
57
  end
58
+ rescue NoMethodError
59
+ nil
43
60
  end
44
61
  end
45
62
  end
@@ -38,7 +38,7 @@ module EmailAddress
38
38
 
39
39
  # Given an email address, this returns an array of [local, host] parts
40
40
  def self.split_local_host(email)
41
- if (lh = email.match(/(.+)@(.+)/))
41
+ if (lh = email.match(/\A(.+)@(.+)\z/))
42
42
  lh.to_a[1, 2]
43
43
  else
44
44
  [email, ""]
@@ -34,7 +34,7 @@ module EmailAddress
34
34
 
35
35
  # From user input, setter
36
36
  def cast(value)
37
- super(Address.new(value).canonical)
37
+ super(value && Address.new(value).canonical)
38
38
  end
39
39
 
40
40
  # From a database value
@@ -112,7 +112,7 @@ module EmailAddress
112
112
 
113
113
  class Config
114
114
  @config = {
115
- dns_lookup: :mx, # :mx, :a, :off
115
+ dns_lookup: :a, # :mx, :a, :off
116
116
  dns_timeout: nil,
117
117
  sha1_secret: "",
118
118
  sha256_secret: "",
@@ -137,6 +137,7 @@ module EmailAddress
137
137
  host_local: false,
138
138
  host_fqdn: true,
139
139
  host_auto_append: true,
140
+ host_timeout: 3,
140
141
 
141
142
  address_validation: :parts, # :parts, :smtp, Proc
142
143
  address_size: 3..254,
@@ -151,14 +152,14 @@ module EmailAddress
151
152
  google: {
152
153
  host_match: %w[gmail.com googlemail.com],
153
154
  exchanger_match: %w[google.com googlemail.com],
154
- local_size: 5..64,
155
+ local_size: 3..64,
155
156
  local_private_size: 1..64, # When hostname not in host_match (private label)
156
157
  mailbox_canonical: ->(m) { m.delete(".") }
157
158
  },
158
159
  msn: {
159
160
  host_match: %w[msn. hotmail. outlook. live.],
160
161
  exchanger_match: %w[outlook.com],
161
- mailbox_validator: ->(m, t) { m =~ /\A\w[\-\w]*(?:\.[\-\w]+)*\z/i }
162
+ mailbox_validator: ->(m, t) { m =~ /\A\w[-\w]*(?:\.[-\w]+)*\z/i }
162
163
  },
163
164
  yahoo: {
164
165
  host_match: %w[yahoo. ymail. rocketmail.],
@@ -34,7 +34,7 @@ module EmailAddress
34
34
 
35
35
  # From user input, setter
36
36
  def cast(value)
37
- super(Address.new(value).normal)
37
+ super(value && Address.new(value).normal)
38
38
  end
39
39
 
40
40
  # From a database value
@@ -34,6 +34,11 @@ module EmailAddress
34
34
  end
35
35
  end
36
36
 
37
+ # Has any MX records?
38
+ def exist?
39
+ count > 0
40
+ end
41
+
37
42
  # Returns the provider name based on the MX-er host names, or nil if not matched
38
43
  def provider
39
44
  return @provider if defined? @provider
@@ -36,7 +36,7 @@ module EmailAddress
36
36
  MAX_HOST_LENGTH = 255
37
37
 
38
38
  # Sometimes, you just need a Regexp...
39
- DNS_HOST_REGEX = / [\p{L}\p{N}]+ (?: (?: -{1,2} | \.) [\p{L}\p{N}]+ )*/x
39
+ DNS_HOST_REGEX = / [\p{L}\p{N}]+ (?: (?: -{1,3} | \.) [\p{L}\p{N}]+ )*/x
40
40
 
41
41
  # The IPv4 and IPv6 were lifted from Resolv::IPv?::Regex and tweaked to not
42
42
  # \A...\z anchor at the edges.
@@ -332,6 +332,10 @@ module EmailAddress
332
332
  @_dns_a_record ||= []
333
333
  end
334
334
 
335
+ def dns_a_record_exist?
336
+ dns_a_record.size > 0
337
+ end
338
+
335
339
  # Returns an array of Exchanger hosts configured in DNS.
336
340
  # The array will be empty if none are configured.
337
341
  def exchangers
@@ -381,18 +385,21 @@ module EmailAddress
381
385
  # Returns true if the host name is valid according to the current configuration
382
386
  def valid?(rules = {})
383
387
  host_validation = rules[:host_validation] || @config[:host_validation] || :mx
384
- dns_lookup = rules[:dns_lookup] || host_validation
388
+ dns_lookup = rules[:dns_lookup] || @config[:dns_lookup] || host_validation
385
389
  self.error_message = nil
390
+ if host_name && !host_name.empty? && !@config[:host_size].include?(host_name.size)
391
+ return set_error(:invalid_host)
392
+ end
386
393
  if ip_address
387
394
  valid_ip?
388
395
  elsif !valid_format?
389
396
  false
390
397
  elsif dns_lookup == :connect
391
398
  valid_mx? && connect
399
+ elsif dns_lookup == :a || host_validation == :a
400
+ valid_dns?
392
401
  elsif dns_lookup == :mx
393
402
  valid_mx?
394
- elsif dns_lookup == :a
395
- valid_dns?
396
403
  else
397
404
  true
398
405
  end
@@ -401,7 +408,7 @@ module EmailAddress
401
408
  # True if the host name has a DNS A Record
402
409
  def valid_dns?
403
410
  return true unless dns_enabled?
404
- dns_a_record.size > 0 || set_error(:domain_unknown)
411
+ dns_a_record_exist? || exchangers.exist? || set_error(:domain_unknown)
405
412
  end
406
413
 
407
414
  # True if the host name has valid MX servers configured in DNS
@@ -462,11 +469,15 @@ module EmailAddress
462
469
  # as an email address check, but is provided to assist in problem resolution.
463
470
  # If you abuse this, you *could* be blocked by the ESP.
464
471
  #
465
- # NOTE: As of Ruby 3.1, Net::SMTP was moved from the standard library to the
466
- # 'net-smtp' gem. In order to avoid adding that dependency for this experimental
467
- # feature, please add the gem to your Gemfile and require it to use this feature.
468
- def connect
472
+ # timeout is the number of seconds to wait before timing out the request and
473
+ # returns false as the connection was unsuccessful.
474
+ #
475
+ # > NOTE: As of Ruby 3.1, Net::SMTP was moved from the standard library to the
476
+ # > 'net-smtp' gem. In order to avoid adding that dependency for this *experimental*
477
+ # > feature, please add the gem to your Gemfile and require it to use this feature.
478
+ def connect(timeout = nil)
469
479
  smtp = Net::SMTP.new(host_name || ip_address)
480
+ smtp.open_timeout = timeout || @config[:host_timeout]
470
481
  smtp.start(@config[:helo_name] || "localhost")
471
482
  smtp.finish
472
483
  true
@@ -474,10 +485,10 @@ module EmailAddress
474
485
  set_error(:server_not_available, e.to_s)
475
486
  rescue SocketError => e
476
487
  set_error(:server_not_available, e.to_s)
488
+ rescue Net::OpenTimeout => e
489
+ set_error(:server_not_available, e.to_s)
477
490
  ensure
478
- if smtp&.started?
479
- smtp.finish
480
- end
491
+ smtp.finish if smtp&.started?
481
492
  end
482
493
 
483
494
  def set_error(err, reason = nil)
@@ -101,10 +101,10 @@ module EmailAddress
101
101
 
102
102
  REDACTED_REGEX = /\A \{ [0-9a-f]{40} \} \z/x # {sha1}
103
103
 
104
- CONVENTIONAL_TAG_REGEX = # AZaz09_!'+-/=
105
- %r{^([\w!'+\-/=.]+)$}i
106
- RELAXED_TAG_REGEX = # AZaz09_!#$%&'*+-/=?^`{|}~
107
- %r/^([\w.!\#$%&'*+\-\/=?\^`{|}~]+)$/i
104
+ # Conventional Tag: word ([,-+'='] word)...
105
+ CONVENTIONAL_TAG_REGEX = /\A [\p{L}\p{N}_]+ (?: [.\-+'=] [\p{L}\p{N}_]+ )* \z/x # word(<punctuation>word)*...
106
+ # Relexed Tag: token ( . token)... token is most punctuation, but not: . \ " space
107
+ RELAXED_TAG_REGEX = %r/^[\w!\#$%&'*+\-\/=?\^`{|}~]+ (\.[\w!\#$%&'*+\-\/=?\^`{|}~])* $/ix
108
108
 
109
109
  def initialize(local, config = {}, host = nil, locale = "en")
110
110
  @config = config.is_a?(Hash) ? Config.new(config) : config
@@ -116,7 +116,7 @@ module EmailAddress
116
116
 
117
117
  def local=(raw)
118
118
  self.original = raw
119
- raw.downcase! if @config[:local_downcase].nil? || @config[:local_downcase]
119
+ raw = raw.downcase if @config[:local_downcase].nil? || @config[:local_downcase]
120
120
  @local = raw
121
121
 
122
122
  if @config[:local_parse].is_a?(Proc)
@@ -333,14 +333,11 @@ module EmailAddress
333
333
  # True if the part matches the conventional format
334
334
  def conventional?
335
335
  self.syntax = :invalid
336
- if tag
337
- return false unless mailbox =~ CONVENTIONAL_MAILBOX_REGEX &&
338
- tag =~ CONVENTIONAL_TAG_REGEX
339
- else
340
- return false unless CONVENTIONAL_MAILBOX_REGEX.match?(local)
341
- end
342
- valid_size? or return false
343
- valid_encoding? or return false
336
+ return false if tag && tag !~ CONVENTIONAL_TAG_REGEX
337
+ return false unless mailbox =~ CONVENTIONAL_MAILBOX_REGEX
338
+ return false if comment
339
+ return false unless valid_size?
340
+ return false unless valid_encoding?
344
341
  self.syntax = :conventional
345
342
  true
346
343
  end
@@ -348,32 +345,23 @@ module EmailAddress
348
345
  # Relaxed conventional is not so strict about character order.
349
346
  def relaxed?
350
347
  self.syntax = :invalid
351
- valid_size? or return false
352
- valid_encoding? or return false
353
- if tag
354
- return false unless RELAXED_MAILBOX_REGEX.match?(mailbox) &&
355
- RELAXED_TAG_REGEX.match?(tag)
356
- self.syntax = :relaxed
357
- true
358
- elsif RELAXED_MAILBOX_REGEX.match?(local)
359
- self.syntax = :relaxed
360
- true
361
- else
362
- false
363
- end
348
+ return false if tag && tag !~ RELAXED_TAG_REGEX
349
+ return false unless mailbox =~ RELAXED_MAILBOX_REGEX
350
+ return false if comment
351
+ return false unless valid_size?
352
+ return false unless valid_encoding?
353
+ self.syntax = :relaxed
354
+ true
364
355
  end
365
356
 
366
357
  # True if the part matches the RFC standard format
367
358
  def standard?
368
359
  self.syntax = :invalid
369
- valid_size? or return false
370
- valid_encoding? or return false
371
- if STANDARD_LOCAL_REGEX.match?(local)
372
- self.syntax = :standard
373
- true
374
- else
375
- false
376
- end
360
+ return false unless valid_size?
361
+ return false unless STANDARD_LOCAL_REGEX.match?(local)
362
+ return false unless valid_encoding?
363
+ self.syntax = :standard
364
+ true
377
365
  end
378
366
 
379
367
  # Matches configured formated form against File glob strings given.
@@ -1,3 +1,3 @@
1
1
  module EmailAddress
2
- VERSION = "0.2.3"
2
+ VERSION = "0.2.9"
3
3
  end
@@ -9,11 +9,21 @@ class TestAR < MiniTest::Test
9
9
  user = User.new(email: "Pat.Jones+ASDF#GMAIL.com")
10
10
  assert_equal false, user.valid?
11
11
  assert user.errors.messages[:email].first
12
+
12
13
  user = User.new(email: "Pat.Jones+ASDF@GMAIL.com")
13
14
  assert_equal true, user.valid?
14
15
  end
15
16
  end
16
17
 
18
+ def test_validation_error_message
19
+ if RUBY_PLATFORM != "java" # jruby
20
+ user = User.new(alternate_email: "Pat.Jones+ASDF#GMAIL.com")
21
+ assert_equal false, user.valid?
22
+ assert user.errors.messages[:alternate_email].first.include?("Check your email")
23
+ assert_equal :some_error_code, user.errors.details[:alternate_email].first[:error]
24
+ end
25
+ end
26
+
17
27
  def test_datatype
18
28
  # Disabled JRuby checks... weird CI failures. Hopefully someone can help?
19
29
  if RUBY_PLATFORM != "java" # jruby
@@ -21,7 +31,21 @@ class TestAR < MiniTest::Test
21
31
  user = User.new(email: "Pat.Jones+ASDF@GMAIL.com")
22
32
  assert_equal "pat.jones+asdf@gmail.com", user.email
23
33
  assert_equal "patjones@gmail.com", user.canonical_email
34
+
35
+ user = User.new(email: nil)
36
+ assert_nil user.email
37
+ assert_nil user.canonical_email
24
38
  end
25
39
  end
26
40
  end
41
+
42
+ def test_store_accessor_valid_email
43
+ user = User.new(support_email: "test@gmail.com")
44
+ assert user.valid?
45
+ end
46
+
47
+ def test_store_accessor_invalid_email
48
+ user = User.new(support_email: "this_is_not_an_email")
49
+ assert_equal false, user.valid?
50
+ end
27
51
  end
@@ -45,13 +45,18 @@ end
45
45
  ################################################################################
46
46
 
47
47
  class User < ApplicationRecord
48
+ store :settings, accessors: [ :support_email ], coder: JSON
49
+
48
50
  if defined?(ActiveRecord) && ::ActiveRecord::VERSION::MAJOR >= 5
49
51
  attribute :email, :email_address
50
52
  attribute :canonical_email, :canonical_email_address
53
+ attribute :alternate_email, :email_address
51
54
  end
52
55
 
53
56
  validates_with EmailAddress::ActiveRecordValidator,
54
- fields: %i[email canonical_email]
57
+ fields: %i[email canonical_email support_email]
58
+ validates_with EmailAddress::ActiveRecordValidator,
59
+ field: :alternate_email, code: :some_error_code, message: "Check your email"
55
60
 
56
61
  def email=(email_address)
57
62
  self[:canonical_email] = email_address
@@ -102,9 +102,9 @@ class TestAddress < Minitest::Test
102
102
  assert_equal "", e.hostname
103
103
  assert_equal false, e.valid? # localhost not allowed by default
104
104
  assert_equal EmailAddress.error("user1"), "Invalid Domain Name"
105
- assert_equal EmailAddress.error("user1", host_local: true), "This domain is not configured to accept email"
105
+ #assert_equal EmailAddress.error("user1", host_local: true), "This domain is not configured to accept email"
106
106
  assert_equal EmailAddress.error("user1", host_local: true, host_auto_append: false), "Invalid Domain Name"
107
- assert_equal EmailAddress.error("user1@localhost", host_local: true), "This domain is not configured to accept email"
107
+ #assert_equal EmailAddress.error("user1@localhost", host_local: true), "This domain is not configured to accept email"
108
108
  assert_equal EmailAddress.error("user1@localhost", host_local: false, host_validation: :syntax), "localhost is not allowed for your domain name"
109
109
  assert_equal EmailAddress.error("user1@localhost", host_local: false, dns_lookup: :off), "localhost is not allowed for your domain name"
110
110
  assert_nil EmailAddress.error("user2@localhost", host_local: true, dns_lookup: :off, host_validation: :syntax)
@@ -149,6 +149,13 @@ class TestAddress < Minitest::Test
149
149
  end
150
150
 
151
151
  def test_nonstandard_tag
152
- assert EmailAddress.valid?("asdfas+-@icloud.com")
152
+ # assert EmailAddress.valid?("asdfas+-@icloud.com")
153
+ end
154
+
155
+ def test_newline_characters
156
+ assert !EmailAddress.valid?("user@foo.com\nother@bar.com")
157
+ assert !EmailAddress.valid?("user@foo.com\r\nother@bar.com")
158
+ assert EmailAddress.valid?("\nuser@foo.com") # valid because strip processing removes \n
159
+ assert EmailAddress.valid?("user@foo.com\r\n") # valid because strip processing removes \n
153
160
  end
154
161
  end
@@ -2,19 +2,19 @@ require_relative "../test_helper"
2
2
 
3
3
  class TestConfig < MiniTest::Test
4
4
  def test_setting
5
- assert_equal :mx, EmailAddress::Config.setting(:dns_lookup)
5
+ assert_equal :a, EmailAddress::Config.setting(:dns_lookup)
6
6
  assert_equal :off, EmailAddress::Config.setting(:dns_lookup, :off)
7
7
  assert_equal :off, EmailAddress::Config.setting(:dns_lookup)
8
- EmailAddress::Config.setting(:dns_lookup, :mx)
8
+ EmailAddress::Config.setting(:dns_lookup, :a)
9
9
  end
10
10
 
11
11
  def test_configure
12
- assert_equal :mx, EmailAddress::Config.setting(:dns_lookup)
12
+ assert_equal :a, EmailAddress::Config.setting(:dns_lookup)
13
13
  assert_equal true, EmailAddress::Config.setting(:local_downcase)
14
14
  EmailAddress::Config.configure(local_downcase: false, dns_lookup: :off)
15
15
  assert_equal :off, EmailAddress::Config.setting(:dns_lookup)
16
16
  assert_equal false, EmailAddress::Config.setting(:local_downcase)
17
- EmailAddress::Config.configure(local_downcase: true, dns_lookup: :mx)
17
+ EmailAddress::Config.configure(local_downcase: true, dns_lookup: :a)
18
18
  end
19
19
 
20
20
  def test_provider
@@ -52,7 +52,7 @@ class TestHost < MiniTest::Test
52
52
  def test_dmarc
53
53
  d = EmailAddress::Host.new("yahoo.com").dmarc
54
54
  assert_equal "reject", d[:p]
55
- d = EmailAddress::Host.new("example.com").dmarc
55
+ d = EmailAddress::Host.new("calculator.net").dmarc
56
56
  assert_equal true, d.empty?
57
57
  end
58
58
 
@@ -127,7 +127,7 @@ class TestHost < MiniTest::Test
127
127
  def test_hosted_service
128
128
  # Is there a gmail-hosted domain that will continue to exist? Removing until then
129
129
  # assert EmailAddress.valid?("test@jiff.com", dns_lookup: :mx)
130
- assert !EmailAddress.valid?("test@gmail.com", dns_lookup: :mx)
130
+ assert !EmailAddress.valid?("t@gmail.com", dns_lookup: :mx)
131
131
  end
132
132
 
133
133
  def test_yahoo_bad_tld
@@ -142,7 +142,7 @@ class TestHost < MiniTest::Test
142
142
 
143
143
  def test_errors
144
144
  assert_nil EmailAddress::Host.new("yahoo.com").error
145
- assert_equal EmailAddress::Host.new("example.com").error, "This domain is not configured to accept email"
145
+ #assert_equal EmailAddress::Host.new("example.com").error, "This domain is not configured to accept email"
146
146
  assert_equal EmailAddress::Host.new("yahoo.wtf").error, "Domain name not registered"
147
147
  assert_nil EmailAddress::Host.new("ajsdfhajshdfklasjhd.wtf", host_validation: :syntax).error
148
148
  assert_equal EmailAddress::Host.new("ya hoo.com", host_validation: :syntax).error, "Invalid Domain Name"
@@ -150,4 +150,24 @@ class TestHost < MiniTest::Test
150
150
  assert_equal EmailAddress::Host.new("[127.0.0.666]", host_allow_ip: true).error, "This is not a valid IPv4 address"
151
151
  assert_equal EmailAddress::Host.new("[IPv6::12t]", host_allow_ip: true).error, "This is not a valid IPv6 address"
152
152
  end
153
+
154
+ def test_host_size
155
+ assert !EmailAddress::Host.new("stackoverflow.com", {host_size: 1..3}).valid?
156
+ end
157
+
158
+ # When a domain is not configured to receive email (missing MX record),
159
+ # Though some MTA's will fallback to the A/AAAA host record, so this isn't a good test
160
+ #def test_no_mx
161
+ # assert !EmailAddress::Host.new("zaboz.com").valid?
162
+ # assert EmailAddress::Host.new("zaboz.com", dns_lookup: :a).valid?
163
+ #end
164
+
165
+ # Issue #102 off---white.com should be valid
166
+ def test_triple_dash_domain
167
+ assert EmailAddress::Host.new("off---white.com").valid?
168
+ end
169
+
170
+ def test_mx_only_domain
171
+ assert EmailAddress::Host.new("mail.usqrl.com").valid?
172
+ end
153
173
  end
@@ -63,13 +63,13 @@ class TestLocal < MiniTest::Test
63
63
  end
64
64
 
65
65
  def test_valid
66
- assert_equal false, EmailAddress::Local.new("first(comment)", local_format: :conventional).valid?
67
- assert_equal true, EmailAddress::Local.new("first(comment)", local_format: :standard).valid?
66
+ refute EmailAddress::Local.new("first(comment)", local_format: :conventional).valid?
67
+ assert EmailAddress::Local.new("first(comment)", local_format: :standard).valid?
68
68
  end
69
69
 
70
70
  def test_format
71
71
  assert_equal :conventional, EmailAddress::Local.new("can1").format?
72
- assert_equal :standard, EmailAddress::Local.new(%("can1")).format?
72
+ assert_equal :standard, EmailAddress::Local.new(%("can 1")).format?
73
73
  assert_equal "can1", EmailAddress::Local.new(%{"can1(commment)"}).format(:conventional)
74
74
  end
75
75
 
@@ -92,7 +92,7 @@ class TestLocal < MiniTest::Test
92
92
 
93
93
  def test_hosted
94
94
  assert EmailAddress.valid?("x@exposure.co")
95
- assert EmailAddress.error("xxxx+subscriber@gmail.com")
95
+ assert EmailAddress.error("xx+subscriber@gmail.com")
96
96
  assert EmailAddress.valid?("xxxxx+subscriber@gmail.com")
97
97
  end
98
98
 
@@ -102,11 +102,13 @@ class TestLocal < MiniTest::Test
102
102
  assert EmailAddress.valid?("username_____@gmail.com")
103
103
  end
104
104
 
105
- def test_tag_punctuation
106
- assert EmailAddress.valid?("first.last+foo.bar@gmail.com")
107
- end
108
-
109
105
  def test_relaxed_tag
110
106
  assert EmailAddress.valid? "foo+abc@example.com", host_validation: :syntax, local_format: :relaxed
111
107
  end
108
+
109
+ def test_tag_punctuation
110
+ assert EmailAddress.valid?("first.last+foo.bar@gmail.com")
111
+ refute EmailAddress.valid?("name+tag.@domain.com", host_validation: :syntax)
112
+ assert EmailAddress.valid?("name+tag.-@domain.com", host_validation: :syntax, local_format: :relaxed)
113
+ end
112
114
  end
data/test/test_helper.rb CHANGED
@@ -1,3 +1,9 @@
1
+ ################################################################################
2
+ # EmailAddress Testing
3
+ # - 🔥 rake
4
+ # - 🔍️ ruby test/email_address/test_local.rb --name test_tag_punctuation
5
+ # - 🧪 rake console
6
+ ################################################################################
1
7
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
2
8
 
3
9
  require "pry"
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: email_address
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Allen Fair
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2022-05-14 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rake
@@ -58,16 +57,30 @@ dependencies:
58
57
  requirements:
59
58
  - - "~>"
60
59
  - !ruby/object:Gem::Version
61
- version: 6.1.4
60
+ version: 8.0.0
62
61
  type: :development
63
62
  prerelease: false
64
63
  version_requirements: !ruby/object:Gem::Requirement
65
64
  requirements:
66
65
  - - "~>"
67
66
  - !ruby/object:Gem::Version
68
- version: 6.1.4
67
+ version: 8.0.0
69
68
  - !ruby/object:Gem::Dependency
70
69
  name: sqlite3
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '2.1'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '2.1'
82
+ - !ruby/object:Gem::Dependency
83
+ name: standard
71
84
  requirement: !ruby/object:Gem::Requirement
72
85
  requirements:
73
86
  - - ">="
@@ -81,19 +94,19 @@ dependencies:
81
94
  - !ruby/object:Gem::Version
82
95
  version: '0'
83
96
  - !ruby/object:Gem::Dependency
84
- name: standard
97
+ name: net-smtp
85
98
  requirement: !ruby/object:Gem::Requirement
86
99
  requirements:
87
- - - "~>"
100
+ - - ">="
88
101
  - !ruby/object:Gem::Version
89
- version: 1.5.0
102
+ version: '0'
90
103
  type: :development
91
104
  prerelease: false
92
105
  version_requirements: !ruby/object:Gem::Requirement
93
106
  requirements:
94
- - - "~>"
107
+ - - ">="
95
108
  - !ruby/object:Gem::Version
96
- version: 1.5.0
109
+ version: '0'
97
110
  - !ruby/object:Gem::Dependency
98
111
  name: simplecov
99
112
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +135,62 @@ dependencies:
122
135
  - - ">="
123
136
  - !ruby/object:Gem::Version
124
137
  version: '0'
138
+ - !ruby/object:Gem::Dependency
139
+ name: bigdecimal
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ - !ruby/object:Gem::Dependency
153
+ name: mutex_m
154
+ requirement: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ type: :development
160
+ prerelease: false
161
+ version_requirements: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ - !ruby/object:Gem::Dependency
167
+ name: irb
168
+ requirement: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ type: :development
174
+ prerelease: false
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ - !ruby/object:Gem::Dependency
181
+ name: base64
182
+ requirement: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ version: '0'
187
+ type: :runtime
188
+ prerelease: false
189
+ version_requirements: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
125
194
  - !ruby/object:Gem::Dependency
126
195
  name: simpleidn
127
196
  requirement: !ruby/object:Gem::Requirement
@@ -177,7 +246,6 @@ homepage: https://github.com/afair/email_address
177
246
  licenses:
178
247
  - MIT
179
248
  metadata: {}
180
- post_install_message:
181
249
  rdoc_options: []
182
250
  require_paths:
183
251
  - lib
@@ -188,29 +256,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
188
256
  version: '2.5'
189
257
  - - "<"
190
258
  - !ruby/object:Gem::Version
191
- version: '4'
259
+ version: '5.0'
192
260
  required_rubygems_version: !ruby/object:Gem::Requirement
193
261
  requirements:
194
262
  - - ">="
195
263
  - !ruby/object:Gem::Version
196
264
  version: '0'
197
265
  requirements: []
198
- rubygems_version: 3.3.3
199
- signing_key:
266
+ rubygems_version: 4.0.6
200
267
  specification_version: 4
201
268
  summary: This gem provides a ruby language library for working with and validating
202
269
  email addresses. By default, it validates against conventional usage, the format
203
270
  preferred for user email addresses. It can be configured to validate against RFC
204
271
  “Standard” formats, common email service provider formats, and perform DNS validation.
205
- test_files:
206
- - test/activerecord/test_ar.rb
207
- - test/activerecord/user.rb
208
- - test/email_address/test_address.rb
209
- - test/email_address/test_config.rb
210
- - test/email_address/test_exchanger.rb
211
- - test/email_address/test_host.rb
212
- - test/email_address/test_local.rb
213
- - test/email_address/test_rewriter.rb
214
- - test/test_aliasing.rb
215
- - test/test_email_address.rb
216
- - test/test_helper.rb
272
+ test_files: []