domainic-type 0.1.0.alpha.3.2.0 → 0.1.0.alpha.3.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -77,6 +77,36 @@ module Domainic
|
|
77
77
|
end
|
78
78
|
alias _List? _Array?
|
79
79
|
|
80
|
+
# Creates a BigDecimalType instance.
|
81
|
+
#
|
82
|
+
# @example
|
83
|
+
# type = _BigDecimal
|
84
|
+
# type.validate(BigDecimal('123.45')) # => true
|
85
|
+
#
|
86
|
+
# @param options [Hash] additional configuration options
|
87
|
+
#
|
88
|
+
# @return [Domainic::Type::BigDecimalType] the created type
|
89
|
+
# @rbs (**__todo__ options) -> BigDecimalType
|
90
|
+
def _BigDecimal(**options)
|
91
|
+
require 'domainic/type/types/core_extended/big_decimal_type'
|
92
|
+
Domainic::Type::BigDecimalType.new(**options)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Creates a nilable BigDecimalType instance.
|
96
|
+
#
|
97
|
+
# @example
|
98
|
+
# type = _BigDecimal?
|
99
|
+
# type.validate(BigDecimal('123.45')) # => true
|
100
|
+
# type.validate(nil) # => true
|
101
|
+
#
|
102
|
+
# @param options [Hash] additional configuration options
|
103
|
+
#
|
104
|
+
# @return [Domainic::Type::UnionType] the created type (BigDecimal or NilClass)
|
105
|
+
# @rbs (**__todo__ options) -> UnionType
|
106
|
+
def _BigDecimal?(**options)
|
107
|
+
_Nilable(_BigDecimal(**options))
|
108
|
+
end
|
109
|
+
|
80
110
|
# Creates a Boolean type.
|
81
111
|
#
|
82
112
|
# Represents a union of TrueClass and FalseClass.
|
@@ -133,6 +163,35 @@ module Domainic
|
|
133
163
|
end
|
134
164
|
alias _Cuid? _CUID?
|
135
165
|
|
166
|
+
# Creates a ComplexType instance.
|
167
|
+
#
|
168
|
+
# @example
|
169
|
+
# type = _Complex
|
170
|
+
# type.validate(Complex(1, 2)) # => true
|
171
|
+
#
|
172
|
+
# @param options [Hash] additional configuration options
|
173
|
+
#
|
174
|
+
# @return [Domainic::Type::ComplexType] the created type
|
175
|
+
# @rbs (**__todo__ options) -> ComplexType
|
176
|
+
def _Complex(**options)
|
177
|
+
require 'domainic/type/types/core/complex_type'
|
178
|
+
Domainic::Type::ComplexType.new(**options)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Creates a nilable ComplexType instance.
|
182
|
+
#
|
183
|
+
# @example
|
184
|
+
# type = _Complex?
|
185
|
+
# type.validate(Complex(1, 2)) # => true
|
186
|
+
#
|
187
|
+
# @param options [Hash] additional configuration options
|
188
|
+
#
|
189
|
+
# @return [Domainic::Type::UnionType] the created type (Complex or NilClass)
|
190
|
+
# @rbs (**__todo__ options) -> UnionType
|
191
|
+
def _Complex?(**options)
|
192
|
+
_Nilable(_Complex(**options))
|
193
|
+
end
|
194
|
+
|
136
195
|
# Creates a DateType instance.
|
137
196
|
#
|
138
197
|
# DateType restricts values to valid `Date` objects.
|
@@ -195,6 +254,39 @@ module Domainic
|
|
195
254
|
_Nilable(_DateTime(**options))
|
196
255
|
end
|
197
256
|
|
257
|
+
# Creates a DateTimeStringType instance.
|
258
|
+
#
|
259
|
+
# DateTimeStringType restricts values to valid date-time strings.
|
260
|
+
#
|
261
|
+
# @example
|
262
|
+
# type = _DateTimeString
|
263
|
+
# type.validate('2024-01-01T12:00:00Z') # => true
|
264
|
+
#
|
265
|
+
# @param options [Hash] additional configuration options
|
266
|
+
#
|
267
|
+
# @return [Domainic::Type::DateTimeStringType] the created type
|
268
|
+
# @rbs (**__todo__ options) -> DateTimeStringType
|
269
|
+
def _DateTimeString(**options)
|
270
|
+
require 'domainic/type/types/datetime/date_time_string_type'
|
271
|
+
Domainic::Type::DateTimeStringType.new(**options)
|
272
|
+
end
|
273
|
+
alias _DateString _DateTimeString
|
274
|
+
|
275
|
+
# Creates a nilable DateTimeStringType instance.
|
276
|
+
#
|
277
|
+
# @example
|
278
|
+
# _DateTimeString? === '2024-01-01T12:00:00Z' # => true
|
279
|
+
# _DateTimeString? === nil # => true
|
280
|
+
#
|
281
|
+
# @param options [Hash] additional configuration options
|
282
|
+
#
|
283
|
+
# @return [Domainic::Type::UnionType] the created type (DateTimeStringType or NilClass)
|
284
|
+
# @rbs (**__todo__ options) -> UnionType
|
285
|
+
def _DateTimeString?(**options)
|
286
|
+
_Nilable(_DateTimeString(**options))
|
287
|
+
end
|
288
|
+
alias _DateString? _DateTimeString?
|
289
|
+
|
198
290
|
# Creates a DuckType instance.
|
199
291
|
#
|
200
292
|
# DuckType allows specifying behavior based on method availability.
|
@@ -477,6 +569,95 @@ module Domainic
|
|
477
569
|
end
|
478
570
|
alias _Nullable _Nilable
|
479
571
|
|
572
|
+
# Creates a RangeType instance.
|
573
|
+
#
|
574
|
+
# @example
|
575
|
+
# type = _Range
|
576
|
+
# type.validate(1..10) # => true
|
577
|
+
#
|
578
|
+
# @param options [Hash] additional configuration options
|
579
|
+
#
|
580
|
+
# @return [Domainic::Type::RangeType] the created type
|
581
|
+
# @rbs (**__todo__ options) -> RangeType
|
582
|
+
def _Range(**options)
|
583
|
+
require 'domainic/type/types/core/range_type'
|
584
|
+
Domainic::Type::RangeType.new(**options)
|
585
|
+
end
|
586
|
+
|
587
|
+
# Creates a nilable RangeType instance.
|
588
|
+
#
|
589
|
+
# @example
|
590
|
+
# type = _Range?
|
591
|
+
# type.validate(1..10) # => true
|
592
|
+
#
|
593
|
+
# @param options [Hash] additional configuration options
|
594
|
+
#
|
595
|
+
# @return [Domainic::Type::UnionType] the created type (Range or NilClass)
|
596
|
+
# @rbs (**__todo__ options) -> UnionType
|
597
|
+
def _Range?(**options)
|
598
|
+
_Nilable(_Range(**options))
|
599
|
+
end
|
600
|
+
|
601
|
+
# Creates a RationalType instance.
|
602
|
+
#
|
603
|
+
# @example
|
604
|
+
# type = _Rational
|
605
|
+
# type.validate(Rational(1, 2)) # => true
|
606
|
+
#
|
607
|
+
# @param options [Hash] additional configuration options
|
608
|
+
#
|
609
|
+
# @return [Domainic::Type::RationalType] the created type
|
610
|
+
# @rbs (**__todo__ options) -> RationalType
|
611
|
+
def _Rational(**options)
|
612
|
+
require 'domainic/type/types/core/rational_type'
|
613
|
+
Domainic::Type::RationalType.new(**options)
|
614
|
+
end
|
615
|
+
|
616
|
+
# Creates a nilable RationalType instance.
|
617
|
+
#
|
618
|
+
# @example
|
619
|
+
# type = _Rational?
|
620
|
+
# type.validate(Rational(1, 2)) # => true
|
621
|
+
# type.validate(nil) # => true
|
622
|
+
#
|
623
|
+
# @param options [Hash] additional configuration options
|
624
|
+
#
|
625
|
+
# @return [Domainic::Type::UnionType] the created type (Rational or NilClass)
|
626
|
+
# @rbs (**__todo__ options) -> UnionType
|
627
|
+
def _Rational?(**options)
|
628
|
+
_Nilable(_Rational(**options))
|
629
|
+
end
|
630
|
+
|
631
|
+
# Creates a SetType instance.
|
632
|
+
#
|
633
|
+
# @example
|
634
|
+
# type = _Set
|
635
|
+
# type.validate(Set[1, 2, 3]) # => true
|
636
|
+
#
|
637
|
+
# @param options [Hash] additional configuration options
|
638
|
+
#
|
639
|
+
# @return [Domainic::Type::SetType] the created type
|
640
|
+
# @rbs (**__todo__ options) -> SetType
|
641
|
+
def _Set(**options)
|
642
|
+
require 'domainic/type/types/core_extended/set_type'
|
643
|
+
Domainic::Type::SetType.new(**options)
|
644
|
+
end
|
645
|
+
|
646
|
+
# Creates a nilable SetType instance.
|
647
|
+
#
|
648
|
+
# @example
|
649
|
+
# type = _Set?
|
650
|
+
# type.validate(Set[1, 2, 3]) # => true
|
651
|
+
# type.validate(nil) # => true
|
652
|
+
#
|
653
|
+
# @param options [Hash] additional configuration options
|
654
|
+
#
|
655
|
+
# @return [Domainic::Type::UnionType] the created type (Set or NilClass)
|
656
|
+
# @rbs (**__todo__ options) -> UnionType
|
657
|
+
def _Set?(**options)
|
658
|
+
_Nilable(_Set(**options))
|
659
|
+
end
|
660
|
+
|
480
661
|
# Creates a StringType instance.
|
481
662
|
#
|
482
663
|
# @example
|
@@ -568,6 +749,37 @@ module Domainic
|
|
568
749
|
_Nilable(_Time(**options))
|
569
750
|
end
|
570
751
|
|
752
|
+
# Creates a TimestampType instance.
|
753
|
+
#
|
754
|
+
# TimestampType restricts values to valid timestamps.
|
755
|
+
#
|
756
|
+
# @example
|
757
|
+
# type = _Timestamp
|
758
|
+
# type.validate(1640995200) # => true
|
759
|
+
#
|
760
|
+
# @param options [Hash] additional configuration options
|
761
|
+
#
|
762
|
+
# @return [Domainic::Type::TimestampType] the created type
|
763
|
+
# @rbs (**__todo__ options) -> TimestampType
|
764
|
+
def _Timestamp(**options)
|
765
|
+
require 'domainic/type/types/datetime/timestamp_type'
|
766
|
+
Domainic::Type::TimestampType.new(**options)
|
767
|
+
end
|
768
|
+
|
769
|
+
# Creates a nilable TimestampType instance.
|
770
|
+
#
|
771
|
+
# @example
|
772
|
+
# _Timestamp? === 1640995200 # => true
|
773
|
+
# _Timestamp? === nil # => true
|
774
|
+
#
|
775
|
+
# @param options [Hash] additional configuration options
|
776
|
+
#
|
777
|
+
# @return [Domainic::Type::UnionType] the created type (TimestampType or NilClass)
|
778
|
+
# @rbs (**__todo__ options) -> UnionType
|
779
|
+
def _Timestamp?(**options)
|
780
|
+
_Nilable(_Timestamp(**options))
|
781
|
+
end
|
782
|
+
|
571
783
|
# Creates a URIType instance.
|
572
784
|
#
|
573
785
|
# URIType restricts values to valid URIs.
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'domainic/type/behavior'
|
4
|
+
require 'domainic/type/behavior/numeric_behavior'
|
5
|
+
|
6
|
+
module Domainic
|
7
|
+
module Type
|
8
|
+
# A type for validating and constraining `Complex` numbers.
|
9
|
+
#
|
10
|
+
# This type extends `NumericBehavior` to provide a fluent interface for numeric-specific validations such as
|
11
|
+
# polarity and divisibility checks.
|
12
|
+
#
|
13
|
+
# @example Validating a `Complex` value
|
14
|
+
# type = Domainic::Type::ComplexType.new
|
15
|
+
# type.validate!(Complex(3, 4)) # => true
|
16
|
+
#
|
17
|
+
# @example Enforcing constraints
|
18
|
+
# type = Domainic::Type::ComplexType.new
|
19
|
+
# type.being_positive.validate!(Complex(42, 0)) # => raises TypeError
|
20
|
+
#
|
21
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
22
|
+
# @since 0.1.0
|
23
|
+
class ComplexType
|
24
|
+
# @rbs! extend Behavior::ClassMethods
|
25
|
+
|
26
|
+
include Behavior
|
27
|
+
include Behavior::NumericBehavior
|
28
|
+
|
29
|
+
intrinsically_constrain :self, :type, Complex, abort_on_failure: true, description: :not_described
|
30
|
+
|
31
|
+
# Constrain the Complex real to be divisible by a given divisor.
|
32
|
+
#
|
33
|
+
# @param arguments [Array<Numeric>] a list of arguments, typically one divisor
|
34
|
+
# @param options [Hash] additional options such as tolerance for floating-point checks
|
35
|
+
# @option options [Numeric] :divisor the divisor to check for divisibility
|
36
|
+
# @option options [Numeric] :tolerance the tolerance for floating-point checks
|
37
|
+
#
|
38
|
+
# @return [self] the current instance for chaining
|
39
|
+
# @rbs (*Numeric arguments, ?divisor: Numeric, ?tolerance: Numeric) -> Behavior
|
40
|
+
def being_divisible_by(*arguments, **options)
|
41
|
+
if arguments.size > 1 || (arguments.empty? && !options.key?(:divisor))
|
42
|
+
raise ArgumentError, "wrong number of arguments (given #{arguments.size}, expected 1)"
|
43
|
+
end
|
44
|
+
|
45
|
+
divisor = arguments.first || options[:divisor]
|
46
|
+
constrain :real, :divisibility, divisor, description: 'being', **options.except(:divisor)
|
47
|
+
end
|
48
|
+
alias being_multiple_of being_divisible_by
|
49
|
+
alias divisible_by being_divisible_by
|
50
|
+
alias multiple_of being_divisible_by
|
51
|
+
|
52
|
+
# Constrain the Complex real to be even.
|
53
|
+
#
|
54
|
+
# @return [self] the current instance for chaining
|
55
|
+
# @rbs () -> Behavior
|
56
|
+
def being_even
|
57
|
+
# @type self: Object & Behavior
|
58
|
+
constrain :real, :parity, :even, description: 'being'
|
59
|
+
end
|
60
|
+
alias even being_even
|
61
|
+
alias not_being_odd being_even
|
62
|
+
|
63
|
+
# Constrain the Complex real to be negative.
|
64
|
+
#
|
65
|
+
# @return [self] the current instance for chaining
|
66
|
+
# @rbs () -> Behavior
|
67
|
+
def being_negative
|
68
|
+
# @type self: Object & Behavior
|
69
|
+
constrain :real, :polarity, :negative, description: 'being'
|
70
|
+
end
|
71
|
+
alias negative being_negative
|
72
|
+
alias not_being_positive being_negative
|
73
|
+
|
74
|
+
# Constrain the Complex real value to be odd.
|
75
|
+
#
|
76
|
+
# @return [self] the current instance for chaining
|
77
|
+
# @rbs () -> Behavior
|
78
|
+
def being_odd
|
79
|
+
# @type self: Object & Behavior
|
80
|
+
constrain :real, :parity, :odd, description: 'being'
|
81
|
+
end
|
82
|
+
alias odd being_odd
|
83
|
+
alias not_being_even being_odd
|
84
|
+
|
85
|
+
# Constrain the Complex real to be positive.
|
86
|
+
#
|
87
|
+
# @return [self] the current instance for chaining
|
88
|
+
# @rbs () -> Behavior
|
89
|
+
def being_positive
|
90
|
+
# @type self: Object & Behavior
|
91
|
+
constrain :real, :polarity, :positive, description: 'being'
|
92
|
+
end
|
93
|
+
alias positive being_positive
|
94
|
+
alias not_being_negative being_positive
|
95
|
+
|
96
|
+
# Constrain the Complex real to not be divisible by a specific divisor.
|
97
|
+
#
|
98
|
+
# @note the divisor MUST be provided as an argument or in the options Hash.
|
99
|
+
#
|
100
|
+
# @param arguments [Array<Numeric>] a list of arguments, typically one divisor
|
101
|
+
# @param options [Hash] additional options such as tolerance for floating-point checks
|
102
|
+
# @option options [Numeric] :divisor the divisor to check for divisibility
|
103
|
+
# @option options [Numeric] :tolerance the tolerance for floating-point checks
|
104
|
+
#
|
105
|
+
# @return [self] the current instance for chaining
|
106
|
+
# @rbs (*Numeric arguments, ?divisor: Numeric, ?tolerance: Numeric) -> Behavior
|
107
|
+
def not_being_divisible_by(*arguments, **options)
|
108
|
+
if arguments.size > 1 || (arguments.empty? && !options.key?(:divisor))
|
109
|
+
raise ArgumentError, "wrong number of arguments (given #{arguments.size}, expected 1)"
|
110
|
+
end
|
111
|
+
|
112
|
+
# @type self: Object & Behavior
|
113
|
+
divisor = arguments.first || options[:divisor]
|
114
|
+
divisible_by = @constraints.prepare :self, :divisibility, divisor, **options.except(:divisor)
|
115
|
+
constrain :real, :not, divisible_by, concerning: :divisibility, description: 'being'
|
116
|
+
end
|
117
|
+
alias not_being_multiple_of not_being_divisible_by
|
118
|
+
alias not_divisible_by not_being_divisible_by
|
119
|
+
alias not_multiple_of not_being_divisible_by
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'domainic/type/behavior'
|
4
|
+
require 'domainic/type/behavior/enumerable_behavior'
|
5
|
+
|
6
|
+
module Domainic
|
7
|
+
module Type
|
8
|
+
# A type for validating `Range` objects with comprehensive constraints.
|
9
|
+
#
|
10
|
+
# This class allows flexible validation of ranges, supporting type checks,
|
11
|
+
# inclusion/exclusion of values, and range-specific behaviors such as bounds validation.
|
12
|
+
# It integrates with `EnumerableBehavior` for enumerable-style validations
|
13
|
+
# (e.g., size constraints, element presence).
|
14
|
+
#
|
15
|
+
# Key features:
|
16
|
+
# - Ensures the value is a `Range`.
|
17
|
+
# - Supports constraints for range boundaries (`begin` and `end`).
|
18
|
+
# - Provides validations for inclusion and exclusion of values.
|
19
|
+
# - Leverages `EnumerableBehavior` for size and collection-style constraints.
|
20
|
+
#
|
21
|
+
# @example Basic usage
|
22
|
+
# type = RangeType.new
|
23
|
+
# type.having_bounds(1, 10) # Validates range bounds (inclusive or exclusive).
|
24
|
+
# type.containing(5) # Ensures value is within the range.
|
25
|
+
# type.not_containing(15) # Ensures value is not within the range.
|
26
|
+
#
|
27
|
+
# @example Integration with EnumerableBehavior
|
28
|
+
# type = RangeType.new
|
29
|
+
# type.having_size(10) # Validates size (total elements in the range).
|
30
|
+
# type.containing_exactly(3, 7) # Validates specific values within the range.
|
31
|
+
#
|
32
|
+
# @example Flexible boundaries
|
33
|
+
# type = RangeType.new
|
34
|
+
# type.having_bounds(1, 10, exclusive: true) # Upper and lower bounds are exclusive.
|
35
|
+
#
|
36
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
37
|
+
# @since 0.1.0
|
38
|
+
class RangeType
|
39
|
+
# @rbs! extend Behavior::ClassMethods
|
40
|
+
|
41
|
+
include Behavior
|
42
|
+
include Behavior::EnumerableBehavior
|
43
|
+
|
44
|
+
intrinsically_constrain :self, :type, Range, abort_on_failure: true, description: :not_described
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'domainic/type/behavior'
|
4
|
+
require 'domainic/type/behavior/numeric_behavior'
|
5
|
+
|
6
|
+
module Domainic
|
7
|
+
module Type
|
8
|
+
# A type for validating Rational numbers.
|
9
|
+
#
|
10
|
+
# This type ensures values are of type `Rational` and supports a range of
|
11
|
+
# constraints for numerical validation, such as positivity, negativity,
|
12
|
+
# and divisibility. It integrates with `NumericBehavior` to provide a
|
13
|
+
# consistent and fluent interface for numeric constraints.
|
14
|
+
#
|
15
|
+
# @example Validating a positive Rational number
|
16
|
+
# type = Domainic::Type::RationalType.new
|
17
|
+
# type.being_positive.validate!(Rational(3, 4)) # => true
|
18
|
+
#
|
19
|
+
# @example Validating divisibility
|
20
|
+
# type = Domainic::Type::RationalType.new
|
21
|
+
# type.being_divisible_by(1).validate!(Rational(3, 1)) # => true
|
22
|
+
#
|
23
|
+
# @example Using constraints with chaining
|
24
|
+
# type = Domainic::Type::RationalType.new
|
25
|
+
# type.being_greater_than(0).being_less_than(1).validate!(Rational(1, 2)) # => true
|
26
|
+
#
|
27
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
28
|
+
# @since 0.1.0
|
29
|
+
class RationalType
|
30
|
+
# @rbs! extend Behavior::ClassMethods
|
31
|
+
|
32
|
+
include Behavior
|
33
|
+
include Behavior::NumericBehavior
|
34
|
+
|
35
|
+
intrinsically_constrain :self, :type, Rational, abort_on_failure: true, description: :not_described
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bigdecimal'
|
4
|
+
require 'domainic/type/behavior'
|
5
|
+
require 'domainic/type/behavior/numeric_behavior'
|
6
|
+
|
7
|
+
module Domainic
|
8
|
+
module Type
|
9
|
+
# A type for validating and constraining `BigDecimal` objects.
|
10
|
+
#
|
11
|
+
# This type extends `NumericBehavior` to provide a fluent interface for numeric-specific validations such as being
|
12
|
+
# positive, being within a range, or satisfying divisibility rules.
|
13
|
+
#
|
14
|
+
# @example Validating a `BigDecimal` value
|
15
|
+
# type = Domainic::Type::BigDecimalType.new
|
16
|
+
# type.validate!(BigDecimal('3.14')) # => true
|
17
|
+
#
|
18
|
+
# @example Enforcing constraints
|
19
|
+
# type = Domainic::Type::BigDecimalType.new
|
20
|
+
# type.being_positive.validate!(BigDecimal('42')) # => true
|
21
|
+
# type.being_positive.validate!(BigDecimal('-42')) # raises TypeError
|
22
|
+
#
|
23
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
24
|
+
# @since 0.1.0
|
25
|
+
class BigDecimalType
|
26
|
+
# @rbs! extend Behavior::ClassMethods
|
27
|
+
|
28
|
+
include Behavior
|
29
|
+
include Behavior::NumericBehavior
|
30
|
+
|
31
|
+
intrinsically_constrain :self, :type, BigDecimal, abort_on_failure: true, description: :not_described
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'domainic/type/behavior'
|
4
|
+
require 'domainic/type/behavior/enumerable_behavior'
|
5
|
+
require 'set'
|
6
|
+
|
7
|
+
module Domainic
|
8
|
+
module Type
|
9
|
+
# A type for validating and constraining `Set` objects.
|
10
|
+
#
|
11
|
+
# This type extends `EnumerableBehavior` to support validations and constraints
|
12
|
+
# such as being empty, containing specific elements, or having a minimum or maximum count.
|
13
|
+
#
|
14
|
+
# @example Validating a `Set` object
|
15
|
+
# type = Domainic::Type::SetType.new
|
16
|
+
# type.validate!(Set.new([1, 2, 3])) # => true
|
17
|
+
#
|
18
|
+
# @example Enforcing constraints
|
19
|
+
# type = Domainic::Type::SetType.new
|
20
|
+
# type.being_empty.validate!(Set.new) # => true
|
21
|
+
# type.being_empty.validate!(Set.new([1])) # raises TypeError
|
22
|
+
#
|
23
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
24
|
+
# @since 0.1.0
|
25
|
+
class SetType
|
26
|
+
# @rbs! extend Behavior::ClassMethods
|
27
|
+
|
28
|
+
include Behavior
|
29
|
+
include Behavior::EnumerableBehavior
|
30
|
+
|
31
|
+
intrinsically_constrain :self, :type, Set, abort_on_failure: true, description: :not_described
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'domainic/type/behavior'
|
4
|
+
require 'domainic/type/behavior/date_time_behavior'
|
5
|
+
|
6
|
+
module Domainic
|
7
|
+
module Type
|
8
|
+
# A type for validating and constraining strings to match various datetime formats.
|
9
|
+
#
|
10
|
+
# This class includes behaviors for handling datetime constraints and ensures that
|
11
|
+
# a string matches one of the predefined datetime formats (e.g., ISO 8601, RFC2822).
|
12
|
+
#
|
13
|
+
# @example Validating American format
|
14
|
+
# type = Domainic::Type::DateTimeStringType.new
|
15
|
+
# type.having_american_format.validate!("01/31/2024")
|
16
|
+
#
|
17
|
+
# @example Validating ISO 8601 format
|
18
|
+
# type = Domainic::Type::DateTimeStringType.new
|
19
|
+
# type.having_iso8601_format.validate!("2024-01-01T12:00:00Z")
|
20
|
+
#
|
21
|
+
# @example Validating RFC2822 format
|
22
|
+
# type = Domainic::Type::DateTimeStringType.new
|
23
|
+
# type.having_rfc2822_format.validate!("Thu, 31 Jan 2024 13:30:00 +0000")
|
24
|
+
#
|
25
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
26
|
+
# @since 0.1.0
|
27
|
+
class DateTimeStringType
|
28
|
+
# @rbs! extend Behavior::ClassMethods
|
29
|
+
|
30
|
+
include Behavior
|
31
|
+
include Behavior::DateTimeBehavior
|
32
|
+
|
33
|
+
# The `Format` module provides a set of regular expressions for validating
|
34
|
+
# various datetime formats, including ISO 8601, RFC2822, American, European,
|
35
|
+
# full month names, abbreviated month names, and 12-hour clock formats.
|
36
|
+
#
|
37
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
38
|
+
# @since 0.1.0
|
39
|
+
module Format
|
40
|
+
# Matches ISO 8601 datetime formats, including:
|
41
|
+
# - `2024-01-01T12:00:00.000+00:00`
|
42
|
+
# - `2024-01-01T12:00:00+00:00`
|
43
|
+
# - `2024-01-01T12:00:00.000`
|
44
|
+
# - `2024-01-01T12:00:00`
|
45
|
+
# - `20240101T120000+0000` (basic format)
|
46
|
+
# @return [Regexp]
|
47
|
+
ISO8601 = /\A\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[-+]\d{2}:?\d{2})?)?\z/ #: Regexp
|
48
|
+
|
49
|
+
# Matches RFC2822 datetime formats, including:
|
50
|
+
# - `Thu, 31 Jan 2024 13:30:00 +0000`
|
51
|
+
# - `31 Jan 2024 13:30:00 +0000`
|
52
|
+
# @return [Regexp]
|
53
|
+
RFC2822 = /\A
|
54
|
+
(?: # Optional day of the week
|
55
|
+
(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s+
|
56
|
+
)?
|
57
|
+
\d{1,2}\s+ # Day of the month
|
58
|
+
(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+ # Month
|
59
|
+
\d{2,4}\s+ # Year
|
60
|
+
\d{2}:\d{2} # Time in HH:MM
|
61
|
+
(?::\d{2})? # Optional seconds
|
62
|
+
\s+ # Space
|
63
|
+
(?: # Time zone
|
64
|
+
[+-]\d{4} | # Offset (e.g., +0000)
|
65
|
+
GMT | UTC # Literal GMT or UTC
|
66
|
+
)
|
67
|
+
\z/x #: Regexp
|
68
|
+
|
69
|
+
# Matches American-style dates with optional time in 12-hour or 24-hour formats, including:
|
70
|
+
# - `01/31/2024`
|
71
|
+
# - `01/31/2024 12:30:00`
|
72
|
+
# - `01/31/2024 12:30 PM`
|
73
|
+
# @return [Regexp]
|
74
|
+
AMERICAN = %r{\A\d{1,2}/\d{1,2}/\d{2,4}(?: \d{1,2}:\d{1,2}(?::\d{1,2})?(?:\s*[AaPp][Mm])?)?\z} #: Regexp
|
75
|
+
|
76
|
+
# Matches European-style dates with optional time in 24-hour format, including:
|
77
|
+
# - `31.01.2024`
|
78
|
+
# - `31.01.2024 12:30:00`
|
79
|
+
# @return [Regexp]
|
80
|
+
EUROPEAN = /\A\d{1,2}\.\d{1,2}\.\d{2,4}(?: \d{1,2}:\d{2}(?::\d{2})?)?\z/ #: Regexp
|
81
|
+
|
82
|
+
# Matches datetime formats with full month names, including:
|
83
|
+
# - `January 31, 2024 12:00:00`
|
84
|
+
# - `January 31, 2024 12:00`
|
85
|
+
# @return [Regexp]
|
86
|
+
FULL_MONTH_NAME = /\A
|
87
|
+
(?: # Full month name
|
88
|
+
(?:January|February|March|April|May|June|July|August|September|October|November|December)
|
89
|
+
)\s+\d{1,2},\s+\d{4}(?:\s+\d{1,2}:\d{2}(?::\d{2})?)?
|
90
|
+
\z/x #: Regexp
|
91
|
+
|
92
|
+
# Matches datetime formats with abbreviated month names, including:
|
93
|
+
# - `Jan 31, 2024 12:00:00`
|
94
|
+
# - `Jan 31, 2024 12:00`
|
95
|
+
# @return [Regexp]
|
96
|
+
ABBREVIATED_MONTH_NAME = /\A
|
97
|
+
\d{1,2}\s+(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec),\s+\d{4}(?:\s+\d{1,2}:\d{2}(?::\d{2})?)?
|
98
|
+
\z/x #: Regexp
|
99
|
+
|
100
|
+
# Matches datetime formats using a 12-hour clock with AM/PM, including:
|
101
|
+
# - `2024-01-01 01:30:00 PM`
|
102
|
+
# - `2024-01-01 01:30 PM`
|
103
|
+
# @return [Regexp]
|
104
|
+
TWELVE_HOUR_FORMAT = /\A\d{4}-\d{2}-\d{2}\s+\d{1,2}:\d{2}(?::\d{2})?\s+[APap][Mm]\z/x #: Regexp
|
105
|
+
end
|
106
|
+
private_constant :Format
|
107
|
+
|
108
|
+
intrinsically_constrain :self, :type, String, abort_on_failure: true, description: :not_described
|
109
|
+
intrinsically_constrain :self, :match_pattern,
|
110
|
+
Regexp.union(Format.constants.map { |name| Format.const_get(name) }),
|
111
|
+
abort_on_failure: true, concerning: :format, description: :not_described
|
112
|
+
|
113
|
+
# Constrain the type to match American-style datetime formats.
|
114
|
+
#
|
115
|
+
# @return [self] self for method chaining
|
116
|
+
# @rbs () -> self
|
117
|
+
def having_american_format
|
118
|
+
constrain :self, :match_pattern, Format::AMERICAN, concerning: :format
|
119
|
+
end
|
120
|
+
alias american having_american_format
|
121
|
+
alias having_us_format having_american_format
|
122
|
+
alias us_format having_american_format
|
123
|
+
|
124
|
+
# Constrain the type to match European-style datetime formats.
|
125
|
+
#
|
126
|
+
# @return [self] self for method chaining
|
127
|
+
# @rbs () -> self
|
128
|
+
def having_european_format
|
129
|
+
constrain :self, :match_pattern, Format::EUROPEAN, concerning: :format
|
130
|
+
end
|
131
|
+
alias european having_european_format
|
132
|
+
alias having_eu_format having_european_format
|
133
|
+
alias eu_format having_european_format
|
134
|
+
|
135
|
+
# Constrain the type to match ISO 8601 datetime formats.
|
136
|
+
#
|
137
|
+
# @return [self] self for method chaining
|
138
|
+
# @rbs () -> self
|
139
|
+
def having_iso8601_format
|
140
|
+
constrain :self, :match_pattern, Format::ISO8601, concerning: :format
|
141
|
+
end
|
142
|
+
alias iso8601 having_iso8601_format
|
143
|
+
alias iso8601_format having_iso8601_format
|
144
|
+
|
145
|
+
# Constrain the type to match RFC2822 datetime formats.
|
146
|
+
#
|
147
|
+
# @return [self] self for method chaining
|
148
|
+
# @rbs () -> self
|
149
|
+
def having_rfc2822_format
|
150
|
+
constrain :self, :match_pattern, Format::RFC2822, concerning: :format
|
151
|
+
end
|
152
|
+
alias rfc2822 having_rfc2822_format
|
153
|
+
alias rfc2822_format having_rfc2822_format
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|