domainic-type 0.1.0.alpha.3.1.0 → 0.1.0.alpha.3.2.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/lib/domainic/type/behavior/date_time_behavior.rb +178 -0
- data/lib/domainic/type/behavior/sizable_behavior.rb +2 -2
- data/lib/domainic/type/config/registry.yml +9 -0
- data/lib/domainic/type/constraint/constraints/range_constraint.rb +66 -23
- data/lib/domainic/type/definitions.rb +129 -32
- data/lib/domainic/type/types/datetime/date_time_type.rb +41 -0
- data/lib/domainic/type/types/datetime/date_type.rb +42 -0
- data/lib/domainic/type/types/datetime/time_type.rb +41 -0
- data/lib/domainic/type/types/identifier/cuid_type.rb +1 -1
- data/lib/domainic/type/types/identifier/uuid_type.rb +1 -1
- data/lib/domainic/type/types/network/email_address_type.rb +1 -1
- data/lib/domainic/type/types/network/hostname_type.rb +1 -1
- data/lib/domainic/type/types/network/uri_type.rb +1 -1
- data/lib/domainic/type/types/specialized/instance_type.rb +1 -1
- data/sig/domainic/type/behavior/date_time_behavior.rbs +115 -0
- data/sig/domainic/type/behavior/sizable_behavior.rbs +1 -1
- data/sig/domainic/type/constraint/constraints/range_constraint.rbs +50 -15
- data/sig/domainic/type/definitions.rbs +107 -27
- data/sig/domainic/type/types/datetime/date_time_type.rbs +34 -0
- data/sig/domainic/type/types/datetime/date_type.rbs +35 -0
- data/sig/domainic/type/types/datetime/time_type.rbs +34 -0
- data/sig/manifest.yaml +3 -0
- metadata +13 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 24abfbef9f478a43dce3382a737ceec2ea4310f69454d5d3e8903699f97454a0
|
4
|
+
data.tar.gz: aaede220a86b78362f4dd2a8e51ffdb320ba7352a88b4980dec244e7452662fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22de008f6805303b6540c879a35a11366c41e49be7dc7ca76347436afaedf256e5b19d8de9242e0351ab0ba78bb6eebf6ad3a202c105b1523ba414836e3a5c64
|
7
|
+
data.tar.gz: 1eb03da0104dcd61ca5149471ae71f774206f342e01d23e73e71917db7189610346d7432d6f9ef9c0ef5a3f92767ec048905adb2004578d99fc7be82faf6c2b6
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
module Domainic
|
6
|
+
module Type
|
7
|
+
module Behavior
|
8
|
+
# A module providing date/time validation constraint methods
|
9
|
+
#
|
10
|
+
# This module extends types with methods for constraining date/time values based on
|
11
|
+
# chronological relationships. It provides a fluent interface for building complex
|
12
|
+
# date/time validation constraints using natural language methods.
|
13
|
+
#
|
14
|
+
# @example Basic date comparisons
|
15
|
+
# type = DateType.new
|
16
|
+
# .being_after(Date.new(2024, 1, 1))
|
17
|
+
# .being_before(Date.new(2024, 12, 31))
|
18
|
+
#
|
19
|
+
# @example Range-based validation
|
20
|
+
# type = DateType.new
|
21
|
+
# .being_between(Date.new(2024, 1, 1), Date.new(2024, 12, 31))
|
22
|
+
#
|
23
|
+
# @example Inclusive/exclusive bounds
|
24
|
+
# type = DateType.new
|
25
|
+
# .being_on_or_after(Date.new(2024, 1, 1))
|
26
|
+
# .being_on_or_before(Date.new(2024, 12, 31))
|
27
|
+
#
|
28
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
29
|
+
# @since 0.1.0
|
30
|
+
module DateTimeBehavior
|
31
|
+
TO_DATETIME_COERCER = lambda { |value|
|
32
|
+
if [Date, DateTime, Time].any? { |type| value.is_a?(type) }
|
33
|
+
value
|
34
|
+
else
|
35
|
+
DateTime.parse(value.to_s)
|
36
|
+
end
|
37
|
+
} #: Proc
|
38
|
+
|
39
|
+
class << self
|
40
|
+
private
|
41
|
+
|
42
|
+
# Parse arguments for being_between
|
43
|
+
#
|
44
|
+
# @note this in my opinion is better than polluting the namespace of the including class even with a private
|
45
|
+
# method. This way, the method is only available within the module itself. See {#being_between}.
|
46
|
+
#
|
47
|
+
# @param after [Date, DateTime, String, Time, nil] minimum size value from positional args
|
48
|
+
# @param before [Date, DateTime, String, Time, nil] maximum size value from positional args
|
49
|
+
# @param options [Hash] keyword arguments containing after/before values
|
50
|
+
#
|
51
|
+
# @raise [ArgumentError] if minimum or maximum value can't be determined
|
52
|
+
# @return [Array<Date, DateTime, String, Time, nil>] parsed [after, before] values
|
53
|
+
# @rbs (
|
54
|
+
# (Date | DateTime | String | Time)? after,
|
55
|
+
# (Date | DateTime | String | Time)? before,
|
56
|
+
# Hash[Symbol, (Date | DateTime | String | Time)?] options
|
57
|
+
# ) -> Array[(Date | DateTime | String | Time)?]
|
58
|
+
def parse_being_between_arguments!(after, before, options)
|
59
|
+
after ||= options[:after]
|
60
|
+
before ||= options[:before]
|
61
|
+
raise_being_between_argument_error!(caller, after, before, options) if after.nil? || before.nil?
|
62
|
+
|
63
|
+
[after, before] #: Array[(Date | DateTime | String | Time)?]
|
64
|
+
end
|
65
|
+
|
66
|
+
# Raise appropriate ArgumentError for being_between
|
67
|
+
#
|
68
|
+
# @param original_caller [Array<String>] caller stack for error
|
69
|
+
# @param after [Date, DateTime, String, Time, nil] after value from positional args
|
70
|
+
# @param before [Date, DateTime, String, Time, nil] before value from positional args
|
71
|
+
# @param options [Hash] keyword arguments containing after/before values
|
72
|
+
#
|
73
|
+
# @raise [ArgumentError] with appropriate message
|
74
|
+
# @return [void]
|
75
|
+
# @rbs (
|
76
|
+
# Array[String] original_caller,
|
77
|
+
# (Date | DateTime | String | Time)? after,
|
78
|
+
# (Date | DateTime | String | Time)? before,
|
79
|
+
# Hash[Symbol, (Date | DateTime | String | Time)?] options
|
80
|
+
# ) -> void
|
81
|
+
def raise_being_between_argument_error!(original_caller, after, before, options)
|
82
|
+
message = if options.empty?
|
83
|
+
"wrong number of arguments (given #{[after, before].compact.count}, expected 2)"
|
84
|
+
else
|
85
|
+
"missing keyword: :#{%i[after before].find { |key| !options.key?(key) }}"
|
86
|
+
end
|
87
|
+
|
88
|
+
error = ArgumentError.new(message)
|
89
|
+
error.set_backtrace(original_caller)
|
90
|
+
raise error
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Constrain the value to be chronologically after a given date/time
|
95
|
+
#
|
96
|
+
# @param other [Date, DateTime, String, Time] the date/time to compare against
|
97
|
+
# @return [self] self for method chaining
|
98
|
+
# @rbs (Date | DateTime | String | Time other) -> Behavior
|
99
|
+
def being_after(other)
|
100
|
+
# @type self: Object & Behavior
|
101
|
+
constrain :self, :range, { minimum: other },
|
102
|
+
coerce_with: TO_DATETIME_COERCER, description: 'being', inclusive: false
|
103
|
+
end
|
104
|
+
alias after being_after
|
105
|
+
|
106
|
+
# Constrain the value to be chronologically before a given date/time
|
107
|
+
#
|
108
|
+
# @param other [Date, DateTime, String, Time] the date/time to compare against
|
109
|
+
# @return [self] self for method chaining
|
110
|
+
# @rbs (Date | DateTime | String | Time other) -> Behavior
|
111
|
+
def being_before(other)
|
112
|
+
# @type self: Object & Behavior
|
113
|
+
constrain :self, :range, { maximum: other },
|
114
|
+
coerce_with: TO_DATETIME_COERCER, description: 'being', inclusive: false
|
115
|
+
end
|
116
|
+
alias before being_before
|
117
|
+
|
118
|
+
# Constrain the value to be chronologically between two date/times
|
119
|
+
#
|
120
|
+
# @param after [Date, DateTime, String, Time] the earliest allowed date/time
|
121
|
+
# @param before [Date, DateTime, String, Time] the latest allowed date/time
|
122
|
+
# @param options [Hash] alternative way to specify after/before via keywords
|
123
|
+
# @option options [Date, DateTime, String, Time] :after earliest allowed date/time
|
124
|
+
# @option options [Date, DateTime, String, Time] :before latest allowed date/time
|
125
|
+
# @return [self] self for method chaining
|
126
|
+
# @rbs (Date | DateTime | String | Time after, Date | DateTime | String | Time before) -> Behavior
|
127
|
+
def being_between(after = nil, before = nil, **options)
|
128
|
+
# @type self: Object & Behavior
|
129
|
+
after, before =
|
130
|
+
DateTimeBehavior.send(:parse_being_between_arguments!, after, before, options.transform_keys(&:to_sym))
|
131
|
+
constrain :self, :range, { minimum: after, maximum: before },
|
132
|
+
coerce_with: TO_DATETIME_COERCER, description: 'being', inclusive: false
|
133
|
+
end
|
134
|
+
alias between being_between
|
135
|
+
|
136
|
+
# Constrain the value to be exactly equal to a given date/time
|
137
|
+
#
|
138
|
+
# @param other [Date, DateTime, String, Time] the date/time to compare against
|
139
|
+
# @return [self] self for method chaining
|
140
|
+
# @rbs (Date | DateTime | String | Time other) -> Behavior
|
141
|
+
def being_equal_to(other)
|
142
|
+
# @type self: Object & Behavior
|
143
|
+
constrain :self, :equality, other,
|
144
|
+
coerce_with: TO_DATETIME_COERCER, description: 'being'
|
145
|
+
end
|
146
|
+
alias at being_equal_to
|
147
|
+
|
148
|
+
# Constrain the value to be chronologically on or after a given date/time
|
149
|
+
#
|
150
|
+
# @param other [Date, DateTime, String, Time] the date/time to compare against
|
151
|
+
# @return [self] self for method chaining
|
152
|
+
# @rbs (Date | DateTime | String | Time other) -> Behavior
|
153
|
+
def being_on_or_after(other)
|
154
|
+
# @type self: Object & Behavior
|
155
|
+
constrain :self, :range, { minimum: other },
|
156
|
+
coerce_with: TO_DATETIME_COERCER, description: 'being'
|
157
|
+
end
|
158
|
+
alias at_or_after being_on_or_after
|
159
|
+
alias being_at_or_after being_on_or_after
|
160
|
+
alias on_or_after being_on_or_after
|
161
|
+
|
162
|
+
# Constrain the value to be chronologically on or before a given date/time
|
163
|
+
#
|
164
|
+
# @param other [Date, DateTime, String, Time] the date/time to compare against
|
165
|
+
# @return [self] self for method chaining
|
166
|
+
# @rbs (Date | DateTime | String | Time other) -> Behavior
|
167
|
+
def being_on_or_before(other)
|
168
|
+
# @type self: Object & Behavior
|
169
|
+
constrain :self, :range, { maximum: other },
|
170
|
+
coerce_with: TO_DATETIME_COERCER, description: 'being'
|
171
|
+
end
|
172
|
+
alias at_or_before being_on_or_before
|
173
|
+
alias being_at_or_before being_on_or_before
|
174
|
+
alias on_or_before being_on_or_before
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -75,7 +75,7 @@ module Domainic
|
|
75
75
|
# @raise [ArgumentError] if minimum or maximum value can't be determined
|
76
76
|
# @return [Array<Integer>] parsed [minimum, maximum] values
|
77
77
|
# @rbs (Integer? minimum, Integer? maximum, Hash[Symbol, Integer?] options) -> Array[Integer]
|
78
|
-
def parse_having_between_arguments(minimum, maximum, options)
|
78
|
+
def parse_having_between_arguments!(minimum, maximum, options)
|
79
79
|
min = minimum || options[:min] || options[:minimum]
|
80
80
|
max = maximum || options[:max] || options[:maximum]
|
81
81
|
raise_having_between_argument_error!(caller, min, max, options) if min.nil? || max.nil?
|
@@ -231,7 +231,7 @@ module Domainic
|
|
231
231
|
def having_size_between(minimum = nil, maximum = nil, **options)
|
232
232
|
# @type self: Object & Behavior
|
233
233
|
min, max =
|
234
|
-
SizableBehavior.send(:parse_having_between_arguments
|
234
|
+
SizableBehavior.send(:parse_having_between_arguments!, minimum, maximum, options.transform_keys(&:to_sym))
|
235
235
|
constrain :size, :range, { minimum: min, maximum: max },
|
236
236
|
concerning: :size, description: "having #{__callee__.to_s.split('_').[](1)}", inclusive: false
|
237
237
|
end
|
@@ -81,6 +81,12 @@ types:
|
|
81
81
|
cuid:
|
82
82
|
constant: Domainic::Type::CUIDType
|
83
83
|
require_path: domainic/type/types/identifier/cuid_type
|
84
|
+
date:
|
85
|
+
constant: Domainic::Type::DateType
|
86
|
+
require_path: domainic/type/types/datetime/date_type
|
87
|
+
date_time:
|
88
|
+
constant: Domainic::Type::DateTimeType
|
89
|
+
require_path: domainic/type/types/datetime/date_time_type
|
84
90
|
duck:
|
85
91
|
constant: Domainic::Type::DuckType
|
86
92
|
require_path: domainic/type/types/specification/duck_type
|
@@ -111,6 +117,9 @@ types:
|
|
111
117
|
symbol:
|
112
118
|
constant: Domainic::Type::SymbolType
|
113
119
|
require_path: domainic/type/types/core/symbol_type
|
120
|
+
time:
|
121
|
+
constant: Domainic::Type::TimeType
|
122
|
+
require_path: domainic/type/types/datetime/time_type
|
114
123
|
union:
|
115
124
|
constant: Domainic::Type::UnionType
|
116
125
|
require_path: domainic/type/types/specification/union_type
|
@@ -5,36 +5,56 @@ require 'domainic/type/constraint/behavior'
|
|
5
5
|
module Domainic
|
6
6
|
module Type
|
7
7
|
module Constraint
|
8
|
-
# A constraint for validating that
|
8
|
+
# A constraint for validating that comparable values fall within a specified range.
|
9
9
|
#
|
10
|
-
# This constraint allows for validating
|
11
|
-
# boundaries. It supports specifying either
|
12
|
-
# open-ended ranges when appropriate.
|
10
|
+
# This constraint allows for validating any values that implement comparison operators
|
11
|
+
# (<, <=, >, >=) against minimum and maximum boundaries. It supports specifying either
|
12
|
+
# or both boundaries, allowing for open-ended ranges when appropriate. This makes it
|
13
|
+
# suitable for numeric ranges, date/time ranges, or any other comparable types.
|
13
14
|
#
|
14
|
-
# @example Validating
|
15
|
+
# @example Validating numeric ranges
|
15
16
|
# constraint = RangeConstraint.new(:self, { minimum: 1, maximum: 10 })
|
16
17
|
# constraint.satisfied?(5) # => true
|
17
18
|
# constraint.satisfied?(15) # => false
|
18
19
|
#
|
19
|
-
# @example Validating
|
20
|
-
# constraint = RangeConstraint.new(:self, {
|
21
|
-
#
|
22
|
-
#
|
20
|
+
# @example Validating date ranges
|
21
|
+
# constraint = RangeConstraint.new(:self, {
|
22
|
+
# minimum: Date.new(2024, 1, 1),
|
23
|
+
# maximum: Date.new(2024, 12, 31)
|
24
|
+
# })
|
25
|
+
# constraint.satisfied?(Date.new(2024, 6, 15)) # => true
|
26
|
+
# constraint.satisfied?(Date.new(2023, 12, 31)) # => false
|
23
27
|
#
|
24
|
-
# @example Validating with only
|
25
|
-
# constraint = RangeConstraint.new(:self, {
|
26
|
-
# constraint.satisfied?(
|
27
|
-
# constraint.satisfied?(
|
28
|
+
# @example Validating with only minimum
|
29
|
+
# constraint = RangeConstraint.new(:self, { minimum: Time.now })
|
30
|
+
# constraint.satisfied?(Time.now + 3600) # => true
|
31
|
+
# constraint.satisfied?(Time.now - 3600) # => false
|
28
32
|
#
|
29
33
|
# @author {https://aaronmallen.me Aaron Allen}
|
30
34
|
# @since 0.1.0
|
31
35
|
class RangeConstraint
|
32
36
|
# @rbs!
|
33
|
-
#
|
37
|
+
# interface _Compatible
|
38
|
+
# def <: (untyped other) -> bool
|
39
|
+
#
|
40
|
+
# def <=: (untyped other) -> bool
|
41
|
+
#
|
42
|
+
# def >: (untyped other) -> bool
|
43
|
+
#
|
44
|
+
# def >=: (untyped other) -> bool
|
45
|
+
#
|
46
|
+
# def inspect: () -> untyped
|
47
|
+
#
|
48
|
+
# def nil?: () -> bool
|
49
|
+
#
|
50
|
+
# def send: (Symbol method_name, *untyped arguments, **untyped keyword_arguments) -> untyped
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# type expected = { ?minimum: _Compatible, ?maximum: _Compatible}
|
34
54
|
#
|
35
55
|
# type options = { ?inclusive: bool }
|
36
56
|
|
37
|
-
include Behavior #[expected,
|
57
|
+
include Behavior #[expected, _Compatible, options]
|
38
58
|
|
39
59
|
# Get a human-readable description of the range constraint.
|
40
60
|
#
|
@@ -55,8 +75,8 @@ module Domainic
|
|
55
75
|
# @rbs override
|
56
76
|
def short_description
|
57
77
|
min, max = @expected.values_at(:minimum, :maximum)
|
58
|
-
min_description = "greater than or equal to #{min}"
|
59
|
-
max_description = "less than or equal to #{max}"
|
78
|
+
min_description = "greater than#{inclusive? ? ' or equal to' : ''} #{min}"
|
79
|
+
max_description = "less than#{inclusive? ? ' or equal to' : ''} #{max}"
|
60
80
|
|
61
81
|
return "#{min_description} and #{max_description}" unless min.nil? || max.nil?
|
62
82
|
return min_description unless min.nil?
|
@@ -91,10 +111,10 @@ module Domainic
|
|
91
111
|
# @rbs override
|
92
112
|
def satisfies_constraint?
|
93
113
|
min, max = @expected.values_at(:minimum, :maximum)
|
94
|
-
min_comparison, max_comparison =
|
114
|
+
min_comparison, max_comparison = inclusive? ? %i[>= <=] : %i[> <]
|
95
115
|
|
96
|
-
@actual.send(min_comparison,
|
97
|
-
@actual.send(max_comparison,
|
116
|
+
(min.nil? ? true : @actual.send(min_comparison, min)) &&
|
117
|
+
(max.nil? ? true : @actual.send(max_comparison, max))
|
98
118
|
end
|
99
119
|
|
100
120
|
# Validate that the expected value is a properly formatted range specification.
|
@@ -112,6 +132,16 @@ module Domainic
|
|
112
132
|
validate_minimum_and_maximum!(expectation)
|
113
133
|
end
|
114
134
|
|
135
|
+
private
|
136
|
+
|
137
|
+
# Check if the range constraint is inclusive.
|
138
|
+
#
|
139
|
+
# @return [Boolean] `true` if the range is inclusive, `false` otherwise
|
140
|
+
# @rbs () -> bool
|
141
|
+
def inclusive?
|
142
|
+
@options.fetch(:inclusive, true) #: bool
|
143
|
+
end
|
144
|
+
|
115
145
|
# Validate the minimum and maximum values in a range specification.
|
116
146
|
#
|
117
147
|
# @param expectation [Hash] The range specification to validate
|
@@ -120,15 +150,28 @@ module Domainic
|
|
120
150
|
# @return [void]
|
121
151
|
# @rbs (untyped expectation) -> void
|
122
152
|
def validate_minimum_and_maximum!(expectation)
|
123
|
-
expectation
|
124
|
-
raise ArgumentError, ":#{property} must be a Numeric" unless value.nil? || value.is_a?(Numeric)
|
125
|
-
end
|
153
|
+
validate_minimum_and_maximum_types!(expectation)
|
126
154
|
|
127
155
|
min, max = expectation.values_at(:minimum, :maximum)
|
128
156
|
return if min.nil? || max.nil? || min <= max
|
129
157
|
|
130
158
|
raise ArgumentError, ':minimum must be less than or equal to :maximum'
|
131
159
|
end
|
160
|
+
|
161
|
+
# Validate the minimum and maximum value types are compatible with the constraint.
|
162
|
+
#
|
163
|
+
# @param expectation [Hash] The range specification to validate
|
164
|
+
#
|
165
|
+
# @raise [ArgumentError] if the values are invalid
|
166
|
+
# @return [void]
|
167
|
+
# @rbs (untyped expectation) -> void
|
168
|
+
def validate_minimum_and_maximum_types!(expectation)
|
169
|
+
expectation.each_pair do |property, value|
|
170
|
+
unless value.nil? || (%i[< <= > >=].all? { |m| value.respond_to?(m) })
|
171
|
+
raise ArgumentError, ":#{property}: #{value} is not compatible with RangeConstraint"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
132
175
|
end
|
133
176
|
end
|
134
177
|
end
|
@@ -133,6 +133,68 @@ module Domainic
|
|
133
133
|
end
|
134
134
|
alias _Cuid? _CUID?
|
135
135
|
|
136
|
+
# Creates a DateType instance.
|
137
|
+
#
|
138
|
+
# DateType restricts values to valid `Date` objects.
|
139
|
+
#
|
140
|
+
# @example
|
141
|
+
# type = _Date
|
142
|
+
# type.validate(Date.new(2024, 1, 1)) # => true
|
143
|
+
#
|
144
|
+
# @param options [Hash] additional configuration options
|
145
|
+
#
|
146
|
+
# @return [Domainic::Type::DateType] the created type
|
147
|
+
# @rbs (**__todo__ options) -> DateType
|
148
|
+
def _Date(**options)
|
149
|
+
require 'domainic/type/types/datetime/date_type'
|
150
|
+
Domainic::Type::DateType.new(**options)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Creates a nilable DateType instance.
|
154
|
+
#
|
155
|
+
# @example
|
156
|
+
# _Date? === Date.new(2024, 1, 1) # => true
|
157
|
+
# _Date? === nil # => true
|
158
|
+
#
|
159
|
+
# @param options [Hash] additional configuration options
|
160
|
+
#
|
161
|
+
# @return [Domainic::Type::UnionType] the created type (DateType or NilClass)
|
162
|
+
# @rbs (**__todo__ options) -> UnionType
|
163
|
+
def _Date?(**options)
|
164
|
+
_Nilable(_Date(**options))
|
165
|
+
end
|
166
|
+
|
167
|
+
# Creates a DateTimeType instance.
|
168
|
+
#
|
169
|
+
# DateTimeType restricts values to valid `DateTime` objects.
|
170
|
+
#
|
171
|
+
# @example
|
172
|
+
# type = _DateTime
|
173
|
+
# type.validate(DateTime.new(2024, 1, 1, 12, 0, 0)) # => true
|
174
|
+
#
|
175
|
+
# @param options [Hash] additional configuration options
|
176
|
+
#
|
177
|
+
# @return [Domainic::Type::DateTimeType] the created type
|
178
|
+
# @rbs (**__todo__ options) -> DateTimeType
|
179
|
+
def _DateTime(**options)
|
180
|
+
require 'domainic/type/types/datetime/date_time_type'
|
181
|
+
Domainic::Type::DateTimeType.new(**options)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Creates a nilable DateTimeType instance.
|
185
|
+
#
|
186
|
+
# @example
|
187
|
+
# _DateTime? === DateTime.new(2024, 1, 1, 12, 0, 0) # => true
|
188
|
+
# _DateTime? === nil # => true
|
189
|
+
#
|
190
|
+
# @param options [Hash] additional configuration options
|
191
|
+
#
|
192
|
+
# @return [Domainic::Type::UnionType] the created type (DateTimeType or NilClass)
|
193
|
+
# @rbs (**__todo__ options) -> UnionType
|
194
|
+
def _DateTime?(**options)
|
195
|
+
_Nilable(_DateTime(**options))
|
196
|
+
end
|
197
|
+
|
136
198
|
# Creates a DuckType instance.
|
137
199
|
#
|
138
200
|
# DuckType allows specifying behavior based on method availability.
|
@@ -475,6 +537,73 @@ module Domainic
|
|
475
537
|
end
|
476
538
|
alias _Interned? _Symbol?
|
477
539
|
|
540
|
+
# Creates a TimeType instance.
|
541
|
+
#
|
542
|
+
# TimeType restricts values to valid `Time` objects.
|
543
|
+
#
|
544
|
+
# @example
|
545
|
+
# type = _Time
|
546
|
+
# type.validate(Time.new(2024, 1, 1, 12, 0, 0)) # => true
|
547
|
+
#
|
548
|
+
# @param options [Hash] additional configuration options
|
549
|
+
#
|
550
|
+
# @return [Domainic::Type::TimeType] the created type
|
551
|
+
# @rbs (**__todo__ options) -> TimeType
|
552
|
+
def _Time(**options)
|
553
|
+
require 'domainic/type/types/datetime/time_type'
|
554
|
+
Domainic::Type::TimeType.new(**options)
|
555
|
+
end
|
556
|
+
|
557
|
+
# Creates a nilable TimeType instance.
|
558
|
+
#
|
559
|
+
# @example
|
560
|
+
# _Time? === Time.new(2024, 1, 1, 12, 0, 0) # => true
|
561
|
+
# _Time? === nil # => true
|
562
|
+
#
|
563
|
+
# @param options [Hash] additional configuration options
|
564
|
+
#
|
565
|
+
# @return [Domainic::Type::UnionType] the created type (TimeType or NilClass)
|
566
|
+
# @rbs (**__todo__ options) -> UnionType
|
567
|
+
def _Time?(**options)
|
568
|
+
_Nilable(_Time(**options))
|
569
|
+
end
|
570
|
+
|
571
|
+
# Creates a URIType instance.
|
572
|
+
#
|
573
|
+
# URIType restricts values to valid URIs.
|
574
|
+
#
|
575
|
+
# @example
|
576
|
+
# type = _URI.having_scheme('https')
|
577
|
+
#
|
578
|
+
# @param options [Hash] additional configuration options
|
579
|
+
#
|
580
|
+
# @return [Domainic::Type::URIType] the created type
|
581
|
+
# @rbs (**__todo__ options) -> URIType
|
582
|
+
def _URI(**options)
|
583
|
+
require 'domainic/type/types/network/uri_type'
|
584
|
+
Domainic::Type::URIType.new(**options)
|
585
|
+
end
|
586
|
+
alias _URL _URI
|
587
|
+
alias _Url _URI
|
588
|
+
alias _Uri _URI
|
589
|
+
|
590
|
+
# Creates a nilable URIType instance.
|
591
|
+
#
|
592
|
+
# @example
|
593
|
+
# _Uri?.validate("https://example.com") # => true
|
594
|
+
# _Uri?.validate(nil) # => true
|
595
|
+
#
|
596
|
+
# @param options [Hash] additional configuration options
|
597
|
+
#
|
598
|
+
# @return [Domainic::Type::UnionType] the created type (URIType or NilClass)
|
599
|
+
# @rbs (**__todo__ options) -> UnionType
|
600
|
+
def _URI?(**options)
|
601
|
+
_Nilable(_Uri(**options))
|
602
|
+
end
|
603
|
+
alias _URL? _URI?
|
604
|
+
alias _Url? _URI?
|
605
|
+
alias _Uri? _URI?
|
606
|
+
|
478
607
|
# Creates a UUIDType instance
|
479
608
|
#
|
480
609
|
# @example
|
@@ -523,38 +652,6 @@ module Domainic
|
|
523
652
|
end
|
524
653
|
alias _Either _Union
|
525
654
|
|
526
|
-
# Creates a URIType instance.
|
527
|
-
#
|
528
|
-
# URIType restricts values to valid URIs.
|
529
|
-
#
|
530
|
-
# @example
|
531
|
-
# type = _URI.having_scheme('https')
|
532
|
-
#
|
533
|
-
# @param options [Hash] additional configuration options
|
534
|
-
#
|
535
|
-
# @return [Domainic::Type::URIType] the created type
|
536
|
-
# @rbs (**__todo__ options) -> URIType
|
537
|
-
def _Uri(**options)
|
538
|
-
require 'domainic/type/types/network/uri_type'
|
539
|
-
Domainic::Type::URIType.new(**options)
|
540
|
-
end
|
541
|
-
alias _Url _Uri
|
542
|
-
|
543
|
-
# Creates a nilable URIType instance.
|
544
|
-
#
|
545
|
-
# @example
|
546
|
-
# _Uri?.validate("https://example.com") # => true
|
547
|
-
# _Uri?.validate(nil) # => true
|
548
|
-
#
|
549
|
-
# @param options [Hash] additional configuration options
|
550
|
-
#
|
551
|
-
# @return [Domainic::Type::UnionType] the created type (URIType or NilClass)
|
552
|
-
# @rbs (**__todo__ options) -> UnionType
|
553
|
-
def _Uri?(**options)
|
554
|
-
_Nilable(_Uri(**options))
|
555
|
-
end
|
556
|
-
alias _Url? _Uri?
|
557
|
-
|
558
655
|
# Creates a VoidType instance.
|
559
656
|
#
|
560
657
|
# Represents an operation that returns no value.
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
require 'domainic/type/behavior'
|
5
|
+
require 'domainic/type/behavior/date_time_behavior'
|
6
|
+
|
7
|
+
module Domainic
|
8
|
+
module Type
|
9
|
+
# A type for validating DateTime objects
|
10
|
+
#
|
11
|
+
# This type ensures the value is a `DateTime` object and supports comprehensive
|
12
|
+
# date-time validation through `DateTimeBehavior`. It is ideal for scenarios requiring
|
13
|
+
# precise date-time constraints.
|
14
|
+
#
|
15
|
+
# Key features:
|
16
|
+
# - Ensures the value is a `DateTime` object
|
17
|
+
# - Supports chronological relationship validation (e.g., before, after)
|
18
|
+
# - Provides range and equality checks
|
19
|
+
#
|
20
|
+
# @example Basic usage
|
21
|
+
# type = DateTimeType.new
|
22
|
+
# type.validate(DateTime.now) # => true
|
23
|
+
# type.validate(Date.today) # => false
|
24
|
+
#
|
25
|
+
# @example Range validation
|
26
|
+
# type = DateTimeType.new
|
27
|
+
# .being_between(DateTime.new(2024, 1, 1, 0, 0, 0), DateTime.new(2024, 12, 31, 23, 59, 59))
|
28
|
+
# type.validate(DateTime.new(2024, 6, 15, 12, 0, 0)) # => true
|
29
|
+
#
|
30
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
31
|
+
# @since 0.1.0
|
32
|
+
class DateTimeType
|
33
|
+
# @rbs! extend Behavior::ClassMethods
|
34
|
+
|
35
|
+
include Behavior
|
36
|
+
include Behavior::DateTimeBehavior
|
37
|
+
|
38
|
+
intrinsically_constrain :self, :type, DateTime, abort_on_failure: true, description: :not_described
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
require 'domainic/type/behavior'
|
5
|
+
require 'domainic/type/behavior/date_time_behavior'
|
6
|
+
|
7
|
+
module Domainic
|
8
|
+
module Type
|
9
|
+
# A type for validating Date objects
|
10
|
+
#
|
11
|
+
# This type provides robust validation for `Date` objects, ensuring values conform
|
12
|
+
# to specified chronological constraints. It supports the full range of date-based
|
13
|
+
# constraints provided by `DateTimeBehavior`.
|
14
|
+
#
|
15
|
+
# Key features:
|
16
|
+
# - Ensures the value is a `Date` object
|
17
|
+
# - Supports validation for chronological relationships (e.g., before, after)
|
18
|
+
# - Full integration with `DateTimeBehavior` for range and equality checks
|
19
|
+
#
|
20
|
+
# @example Basic usage
|
21
|
+
# type = DateType.new
|
22
|
+
# type.validate(Date.today) # => true
|
23
|
+
# type.validate(DateTime.now) # => false
|
24
|
+
#
|
25
|
+
# @example With range constraints
|
26
|
+
# type = DateType.new
|
27
|
+
# .being_between(Date.new(2024, 1, 1), Date.new(2024, 12, 31))
|
28
|
+
# type.validate(Date.new(2024, 6, 15)) # => true
|
29
|
+
# type.validate(Date.new(2023, 12, 31)) # => false
|
30
|
+
#
|
31
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
32
|
+
# @since 0.1.0
|
33
|
+
class DateType
|
34
|
+
# @rbs! extend Behavior::ClassMethods
|
35
|
+
|
36
|
+
include Behavior
|
37
|
+
include Behavior::DateTimeBehavior
|
38
|
+
|
39
|
+
intrinsically_constrain :self, :type, Date, abort_on_failure: true, description: :not_described
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
require 'domainic/type/behavior'
|
5
|
+
require 'domainic/type/behavior/date_time_behavior'
|
6
|
+
|
7
|
+
module Domainic
|
8
|
+
module Type
|
9
|
+
# A type for validating Time objects
|
10
|
+
#
|
11
|
+
# This type ensures the value is a `Time` object and integrates with
|
12
|
+
# `DateTimeBehavior` to provide a rich set of time-based validation capabilities.
|
13
|
+
#
|
14
|
+
# Key features:
|
15
|
+
# - Ensures the value is a `Time` object
|
16
|
+
# - Supports chronological relationship constraints (e.g., before, after)
|
17
|
+
# - Provides range and equality checks
|
18
|
+
#
|
19
|
+
# @example Basic usage
|
20
|
+
# type = TimeType.new
|
21
|
+
# type.validate(Time.now) # => true
|
22
|
+
# type.validate(Date.today) # => false
|
23
|
+
#
|
24
|
+
# @example Range validation
|
25
|
+
# type = TimeType.new
|
26
|
+
# .being_between(Time.now, Time.now + 3600)
|
27
|
+
# type.validate(Time.now + 1800) # => true
|
28
|
+
# type.validate(Time.now + 7200) # => false
|
29
|
+
#
|
30
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
31
|
+
# @since 0.1.0
|
32
|
+
class TimeType
|
33
|
+
# @rbs! extend Behavior::ClassMethods
|
34
|
+
|
35
|
+
include Behavior
|
36
|
+
include Behavior::DateTimeBehavior
|
37
|
+
|
38
|
+
intrinsically_constrain :self, :type, Time, abort_on_failure: true, description: :not_described
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -60,7 +60,7 @@ module Domainic
|
|
60
60
|
end
|
61
61
|
|
62
62
|
# Core CUID constraints
|
63
|
-
intrinsically_constrain :self, :type, String, description: :not_described
|
63
|
+
intrinsically_constrain :self, :type, String, abort_on_failure: true, description: :not_described
|
64
64
|
|
65
65
|
# Accept either v1 or v2 format by default
|
66
66
|
intrinsically_constrain :self, :match_pattern, CUID::ALL_REGEXP,
|
@@ -176,7 +176,7 @@ module Domainic
|
|
176
176
|
end
|
177
177
|
|
178
178
|
# Core UUID constraints based on RFC 4122
|
179
|
-
intrinsically_constrain :self, :type, String, description: :not_described
|
179
|
+
intrinsically_constrain :self, :type, String, abort_on_failure: true, description: :not_described
|
180
180
|
|
181
181
|
# The base UUID type should accept either standard or compact format
|
182
182
|
intrinsically_constrain :self, :match_pattern,
|
@@ -47,7 +47,7 @@ module Domainic
|
|
47
47
|
include Behavior::URIBehavior
|
48
48
|
|
49
49
|
# Core email constraints based on RFCs 5321 and 5322
|
50
|
-
intrinsically_constrain :self, :type, String, description: :not_described
|
50
|
+
intrinsically_constrain :self, :type, String, abort_on_failure: true, description: :not_described
|
51
51
|
intrinsically_constrain :self, :match_pattern, URI::MailTo::EMAIL_REGEXP, description: :not_described
|
52
52
|
intrinsically_constrain :length, :range, { maximum: 254 }, description: :not_described, concerning: :size
|
53
53
|
intrinsically_constrain :self, :character_set, :ascii, description: :not_described
|
@@ -60,7 +60,7 @@ module Domainic
|
|
60
60
|
\z/x #: Regexp
|
61
61
|
|
62
62
|
# Core hostname constraints based on RFC standards
|
63
|
-
intrinsically_constrain :self, :type, String, description: :not_described
|
63
|
+
intrinsically_constrain :self, :type, String, abort_on_failure: true, description: :not_described
|
64
64
|
intrinsically_constrain :self, :match_pattern, RFC_HOSTNAME_REGEXP, description: :not_described
|
65
65
|
intrinsically_constrain :length, :range, { maximum: 253 }, description: :not_described, concerning: :size
|
66
66
|
intrinsically_constrain :self, :character_set, :ascii, description: :not_described
|
@@ -52,7 +52,7 @@ module Domainic
|
|
52
52
|
URI_REGEXP = /\A#{URI::DEFAULT_PARSER.make_regexp}\z/ #: Regexp
|
53
53
|
|
54
54
|
# Core URI constraints based on RFC standards
|
55
|
-
intrinsically_constrain :self, :type, String, description: :not_described
|
55
|
+
intrinsically_constrain :self, :type, String, abort_on_failure: true, description: :not_described
|
56
56
|
intrinsically_constrain :self, :match_pattern, URI_REGEXP, description: :not_described
|
57
57
|
intrinsically_constrain :self, :character_set, :ascii, description: :not_described
|
58
58
|
|
@@ -66,7 +66,7 @@ module Domainic
|
|
66
66
|
# @return [self] self for method chaining
|
67
67
|
# @rbs (Class | Module | Behavior type) -> self
|
68
68
|
def of(type)
|
69
|
-
constrain :self, :instance_of, type, description: 'of'
|
69
|
+
constrain :self, :instance_of, type, abort_on_failure: true, description: 'of'
|
70
70
|
end
|
71
71
|
end
|
72
72
|
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Domainic
|
2
|
+
module Type
|
3
|
+
module Behavior
|
4
|
+
# A module providing date/time validation constraint methods
|
5
|
+
#
|
6
|
+
# This module extends types with methods for constraining date/time values based on
|
7
|
+
# chronological relationships. It provides a fluent interface for building complex
|
8
|
+
# date/time validation constraints using natural language methods.
|
9
|
+
#
|
10
|
+
# @example Basic date comparisons
|
11
|
+
# type = DateType.new
|
12
|
+
# .being_after(Date.new(2024, 1, 1))
|
13
|
+
# .being_before(Date.new(2024, 12, 31))
|
14
|
+
#
|
15
|
+
# @example Range-based validation
|
16
|
+
# type = DateType.new
|
17
|
+
# .being_between(Date.new(2024, 1, 1), Date.new(2024, 12, 31))
|
18
|
+
#
|
19
|
+
# @example Inclusive/exclusive bounds
|
20
|
+
# type = DateType.new
|
21
|
+
# .being_on_or_after(Date.new(2024, 1, 1))
|
22
|
+
# .being_on_or_before(Date.new(2024, 12, 31))
|
23
|
+
#
|
24
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
25
|
+
# @since 0.1.0
|
26
|
+
module DateTimeBehavior
|
27
|
+
TO_DATETIME_COERCER: Proc
|
28
|
+
|
29
|
+
# Parse arguments for being_between
|
30
|
+
#
|
31
|
+
# @note this in my opinion is better than polluting the namespace of the including class even with a private
|
32
|
+
# method. This way, the method is only available within the module itself. See {#being_between}.
|
33
|
+
#
|
34
|
+
# @param after [Date, DateTime, String, Time, nil] minimum size value from positional args
|
35
|
+
# @param before [Date, DateTime, String, Time, nil] maximum size value from positional args
|
36
|
+
# @param options [Hash] keyword arguments containing after/before values
|
37
|
+
#
|
38
|
+
# @raise [ArgumentError] if minimum or maximum value can't be determined
|
39
|
+
# @return [Array<Date, DateTime, String, Time, nil>] parsed [after, before] values
|
40
|
+
private def self.parse_being_between_arguments!: ((Date | DateTime | String | Time)? after, (Date | DateTime | String | Time)? before, Hash[Symbol, (Date | DateTime | String | Time)?] options) -> Array[(Date | DateTime | String | Time)?]
|
41
|
+
|
42
|
+
# Raise appropriate ArgumentError for being_between
|
43
|
+
#
|
44
|
+
# @param original_caller [Array<String>] caller stack for error
|
45
|
+
# @param after [Date, DateTime, String, Time, nil] after value from positional args
|
46
|
+
# @param before [Date, DateTime, String, Time, nil] before value from positional args
|
47
|
+
# @param options [Hash] keyword arguments containing after/before values
|
48
|
+
#
|
49
|
+
# @raise [ArgumentError] with appropriate message
|
50
|
+
# @return [void]
|
51
|
+
private def self.raise_being_between_argument_error!: (Array[String] original_caller, (Date | DateTime | String | Time)? after, (Date | DateTime | String | Time)? before, Hash[Symbol, (Date | DateTime | String | Time)?] options) -> void
|
52
|
+
|
53
|
+
# Constrain the value to be chronologically after a given date/time
|
54
|
+
#
|
55
|
+
# @param other [Date, DateTime, String, Time] the date/time to compare against
|
56
|
+
# @return [self] self for method chaining
|
57
|
+
def being_after: (Date | DateTime | String | Time other) -> Behavior
|
58
|
+
|
59
|
+
alias after being_after
|
60
|
+
|
61
|
+
# Constrain the value to be chronologically before a given date/time
|
62
|
+
#
|
63
|
+
# @param other [Date, DateTime, String, Time] the date/time to compare against
|
64
|
+
# @return [self] self for method chaining
|
65
|
+
def being_before: (Date | DateTime | String | Time other) -> Behavior
|
66
|
+
|
67
|
+
alias before being_before
|
68
|
+
|
69
|
+
# Constrain the value to be chronologically between two date/times
|
70
|
+
#
|
71
|
+
# @param after [Date, DateTime, String, Time] the earliest allowed date/time
|
72
|
+
# @param before [Date, DateTime, String, Time] the latest allowed date/time
|
73
|
+
# @param options [Hash] alternative way to specify after/before via keywords
|
74
|
+
# @option options [Date, DateTime, String, Time] :after earliest allowed date/time
|
75
|
+
# @option options [Date, DateTime, String, Time] :before latest allowed date/time
|
76
|
+
# @return [self] self for method chaining
|
77
|
+
def being_between: (Date | DateTime | String | Time after, Date | DateTime | String | Time before) -> Behavior
|
78
|
+
|
79
|
+
alias between being_between
|
80
|
+
|
81
|
+
# Constrain the value to be exactly equal to a given date/time
|
82
|
+
#
|
83
|
+
# @param other [Date, DateTime, String, Time] the date/time to compare against
|
84
|
+
# @return [self] self for method chaining
|
85
|
+
def being_equal_to: (Date | DateTime | String | Time other) -> Behavior
|
86
|
+
|
87
|
+
alias at being_equal_to
|
88
|
+
|
89
|
+
# Constrain the value to be chronologically on or after a given date/time
|
90
|
+
#
|
91
|
+
# @param other [Date, DateTime, String, Time] the date/time to compare against
|
92
|
+
# @return [self] self for method chaining
|
93
|
+
def being_on_or_after: (Date | DateTime | String | Time other) -> Behavior
|
94
|
+
|
95
|
+
alias at_or_after being_on_or_after
|
96
|
+
|
97
|
+
alias being_at_or_after being_on_or_after
|
98
|
+
|
99
|
+
alias on_or_after being_on_or_after
|
100
|
+
|
101
|
+
# Constrain the value to be chronologically on or before a given date/time
|
102
|
+
#
|
103
|
+
# @param other [Date, DateTime, String, Time] the date/time to compare against
|
104
|
+
# @return [self] self for method chaining
|
105
|
+
def being_on_or_before: (Date | DateTime | String | Time other) -> Behavior
|
106
|
+
|
107
|
+
alias at_or_before being_on_or_before
|
108
|
+
|
109
|
+
alias being_at_or_before being_on_or_before
|
110
|
+
|
111
|
+
alias on_or_before being_on_or_before
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -67,7 +67,7 @@ module Domainic
|
|
67
67
|
#
|
68
68
|
# @raise [ArgumentError] if minimum or maximum value can't be determined
|
69
69
|
# @return [Array<Integer>] parsed [minimum, maximum] values
|
70
|
-
private def self.parse_having_between_arguments
|
70
|
+
private def self.parse_having_between_arguments!: (Integer? minimum, Integer? maximum, Hash[Symbol, Integer?] options) -> Array[Integer]
|
71
71
|
|
72
72
|
# Raise appropriate ArgumentError for having_size_between
|
73
73
|
#
|
@@ -1,35 +1,55 @@
|
|
1
1
|
module Domainic
|
2
2
|
module Type
|
3
3
|
module Constraint
|
4
|
-
# A constraint for validating that
|
4
|
+
# A constraint for validating that comparable values fall within a specified range.
|
5
5
|
#
|
6
|
-
# This constraint allows for validating
|
7
|
-
# boundaries. It supports specifying either
|
8
|
-
# open-ended ranges when appropriate.
|
6
|
+
# This constraint allows for validating any values that implement comparison operators
|
7
|
+
# (<, <=, >, >=) against minimum and maximum boundaries. It supports specifying either
|
8
|
+
# or both boundaries, allowing for open-ended ranges when appropriate. This makes it
|
9
|
+
# suitable for numeric ranges, date/time ranges, or any other comparable types.
|
9
10
|
#
|
10
|
-
# @example Validating
|
11
|
+
# @example Validating numeric ranges
|
11
12
|
# constraint = RangeConstraint.new(:self, { minimum: 1, maximum: 10 })
|
12
13
|
# constraint.satisfied?(5) # => true
|
13
14
|
# constraint.satisfied?(15) # => false
|
14
15
|
#
|
15
|
-
# @example Validating
|
16
|
-
# constraint = RangeConstraint.new(:self, {
|
17
|
-
#
|
18
|
-
#
|
16
|
+
# @example Validating date ranges
|
17
|
+
# constraint = RangeConstraint.new(:self, {
|
18
|
+
# minimum: Date.new(2024, 1, 1),
|
19
|
+
# maximum: Date.new(2024, 12, 31)
|
20
|
+
# })
|
21
|
+
# constraint.satisfied?(Date.new(2024, 6, 15)) # => true
|
22
|
+
# constraint.satisfied?(Date.new(2023, 12, 31)) # => false
|
19
23
|
#
|
20
|
-
# @example Validating with only
|
21
|
-
# constraint = RangeConstraint.new(:self, {
|
22
|
-
# constraint.satisfied?(
|
23
|
-
# constraint.satisfied?(
|
24
|
+
# @example Validating with only minimum
|
25
|
+
# constraint = RangeConstraint.new(:self, { minimum: Time.now })
|
26
|
+
# constraint.satisfied?(Time.now + 3600) # => true
|
27
|
+
# constraint.satisfied?(Time.now - 3600) # => false
|
24
28
|
#
|
25
29
|
# @author {https://aaronmallen.me Aaron Allen}
|
26
30
|
# @since 0.1.0
|
27
31
|
class RangeConstraint
|
28
|
-
|
32
|
+
interface _Compatible
|
33
|
+
def <: (untyped other) -> bool
|
34
|
+
|
35
|
+
def <=: (untyped other) -> bool
|
36
|
+
|
37
|
+
def >: (untyped other) -> bool
|
38
|
+
|
39
|
+
def >=: (untyped other) -> bool
|
40
|
+
|
41
|
+
def inspect: () -> untyped
|
42
|
+
|
43
|
+
def nil?: () -> bool
|
44
|
+
|
45
|
+
def send: (Symbol method_name, *untyped arguments, **untyped keyword_arguments) -> untyped
|
46
|
+
end
|
47
|
+
|
48
|
+
type expected = { ?minimum: _Compatible, ?maximum: _Compatible }
|
29
49
|
|
30
50
|
type options = { ?inclusive: bool }
|
31
51
|
|
32
|
-
include Behavior[expected,
|
52
|
+
include Behavior[expected, _Compatible, options]
|
33
53
|
|
34
54
|
# Get a human-readable description of the range constraint.
|
35
55
|
#
|
@@ -75,6 +95,13 @@ module Domainic
|
|
75
95
|
# @return [void]
|
76
96
|
def validate_expectation!: ...
|
77
97
|
|
98
|
+
private
|
99
|
+
|
100
|
+
# Check if the range constraint is inclusive.
|
101
|
+
#
|
102
|
+
# @return [Boolean] `true` if the range is inclusive, `false` otherwise
|
103
|
+
def inclusive?: () -> bool
|
104
|
+
|
78
105
|
# Validate the minimum and maximum values in a range specification.
|
79
106
|
#
|
80
107
|
# @param expectation [Hash] The range specification to validate
|
@@ -82,6 +109,14 @@ module Domainic
|
|
82
109
|
# @raise [ArgumentError] if the values are invalid
|
83
110
|
# @return [void]
|
84
111
|
def validate_minimum_and_maximum!: (untyped expectation) -> void
|
112
|
+
|
113
|
+
# Validate the minimum and maximum value types are compatible with the constraint.
|
114
|
+
#
|
115
|
+
# @param expectation [Hash] The range specification to validate
|
116
|
+
#
|
117
|
+
# @raise [ArgumentError] if the values are invalid
|
118
|
+
# @return [void]
|
119
|
+
def validate_minimum_and_maximum_types!: (untyped expectation) -> void
|
85
120
|
end
|
86
121
|
end
|
87
122
|
end
|
@@ -111,6 +111,54 @@ module Domainic
|
|
111
111
|
|
112
112
|
alias _Cuid? _CUID?
|
113
113
|
|
114
|
+
# Creates a DateType instance.
|
115
|
+
#
|
116
|
+
# DateType restricts values to valid `Date` objects.
|
117
|
+
#
|
118
|
+
# @example
|
119
|
+
# type = _Date
|
120
|
+
# type.validate(Date.new(2024, 1, 1)) # => true
|
121
|
+
#
|
122
|
+
# @param options [Hash] additional configuration options
|
123
|
+
#
|
124
|
+
# @return [Domainic::Type::DateType] the created type
|
125
|
+
def _Date: (**__todo__ options) -> DateType
|
126
|
+
|
127
|
+
# Creates a nilable DateType instance.
|
128
|
+
#
|
129
|
+
# @example
|
130
|
+
# _Date? === Date.new(2024, 1, 1) # => true
|
131
|
+
# _Date? === nil # => true
|
132
|
+
#
|
133
|
+
# @param options [Hash] additional configuration options
|
134
|
+
#
|
135
|
+
# @return [Domainic::Type::UnionType] the created type (DateType or NilClass)
|
136
|
+
def _Date?: (**__todo__ options) -> UnionType
|
137
|
+
|
138
|
+
# Creates a DateTimeType instance.
|
139
|
+
#
|
140
|
+
# DateTimeType restricts values to valid `DateTime` objects.
|
141
|
+
#
|
142
|
+
# @example
|
143
|
+
# type = _DateTime
|
144
|
+
# type.validate(DateTime.new(2024, 1, 1, 12, 0, 0)) # => true
|
145
|
+
#
|
146
|
+
# @param options [Hash] additional configuration options
|
147
|
+
#
|
148
|
+
# @return [Domainic::Type::DateTimeType] the created type
|
149
|
+
def _DateTime: (**__todo__ options) -> DateTimeType
|
150
|
+
|
151
|
+
# Creates a nilable DateTimeType instance.
|
152
|
+
#
|
153
|
+
# @example
|
154
|
+
# _DateTime? === DateTime.new(2024, 1, 1, 12, 0, 0) # => true
|
155
|
+
# _DateTime? === nil # => true
|
156
|
+
#
|
157
|
+
# @param options [Hash] additional configuration options
|
158
|
+
#
|
159
|
+
# @return [Domainic::Type::UnionType] the created type (DateTimeType or NilClass)
|
160
|
+
def _DateTime?: (**__todo__ options) -> UnionType
|
161
|
+
|
114
162
|
# Creates a DuckType instance.
|
115
163
|
#
|
116
164
|
# DuckType allows specifying behavior based on method availability.
|
@@ -395,6 +443,65 @@ module Domainic
|
|
395
443
|
|
396
444
|
alias _Interned? _Symbol?
|
397
445
|
|
446
|
+
# Creates a TimeType instance.
|
447
|
+
#
|
448
|
+
# TimeType restricts values to valid `Time` objects.
|
449
|
+
#
|
450
|
+
# @example
|
451
|
+
# type = _Time
|
452
|
+
# type.validate(Time.new(2024, 1, 1, 12, 0, 0)) # => true
|
453
|
+
#
|
454
|
+
# @param options [Hash] additional configuration options
|
455
|
+
#
|
456
|
+
# @return [Domainic::Type::TimeType] the created type
|
457
|
+
def _Time: (**__todo__ options) -> TimeType
|
458
|
+
|
459
|
+
# Creates a nilable TimeType instance.
|
460
|
+
#
|
461
|
+
# @example
|
462
|
+
# _Time? === Time.new(2024, 1, 1, 12, 0, 0) # => true
|
463
|
+
# _Time? === nil # => true
|
464
|
+
#
|
465
|
+
# @param options [Hash] additional configuration options
|
466
|
+
#
|
467
|
+
# @return [Domainic::Type::UnionType] the created type (TimeType or NilClass)
|
468
|
+
def _Time?: (**__todo__ options) -> UnionType
|
469
|
+
|
470
|
+
# Creates a URIType instance.
|
471
|
+
#
|
472
|
+
# URIType restricts values to valid URIs.
|
473
|
+
#
|
474
|
+
# @example
|
475
|
+
# type = _URI.having_scheme('https')
|
476
|
+
#
|
477
|
+
# @param options [Hash] additional configuration options
|
478
|
+
#
|
479
|
+
# @return [Domainic::Type::URIType] the created type
|
480
|
+
def _URI: (**__todo__ options) -> URIType
|
481
|
+
|
482
|
+
alias _URL _URI
|
483
|
+
|
484
|
+
alias _Url _URI
|
485
|
+
|
486
|
+
alias _Uri _URI
|
487
|
+
|
488
|
+
# Creates a nilable URIType instance.
|
489
|
+
#
|
490
|
+
# @example
|
491
|
+
# _Uri?.validate("https://example.com") # => true
|
492
|
+
# _Uri?.validate(nil) # => true
|
493
|
+
#
|
494
|
+
# @param options [Hash] additional configuration options
|
495
|
+
#
|
496
|
+
# @return [Domainic::Type::UnionType] the created type (URIType or NilClass)
|
497
|
+
def _URI?: (**__todo__ options) -> UnionType
|
498
|
+
|
499
|
+
alias _URL? _URI?
|
500
|
+
|
501
|
+
alias _Url? _URI?
|
502
|
+
|
503
|
+
alias _Uri? _URI?
|
504
|
+
|
398
505
|
# Creates a UUIDType instance
|
399
506
|
#
|
400
507
|
# @example
|
@@ -435,33 +542,6 @@ module Domainic
|
|
435
542
|
|
436
543
|
alias _Either _Union
|
437
544
|
|
438
|
-
# Creates a URIType instance.
|
439
|
-
#
|
440
|
-
# URIType restricts values to valid URIs.
|
441
|
-
#
|
442
|
-
# @example
|
443
|
-
# type = _URI.having_scheme('https')
|
444
|
-
#
|
445
|
-
# @param options [Hash] additional configuration options
|
446
|
-
#
|
447
|
-
# @return [Domainic::Type::URIType] the created type
|
448
|
-
def _Uri: (**__todo__ options) -> URIType
|
449
|
-
|
450
|
-
alias _Url _Uri
|
451
|
-
|
452
|
-
# Creates a nilable URIType instance.
|
453
|
-
#
|
454
|
-
# @example
|
455
|
-
# _Uri?.validate("https://example.com") # => true
|
456
|
-
# _Uri?.validate(nil) # => true
|
457
|
-
#
|
458
|
-
# @param options [Hash] additional configuration options
|
459
|
-
#
|
460
|
-
# @return [Domainic::Type::UnionType] the created type (URIType or NilClass)
|
461
|
-
def _Uri?: (**__todo__ options) -> UnionType
|
462
|
-
|
463
|
-
alias _Url? _Uri?
|
464
|
-
|
465
545
|
# Creates a VoidType instance.
|
466
546
|
#
|
467
547
|
# Represents an operation that returns no value.
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Domainic
|
2
|
+
module Type
|
3
|
+
# A type for validating DateTime objects
|
4
|
+
#
|
5
|
+
# This type ensures the value is a `DateTime` object and supports comprehensive
|
6
|
+
# date-time validation through `DateTimeBehavior`. It is ideal for scenarios requiring
|
7
|
+
# precise date-time constraints.
|
8
|
+
#
|
9
|
+
# Key features:
|
10
|
+
# - Ensures the value is a `DateTime` object
|
11
|
+
# - Supports chronological relationship validation (e.g., before, after)
|
12
|
+
# - Provides range and equality checks
|
13
|
+
#
|
14
|
+
# @example Basic usage
|
15
|
+
# type = DateTimeType.new
|
16
|
+
# type.validate(DateTime.now) # => true
|
17
|
+
# type.validate(Date.today) # => false
|
18
|
+
#
|
19
|
+
# @example Range validation
|
20
|
+
# type = DateTimeType.new
|
21
|
+
# .being_between(DateTime.new(2024, 1, 1, 0, 0, 0), DateTime.new(2024, 12, 31, 23, 59, 59))
|
22
|
+
# type.validate(DateTime.new(2024, 6, 15, 12, 0, 0)) # => true
|
23
|
+
#
|
24
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
25
|
+
# @since 0.1.0
|
26
|
+
class DateTimeType
|
27
|
+
extend Behavior::ClassMethods
|
28
|
+
|
29
|
+
include Behavior
|
30
|
+
|
31
|
+
include Behavior::DateTimeBehavior
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Domainic
|
2
|
+
module Type
|
3
|
+
# A type for validating Date objects
|
4
|
+
#
|
5
|
+
# This type provides robust validation for `Date` objects, ensuring values conform
|
6
|
+
# to specified chronological constraints. It supports the full range of date-based
|
7
|
+
# constraints provided by `DateTimeBehavior`.
|
8
|
+
#
|
9
|
+
# Key features:
|
10
|
+
# - Ensures the value is a `Date` object
|
11
|
+
# - Supports validation for chronological relationships (e.g., before, after)
|
12
|
+
# - Full integration with `DateTimeBehavior` for range and equality checks
|
13
|
+
#
|
14
|
+
# @example Basic usage
|
15
|
+
# type = DateType.new
|
16
|
+
# type.validate(Date.today) # => true
|
17
|
+
# type.validate(DateTime.now) # => false
|
18
|
+
#
|
19
|
+
# @example With range constraints
|
20
|
+
# type = DateType.new
|
21
|
+
# .being_between(Date.new(2024, 1, 1), Date.new(2024, 12, 31))
|
22
|
+
# type.validate(Date.new(2024, 6, 15)) # => true
|
23
|
+
# type.validate(Date.new(2023, 12, 31)) # => false
|
24
|
+
#
|
25
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
26
|
+
# @since 0.1.0
|
27
|
+
class DateType
|
28
|
+
extend Behavior::ClassMethods
|
29
|
+
|
30
|
+
include Behavior
|
31
|
+
|
32
|
+
include Behavior::DateTimeBehavior
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Domainic
|
2
|
+
module Type
|
3
|
+
# A type for validating Time objects
|
4
|
+
#
|
5
|
+
# This type ensures the value is a `Time` object and integrates with
|
6
|
+
# `DateTimeBehavior` to provide a rich set of time-based validation capabilities.
|
7
|
+
#
|
8
|
+
# Key features:
|
9
|
+
# - Ensures the value is a `Time` object
|
10
|
+
# - Supports chronological relationship constraints (e.g., before, after)
|
11
|
+
# - Provides range and equality checks
|
12
|
+
#
|
13
|
+
# @example Basic usage
|
14
|
+
# type = TimeType.new
|
15
|
+
# type.validate(Time.now) # => true
|
16
|
+
# type.validate(Date.today) # => false
|
17
|
+
#
|
18
|
+
# @example Range validation
|
19
|
+
# type = TimeType.new
|
20
|
+
# .being_between(Time.now, Time.now + 3600)
|
21
|
+
# type.validate(Time.now + 1800) # => true
|
22
|
+
# type.validate(Time.now + 7200) # => false
|
23
|
+
#
|
24
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
25
|
+
# @since 0.1.0
|
26
|
+
class TimeType
|
27
|
+
extend Behavior::ClassMethods
|
28
|
+
|
29
|
+
include Behavior
|
30
|
+
|
31
|
+
include Behavior::DateTimeBehavior
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/sig/manifest.yaml
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: domainic-type
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.alpha.3.
|
4
|
+
version: 0.1.0.alpha.3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Allen
|
@@ -28,6 +28,7 @@ files:
|
|
28
28
|
- lib/domainic/type.rb
|
29
29
|
- lib/domainic/type/accessors.rb
|
30
30
|
- lib/domainic/type/behavior.rb
|
31
|
+
- lib/domainic/type/behavior/date_time_behavior.rb
|
31
32
|
- lib/domainic/type/behavior/enumerable_behavior.rb
|
32
33
|
- lib/domainic/type/behavior/numeric_behavior.rb
|
33
34
|
- lib/domainic/type/behavior/sizable_behavior.rb
|
@@ -69,6 +70,9 @@ files:
|
|
69
70
|
- lib/domainic/type/types/core/integer_type.rb
|
70
71
|
- lib/domainic/type/types/core/string_type.rb
|
71
72
|
- lib/domainic/type/types/core/symbol_type.rb
|
73
|
+
- lib/domainic/type/types/datetime/date_time_type.rb
|
74
|
+
- lib/domainic/type/types/datetime/date_type.rb
|
75
|
+
- lib/domainic/type/types/datetime/time_type.rb
|
72
76
|
- lib/domainic/type/types/identifier/cuid_type.rb
|
73
77
|
- lib/domainic/type/types/identifier/uuid_type.rb
|
74
78
|
- lib/domainic/type/types/network/email_address_type.rb
|
@@ -84,6 +88,7 @@ files:
|
|
84
88
|
- sig/domainic/type.rbs
|
85
89
|
- sig/domainic/type/accessors.rbs
|
86
90
|
- sig/domainic/type/behavior.rbs
|
91
|
+
- sig/domainic/type/behavior/date_time_behavior.rbs
|
87
92
|
- sig/domainic/type/behavior/enumerable_behavior.rbs
|
88
93
|
- sig/domainic/type/behavior/numeric_behavior.rbs
|
89
94
|
- sig/domainic/type/behavior/sizable_behavior.rbs
|
@@ -124,6 +129,9 @@ files:
|
|
124
129
|
- sig/domainic/type/types/core/integer_type.rbs
|
125
130
|
- sig/domainic/type/types/core/string_type.rbs
|
126
131
|
- sig/domainic/type/types/core/symbol_type.rbs
|
132
|
+
- sig/domainic/type/types/datetime/date_time_type.rbs
|
133
|
+
- sig/domainic/type/types/datetime/date_type.rbs
|
134
|
+
- sig/domainic/type/types/datetime/time_type.rbs
|
127
135
|
- sig/domainic/type/types/identifier/cuid_type.rbs
|
128
136
|
- sig/domainic/type/types/identifier/uuid_type.rbs
|
129
137
|
- sig/domainic/type/types/network/email_address_type.rbs
|
@@ -136,15 +144,15 @@ files:
|
|
136
144
|
- sig/domainic/type/types/specification/union_type.rbs
|
137
145
|
- sig/domainic/type/types/specification/void_type.rbs
|
138
146
|
- sig/manifest.yaml
|
139
|
-
homepage: https://github.com/domainic/domainic/tree/domainic-type-v0.1.0-alpha.3.
|
147
|
+
homepage: https://github.com/domainic/domainic/tree/domainic-type-v0.1.0-alpha.3.2.0/domainic-type
|
140
148
|
licenses:
|
141
149
|
- MIT
|
142
150
|
metadata:
|
143
151
|
bug_tracker_uri: https://github.com/domainic/domainic/issues
|
144
|
-
changelog_uri: https://github.com/domainic/domainic/releases/tag/domainic-type-v0.1.0-alpha.3.
|
145
|
-
homepage_uri: https://github.com/domainic/domainic/tree/domainic-type-v0.1.0-alpha.3.
|
152
|
+
changelog_uri: https://github.com/domainic/domainic/releases/tag/domainic-type-v0.1.0-alpha.3.2.0
|
153
|
+
homepage_uri: https://github.com/domainic/domainic/tree/domainic-type-v0.1.0-alpha.3.2.0/domainic-type
|
146
154
|
rubygems_mfa_required: 'true'
|
147
|
-
source_code_uri: https://github.com/domainic/domainic/tree/domainic-type-v0.1.0-alpha.3.
|
155
|
+
source_code_uri: https://github.com/domainic/domainic/tree/domainic-type-v0.1.0-alpha.3.2.0/domainic-type
|
148
156
|
post_install_message:
|
149
157
|
rdoc_options: []
|
150
158
|
require_paths:
|