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
@@ -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
|