email_address 0.1.16 → 0.2.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: 46541e8de9140acd70cc23c1f60515c077ea919e0b04444a83d681e19fcbd4e1
4
- data.tar.gz: 34f52f8bfa70a33d8e40c5986d1c9d4a49956a4c12d6bc69df8037705f76c417
3
+ metadata.gz: f9667acc31d6ce84fd3acb2e8e2f3ea04e496df5ff9dc5f89815fa148df1113e
4
+ data.tar.gz: e17aa2b229cb28ca80dcbf37226a5411052758db1e15cee0ddc2038d5244de42
5
5
  SHA512:
6
- metadata.gz: adde7e6139b151e6759861a0b7054c7f179a7f590d8180141c31b786ebba81aba2d01657c4073158779e879ec27b3c76389679131061661dae46cf1f1c315b92
7
- data.tar.gz: 3420904e9bbe4713bf6e57e7bd6d1d059c7616f529a650c07828c9e45eb1a1ce3566412d86358b8c04c335ad49f55aed1f5e715d08a7eb8e4e066a703231f28e
6
+ metadata.gz: 908fa1ffaa5692b07fa4e586a66a8fdf6395c1a51013ee389e0437df175d6712ecc57831b6616cfabd28463a87c7a60df9b6ba8382cba0c58d8449e4ada51c6d
7
+ data.tar.gz: a9f2a9bb8280118af5b21b08e8ca2273b16f3d5f700b91cb7c36b22444a7d5878e54a45292a2080243aff6fa4d31cd2ca3af4bd664f182b49d52ab42b864c711
@@ -0,0 +1,18 @@
1
+ name: CI Build
2
+ on: [push, pull_request]
3
+ jobs:
4
+ build:
5
+ runs-on: ubuntu-latest
6
+ strategy:
7
+ matrix:
8
+ ruby-version: [2.6, 2.7, 3.0, jruby]
9
+ steps:
10
+ - uses: actions/checkout@v2
11
+ - uses: ruby/setup-ruby@v1
12
+ with:
13
+ ruby-version: ${{ matrix.ruby-version }}
14
+ bundler-cache: true # runs `bundle install` and caches installed gems automatically
15
+ - name: Install dependencies
16
+ run: bundle install
17
+ - name: Run tests
18
+ run: bundle exec rake
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in email_address.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Email Address
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/email_address.svg)](http://rubygems.org/gems/email_address)
4
- [![Build Status](https://travis-ci.org/afair/email_address.svg?branch=v0.1)](https://travis-ci.org/afair/email_address)
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
7
  The `email_address` gem provides a ruby language library for working
@@ -50,7 +50,7 @@ It does not check:
50
50
  By default, MX records are required in DNS. MX or "mail exchanger" records
51
51
  tell where to deliver email for the domain. Many domains run their
52
52
  website on one provider (ISP, Heroku, etc.), and email on a different
53
- provider (such as Google Apps). Note that `example.com`, while
53
+ provider (such as G Suite). Note that `example.com`, while
54
54
  a valid domain name, does not have MX records.
55
55
 
56
56
  ```ruby
@@ -98,7 +98,7 @@ introduces terms to distinguish types of email addresses.
98
98
  madness!."()<>[]:,;@\\\"!#$%&'*+-/=?^_`{}| ~.a(comment )"@example.org
99
99
 
100
100
  * *Base* - A unique mailbox without tags. For gmail, is uses the incoming
101
- punctation, essential when building an MD5 or SHA1 to match services
101
+ punctation, essential when building an MD5, SHA1, or SHA256 to match services
102
102
  like Gravatar, and email address digest interchange.
103
103
 
104
104
  * *Canonical* - An unique account address, lower-cased, without the
@@ -234,6 +234,7 @@ Here are some other methods that are available.
234
234
  ```ruby
235
235
  email.redact #=> "{bea3f3560a757f8142d38d212a931237b218eb5e}@gmail.com"
236
236
  email.sha1 #=> "bea3f3560a757f8142d38d212a931237b218eb5e"
237
+ email.sha256 #=> "9e2a0270f2d6778e5f647fc9eaf6992705ca183c23d1ed1166586fd54e859f75"
237
238
  email.md5 #=> "c5be3597c391169a5ad2870f9ca51901"
238
239
  email.host_name #=> "gmail.com"
239
240
  email.provider #=> :google
@@ -248,6 +249,16 @@ EmailAddress.normal("HIRO@こんにちは世界.com")
248
249
  EmailAddress.normal("hiro@xn--28j2a3ar1pp75ovm7c.com", host_encoding: :unicode)
249
250
  #=> "hiro@こんにちは世界.com"
250
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
+
251
262
 
252
263
  #### Rails Validator
253
264
 
@@ -262,6 +273,25 @@ class User < ActiveRecord::Base
262
273
  end
263
274
  ```
264
275
 
276
+ #### Rails I18n
277
+
278
+ Copy and adapt `lib/email_address/messages.yaml` into your locales and
279
+ create an after initialization callback:
280
+
281
+ ```ruby
282
+ # config/initializers/email_address.rb
283
+
284
+ Rails.application.config.after_initialize do
285
+ I18n.available_locales.each do |locale|
286
+ translations = I18n.t(:email_address, locale: locale)
287
+
288
+ next unless translations.is_a? Hash
289
+
290
+ EmailAddress::Config.error_messages translations.transform_keys(&:to_s), locale.to_s
291
+ end
292
+ end
293
+ ```
294
+
265
295
  #### Rails Email Address Type Attribute
266
296
 
267
297
  Initial support is provided for Active Record 5.0 attributes API.
@@ -466,6 +496,11 @@ Full translation support would be ideal though.
466
496
  the SHA1 Digest, making it unique to your application so it can't easily be
467
497
  discovered by comparing against a known list of email/sha1 pairs.
468
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
+
469
504
  * munge_string - "*****", the string to replace into munged addresses.
470
505
 
471
506
  For local part configuration:
@@ -534,14 +569,38 @@ For the mailbox (AKA account, role), without the tag
534
569
  * address_size: 3..254,
535
570
  A range specifying the size limit of the complete address
536
571
 
537
- * address_local: false,
538
- Allow localhost, no domain, or local subdomains.
539
-
540
572
  For provider rules to match to domain names and Exchanger hosts
541
573
  The value is an array of match tokens.
542
574
  * host_match: %w(.org example.com hotmail. user*@ sub.*.com)
543
575
  * exchanger_match: %w(google.com 127.0.0.1 10.9.8.0/24 ::1/64)
544
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
+ ```
603
+
545
604
  ## Notes
546
605
 
547
606
  #### Internationalization
data/Rakefile CHANGED
@@ -1,9 +1,9 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
3
  require "bundler/setup"
4
- require 'rake/testtask'
4
+ require "rake/testtask"
5
5
 
6
- task :default => :test
6
+ task default: :test
7
7
 
8
8
  desc "Run the Test Suite, toot suite"
9
9
  Rake::TestTask.new do |t|
@@ -1,37 +1,36 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path("../lib", __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'email_address/version'
3
+ require "email_address/version"
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "email_address"
8
- spec.version = EmailAddress::VERSION
9
- spec.authors = ["Allen Fair"]
10
- spec.email = ["allen.fair@gmail.com"]
11
- spec.description = %q{The EmailAddress Gem to work with and validate email addresses.}
12
- spec.summary = %q{This gem provides a ruby language library for working with and validating email addresses. By default, it validates against conventional usage, the format preferred for user email addresses. It can be configured to validate against RFC “Standard” formats, common email service provider formats, and perform DNS validation.}
13
- spec.homepage = "https://github.com/afair/email_address"
14
- spec.license = "MIT"
6
+ spec.name = "email_address"
7
+ spec.version = EmailAddress::VERSION
8
+ spec.authors = ["Allen Fair"]
9
+ spec.email = ["allen.fair@gmail.com"]
10
+ spec.description = "The EmailAddress Gem to work with and validate email addresses."
11
+ spec.summary = "This gem provides a ruby language library for working with and validating email addresses. By default, it validates against conventional usage, the format preferred for user email addresses. It can be configured to validate against RFC “Standard” formats, common email service provider formats, and perform DNS validation."
12
+ spec.homepage = "https://github.com/afair/email_address"
13
+ spec.license = "MIT"
15
14
 
16
- #spec.required_ruby_version = ">= 2.3.0"
17
- spec.files = `git ls-files`.split($/)
18
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
15
+ spec.required_ruby_version = ">= 2.5", "< 4"
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
19
  spec.require_paths = ["lib"]
21
20
 
22
21
  spec.add_development_dependency "rake"
23
- spec.add_development_dependency "minitest", "~> 5.11"
24
- spec.add_development_dependency "bundler" #, "~> 1.16.0"
25
- if RUBY_PLATFORM == 'java'
26
- spec.add_development_dependency "activerecord", "= 4.2.10"
27
- spec.add_development_dependency "activerecord-jdbcsqlite3-adapter", '~> 1.3.24'
22
+ spec.add_development_dependency "minitest", "~> 5.11"
23
+ spec.add_development_dependency "bundler" # , "~> 1.16.0"
24
+ if RUBY_PLATFORM == "java"
25
+ spec.add_development_dependency "activerecord", "~> 5.2.6"
26
+ spec.add_development_dependency "activerecord-jdbcsqlite3-adapter", "~> 52.7"
28
27
  else
29
- spec.add_development_dependency "activerecord", "~> 5.2.4"
28
+ spec.add_development_dependency "activerecord", "~> 6.1.4"
30
29
  spec.add_development_dependency "sqlite3"
31
30
  end
32
- #spec.add_development_dependency "codeclimate-test-reporter"
33
31
  spec.add_development_dependency "simplecov"
32
+ spec.add_development_dependency "pry"
33
+ spec.add_development_dependency "standard", "~> 1.1.1"
34
34
 
35
35
  spec.add_dependency "simpleidn"
36
- spec.add_dependency "netaddr", '>= 2.0.4', '< 3'
37
36
  end
data/lib/email_address.rb CHANGED
@@ -3,7 +3,6 @@
3
3
  # EmailAddress parses and validates email addresses against RFC standard,
4
4
  # conventional, canonical, formats and other special uses.
5
5
  module EmailAddress
6
-
7
6
  require "email_address/config"
8
7
  require "email_address/exchanger"
9
8
  require "email_address/host"
@@ -40,31 +39,30 @@ module EmailAddress
40
39
  # secret to use in options[:secret]
41
40
  class << self
42
41
  (%i[valid? error normal redact munge canonical reference base srs] &
43
- EmailAddress::Address.public_instance_methods
42
+ Address.public_instance_methods
44
43
  ).each do |proxy_method|
45
44
  define_method(proxy_method) do |*args, &block|
46
- EmailAddress::Address.new(*args).public_send(proxy_method, &block)
45
+ Address.new(*args).public_send(proxy_method, &block)
47
46
  end
48
47
  end
49
- end
50
-
51
48
 
52
- # Creates an instance of this email address.
53
- # This is a short-cut to Email::Address::Address.new
54
- def self.new(email_address, config={})
55
- EmailAddress::Address.new(email_address, config)
56
- end
49
+ # Creates an instance of this email address.
50
+ # This is a short-cut to EmailAddress::Address.new
51
+ def new(email_address, config = {}, locale = "en")
52
+ Address.new(email_address, config, locale)
53
+ end
57
54
 
58
- def self.new_redacted(email_address, config={})
59
- EmailAddress::Address.new(EmailAddress::Address.new(email_address, config).redact)
60
- end
55
+ def new_redacted(email_address, config = {}, locale = "en")
56
+ Address.new(Address.new(email_address, config, locale).redact)
57
+ end
61
58
 
62
- def self.new_canonical(email_address, config={})
63
- EmailAddress::Address.new(EmailAddress::Address.new(email_address, config).canonical, config)
64
- end
59
+ def new_canonical(email_address, config = {}, locale = "en")
60
+ Address.new(Address.new(email_address, config, locale).canonical, config)
61
+ end
65
62
 
66
- # Does the email address match any of the given rules
67
- def self.matches?(email_address, rules, config={})
68
- EmailAddress::Address.new(email_address, config).matches?(rules)
63
+ # Does the email address match any of the given rules
64
+ def matches?(email_address, rules, config = {}, locale = "en")
65
+ Address.new(email_address, config, locale).matches?(rules)
66
+ end
69
67
  end
70
68
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EmailAddress
4
-
5
4
  # ActiveRecord validator class for validating an email
6
5
  # address with this library.
7
6
  # Note the initialization happens once per process.
@@ -16,14 +15,13 @@ module EmailAddress
16
15
  # Default field: :email or :email_address (first found)
17
16
  #
18
17
  class ActiveRecordValidator < ActiveModel::Validator
19
-
20
- def initialize(options={})
18
+ def initialize(options = {})
21
19
  @opt = options
22
20
  end
23
21
 
24
22
  def validate(r)
25
23
  if @opt[:fields]
26
- @opt[:fields].each {|f| validate_email(r, f) }
24
+ @opt[:fields].each { |f| validate_email(r, f) }
27
25
  elsif @opt[:field]
28
26
  validate_email(r, @opt[:field])
29
27
  elsif r.respond_to? :email
@@ -33,16 +31,15 @@ module EmailAddress
33
31
  end
34
32
  end
35
33
 
36
- def validate_email(r,f)
34
+ def validate_email(r, f)
37
35
  return if r[f].nil?
38
- e = EmailAddress.new(r[f])
36
+ e = Address.new(r[f])
39
37
  unless e.valid?
40
- r.errors[f] << (@opt[:message] ||
41
- EmailAddress::Config.error_messages[:invalid_address] ||
42
- "Invalid Email Address")
38
+ error_message = @opt[:message] ||
39
+ Config.error_message(:invalid_address, I18n.locale.to_s) ||
40
+ "Invalid Email Address"
41
+ r.errors.add(f, error_message)
43
42
  end
44
43
  end
45
-
46
44
  end
47
-
48
45
  end
@@ -1,36 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "digest/sha1"
4
+ require "digest/sha2"
4
5
  require "digest/md5"
5
6
 
6
7
  module EmailAddress
7
8
  # Implements the Email Address container, which hold the Local
8
- # (EmailAddress::Local) and Host (Email::AddressHost) parts.
9
+ # (EmailAddress::Local) and Host (EmailAddress::Host) parts.
9
10
  class Address
10
11
  include Comparable
11
- include EmailAddress::Rewriter
12
+ include Rewriter
12
13
 
13
- attr_accessor :original, :local, :host, :config, :reason
14
+ attr_accessor :original, :local, :host, :config, :reason, :locale
14
15
 
15
- CONVENTIONAL_REGEX = /\A#{::EmailAddress::Local::CONVENTIONAL_MAILBOX_WITHIN}
16
- @#{::EmailAddress::Host::DNS_HOST_REGEX}\z/x
17
- STANDARD_REGEX = /\A#{::EmailAddress::Local::STANDARD_LOCAL_WITHIN}
18
- @#{::EmailAddress::Host::DNS_HOST_REGEX}\z/x
19
- RELAXED_REGEX = /\A#{::EmailAddress::Local::RELAXED_MAILBOX_WITHIN}
20
- @#{::EmailAddress::Host::DNS_HOST_REGEX}\z/x
16
+ CONVENTIONAL_REGEX = /\A#{Local::CONVENTIONAL_MAILBOX_WITHIN}
17
+ @#{Host::DNS_HOST_REGEX}\z/x
18
+ STANDARD_REGEX = /\A#{Local::STANDARD_LOCAL_WITHIN}
19
+ @#{Host::DNS_HOST_REGEX}\z/x
20
+ RELAXED_REGEX = /\A#{Local::RELAXED_MAILBOX_WITHIN}
21
+ @#{Host::DNS_HOST_REGEX}\z/x
21
22
 
22
23
  # Given an email address of the form "local@hostname", this sets up the
23
24
  # instance, and initializes the address to the "normalized" format of the
24
25
  # address. The original string is available in the #original method.
25
- def initialize(email_address, config = {})
26
- @config = EmailAddress::Config.new(config)
26
+ def initialize(email_address, config = {}, locale = "en")
27
+ @config = Config.new(config)
27
28
  @original = email_address
29
+ @locale = locale
28
30
  email_address = (email_address || "").strip
29
31
  email_address = parse_rewritten(email_address) unless config[:skip_rewrite]
30
- local, host = EmailAddress::Address.split_local_host(email_address)
32
+ local, host = Address.split_local_host(email_address)
31
33
 
32
- @host = EmailAddress::Host.new(host, @config)
33
- @local = EmailAddress::Local.new(local, @config, @host)
34
+ @host = Host.new(host, @config, locale)
35
+ @local = Local.new(local, @config, @host, locale)
34
36
  @error = @error_message = nil
35
37
  end
36
38
 
@@ -86,8 +88,8 @@ module EmailAddress
86
88
  def host_name
87
89
  @host.host_name
88
90
  end
89
- alias right host_name
90
- alias hostname host_name
91
+ alias_method :right, :host_name
92
+ alias_method :hostname, :host_name
91
93
 
92
94
  # Returns the ESP (Email Service Provider) or ISP name derived
93
95
  # using the provider configuration rules.
@@ -111,10 +113,10 @@ module EmailAddress
111
113
  "#{local}@#{host}"
112
114
  end
113
115
  end
114
- alias to_s normal
116
+ alias_method :to_s, :normal
115
117
 
116
118
  def inspect
117
- "#<EmailAddress::Address:0x#{object_id.to_s(16)} address=\"#{self}\">"
119
+ "#<#{self.class}:0x#{object_id.to_s(16)} address=\"#{self}\">"
118
120
  end
119
121
 
120
122
  # Returns the canonical email address according to the provider
@@ -159,50 +161,54 @@ module EmailAddress
159
161
  [local.munge, host.munge].join("@")
160
162
  end
161
163
 
162
- # Returns and MD5 of the canonical address form. Some cross-system systems
164
+ # Returns and MD5 of the base address form. Some cross-system systems
163
165
  # use the email address MD5 instead of the actual address to refer to the
164
166
  # same shared user identity without exposing the actual address when it
165
167
  # is not known in common.
166
168
  def reference(form = :base)
167
169
  Digest::MD5.hexdigest(send(form))
168
170
  end
169
- alias md5 reference
171
+ alias_method :md5, :reference
170
172
 
171
- # This returns the SHA1 digest (in a hex string) of the canonical email
173
+ # This returns the SHA1 digest (in a hex string) of the base email
172
174
  # address. See #md5 for more background.
173
175
  def sha1(form = :base)
174
176
  Digest::SHA1.hexdigest((send(form) || "") + (@config[:sha1_secret] || ""))
175
177
  end
176
178
 
179
+ def sha256(form = :base)
180
+ Digest::SHA256.hexdigest((send(form) || "") + (@config[:sha256_secret] || ""))
181
+ end
182
+
177
183
  #---------------------------------------------------------------------------
178
184
  # Comparisons & Matching
179
185
  #---------------------------------------------------------------------------
180
186
 
181
187
  # Equal matches the normalized version of each address. Use the Threequal to check
182
188
  # for match on canonical or redacted versions of addresses
183
- def ==(other_email)
184
- to_s == other_email.to_s
189
+ def ==(other)
190
+ to_s == other.to_s
185
191
  end
186
- alias eql? ==
187
- alias equal? ==
192
+ alias_method :eql?, :==
193
+ alias_method :equal?, :==
188
194
 
189
195
  # Return the <=> or CMP comparison operator result (-1, 0, +1) on the comparison
190
196
  # of this addres with another, using the canonical or redacted forms.
191
197
  def same_as?(other_email)
192
198
  if other_email.is_a?(String)
193
- other_email = EmailAddress::Address.new(other_email)
199
+ other_email = Address.new(other_email)
194
200
  end
195
201
 
196
202
  canonical == other_email.canonical ||
197
203
  redact == other_email.canonical ||
198
204
  canonical == other_email.redact
199
205
  end
200
- alias include? same_as?
206
+ alias_method :include?, :same_as?
201
207
 
202
208
  # Return the <=> or CMP comparison operator result (-1, 0, +1) on the comparison
203
209
  # of this addres with another, using the normalized form.
204
- def <=>(other_email)
205
- to_s <=> other_email.to_s
210
+ def <=>(other)
211
+ to_s <=> other.to_s
206
212
  end
207
213
 
208
214
  # Address matches one of these Matcher rule patterns
@@ -214,7 +220,7 @@ module EmailAddress
214
220
 
215
221
  # Does "root@*.com" match "root@example.com" domain name
216
222
  rules.each do |r|
217
- if r.match(/.+@.+/)
223
+ if /.+@.+/.match?(r)
218
224
  return r if File.fnmatch?(r, to_s)
219
225
  end
220
226
  end
@@ -276,7 +282,7 @@ module EmailAddress
276
282
  def set_error(err, reason = nil)
277
283
  @error = err
278
284
  @reason = reason
279
- @error_message = EmailAddress::Config.error_message(err)
285
+ @error_message = Config.error_message(err, locale)
280
286
  false
281
287
  end
282
288