domainic-type 0.1.0.alpha.3.1.0 → 0.1.0.alpha.3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|