email_domain_checker 0.1.0 → 0.1.2

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: 946d5e9368662216b21d1dfa1434edbdbe992b6c1354d3b53b73a62e8d262a06
4
- data.tar.gz: 9e47f0583115b145f461d7fcbc2cda5a4db3abe263a8edab82df98d160242431
3
+ metadata.gz: 9482f21ae74333979292271bf830aa22164a859388ba73b29a090118185ac797
4
+ data.tar.gz: 12fa14a045d73af82ff4d179c55d9f7c425ec25a1bc171d3328e60f5045f92de
5
5
  SHA512:
6
- metadata.gz: c333b2f6dd62889860581493a2453ee0f16db2f9db024f3f3ff8db88c2496e4d438221ff31a55d8fc2fd3174d5aec834615e221a2e49b377c22dc4bc3c569ed3
7
- data.tar.gz: 2fd79606ecdf91e1c665b71ccb343f2b3703082db2c5d63085919435fabc7955f89cae73d2a66a8fefb9c8d4d26611b25346e06c4b69f6e1958852f2228f8b0e
6
+ metadata.gz: ab00a7b94993e50f3e5e8c4b7d7fa74c270a77778c4bd7c5796f84420fe1ffb0c929ec3efa4999d573ba38325e431cca20359e55dcfe68d4b73f67367f57a92d
7
+ data.tar.gz: 7a1f651e88f540389f8d213e381a37047c96370d3ff2bb1b4bd8a3655045ac9bb9cf749695ca8c774d2cca38050028e11468f181c883ff6b391abaf57f1d544a
data/CHANGELOG.md CHANGED
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.2] - 2025-11-05
9
+
10
+ ### Added
11
+ - Test mode feature to skip DNS checks during testing with block syntax support for configuration ([#4](https://github.com/tatematsu-k/email_domain_checker/pull/4))
12
+
13
+ ## [0.1.1] - 2025-11-04
14
+
15
+ ### Added
16
+ - ActiveModel/ActiveRecord validator integration (`DomainCheckValidator`) ([#3](https://github.com/tatematsu-k/email_domain_checker/pull/3))
17
+ - Automatic email normalization option in validator ([#3](https://github.com/tatematsu-k/email_domain_checker/pull/3))
18
+ - Ruby 3.4+ compatibility support for ActiveModel integration ([#3](https://github.com/tatematsu-k/email_domain_checker/pull/3))
19
+ - Comprehensive test coverage for ActiveModel validator ([#3](https://github.com/tatematsu-k/email_domain_checker/pull/3))
20
+
21
+ ### Changed
22
+ - CI: Added ActiveRecord version matrix testing for better compatibility testing ([#3](https://github.com/tatematsu-k/email_domain_checker/pull/3))
23
+
24
+ ### Documentation
25
+ - Added ActiveModel integration documentation with usage examples ([#3](https://github.com/tatematsu-k/email_domain_checker/pull/3))
26
+
8
27
  ## [0.1.0] - 2025-11-4
9
28
 
10
29
  ### Added
data/README.md CHANGED
@@ -39,6 +39,11 @@ EmailDomainChecker.normalize("User@Example.COM") # => "user@example.com"
39
39
 
40
40
  # Configure default options globally
41
41
  EmailDomainChecker.configure(timeout: 10, check_mx: true)
42
+
43
+ # Enable test mode (skips DNS checks)
44
+ EmailDomainChecker.configure do |config|
45
+ config.test_mode = true
46
+ end
42
47
  ```
43
48
 
44
49
  ### Using Checker class
@@ -73,6 +78,51 @@ checker = EmailDomainChecker::Checker.new("user@example.com")
73
78
  checker.redacted_email # => "{hash}@example.com"
74
79
  ```
75
80
 
81
+ ### ActiveModel/ActiveRecord Integration
82
+
83
+ When ActiveModel is available, you can use the `DomainCheckValidator` for easy validation and normalization in your models:
84
+
85
+ ```ruby
86
+ class User < ActiveRecord::Base
87
+ validates :email, domain_check: { check_mx: true, timeout: 3 }, normalize: true
88
+ end
89
+ ```
90
+
91
+ #### Options
92
+
93
+ - `domain_check`: Hash of options for domain validation
94
+ - `check_mx`: Check MX records (default: `true`)
95
+ - `check_a`: Check A records (default: `false`)
96
+ - `timeout`: DNS query timeout in seconds (default: `5`)
97
+ - `validate_format`: Validate email format (default: `true`)
98
+ - `validate_domain`: Validate domain (default: `true`)
99
+ - `normalize`: Normalize email before validation (default: `false`)
100
+ - `message`: Custom error message
101
+
102
+ #### Examples
103
+
104
+ ```ruby
105
+ # Basic validation with domain check
106
+ class User < ActiveRecord::Base
107
+ validates :email, domain_check: { check_mx: true, timeout: 3 }
108
+ end
109
+
110
+ # Format validation only (skip domain check)
111
+ class User < ActiveRecord::Base
112
+ validates :email, domain_check: { validate_domain: false }
113
+ end
114
+
115
+ # With automatic normalization
116
+ class User < ActiveRecord::Base
117
+ validates :email, domain_check: { check_mx: true }, normalize: true
118
+ end
119
+
120
+ # With custom error message
121
+ class User < ActiveRecord::Base
122
+ validates :email, domain_check: { check_mx: true }, message: "Invalid email address"
123
+ end
124
+ ```
125
+
76
126
  ### Configuration Options
77
127
 
78
128
  - `validate_format`: Validate email format using email_address gem (default: true)
@@ -80,6 +130,25 @@ checker.redacted_email # => "{hash}@example.com"
80
130
  - `check_mx`: Check MX records for domain (default: true)
81
131
  - `check_a`: Check A records for domain (default: false)
82
132
  - `timeout`: DNS lookup timeout in seconds (default: 5)
133
+ - `test_mode`: Skip DNS checks (useful for testing with dummy data) (default: false)
134
+
135
+ ### Test Mode
136
+
137
+ When writing tests, you may want to skip DNS checks to avoid external requests. Enable test mode to skip all DNS validations (MX and A record checks):
138
+
139
+ ```ruby
140
+ # In spec_helper.rb or test_helper.rb
141
+ EmailDomainChecker.configure do |config|
142
+ config.test_mode = true
143
+ end
144
+
145
+ # Or in a before block
146
+ before do
147
+ EmailDomainChecker::Config.test_mode = true
148
+ end
149
+ ```
150
+
151
+ When test mode is enabled, domain validation will always return `true` without making any DNS requests, allowing you to use dummy email addresses in your tests without external dependencies.
83
152
 
84
153
  ## Development
85
154
 
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model"
4
+
5
+ require_relative "checker"
6
+ require_relative "normalizer"
7
+
8
+ module EmailDomainChecker
9
+ # ActiveModel validator for email domain checking
10
+ #
11
+ # Usage:
12
+ # class User < ActiveRecord::Base
13
+ # validates :email, domain_check: { check_mx: true, timeout: 3 }, normalize: true
14
+ # end
15
+ #
16
+ # Options:
17
+ # - domain_check: Hash of options for domain validation
18
+ # * check_mx: Boolean (default: true) - Check MX records
19
+ # * check_a: Boolean (default: false) - Check A records
20
+ # * timeout: Integer (default: 5) - DNS query timeout in seconds
21
+ # * validate_format: Boolean (default: true) - Validate email format
22
+ # * validate_domain: Boolean (default: true) - Validate domain
23
+ # - normalize: Boolean (default: false) - Normalize email before validation
24
+ # - message: String or Symbol - Custom error message
25
+ class DomainCheckValidator < ActiveModel::EachValidator
26
+ def validate_each(record, attribute, value)
27
+ return if value.blank?
28
+
29
+ # ActiveModel passes domain_check hash contents directly to options
30
+ # when using validates :email, domain_check: { normalize: false }
31
+ # So options will be { validate_domain: false, normalize: false } etc.
32
+ normalize_option = options[:normalize] == true
33
+
34
+ original_value = value.is_a?(String) ? value : value.to_s
35
+ normalized_value = normalize_option ? Normalizer.normalize(original_value) : original_value
36
+
37
+ if normalize_option && normalized_value != original_value
38
+ record.public_send("#{attribute}=", normalized_value)
39
+ end
40
+
41
+ validation_options = build_validation_options(record, attribute)
42
+ value_for_checker = normalize_option ? normalized_value : original_value
43
+ checker = Checker.new(value_for_checker, validation_options)
44
+
45
+ unless checker.valid?
46
+ error_message = error_message_for(record, attribute, checker)
47
+ record.errors.add(attribute, error_message[:key], message: error_message[:message])
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def build_validation_options(record, attribute)
54
+ default_options = {
55
+ validate_format: true,
56
+ validate_domain: true,
57
+ check_mx: true,
58
+ check_a: false,
59
+ timeout: 5
60
+ }
61
+
62
+ # ActiveModel passes domain_check hash contents directly to options
63
+ # Exclude normalize and message from validation options
64
+ domain_check_options = options.reject { |k, _v| k == :normalize || k == :message }
65
+ default_options.merge(domain_check_options)
66
+ end
67
+
68
+ def error_message_for(record, attribute, checker)
69
+ message_option = options[:message]
70
+ if message_option
71
+ return {
72
+ key: :invalid,
73
+ message: message_option.is_a?(Symbol) ? record.errors.generate_message(attribute, message_option) : message_option
74
+ }
75
+ end
76
+
77
+ unless checker.format_valid?
78
+ return {
79
+ key: :invalid_format,
80
+ message: i18n_message("errors.messages.invalid_email_format", "is not a valid email format")
81
+ }
82
+ end
83
+
84
+ unless checker.domain_valid?
85
+ return {
86
+ key: :invalid_domain,
87
+ message: i18n_message("errors.messages.invalid_email_domain", "has an invalid domain")
88
+ }
89
+ end
90
+
91
+ {
92
+ key: :invalid,
93
+ message: i18n_message("errors.messages.invalid_email", "is not a valid email address")
94
+ }
95
+ end
96
+
97
+ def i18n_message(key, default)
98
+ return default unless defined?(I18n)
99
+
100
+ I18n.t(key, default: default)
101
+ rescue StandardError
102
+ default
103
+ end
104
+ end
105
+ end
106
+
107
+ DomainCheckValidator = EmailDomainChecker::DomainCheckValidator unless defined?(DomainCheckValidator)
@@ -11,15 +11,43 @@ module EmailDomainChecker
11
11
  }.freeze
12
12
 
13
13
  class << self
14
- attr_accessor :default_options
14
+ attr_accessor :default_options, :test_mode
15
15
 
16
- def configure(options = {})
17
- @default_options = DEFAULT_OPTIONS.merge(options)
16
+ def configure(options = {}, &block)
17
+ if block_given?
18
+ config_instance = new
19
+ block.call(config_instance)
20
+ @default_options = DEFAULT_OPTIONS.merge(options)
21
+ config_instance
22
+ else
23
+ @default_options = DEFAULT_OPTIONS.merge(options)
24
+ new
25
+ end
18
26
  end
19
27
 
20
28
  def reset
21
29
  @default_options = DEFAULT_OPTIONS.dup
30
+ @test_mode = false
22
31
  end
32
+
33
+ def test_mode=(value)
34
+ @test_mode = value
35
+ end
36
+
37
+ def test_mode?
38
+ @test_mode == true
39
+ end
40
+ end
41
+
42
+ attr_accessor :test_mode
43
+
44
+ def initialize
45
+ @test_mode = self.class.test_mode || false
46
+ end
47
+
48
+ def test_mode=(value)
49
+ @test_mode = value
50
+ self.class.test_mode = value
23
51
  end
24
52
 
25
53
  reset
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "dns_resolver"
4
+ require_relative "config"
4
5
 
5
6
  module EmailDomainChecker
6
7
  class DomainValidator
@@ -18,6 +19,9 @@ module EmailDomainChecker
18
19
  def valid?(domain)
19
20
  return false if domain.nil? || domain.empty?
20
21
 
22
+ # Skip DNS checks if test mode is enabled
23
+ return true if Config.test_mode?
24
+
21
25
  check_domain_records(domain)
22
26
  end
23
27
 
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EmailDomainChecker
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.2"
5
5
  end
6
-
@@ -8,6 +8,17 @@ require_relative "email_domain_checker/domain_validator"
8
8
  require_relative "email_domain_checker/email_address_adapter"
9
9
  require_relative "email_domain_checker/checker"
10
10
 
11
+ # Conditionally load ActiveModel integration if ActiveModel is available
12
+ begin
13
+ # Require logger gem for older Rails/ActiveModel versions on Ruby 3.4+
14
+ # This is needed because logger was removed from Ruby standard library in 3.4+
15
+ require "logger" if RUBY_VERSION >= "3.4"
16
+ require "active_model"
17
+ require_relative "email_domain_checker/active_model_validator"
18
+ rescue LoadError
19
+ # ActiveModel is not available, skip integration
20
+ end
21
+
11
22
  module EmailDomainChecker
12
23
  class Error < StandardError; end
13
24
 
@@ -32,7 +43,7 @@ module EmailDomainChecker
32
43
  end
33
44
 
34
45
  # Configure default options
35
- def self.configure(options = {})
36
- Config.configure(options)
46
+ def self.configure(options = {}, &block)
47
+ Config.configure(options, &block)
37
48
  end
38
49
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: email_domain_checker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Koki Tatematsu
@@ -79,6 +79,7 @@ files:
79
79
  - Rakefile
80
80
  - email_domain_checker.gemspec
81
81
  - lib/email_domain_checker.rb
82
+ - lib/email_domain_checker/active_model_validator.rb
82
83
  - lib/email_domain_checker/checker.rb
83
84
  - lib/email_domain_checker/config.rb
84
85
  - lib/email_domain_checker/dns_resolver.rb