domainic-type 0.1.0.alpha.3.2.0 → 0.1.0.alpha.3.4.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.
- checksums.yaml +4 -4
- data/.yardopts +11 -0
- data/README.md +66 -10
- data/docs/USAGE.md +787 -0
- data/lib/domainic/type/accessors.rb +3 -2
- data/lib/domainic/type/behavior/date_time_behavior.rb +121 -37
- data/lib/domainic/type/behavior.rb +16 -0
- data/lib/domainic/type/config/registry.yml +24 -0
- data/lib/domainic/type/constraint/constraints/nor_constraint.rb +1 -1
- data/lib/domainic/type/constraint/constraints/predicate_constraint.rb +76 -0
- data/lib/domainic/type/definitions.rb +212 -0
- data/lib/domainic/type/types/core/complex_type.rb +122 -0
- data/lib/domainic/type/types/core/range_type.rb +47 -0
- data/lib/domainic/type/types/core/rational_type.rb +38 -0
- data/lib/domainic/type/types/core_extended/big_decimal_type.rb +34 -0
- data/lib/domainic/type/types/core_extended/set_type.rb +34 -0
- data/lib/domainic/type/types/datetime/date_time_string_type.rb +156 -0
- data/lib/domainic/type/types/datetime/timestamp_type.rb +50 -0
- data/sig/domainic/type/accessors.rbs +2 -2
- data/sig/domainic/type/behavior/date_time_behavior.rbs +35 -23
- data/sig/domainic/type/behavior.rbs +9 -0
- data/sig/domainic/type/constraint/constraints/predicate_constraint.rbs +56 -0
- data/sig/domainic/type/definitions.rbs +165 -0
- data/sig/domainic/type/types/core/complex_type.rbs +96 -0
- data/sig/domainic/type/types/core/range_type.rbs +41 -0
- data/sig/domainic/type/types/core/rational_type.rbs +32 -0
- data/sig/domainic/type/types/core_extended/big_decimal_type.rbs +27 -0
- data/sig/domainic/type/types/core_extended/set_type.rbs +27 -0
- data/sig/domainic/type/types/datetime/date_time_string_type.rbs +124 -0
- data/sig/domainic/type/types/datetime/timestamp_type.rbs +44 -0
- metadata +25 -6
data/docs/USAGE.md
ADDED
@@ -0,0 +1,787 @@
|
|
1
|
+
# Domainic::Type Usage Guide
|
2
|
+
|
3
|
+
A comprehensive guide to all features and capabilities of Domainic::Type. See the [README.md](../README.md) for a quick
|
4
|
+
introduction and installation instructions.
|
5
|
+
|
6
|
+
## Table of Contents
|
7
|
+
|
8
|
+
* [About](#about)
|
9
|
+
* [Core Concepts](#core-concepts)
|
10
|
+
* [Basic Type Validation](#basic-type-validation)
|
11
|
+
* [Type Constraints](#type-constraints)
|
12
|
+
* [Custom Constraints](#custom-constraints)
|
13
|
+
* [Error Messages](#error-messages)
|
14
|
+
* [Built-in Types](#built-in-types)
|
15
|
+
* [Simple Types](#simple-types)
|
16
|
+
* [Collection Types](#collection-types)
|
17
|
+
* [Date and Time Types](#date-and-time-types)
|
18
|
+
* [Network Types](#network-types)
|
19
|
+
* [Identifier Types](#identifier-types)
|
20
|
+
* [Specification Types](#specification-types)
|
21
|
+
* [Advanced Usage](#advanced-usage)
|
22
|
+
* [Custom Types](#custom-types)
|
23
|
+
* [Type Composition](#type-composition)
|
24
|
+
* [Integration Patterns](#integration-patterns)
|
25
|
+
|
26
|
+
## About
|
27
|
+
|
28
|
+
Stop wrestling with complex type validations and unclear error messages. Domainic::Type brings type validation to Ruby
|
29
|
+
that is both powerful and delightful to use. Build composable type constraints with crystal-clear error messages that
|
30
|
+
actually tell you what went wrong. From simple type checks to complex collection validations, make your types work for
|
31
|
+
you, not against you!
|
32
|
+
|
33
|
+
Key features include:
|
34
|
+
|
35
|
+
* Rich set of built-in types with purpose-specific validation:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
_String.being_uppercase.matching(/^[A-Z]+$/) # String validation
|
39
|
+
_Integer.being_positive.being_even # Number validation
|
40
|
+
_Array.of(_String).being_distinct # Collection validation
|
41
|
+
_EmailAddress.having_hostname("example.com") # Email validation
|
42
|
+
_UUID.being_version(4).being_standard # ID validation
|
43
|
+
```
|
44
|
+
|
45
|
+
* Composable type constraints for precise validation rules:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
username = _String
|
49
|
+
.being_lowercase # Must be lowercase
|
50
|
+
.being_alphanumeric # Letters and numbers only
|
51
|
+
.having_size_between(3, 20) # Length constraint
|
52
|
+
```
|
53
|
+
|
54
|
+
* Clear, actionable error messages that explain what failed:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
username.validate!("BAD!")
|
58
|
+
# => TypeError: Expected String(being lowercase, having size between 3 and 20),
|
59
|
+
# got String(not lowercase, having size 4)
|
60
|
+
```
|
61
|
+
|
62
|
+
* Custom type definitions for reusable validation:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
Username = _String.being_lowercase.having_size_between(3, 20)
|
66
|
+
EmailAddress = _String.matching(URI::MailTo::EMAIL_REGEXP)
|
67
|
+
```
|
68
|
+
|
69
|
+
* Full integration with Ruby's pattern matching:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
case value
|
73
|
+
when _String.being_uppercase then puts "Uppercase string"
|
74
|
+
when _Integer.being_positive then puts "Positive number"
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
* Support for optional values and complex type combinations:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
_Nilable(_String) # String or nil (also as _String?)
|
82
|
+
_Union(_String, _Integer) # String or Integer
|
83
|
+
```
|
84
|
+
|
85
|
+
## Core Concepts
|
86
|
+
|
87
|
+
### Basic Type Validation
|
88
|
+
|
89
|
+
Domainic::Type provides two main validation methods:
|
90
|
+
|
91
|
+
* `validate` - Returns true/false without raising errors
|
92
|
+
* `validate!` - Raises TypeError for invalid values
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
require 'domainic/type/definitions'
|
96
|
+
include Domainic::Type::Definitions
|
97
|
+
|
98
|
+
string_type = _String
|
99
|
+
string_type.validate("hello") # => true
|
100
|
+
string_type.validate(123) # => false
|
101
|
+
string_type.validate!("hello") # => true
|
102
|
+
string_type.validate!(123) # => TypeError: Expected String, but got Integer
|
103
|
+
```
|
104
|
+
|
105
|
+
All types also support the case equality operator (===):
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
case value
|
109
|
+
when _String then puts "It's a string!"
|
110
|
+
when _Integer then puts "It's an integer!"
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
### Type Constraints
|
115
|
+
|
116
|
+
Types can be constrained with additional validation rules. Constraints are chainable and combine logically. When
|
117
|
+
multiple constraints are applied, they form a single compound constraint that must be fully satisfied:
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
# String with multiple constraints
|
121
|
+
username = _String
|
122
|
+
.being_lowercase # Must be lowercase
|
123
|
+
.being_alphanumeric # Only letters and numbers
|
124
|
+
.having_size_between(3, 20) # Length between 3-20 chars
|
125
|
+
.not_matching(/^admin/i) # Can't start with 'admin'
|
126
|
+
|
127
|
+
# Array with element and size constraints
|
128
|
+
numbers = _Array
|
129
|
+
.of(_Integer) # Elements must be integers
|
130
|
+
.being_ordered # Must be sorted
|
131
|
+
.having_minimum_size(1) # At least one element
|
132
|
+
.containing(42) # Must include 42
|
133
|
+
```
|
134
|
+
|
135
|
+
### Custom Constraints
|
136
|
+
|
137
|
+
All types in Domainic::Type support adding custom constraints through the `satisfies` method. This is useful when you
|
138
|
+
need validation logic that goes beyond what the built-in constraints provide:
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
# Basic value range validation that can't be expressed with built-in constraints
|
142
|
+
kelvin = _Float.satisfies(
|
143
|
+
->(value) { value >= 0.0 },
|
144
|
+
description: 'being a valid Kelvin temperature',
|
145
|
+
violation_description: 'temperature below absolute zero'
|
146
|
+
)
|
147
|
+
|
148
|
+
kelvin.validate!(0.0) # => true
|
149
|
+
kelvin.validate!(-1.0)
|
150
|
+
# => TypeError: Expected Float(being a valid Kelvin temperature), got Float(temperature below absolute zero)
|
151
|
+
|
152
|
+
# Validating relationships between values in complex objects
|
153
|
+
booking = _Hash
|
154
|
+
.of(_Symbol => _DateTime)
|
155
|
+
.containing_keys(:check_in, :check_out)
|
156
|
+
.satisfies(
|
157
|
+
->(dates) { dates[:check_out] > dates[:check_in] },
|
158
|
+
description: 'having valid stay duration',
|
159
|
+
violation_description: 'check-out not after check-in'
|
160
|
+
)
|
161
|
+
|
162
|
+
# Access different parts of objects with accessors
|
163
|
+
balanced_ledger = _Array
|
164
|
+
.of(_Hash)
|
165
|
+
.satisfies(
|
166
|
+
->(entries) { entries.sum { |e| e[:amount] }.zero? },
|
167
|
+
accessor: :entries,
|
168
|
+
description: 'having balanced transactions',
|
169
|
+
violation_description: 'transactions do not sum to zero'
|
170
|
+
)
|
171
|
+
```
|
172
|
+
|
173
|
+
The `satisfies` method accepts:
|
174
|
+
|
175
|
+
* A predicate function that returns true/false for validating values
|
176
|
+
* Optional description that explains what makes a value valid
|
177
|
+
* Optional violation_description that explains why validation failed
|
178
|
+
* Optional accessor to validate specific parts of complex objects
|
179
|
+
|
180
|
+
### Error Messages
|
181
|
+
|
182
|
+
Error messages clearly indicate what validation failed and why. For compound constraints, the error message shows all
|
183
|
+
failing constraints to help pinpoint the exact issues:
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
# Type mismatch
|
187
|
+
_String.validate!(123)
|
188
|
+
# => TypeError: Expected String, but got Integer
|
189
|
+
|
190
|
+
# Constraint violation
|
191
|
+
_String.being_uppercase.validate!("hello")
|
192
|
+
# => TypeError: Expected String(being upper case), but got String(not upper case)
|
193
|
+
|
194
|
+
# Multiple constraints
|
195
|
+
_Integer.being_positive.being_even.validate!(-2)
|
196
|
+
# => TypeError: Expected Integer(being positive), got Integer(negative)
|
197
|
+
|
198
|
+
# Complex validation
|
199
|
+
_Array.of(_String.being_uppercase).validate!(["Hello", "WORLD"])
|
200
|
+
# => TypeError: Expected Array of String(being upper case), got Array containing String(not upper case)
|
201
|
+
```
|
202
|
+
|
203
|
+
## Built-in Types
|
204
|
+
|
205
|
+
### Simple Types
|
206
|
+
|
207
|
+
#### _String
|
208
|
+
|
209
|
+
String validation with comprehensive text manipulation constraints:
|
210
|
+
|
211
|
+
```ruby
|
212
|
+
_String # Basic string validation
|
213
|
+
.being_ascii # ASCII characters only
|
214
|
+
.being_alphanumeric # Letters and numbers only
|
215
|
+
.being_lowercase # Must be lowercase
|
216
|
+
.being_uppercase # Must be uppercase
|
217
|
+
.being_titlecase # Title Case Format
|
218
|
+
.being_empty # Must be empty
|
219
|
+
.being_printable # Printable characters only
|
220
|
+
.containing("text") # Must contain substring
|
221
|
+
.excluding("bad") # Must not contain substring
|
222
|
+
.matching(/pattern/) # Must match pattern
|
223
|
+
.not_matching(/admin/) # Must not match pattern
|
224
|
+
.having_size(10) # Exact length
|
225
|
+
.having_size_between(1, 100) # Length range
|
226
|
+
```
|
227
|
+
|
228
|
+
Also available as `_Text` and in nilable variants `_String?` and `_Text?`.
|
229
|
+
|
230
|
+
#### _Symbol
|
231
|
+
|
232
|
+
Symbol validation with all string constraints applied to the symbol name:
|
233
|
+
|
234
|
+
```ruby
|
235
|
+
_Symbol # Basic symbol validation
|
236
|
+
.being_ascii # ASCII characters only
|
237
|
+
.being_alphanumeric # Letters and numbers only
|
238
|
+
.being_lowercase # Must be lowercase
|
239
|
+
.having_size_between(1, 100) # Name length range
|
240
|
+
.matching(/^[a-z_][a-z0-9_]*$/) # Pattern matching
|
241
|
+
```
|
242
|
+
|
243
|
+
Also available as `_Interned` and in nilable variants `_Symbol?` and `_Interned?`.
|
244
|
+
|
245
|
+
#### _Integer
|
246
|
+
|
247
|
+
Integer validation with numeric constraints:
|
248
|
+
|
249
|
+
```ruby
|
250
|
+
_Integer # Basic integer validation
|
251
|
+
.being_positive # Must be positive
|
252
|
+
.being_negative # Must be negative
|
253
|
+
.being_even # Must be even
|
254
|
+
.being_odd # Must be odd
|
255
|
+
.being_zero # Must be zero
|
256
|
+
.being_divisible_by(3) # Must be divisible by 3
|
257
|
+
.being_greater_than(0) # Must be > 0
|
258
|
+
.being_less_than(100) # Must be < 100
|
259
|
+
.being_between(1, 10) # Must be between 1 and 10
|
260
|
+
```
|
261
|
+
|
262
|
+
Also available as `_Int`, `_Number` and in nilable variants `_Integer?`, `_Int?`, `_Number?`.
|
263
|
+
|
264
|
+
#### _Float
|
265
|
+
|
266
|
+
Float validation with numeric constraints:
|
267
|
+
|
268
|
+
```ruby
|
269
|
+
_Float # Basic float validation
|
270
|
+
.being_positive # Must be positive
|
271
|
+
.being_negative # Must be negative
|
272
|
+
.being_finite # Must be finite
|
273
|
+
.being_infinite # Must be infinite
|
274
|
+
.being_divisible_by(0.5) # Must be divisible by 0.5
|
275
|
+
.being_greater_than(0.0) # Must be > 0.0
|
276
|
+
.being_less_than(1.0) # Must be < 1.0
|
277
|
+
.being_between(0.0, 1.0) # Must be between 0.0 and 1.0
|
278
|
+
```
|
279
|
+
|
280
|
+
Also available as `_Decimal`, `_Real` and in nilable variants `_Float?`, `_Decimal?`, `_Real?`.
|
281
|
+
|
282
|
+
#### _Boolean
|
283
|
+
|
284
|
+
Boolean validation accepting only true or false:
|
285
|
+
|
286
|
+
```ruby
|
287
|
+
_Boolean === true # => true
|
288
|
+
_Boolean === false # => true
|
289
|
+
_Boolean === 1 # => false
|
290
|
+
_Boolean === 'true' # => false
|
291
|
+
```
|
292
|
+
|
293
|
+
Also available as `_Bool` and in nilable variants `_Boolean?`, `_Bool?`.
|
294
|
+
|
295
|
+
#### _BigDecimal
|
296
|
+
|
297
|
+
Validation for arbitrary-precision decimal numbers:
|
298
|
+
|
299
|
+
```ruby
|
300
|
+
_BigDecimal # Basic BigDecimal validation
|
301
|
+
.being_positive # Must be positive
|
302
|
+
.being_negative # Must be negative
|
303
|
+
.being_finite # Must be finite
|
304
|
+
.being_infinite # Must be infinite
|
305
|
+
.being_divisible_by( # Must be divisible by 0.5
|
306
|
+
BigDecimal('0.5')
|
307
|
+
)
|
308
|
+
.being_greater_than( # Must be > 0
|
309
|
+
BigDecimal('0')
|
310
|
+
)
|
311
|
+
.being_less_than( # Must be < 1
|
312
|
+
BigDecimal('1')
|
313
|
+
)
|
314
|
+
```
|
315
|
+
|
316
|
+
Also available in nilable variant `_BigDecimal?`.
|
317
|
+
|
318
|
+
#### _Complex
|
319
|
+
|
320
|
+
Complex number validation:
|
321
|
+
|
322
|
+
```ruby
|
323
|
+
_Complex # Basic Complex validation
|
324
|
+
.being_divisible_by(2) # Real part divisible by 2
|
325
|
+
.being_positive # Real part must be positive
|
326
|
+
.being_negative # Real part must be negative
|
327
|
+
.being_even # Real part must be even
|
328
|
+
.being_odd # Real part must be odd
|
329
|
+
```
|
330
|
+
|
331
|
+
Also available in nilable variant `_Complex?`.
|
332
|
+
|
333
|
+
#### _Rational
|
334
|
+
|
335
|
+
Rational number validation:
|
336
|
+
|
337
|
+
```ruby
|
338
|
+
_Rational # Basic Rational validation
|
339
|
+
.being_positive # Must be positive
|
340
|
+
.being_negative # Must be negative
|
341
|
+
.being_divisible_by( # Must be divisible by 1/2
|
342
|
+
Rational(1, 2)
|
343
|
+
)
|
344
|
+
.being_greater_than( # Must be > 0
|
345
|
+
Rational(0)
|
346
|
+
)
|
347
|
+
.being_less_than( # Must be < 1
|
348
|
+
Rational(1)
|
349
|
+
)
|
350
|
+
```
|
351
|
+
|
352
|
+
Also available in nilable variant `_Rational?`.
|
353
|
+
|
354
|
+
### Collection Types
|
355
|
+
|
356
|
+
#### _Array
|
357
|
+
|
358
|
+
Array validation with element and collection constraints:
|
359
|
+
|
360
|
+
```ruby
|
361
|
+
_Array # Basic array validation
|
362
|
+
.of(_String) # Element type constraint
|
363
|
+
.being_empty # Must be empty
|
364
|
+
.being_populated # Must not be empty
|
365
|
+
.being_distinct # No duplicate elements
|
366
|
+
.being_ordered # Must be sorted
|
367
|
+
.containing(1, 2) # Must contain elements
|
368
|
+
.excluding(3, 4) # Must not contain elements
|
369
|
+
.having_size(3) # Exact size
|
370
|
+
.having_minimum_size(1) # Minimum size
|
371
|
+
.having_maximum_size(10) # Maximum size
|
372
|
+
```
|
373
|
+
|
374
|
+
Also available as `_List` and in nilable variants `_Array?`, `_List?`.
|
375
|
+
|
376
|
+
#### _Hash
|
377
|
+
|
378
|
+
Hash validation with key/value and collection constraints:
|
379
|
+
|
380
|
+
```ruby
|
381
|
+
_Hash # Basic hash validation
|
382
|
+
.of(_Symbol => _String) # Key/value type constraints
|
383
|
+
.containing_keys(:a, :b) # Required keys
|
384
|
+
.excluding_keys(:c, :d) # Forbidden keys
|
385
|
+
.containing_values(1, 2) # Required values
|
386
|
+
.excluding_values(nil) # Forbidden values
|
387
|
+
.having_size(3) # Exact size
|
388
|
+
.having_minimum_size(1) # Minimum size
|
389
|
+
```
|
390
|
+
|
391
|
+
Also available as `_Map` and in nilable variants `_Hash?`, `_Map?`.
|
392
|
+
|
393
|
+
#### _Range
|
394
|
+
|
395
|
+
Range validation with element constraints:
|
396
|
+
|
397
|
+
```ruby
|
398
|
+
_Range # Basic Range validation
|
399
|
+
.being_empty # Must be empty
|
400
|
+
.being_populated # Must not be empty
|
401
|
+
.containing(5) # Must include specific value
|
402
|
+
.excluding(0) # Must not include specific value
|
403
|
+
```
|
404
|
+
|
405
|
+
Also available in nilable variant `_Range?`.
|
406
|
+
|
407
|
+
#### _Set
|
408
|
+
|
409
|
+
Set validation with element and collection constraints:
|
410
|
+
|
411
|
+
```ruby
|
412
|
+
_Set # Basic Set validation
|
413
|
+
.of(_String) # Element type constraint
|
414
|
+
.being_empty # Must be empty
|
415
|
+
.being_populated # Must not be empty
|
416
|
+
.being_distinct # No duplicate elements
|
417
|
+
.containing(1, 2) # Must contain elements
|
418
|
+
.excluding(3, 4) # Must not contain elements
|
419
|
+
.having_size(3) # Exact size
|
420
|
+
```
|
421
|
+
|
422
|
+
Also available in nilable variant `_Set?`.
|
423
|
+
|
424
|
+
### Date and Time Types
|
425
|
+
|
426
|
+
#### _Date
|
427
|
+
|
428
|
+
Date validation with chronological constraints:
|
429
|
+
|
430
|
+
```ruby
|
431
|
+
_Date # Basic date validation
|
432
|
+
.being_after(Date.today) # Must be after date
|
433
|
+
.being_before(Date.today) # Must be before date
|
434
|
+
.being_between( # Must be in date range
|
435
|
+
Date.today,
|
436
|
+
Date.today + 30
|
437
|
+
)
|
438
|
+
```
|
439
|
+
|
440
|
+
Available in nilable variant `_Date?`.
|
441
|
+
|
442
|
+
#### _DateTime
|
443
|
+
|
444
|
+
DateTime validation with chronological constraints:
|
445
|
+
|
446
|
+
```ruby
|
447
|
+
_DateTime # Basic datetime validation
|
448
|
+
.being_after(DateTime.now) # Must be after datetime
|
449
|
+
.being_before(DateTime.now) # Must be before datetime
|
450
|
+
.being_on_or_after( # Must be >= datetime
|
451
|
+
DateTime.now
|
452
|
+
)
|
453
|
+
```
|
454
|
+
|
455
|
+
Available in nilable variant `_DateTime?`.
|
456
|
+
|
457
|
+
#### _DateTimeString
|
458
|
+
|
459
|
+
String-based datetime validation with format constraints and full datetime behavior support:
|
460
|
+
|
461
|
+
```ruby
|
462
|
+
_DateTimeString # Basic datetime string validation
|
463
|
+
# Format constraints
|
464
|
+
.having_american_format # MM/DD/YYYY format
|
465
|
+
.having_european_format # DD.MM.YYYY format
|
466
|
+
.having_iso8601_format # ISO 8601 format
|
467
|
+
.having_rfc2822_format # RFC 2822 format
|
468
|
+
|
469
|
+
# Inherits all datetime constraints
|
470
|
+
.being_after('2024-01-01') # Must be after date
|
471
|
+
.being_before('2024-12-31') # Must be before date
|
472
|
+
.being_between( # Must be in range
|
473
|
+
'2024-01-01',
|
474
|
+
'2024-12-31'
|
475
|
+
)
|
476
|
+
```
|
477
|
+
|
478
|
+
Also available as `_DateString` and in nilable variants `_DateTimeString?`, `_DateString?`.
|
479
|
+
|
480
|
+
#### _Time
|
481
|
+
|
482
|
+
Time validation with chronological constraints:
|
483
|
+
|
484
|
+
```ruby
|
485
|
+
_Time # Basic time validation
|
486
|
+
.being_after(Time.now) # Must be after time
|
487
|
+
.being_before(Time.now) # Must be before time
|
488
|
+
.being_on_or_before( # Must be <= time
|
489
|
+
Time.now
|
490
|
+
)
|
491
|
+
```
|
492
|
+
|
493
|
+
Available in nilable variant `_Time?`.
|
494
|
+
|
495
|
+
#### _Timestamp
|
496
|
+
|
497
|
+
Unix timestamp validation:
|
498
|
+
|
499
|
+
```ruby
|
500
|
+
_Timestamp # Basic timestamp validation
|
501
|
+
.being_after(1640995200) # Must be after timestamp
|
502
|
+
.being_before(1672531200) # Must be before timestamp
|
503
|
+
.being_between( # Must be in range
|
504
|
+
1640995200,
|
505
|
+
1672531200
|
506
|
+
)
|
507
|
+
```
|
508
|
+
|
509
|
+
Available in nilable variant `_Timestamp?`.
|
510
|
+
|
511
|
+
### Network Types
|
512
|
+
|
513
|
+
#### _EmailAddress
|
514
|
+
|
515
|
+
Email validation with domain and format constraints:
|
516
|
+
|
517
|
+
```ruby
|
518
|
+
_EmailAddress # Basic email validation
|
519
|
+
.having_hostname("example.com") # Specific domain
|
520
|
+
.having_local_matching(/^[a-z]+$/) # Username format
|
521
|
+
.not_having_hostname("blocked.com") # Blocked domains
|
522
|
+
```
|
523
|
+
|
524
|
+
Also available as `_Email` and in nilable variants `_EmailAddress?`, `_Email?`.
|
525
|
+
|
526
|
+
#### _URI
|
527
|
+
|
528
|
+
URI validation with component and format constraints:
|
529
|
+
|
530
|
+
```ruby
|
531
|
+
_URI # Basic URI validation
|
532
|
+
.having_scheme("https") # Specific scheme
|
533
|
+
.having_hostname("api.example.com") # Specific host
|
534
|
+
.having_path("/v1/users") # Specific path
|
535
|
+
.not_having_scheme("ftp") # Forbidden scheme
|
536
|
+
```
|
537
|
+
|
538
|
+
Also available as `_URL`, `_Url`, `_Uri` and in nilable variants `_URI?`, `_URL?`, `_Url?`, `_Uri?`.
|
539
|
+
|
540
|
+
#### _Hostname
|
541
|
+
|
542
|
+
Hostname validation according to RFC standards:
|
543
|
+
|
544
|
+
```ruby
|
545
|
+
_Hostname # Basic hostname validation
|
546
|
+
.matching("example.com") # Exact hostname match
|
547
|
+
.not_matching("blocked") # Hostname exclusion
|
548
|
+
.being_ascii # ASCII characters only
|
549
|
+
.having_size_between(1, 253) # Valid length range
|
550
|
+
```
|
551
|
+
|
552
|
+
Available in nilable variant `_Hostname?`.
|
553
|
+
|
554
|
+
### Identifier Types
|
555
|
+
|
556
|
+
#### _UUID
|
557
|
+
|
558
|
+
UUID validation with version and format constraints:
|
559
|
+
|
560
|
+
```ruby
|
561
|
+
_UUID # Basic UUID validation
|
562
|
+
.being_version(4) # Specific UUID version
|
563
|
+
.being_compact # No hyphens format
|
564
|
+
.being_standard # With hyphens format
|
565
|
+
.being_version_four # Must be v4 UUID
|
566
|
+
.being_version_seven # Must be v7 UUID
|
567
|
+
```
|
568
|
+
|
569
|
+
Also available as `_Uuid` and in nilable variants `_UUID?`, `_Uuid?`.
|
570
|
+
|
571
|
+
#### _CUID
|
572
|
+
|
573
|
+
CUID validation with version and format constraints:
|
574
|
+
|
575
|
+
```ruby
|
576
|
+
_CUID # Basic CUID validation
|
577
|
+
.being_version(2) # Specific CUID version
|
578
|
+
.being_version_one # Must be v1 CUID
|
579
|
+
.being_version_two # Must be v2 CUID
|
580
|
+
```
|
581
|
+
|
582
|
+
Also available as `_Cuid` and in nilable variants `_CUID?`, `_Cuid?`.
|
583
|
+
|
584
|
+
#### _ID
|
585
|
+
|
586
|
+
A union type accepting common ID formats:
|
587
|
+
|
588
|
+
```ruby
|
589
|
+
_ID === 123 # Integer IDs
|
590
|
+
_ID === "clh3am1f30000bhqg" # CUIDs
|
591
|
+
_ID === "123e4567-e89b-12d3" # UUIDs
|
592
|
+
```
|
593
|
+
|
594
|
+
Available in nilable variant `_ID?`.
|
595
|
+
|
596
|
+
### Specification Types
|
597
|
+
|
598
|
+
#### _Anything
|
599
|
+
|
600
|
+
Accepts any value except those explicitly excluded:
|
601
|
+
|
602
|
+
```ruby
|
603
|
+
_Anything # Accepts any value
|
604
|
+
_Anything.but(_String) # Anything except strings
|
605
|
+
_Any # Alias for _Anything
|
606
|
+
```
|
607
|
+
|
608
|
+
#### _Duck
|
609
|
+
|
610
|
+
Duck type validation based on method presence:
|
611
|
+
|
612
|
+
```ruby
|
613
|
+
_Duck # Duck type validation
|
614
|
+
.responding_to(:to_s, :to_i) # Must have methods
|
615
|
+
.not_responding_to(:dangerous_op) # Must not have methods
|
616
|
+
```
|
617
|
+
|
618
|
+
Also available as `_Interface`, `_Protocol`, `_RespondingTo`.
|
619
|
+
|
620
|
+
#### _Enum
|
621
|
+
|
622
|
+
Enumerated value validation:
|
623
|
+
|
624
|
+
```ruby
|
625
|
+
_Enum(:red, :green, :blue) # Must be one of values
|
626
|
+
_Enum("draft", "published") # String enumeration
|
627
|
+
```
|
628
|
+
|
629
|
+
Also available as `_Literal` and in nilable variants `_Enum?`, `_Literal?`.
|
630
|
+
|
631
|
+
#### _Instance
|
632
|
+
|
633
|
+
Instance type validation with attribute constraints:
|
634
|
+
|
635
|
+
```ruby
|
636
|
+
_Instance # Instance validation
|
637
|
+
.of(User) # Must be User instance
|
638
|
+
.having_attributes( # With attribute types
|
639
|
+
name: _String,
|
640
|
+
age: _Integer
|
641
|
+
)
|
642
|
+
```
|
643
|
+
|
644
|
+
Also available as `_Record` and in nilable variants `_Instance?`, `_Record?`.
|
645
|
+
|
646
|
+
#### _Union
|
647
|
+
|
648
|
+
Combine multiple types with OR logic:
|
649
|
+
|
650
|
+
```ruby
|
651
|
+
_Union(_String, _Integer) # String OR Integer
|
652
|
+
_Union( # Complex union
|
653
|
+
_String.being_uppercase,
|
654
|
+
_Integer.being_positive
|
655
|
+
)
|
656
|
+
```
|
657
|
+
|
658
|
+
Also available as `_Either`.
|
659
|
+
|
660
|
+
#### _Nilable
|
661
|
+
|
662
|
+
Make any type accept nil values:
|
663
|
+
|
664
|
+
```ruby
|
665
|
+
_Nilable(_String) # String or nil
|
666
|
+
_String? # Same as above
|
667
|
+
```
|
668
|
+
|
669
|
+
Also available as `_Nullable`.
|
670
|
+
|
671
|
+
#### _Void
|
672
|
+
|
673
|
+
Accepts any value, useful for void returns:
|
674
|
+
|
675
|
+
```ruby
|
676
|
+
_Void === nil # => true
|
677
|
+
_Void === false # => true
|
678
|
+
_Void === Object.new # => true
|
679
|
+
```
|
680
|
+
|
681
|
+
## Advanced Usage
|
682
|
+
|
683
|
+
### Custom Types
|
684
|
+
|
685
|
+
Create reusable custom types:
|
686
|
+
|
687
|
+
```ruby
|
688
|
+
module Types
|
689
|
+
extend Domainic::Type::Definitions
|
690
|
+
|
691
|
+
Username = _String
|
692
|
+
.being_lowercase
|
693
|
+
.being_alphanumeric
|
694
|
+
.having_size_between(3, 20)
|
695
|
+
.not_matching(/^admin/i)
|
696
|
+
|
697
|
+
UserPassword = lambda { |username
|
698
|
+
_String
|
699
|
+
.having_size_between(8, 20)
|
700
|
+
.being_mixedcase
|
701
|
+
.not_matching(username)
|
702
|
+
}
|
703
|
+
end
|
704
|
+
|
705
|
+
username = "alice123"
|
706
|
+
Types::Username.validate!(username) # => true
|
707
|
+
Types::UserPassword.call(username).validate!("p@$Sw0rd") # => true
|
708
|
+
```
|
709
|
+
|
710
|
+
### Type Composition
|
711
|
+
|
712
|
+
Build complex type hierarchies:
|
713
|
+
|
714
|
+
```ruby
|
715
|
+
# API response validation
|
716
|
+
ApiResponse = _Hash.of(_Symbol => _Union(
|
717
|
+
# Success case
|
718
|
+
_Hash.of(
|
719
|
+
_Symbol => _Union(
|
720
|
+
_String,
|
721
|
+
_Array.of(_Integer.being_positive),
|
722
|
+
_Hash.of(_Symbol => _Boolean)
|
723
|
+
)
|
724
|
+
),
|
725
|
+
# Error case
|
726
|
+
_Hash.of(_Symbol => _String)
|
727
|
+
.containing_keys(:error, :message)
|
728
|
+
))
|
729
|
+
|
730
|
+
# Form data validation
|
731
|
+
FormData = _Hash.of(
|
732
|
+
_Symbol => _Union(
|
733
|
+
_String.being_alphanumeric.having_size_between(3, 20),
|
734
|
+
_String.matching(URI::MailTo::EMAIL_REGEXP),
|
735
|
+
_Nilable(_Integer.being_greater_than_or_equal_to(18)),
|
736
|
+
_Array.of(_String).having_maximum_size(5)
|
737
|
+
)
|
738
|
+
).containing_keys(:username, :email, :age, :interests)
|
739
|
+
```
|
740
|
+
|
741
|
+
### Integration Patterns
|
742
|
+
|
743
|
+
#### With Classes
|
744
|
+
|
745
|
+
```ruby
|
746
|
+
class User
|
747
|
+
extend Domainic::Type::Definitions
|
748
|
+
|
749
|
+
attr_reader :name, :email
|
750
|
+
|
751
|
+
def initialize(name, email)
|
752
|
+
self.name = name
|
753
|
+
self.email = email
|
754
|
+
end
|
755
|
+
|
756
|
+
def name=(value)
|
757
|
+
_String
|
758
|
+
.having_size_between(2, 50)
|
759
|
+
.validate!(value)
|
760
|
+
@name = value
|
761
|
+
end
|
762
|
+
|
763
|
+
def email=(value)
|
764
|
+
_EmailAddress.validate!(value)
|
765
|
+
@email = value
|
766
|
+
end
|
767
|
+
end
|
768
|
+
```
|
769
|
+
|
770
|
+
#### With Domainic::Attributer
|
771
|
+
|
772
|
+
```ruby
|
773
|
+
require 'domainic/attributer'
|
774
|
+
require 'domainic/type/definitions'
|
775
|
+
|
776
|
+
class Configuration
|
777
|
+
extend Domainic::Type::Definitions
|
778
|
+
include Domainic::Attributer
|
779
|
+
|
780
|
+
argument :environment, _Enum(:development, :test, :production)
|
781
|
+
argument :log_level, _Enum(:debug, :info, :warn, :error)
|
782
|
+
|
783
|
+
option :database_url, _String.matching(%r{\Apostgres://})
|
784
|
+
option :redis_url, _String.matching(%r{\Aredis://})
|
785
|
+
option :api_keys, _Array.of(_String).being_distinct
|
786
|
+
end
|
787
|
+
```
|