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