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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba2ef6a6a8d06966d736c794476322b088878573154468dc55de61a85704f459
4
- data.tar.gz: 8f7fcb77831968e9f6f4f42c58bc2f63892b56bae35902aec892932dc5425acc
3
+ metadata.gz: 24abfbef9f478a43dce3382a737ceec2ea4310f69454d5d3e8903699f97454a0
4
+ data.tar.gz: aaede220a86b78362f4dd2a8e51ffdb320ba7352a88b4980dec244e7452662fc
5
5
  SHA512:
6
- metadata.gz: 8fbe93a49fd3d0063c7d9e392cc1e99180cbdfacb3a1a355c813eaa3df5f476be03e6c44b265871bdea82c646c250b5f486b4446f8c0ce2efbc4481c778d5553
7
- data.tar.gz: 2509ae814a1dbb07a2100c24fe0aff90b8c59123f41d8b37a90bce60c9ad33fe1ecd9d57562533e3f7c52f5c051c27120136d1d7f1efe1df4714212281663827
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, minimum, maximum, options.transform_keys(&:to_sym))
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 numeric values fall within a specified range.
8
+ # A constraint for validating that comparable values fall within a specified range.
9
9
  #
10
- # This constraint allows for validating numeric values against minimum and maximum
11
- # boundaries. It supports specifying either or both boundaries, allowing for
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 with both minimum and maximum
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 with only minimum
20
- # constraint = RangeConstraint.new(:self, { minimum: 0 })
21
- # constraint.satisfied?(10) # => true
22
- # constraint.satisfied?(-1) # => false
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 maximum
25
- # constraint = RangeConstraint.new(:self, { maximum: 100 })
26
- # constraint.satisfied?(50) # => true
27
- # constraint.satisfied?(150) # => false
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
- # type expected = { ?minimum: Numeric, ?maximum: Numeric }
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, Numeric, options]
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 = @options.fetch(:inclusive, true) ? %i[>= <=] : %i[> <]
114
+ min_comparison, max_comparison = inclusive? ? %i[>= <=] : %i[> <]
95
115
 
96
- @actual.send(min_comparison, (min || -Float::INFINITY)) &&
97
- @actual.send(max_comparison, (max || Float::INFINITY))
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.each_pair do |property, value|
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: (Integer? minimum, Integer? maximum, Hash[Symbol, Integer?] options) -> Array[Integer]
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 numeric values fall within a specified range.
4
+ # A constraint for validating that comparable values fall within a specified range.
5
5
  #
6
- # This constraint allows for validating numeric values against minimum and maximum
7
- # boundaries. It supports specifying either or both boundaries, allowing for
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 with both minimum and maximum
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 with only minimum
16
- # constraint = RangeConstraint.new(:self, { minimum: 0 })
17
- # constraint.satisfied?(10) # => true
18
- # constraint.satisfied?(-1) # => false
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 maximum
21
- # constraint = RangeConstraint.new(:self, { maximum: 100 })
22
- # constraint.satisfied?(50) # => true
23
- # constraint.satisfied?(150) # => false
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
- type expected = { ?minimum: Numeric, ?maximum: Numeric }
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, Numeric, options]
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
@@ -1,2 +1,5 @@
1
1
  dependencies:
2
+ - name: forwardable
3
+ - name: date
2
4
  - name: yaml
5
+ - name: uri
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.1.0
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.1.0/domainic-type
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.1.0
145
- homepage_uri: https://github.com/domainic/domainic/tree/domainic-type-v0.1.0-alpha.3.1.0/domainic-type
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.1.0/domainic-type
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: