domainic-type 0.1.0.alpha.3.0.2 → 0.1.0.alpha.3.1.0

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.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/lib/domainic/type/behavior/string_behavior/matching_behavior.rb +115 -0
  3. data/lib/domainic/type/behavior/string_behavior.rb +2 -111
  4. data/lib/domainic/type/behavior/uri_behavior.rb +97 -0
  5. data/lib/domainic/type/behavior.rb +21 -1
  6. data/lib/domainic/type/config/registry.yml +15 -0
  7. data/lib/domainic/type/definitions.rb +182 -1
  8. data/lib/domainic/type/types/core/array_type.rb +1 -1
  9. data/lib/domainic/type/types/core/float_type.rb +1 -1
  10. data/lib/domainic/type/types/core/hash_type.rb +1 -1
  11. data/lib/domainic/type/types/core/integer_type.rb +1 -1
  12. data/lib/domainic/type/types/core/string_type.rb +1 -1
  13. data/lib/domainic/type/types/core/symbol_type.rb +1 -1
  14. data/lib/domainic/type/types/identifier/cuid_type.rb +140 -0
  15. data/lib/domainic/type/types/identifier/uuid_type.rb +513 -0
  16. data/lib/domainic/type/types/network/email_address_type.rb +149 -0
  17. data/lib/domainic/type/types/network/hostname_type.rb +107 -0
  18. data/lib/domainic/type/types/network/uri_type.rb +224 -0
  19. data/sig/domainic/type/behavior/string_behavior/matching_behavior.rbs +84 -0
  20. data/sig/domainic/type/behavior/string_behavior.rbs +2 -82
  21. data/sig/domainic/type/behavior/uri_behavior.rbs +95 -0
  22. data/sig/domainic/type/behavior.rbs +9 -0
  23. data/sig/domainic/type/definitions.rbs +149 -1
  24. data/sig/domainic/type/types/identifier/cuid_type.rbs +110 -0
  25. data/sig/domainic/type/types/identifier/uuid_type.rbs +469 -0
  26. data/sig/domainic/type/types/network/email_address_type.rbs +127 -0
  27. data/sig/domainic/type/types/network/hostname_type.rbs +76 -0
  28. data/sig/domainic/type/types/network/uri_type.rbs +159 -0
  29. metadata +20 -6
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'domainic/type/behavior'
4
+ require 'domainic/type/behavior/string_behavior/matching_behavior'
5
+ require 'domainic/type/behavior/sizable_behavior'
6
+ require 'domainic/type/behavior/uri_behavior'
7
+
8
+ module Domainic
9
+ module Type
10
+ # A type for validating hostnames according to RFC 1034 and RFC 1123 standards
11
+ #
12
+ # This type provides comprehensive hostname validation, ensuring that values conform to
13
+ # DNS standards for hostnames. It supports constraints on the domain structure, such as
14
+ # top-level domain validation and hostname pattern matching.
15
+ #
16
+ # Key features:
17
+ # - RFC-compliant hostname validation
18
+ # - Maximum length enforcement (253 characters)
19
+ # - ASCII character set requirement
20
+ # - Validation of allowed or disallowed hostnames and TLDs
21
+ #
22
+ # @example Basic usage
23
+ # type = HostNameType.new
24
+ # type.validate("example.com") # => true
25
+ # type.validate("invalid_host") # => false
26
+ #
27
+ # @example With domain constraints
28
+ # type = HostNameType.new
29
+ # .having_top_level_domain("com", "org")
30
+ #
31
+ # @example With hostname inclusion/exclusion
32
+ # type = HostNameType.new
33
+ # .having_hostname("example.com")
34
+ # .not_having_hostname("forbidden.com")
35
+ #
36
+ # @author {https://aaronmallen.me Aaron Allen}
37
+ # @since 0.1.0
38
+ class HostnameType
39
+ # @rbs! extend Behavior::ClassMethods
40
+
41
+ include Behavior
42
+ include Behavior::StringBehavior::MatchingBehavior
43
+ include Behavior::SizableBehavior
44
+ include Behavior::URIBehavior
45
+
46
+ RFC_HOSTNAME_REGEXP = /\A
47
+ [a-zA-Z0-9] # Start with an alphanumeric character
48
+ (?: # Begin optional group for the rest of the segment
49
+ [a-zA-Z0-9-]{0,61} # Allow up to 61 characters (letters, digits, hyphens)
50
+ [a-zA-Z0-9] # End segment with an alphanumeric character
51
+ )? # The group is optional
52
+ (?: # Begin optional group for additional domain segments
53
+ \. # Segment must start with a dot
54
+ [a-zA-Z0-9] # Alphanumeric character at the start of the new segment
55
+ (?: # Optional group for segment body
56
+ [a-zA-Z0-9-]{0,61} # Allow up to 61 characters (letters, digits, hyphens)
57
+ [a-zA-Z0-9] # End segment with an alphanumeric character
58
+ )? # End optional segment body
59
+ )* # Allow zero or more additional segments
60
+ \z/x #: Regexp
61
+
62
+ # Core hostname constraints based on RFC standards
63
+ intrinsically_constrain :self, :type, String, description: :not_described
64
+ intrinsically_constrain :self, :match_pattern, RFC_HOSTNAME_REGEXP, description: :not_described
65
+ intrinsically_constrain :length, :range, { maximum: 253 }, description: :not_described, concerning: :size
66
+ intrinsically_constrain :self, :character_set, :ascii, description: :not_described
67
+
68
+ # Constrain hostname to allowed hostnames
69
+ #
70
+ # Creates a constraint ensuring the hostname matches one of the specified hostnames.
71
+ # This is useful for restricting to specific domains.
72
+ #
73
+ # @example
74
+ # type.having_hostname("example.com", "company.com")
75
+ # type.validate("example.com") # => true
76
+ # type.validate("other.com") # => false
77
+ #
78
+ # @param hostnames [Array<String>] List of allowed hostnames
79
+ # @return [self] self for method chaining
80
+ # @rbs (*String hostnames) -> self
81
+ def matching(*hostnames)
82
+ pattern = /\A(?:#{hostnames.map { |h| Regexp.escape(h) }.join('|')})\z/i
83
+ constrain :self, :match_pattern, pattern, concerning: :inclusion
84
+ end
85
+ alias allowing matching
86
+
87
+ # Constrain hostname to exclude specific hostnames
88
+ #
89
+ # Ensures the hostname does not match any of the specified hostnames. Useful for blacklisting.
90
+ #
91
+ # @example
92
+ # type.not_having_hostname("forbidden.com")
93
+ # type.validate("allowed.com") # => true
94
+ # type.validate("forbidden.com") # => false
95
+ #
96
+ # @param hostnames [Array<String>] List of forbidden hostnames
97
+ # @return [self] self for method chaining
98
+ # @rbs (*String hostnames) -> self
99
+ def not_matching(*hostnames)
100
+ pattern = /\A(?:#{hostnames.map { |h| Regexp.escape(h) }.join('|')})\z/i
101
+ hostname_pattern = @constraints.prepare :self, :match_pattern, pattern
102
+ constrain :self, :not, hostname_pattern, concerning: :hostname_exclusion
103
+ end
104
+ alias forbidding not_matching
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,224 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'domainic/type/behavior'
4
+ require 'domainic/type/behavior/string_behavior/matching_behavior'
5
+ require 'domainic/type/behavior/sizable_behavior'
6
+ require 'domainic/type/behavior/uri_behavior'
7
+ require 'domainic/type/types/network/hostname_type'
8
+ require 'uri'
9
+
10
+ module Domainic
11
+ module Type
12
+ # A type for validating URIs according to RFC 3986 standards
13
+ #
14
+ # This type provides comprehensive URI validation, ensuring that values conform to
15
+ # standard URI syntax. It supports constraints on components like the scheme, hostname,
16
+ # path, and query string.
17
+ #
18
+ # Key features:
19
+ # - RFC-compliant URI validation
20
+ # - Scheme, hostname, path, and query validation
21
+ # - Maximum length enforcement
22
+ # - ASCII character set requirement
23
+ #
24
+ # @example Basic usage
25
+ # type = UriType.new
26
+ # type.validate("https://example.com/path?query=1") # => true
27
+ # type.validate("not-a-uri") # => false
28
+ #
29
+ # @example With scheme constraints
30
+ # type = UriType.new
31
+ # .having_scheme("http", "https")
32
+ #
33
+ # @example With hostname and path constraints
34
+ # type = UriType.new
35
+ # .having_hostname("example.com")
36
+ # .matching_path(/^\/[a-z]+$/)
37
+ #
38
+ # @example With maximum length
39
+ # type = UriType.new
40
+ # .having_maximum_size(2000)
41
+ #
42
+ # @author {https://aaronmallen.me Aaron Allen}
43
+ # @since 0.1.0
44
+ class URIType
45
+ # @rbs! extend Behavior::ClassMethods
46
+
47
+ include Behavior
48
+ include Behavior::StringBehavior::MatchingBehavior
49
+ include Behavior::SizableBehavior
50
+ include Behavior::URIBehavior
51
+
52
+ URI_REGEXP = /\A#{URI::DEFAULT_PARSER.make_regexp}\z/ #: Regexp
53
+
54
+ # Core URI constraints based on RFC standards
55
+ intrinsically_constrain :self, :type, String, description: :not_described
56
+ intrinsically_constrain :self, :match_pattern, URI_REGEXP, description: :not_described
57
+ intrinsically_constrain :self, :character_set, :ascii, description: :not_described
58
+
59
+ # Constrain URI hostname to allowed values
60
+ #
61
+ # Uses `HostnameType` to validate the hostname part of the URI.
62
+ #
63
+ # @example
64
+ # type.having_hostname("example.com")
65
+ # type.validate("https://example.com") # => true
66
+ # type.validate("https://other.com") # => false
67
+ #
68
+ # @param hostnames [Array<String>] List of allowed hostnames
69
+ # @return [self] self for method chaining
70
+ # @rbs (*String hostnames) -> self
71
+ def having_hostname(*hostnames)
72
+ hostname_type = HostnameType.new(matching: hostnames)
73
+ constrain :self, :type, hostname_type,
74
+ coerce_with: lambda { |value|
75
+ begin
76
+ URI.parse(value).host
77
+ rescue StandardError
78
+ nil
79
+ end
80
+ }, concerning: :hostname_inclusion
81
+ end
82
+ alias allowing_hostname having_hostname
83
+ alias host having_hostname
84
+ alias hostname having_hostname
85
+ alias with_hostname having_hostname
86
+
87
+ # Constrain URI path to match any of the specified patterns
88
+ #
89
+ # Ensures that the path part of the URI matches at least one of the given patterns.
90
+ #
91
+ # @example
92
+ # type.having_path(/^\/[a-z]+$/, /^\/api\/v\d+/)
93
+ # type.validate("https://example.com/path") # => true
94
+ # type.validate("https://example.com/api/v1") # => true
95
+ # type.validate("https://example.com/123") # => false
96
+ #
97
+ # @param patterns [Array<Regexp>] Patterns the path must match
98
+ # @return [self] self for method chaining
99
+ # @rbs (*String | Regexp patterns) -> self
100
+ def having_path(*patterns)
101
+ patterns = patterns.map { |p| p.is_a?(String) ? Regexp.new(p) : p }
102
+ combined_pattern = Regexp.union(patterns)
103
+ constrain :self, :match_pattern, combined_pattern,
104
+ coerce_with: lambda { |value|
105
+ begin
106
+ URI.parse(value).path
107
+ rescue StandardError
108
+ nil
109
+ end
110
+ }, concerning: :path_inclusion
111
+ end
112
+ alias allowing_path having_path
113
+ alias with_path having_path
114
+
115
+ # Constrain URI scheme to specific values
116
+ #
117
+ # Ensures that the scheme part of the URI matches one of the specified schemes.
118
+ #
119
+ # @example
120
+ # type.having_scheme("http", "https")
121
+ # type.validate("https://example.com") # => true
122
+ # type.validate("ftp://example.com") # => false
123
+ #
124
+ # @param schemes [Array<String>] List of allowed schemes
125
+ # @return [self] self for method chaining
126
+ # @rbs (*String schemes) -> self
127
+ def having_scheme(*schemes)
128
+ included_schemes = schemes.map { |scheme| @constraints.prepare :self, :inclusion, scheme.downcase }
129
+ constrain :self, :or, included_schemes,
130
+ coerce_with: lambda { |value|
131
+ begin
132
+ URI.parse(value).scheme&.downcase
133
+ rescue URI::InvalidURIError
134
+ nil
135
+ end
136
+ }, concerning: :scheme_inclusion
137
+ end
138
+ alias allowing_scheme having_scheme
139
+ alias scheme having_scheme
140
+
141
+ # Constrain URI hostname to exclude specific values
142
+ #
143
+ # Uses `HostnameType` to blacklist hostnames.
144
+ #
145
+ # @example
146
+ # type.not_having_hostname("forbidden.com")
147
+ # type.validate("https://allowed.com") # => true
148
+ # type.validate("https://forbidden.com") # => false
149
+ #
150
+ # @param hostnames [Array<String>] List of forbidden hostnames
151
+ # @return [self] self for method chaining
152
+ # @rbs (*String hostnames) -> self
153
+ def not_having_hostname(*hostnames)
154
+ hostname_type = HostnameType.new(not_matching: hostnames)
155
+ constrain :self, :type, hostname_type,
156
+ coerce_with: lambda { |value|
157
+ begin
158
+ URI.parse(value).host
159
+ rescue StandardError
160
+ nil
161
+ end
162
+ }, concerning: :hostname_exclusion
163
+ end
164
+ alias forbidding_hostname not_having_hostname
165
+ alias not_host not_having_hostname
166
+ alias not_hostname not_having_hostname
167
+
168
+ # Constrain URI path to exclude any of the specified patterns
169
+ #
170
+ # Ensures that the path part of the URI does not match any of the given patterns.
171
+ #
172
+ # @example
173
+ # type.not_having_path(/^\/admin/, /^\/private/)
174
+ # type.validate("https://example.com/user") # => true
175
+ # type.validate("https://example.com/admin") # => false
176
+ # type.validate("https://example.com/private") # => false
177
+ #
178
+ # @param patterns [Array<Regexp>] Patterns the path must not match
179
+ # @return [self] self for method chaining
180
+ # @rbs (*String | Regexp patterns) -> self
181
+ def not_having_path(*patterns)
182
+ patterns = patterns.map { |p| p.is_a?(String) ? Regexp.new(p) : p }
183
+ combined_pattern = Regexp.union(patterns)
184
+ path_pattern = @constraints.prepare :self, :match_pattern, combined_pattern,
185
+ coerce_with: lambda { |value|
186
+ begin
187
+ URI.parse(value).path
188
+ rescue StandardError
189
+ nil
190
+ end
191
+ }
192
+ constrain :self, :not, path_pattern, concerning: :path_exclusion
193
+ end
194
+ alias forbidding_path not_having_path
195
+ alias not_allowing_path not_having_path
196
+
197
+ # Constrain URI scheme to exclude specific values
198
+ #
199
+ # Ensures that the scheme part of the URI does not match the specified schemes.
200
+ #
201
+ # @example
202
+ # type.not_having_scheme("ftp")
203
+ # type.validate("https://example.com") # => true
204
+ # type.validate("ftp://example.com") # => false
205
+ #
206
+ # @param schemes [Array<String>] List of forbidden schemes
207
+ # @return [self] self for method chaining
208
+ # @rbs (*String schemes) -> self
209
+ def not_having_scheme(*schemes)
210
+ included_schemes = schemes.map { |scheme| @constraints.prepare :self, :inclusion, scheme.downcase }
211
+ constrain :self, :nor, included_schemes,
212
+ coerce_with: lambda { |value|
213
+ begin
214
+ URI.parse(value).scheme&.downcase
215
+ rescue URI::InvalidURIError
216
+ nil
217
+ end
218
+ }, concerning: :scheme_inclusion
219
+ end
220
+ alias forbidding_scheme not_having_scheme
221
+ alias not_allowing_scheme not_having_scheme
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,84 @@
1
+ module Domainic
2
+ module Type
3
+ module Behavior
4
+ module StringBehavior
5
+ # A module providing string matching constraint methods
6
+ #
7
+ # This module extends types with methods for constraining string values based on
8
+ # equality, pattern matching, and substring inclusion/exclusion. It provides a
9
+ # fluent interface for building complex string matching constraints.
10
+ #
11
+ # @example Basic equality constraints
12
+ # type = StringType.new
13
+ # .being_equal_to("expected")
14
+ # .not_being_equal_to("forbidden")
15
+ #
16
+ # @example Pattern matching constraints
17
+ # type = StringType.new
18
+ # .matching(/^\w+$/, /[0-9]/)
19
+ # .not_matching(/admin/i)
20
+ #
21
+ # @example Substring constraints
22
+ # type = StringType.new
23
+ # .containing("allowed", "required")
24
+ # .excluding("forbidden", "blocked")
25
+ #
26
+ # @author {https://aaronmallen.me Aaron Allen}
27
+ # @since 0.1.0
28
+ module MatchingBehavior
29
+ # Constrain string to equal a specific value
30
+ #
31
+ # @param literal [String, Symbol] the value to match against
32
+ # @return [Behavior] self for method chaining
33
+ def being_equal_to: (String | Symbol literal) -> Behavior
34
+
35
+ alias eql being_equal_to
36
+
37
+ alias equal_to being_equal_to
38
+
39
+ alias equaling being_equal_to
40
+
41
+ # Constrain string to contain specific substrings
42
+ #
43
+ # @param literals [Array<String, Symbol>] the required substrings
44
+ # @return [Behavior] self for method chaining
45
+ def containing: (*String | Symbol literals) -> Behavior
46
+
47
+ alias including containing
48
+
49
+ # Constrain string to exclude specific substrings
50
+ #
51
+ # @param literals [Array<String, Symbol>] the forbidden substrings
52
+ # @return [Behavior] self for method chaining
53
+ def excluding: (*String | Symbol literals) -> Behavior
54
+
55
+ alias omitting excluding
56
+
57
+ # Constrain string to match specific patterns
58
+ #
59
+ # @param patterns [Array<String, Regexp>] the required patterns
60
+ # @return [Behavior] self for method chaining
61
+ def matching: (*String | Regexp patterns) -> Behavior
62
+
63
+ # Constrain string to not equal a specific value
64
+ #
65
+ # @param literal [String, Symbol] the forbidden value
66
+ # @return [Behavior] self for method chaining
67
+ def not_being_equal_to: (String | Symbol literal) -> Behavior
68
+
69
+ alias not_eql not_being_equal_to
70
+
71
+ alias not_equal_to not_being_equal_to
72
+
73
+ alias not_equaling not_being_equal_to
74
+
75
+ # Constrain string to not match specific patterns
76
+ #
77
+ # @param patterns [Array<String, Regexp>] the forbidden patterns
78
+ # @return [Behavior] self for method chaining
79
+ def not_matching: (*String | Regexp patterns) -> Behavior
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -50,6 +50,8 @@ module Domainic
50
50
  # @author {https://aaronmallen.me Aaron Allen}
51
51
  # @since 0.1.0
52
52
  module StringBehavior
53
+ include MatchingBehavior
54
+
53
55
  include SizableBehavior
54
56
 
55
57
  # Validate string contains only alphanumeric characters.
@@ -90,23 +92,6 @@ module Domainic
90
92
 
91
93
  alias empty being_empty
92
94
 
93
- # Validate string equals a specific value.
94
- #
95
- # @example
96
- # type.being_equal_to("hello")
97
- # type.validate("hello") # => true
98
- # type.validate("world") # => false
99
- #
100
- # @param literal [String, Symbol] the value to compare against
101
- # @return [Behavior] self for method chaining
102
- def being_equal_to: (String | Symbol literal) -> Behavior
103
-
104
- alias eql being_equal_to
105
-
106
- alias equal_to being_equal_to
107
-
108
- alias equaling being_equal_to
109
-
110
95
  # Validate string is lowercase.
111
96
  #
112
97
  # @example
@@ -235,43 +220,6 @@ module Domainic
235
220
 
236
221
  alias uppercase being_uppercase
237
222
 
238
- # Validate string contains all specified substrings.
239
- #
240
- # @example
241
- # type.containing("hello", "world")
242
- # type.validate("hello world") # => true
243
- # type.validate("hello") # => false
244
- #
245
- # @param literals [Array<String, Symbol>] the substrings to look for
246
- # @return [Behavior] self for method chaining
247
- def containing: (*String | Symbol literals) -> Behavior
248
-
249
- alias including containing
250
-
251
- # Validate string does not contain any specified substrings.
252
- #
253
- # @example
254
- # type.excluding("foo", "bar")
255
- # type.validate("hello world") # => true
256
- # type.validate("foo bar") # => false
257
- #
258
- # @param literals [Array<String, Symbol>] the substrings to exclude
259
- # @return [Behavior] self for method chaining
260
- def excluding: (*String | Symbol literals) -> Behavior
261
-
262
- alias omitting excluding
263
-
264
- # Validate string matches all specified patterns.
265
- #
266
- # @example
267
- # type.matching(/^\w+$/, /\d/)
268
- # type.validate("hello123") # => true
269
- # type.validate("hello") # => false
270
- #
271
- # @param patterns [Array<String, Regexp>] the patterns to match against
272
- # @return [Behavior] self for method chaining
273
- def matching: (*String | Regexp patterns) -> Behavior
274
-
275
223
  # Validate string is not empty.
276
224
  #
277
225
  # @example
@@ -281,34 +229,6 @@ module Domainic
281
229
  #
282
230
  # @return [Behavior] self for method chaining
283
231
  def not_being_empty: () -> Behavior
284
-
285
- # Validate string does not equal a specific value.
286
- #
287
- # @example
288
- # type.not_being_equal_to("admin")
289
- # type.validate("user") # => true
290
- # type.validate("admin") # => false
291
- #
292
- # @param literal [String, Symbol] the value to compare against
293
- # @return [Behavior] self for method chaining
294
- def not_being_equal_to: (String | Symbol literal) -> Behavior
295
-
296
- alias not_eql not_being_equal_to
297
-
298
- alias not_equal_to not_being_equal_to
299
-
300
- alias not_equaling not_being_equal_to
301
-
302
- # Validate string does not match any specified patterns.
303
- #
304
- # @example
305
- # type.not_matching(/\d/, /[A-Z]/)
306
- # type.validate("hello") # => true
307
- # type.validate("Hello123") # => false
308
- #
309
- # @param patterns [Array<String, Regexp>] the patterns to avoid matching
310
- # @return [Behavior] self for method chaining
311
- def not_matching: (*String | Regexp patterns) -> Behavior
312
232
  end
313
233
  end
314
234
  end
@@ -0,0 +1,95 @@
1
+ module Domainic
2
+ module Type
3
+ module Behavior
4
+ # A module providing URI-based validation behaviors for types.
5
+ #
6
+ # This module extends the base Type::Behavior with methods designed for validating URI-related constraints.
7
+ # It focuses on ensuring URIs conform to specific rules, such as restricting or excluding particular
8
+ # top-level domains (TLDs). These features are useful for scenarios like domain-specific validations
9
+ # or blocking specific domain extensions.
10
+ #
11
+ # Key features:
12
+ # - Top-level domain inclusion constraints
13
+ # - Top-level domain exclusion constraints
14
+ # - Flexible and customizable pattern matching
15
+ # - Consistent interface for defining URI-related constraints
16
+ #
17
+ # @example Basic usage
18
+ # class MyType
19
+ # include Domainic::Type::Behavior::URIBehavior
20
+ #
21
+ # def initialize
22
+ # super
23
+ # having_top_level_domain("com", "org") # Allow only .com and .org
24
+ # not_having_top_level_domain("test", "dev") # Exclude .test and .dev
25
+ # end
26
+ # end
27
+ #
28
+ # @example Method aliases
29
+ # type.having_top_level_domain("com") # Allow only .com
30
+ # type.tld("com") # Same as above
31
+ # type.with_tld("com") # Same as above
32
+ #
33
+ # type.not_having_top_level_domain("test") # Exclude .test
34
+ # type.not_tld("test") # Same as above
35
+ # type.not_top_level_domain("test") # Same as above
36
+ #
37
+ # This module provides a consistent interface for working with URIs, ensuring flexibility and
38
+ # extensibility for domain and TLD validation tasks.
39
+ #
40
+ # @author {https://aaronmallen.me Aaron Allen}
41
+ # @since 0.1.0
42
+ module URIBehavior
43
+ # Constrain URIBehavior to allowed top-level domains
44
+ #
45
+ # Creates a constraint ensuring the URIBehavior uses one of the specified top-level
46
+ # domains (TLDs). This allows restricting emails to specific TLDs like .com or .org.
47
+ #
48
+ # @example
49
+ # type.having_top_level_domain("com", "org")
50
+ # type.validate("example.com") # => true
51
+ # type.validate("example.net") # => false
52
+ #
53
+ # @param top_level_domains [Array<String>] List of allowed TLDs
54
+ # @return [self] self for method chaining
55
+ def having_top_level_domain: (*String top_level_domains) -> Behavior
56
+
57
+ alias allowing_tld having_top_level_domain
58
+
59
+ alias allowing_top_level_domain having_top_level_domain
60
+
61
+ alias having_tld having_top_level_domain
62
+
63
+ alias tld having_top_level_domain
64
+
65
+ alias with_tld having_top_level_domain
66
+
67
+ alias with_top_level_domain having_top_level_domain
68
+
69
+ # Constrain URIBehavior to exclude specific top-level domains
70
+ #
71
+ # Creates a constraint ensuring the email does not use any of the specified
72
+ # top-level domains (TLDs). Useful for blocking certain TLDs.
73
+ #
74
+ # @example
75
+ # type.not_having_top_level_domain("test")
76
+ # type.validate("example.com") # => true
77
+ # type.validate("example.test") # => false
78
+ #
79
+ # @param top_level_domains [Array<String>] List of forbidden TLDs
80
+ # @return [self] self for method chaining
81
+ def not_having_top_level_domain: (*String top_level_domains) -> Behavior
82
+
83
+ alias forbidding_tld not_having_top_level_domain
84
+
85
+ alias forbidding_top_level_domain not_having_top_level_domain
86
+
87
+ alias not_having_tld not_having_top_level_domain
88
+
89
+ alias not_tld not_having_top_level_domain
90
+
91
+ alias not_top_level_domain not_having_top_level_domain
92
+ end
93
+ end
94
+ end
95
+ end
@@ -87,6 +87,8 @@ module Domainic
87
87
  #
88
88
  # @see Constraint::Set#add
89
89
  #
90
+ # @deprecated Use {#intrinsically_constrain} instead
91
+ #
90
92
  # @return [void]
91
93
  def intrinsic: (Type::accessor accessor, String | Symbol constraint_type, ?untyped expectation, **untyped options) -> void
92
94
 
@@ -95,6 +97,13 @@ module Domainic
95
97
  # @return [Constraint::Set] The constraint set
96
98
  def intrinsic_constraints: () -> Constraint::Set
97
99
 
100
+ # Add an intrinsic constraint to this type.
101
+ #
102
+ # @see Constraint::Set#add
103
+ #
104
+ # @return [void]
105
+ def intrinsically_constrain: (Type::accessor accessor, String | Symbol constraint_type, ?untyped expectation, **untyped options) -> void
106
+
98
107
  # Delegate unknown methods to a new instance.
99
108
  #
100
109
  # @return [Object] The result of calling the method on a new instance