domainic-type 0.1.0.alpha.3.2.0 → 0.1.0.alpha.3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +66 -10
- data/docs/USAGE.md +732 -0
- data/lib/domainic/type/accessors.rb +3 -2
- data/lib/domainic/type/behavior/date_time_behavior.rb +117 -37
- data/lib/domainic/type/config/registry.yml +21 -0
- data/lib/domainic/type/definitions.rb +212 -0
- data/lib/domainic/type/types/core/complex_type.rb +122 -0
- data/lib/domainic/type/types/core/range_type.rb +47 -0
- data/lib/domainic/type/types/core/rational_type.rb +38 -0
- data/lib/domainic/type/types/core_extended/big_decimal_type.rb +34 -0
- data/lib/domainic/type/types/core_extended/set_type.rb +34 -0
- data/lib/domainic/type/types/datetime/date_time_string_type.rb +156 -0
- data/lib/domainic/type/types/datetime/timestamp_type.rb +50 -0
- data/sig/domainic/type/accessors.rbs +2 -2
- data/sig/domainic/type/behavior/date_time_behavior.rbs +35 -23
- data/sig/domainic/type/definitions.rbs +165 -0
- data/sig/domainic/type/types/core/complex_type.rbs +96 -0
- data/sig/domainic/type/types/core/range_type.rbs +41 -0
- data/sig/domainic/type/types/core/rational_type.rbs +32 -0
- data/sig/domainic/type/types/core_extended/big_decimal_type.rbs +27 -0
- data/sig/domainic/type/types/core_extended/set_type.rbs +27 -0
- data/sig/domainic/type/types/datetime/date_time_string_type.rbs +124 -0
- data/sig/domainic/type/types/datetime/timestamp_type.rbs +44 -0
- metadata +21 -6
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'domainic/type/behavior'
|
4
|
+
require 'domainic/type/behavior/numeric_behavior'
|
5
|
+
|
6
|
+
module Domainic
|
7
|
+
module Type
|
8
|
+
# A type for validating and constraining `Complex` numbers.
|
9
|
+
#
|
10
|
+
# This type extends `NumericBehavior` to provide a fluent interface for numeric-specific validations such as
|
11
|
+
# polarity and divisibility checks.
|
12
|
+
#
|
13
|
+
# @example Validating a `Complex` value
|
14
|
+
# type = Domainic::Type::ComplexType.new
|
15
|
+
# type.validate!(Complex(3, 4)) # => true
|
16
|
+
#
|
17
|
+
# @example Enforcing constraints
|
18
|
+
# type = Domainic::Type::ComplexType.new
|
19
|
+
# type.being_positive.validate!(Complex(42, 0)) # => raises TypeError
|
20
|
+
#
|
21
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
22
|
+
# @since 0.1.0
|
23
|
+
class ComplexType
|
24
|
+
# @rbs! extend Behavior::ClassMethods
|
25
|
+
|
26
|
+
include Behavior
|
27
|
+
include Behavior::NumericBehavior
|
28
|
+
|
29
|
+
intrinsically_constrain :self, :type, Complex, abort_on_failure: true, description: :not_described
|
30
|
+
|
31
|
+
# Constrain the Complex real to be divisible by a given divisor.
|
32
|
+
#
|
33
|
+
# @param arguments [Array<Numeric>] a list of arguments, typically one divisor
|
34
|
+
# @param options [Hash] additional options such as tolerance for floating-point checks
|
35
|
+
# @option options [Numeric] :divisor the divisor to check for divisibility
|
36
|
+
# @option options [Numeric] :tolerance the tolerance for floating-point checks
|
37
|
+
#
|
38
|
+
# @return [self] the current instance for chaining
|
39
|
+
# @rbs (*Numeric arguments, ?divisor: Numeric, ?tolerance: Numeric) -> Behavior
|
40
|
+
def being_divisible_by(*arguments, **options)
|
41
|
+
if arguments.size > 1 || (arguments.empty? && !options.key?(:divisor))
|
42
|
+
raise ArgumentError, "wrong number of arguments (given #{arguments.size}, expected 1)"
|
43
|
+
end
|
44
|
+
|
45
|
+
divisor = arguments.first || options[:divisor]
|
46
|
+
constrain :real, :divisibility, divisor, description: 'being', **options.except(:divisor)
|
47
|
+
end
|
48
|
+
alias being_multiple_of being_divisible_by
|
49
|
+
alias divisible_by being_divisible_by
|
50
|
+
alias multiple_of being_divisible_by
|
51
|
+
|
52
|
+
# Constrain the Complex real to be even.
|
53
|
+
#
|
54
|
+
# @return [self] the current instance for chaining
|
55
|
+
# @rbs () -> Behavior
|
56
|
+
def being_even
|
57
|
+
# @type self: Object & Behavior
|
58
|
+
constrain :real, :parity, :even, description: 'being'
|
59
|
+
end
|
60
|
+
alias even being_even
|
61
|
+
alias not_being_odd being_even
|
62
|
+
|
63
|
+
# Constrain the Complex real to be negative.
|
64
|
+
#
|
65
|
+
# @return [self] the current instance for chaining
|
66
|
+
# @rbs () -> Behavior
|
67
|
+
def being_negative
|
68
|
+
# @type self: Object & Behavior
|
69
|
+
constrain :real, :polarity, :negative, description: 'being'
|
70
|
+
end
|
71
|
+
alias negative being_negative
|
72
|
+
alias not_being_positive being_negative
|
73
|
+
|
74
|
+
# Constrain the Complex real value to be odd.
|
75
|
+
#
|
76
|
+
# @return [self] the current instance for chaining
|
77
|
+
# @rbs () -> Behavior
|
78
|
+
def being_odd
|
79
|
+
# @type self: Object & Behavior
|
80
|
+
constrain :real, :parity, :odd, description: 'being'
|
81
|
+
end
|
82
|
+
alias odd being_odd
|
83
|
+
alias not_being_even being_odd
|
84
|
+
|
85
|
+
# Constrain the Complex real to be positive.
|
86
|
+
#
|
87
|
+
# @return [self] the current instance for chaining
|
88
|
+
# @rbs () -> Behavior
|
89
|
+
def being_positive
|
90
|
+
# @type self: Object & Behavior
|
91
|
+
constrain :real, :polarity, :positive, description: 'being'
|
92
|
+
end
|
93
|
+
alias positive being_positive
|
94
|
+
alias not_being_negative being_positive
|
95
|
+
|
96
|
+
# Constrain the Complex real to not be divisible by a specific divisor.
|
97
|
+
#
|
98
|
+
# @note the divisor MUST be provided as an argument or in the options Hash.
|
99
|
+
#
|
100
|
+
# @param arguments [Array<Numeric>] a list of arguments, typically one divisor
|
101
|
+
# @param options [Hash] additional options such as tolerance for floating-point checks
|
102
|
+
# @option options [Numeric] :divisor the divisor to check for divisibility
|
103
|
+
# @option options [Numeric] :tolerance the tolerance for floating-point checks
|
104
|
+
#
|
105
|
+
# @return [self] the current instance for chaining
|
106
|
+
# @rbs (*Numeric arguments, ?divisor: Numeric, ?tolerance: Numeric) -> Behavior
|
107
|
+
def not_being_divisible_by(*arguments, **options)
|
108
|
+
if arguments.size > 1 || (arguments.empty? && !options.key?(:divisor))
|
109
|
+
raise ArgumentError, "wrong number of arguments (given #{arguments.size}, expected 1)"
|
110
|
+
end
|
111
|
+
|
112
|
+
# @type self: Object & Behavior
|
113
|
+
divisor = arguments.first || options[:divisor]
|
114
|
+
divisible_by = @constraints.prepare :self, :divisibility, divisor, **options.except(:divisor)
|
115
|
+
constrain :real, :not, divisible_by, concerning: :divisibility, description: 'being'
|
116
|
+
end
|
117
|
+
alias not_being_multiple_of not_being_divisible_by
|
118
|
+
alias not_divisible_by not_being_divisible_by
|
119
|
+
alias not_multiple_of not_being_divisible_by
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'domainic/type/behavior'
|
4
|
+
require 'domainic/type/behavior/enumerable_behavior'
|
5
|
+
|
6
|
+
module Domainic
|
7
|
+
module Type
|
8
|
+
# A type for validating `Range` objects with comprehensive constraints.
|
9
|
+
#
|
10
|
+
# This class allows flexible validation of ranges, supporting type checks,
|
11
|
+
# inclusion/exclusion of values, and range-specific behaviors such as bounds validation.
|
12
|
+
# It integrates with `EnumerableBehavior` for enumerable-style validations
|
13
|
+
# (e.g., size constraints, element presence).
|
14
|
+
#
|
15
|
+
# Key features:
|
16
|
+
# - Ensures the value is a `Range`.
|
17
|
+
# - Supports constraints for range boundaries (`begin` and `end`).
|
18
|
+
# - Provides validations for inclusion and exclusion of values.
|
19
|
+
# - Leverages `EnumerableBehavior` for size and collection-style constraints.
|
20
|
+
#
|
21
|
+
# @example Basic usage
|
22
|
+
# type = RangeType.new
|
23
|
+
# type.having_bounds(1, 10) # Validates range bounds (inclusive or exclusive).
|
24
|
+
# type.containing(5) # Ensures value is within the range.
|
25
|
+
# type.not_containing(15) # Ensures value is not within the range.
|
26
|
+
#
|
27
|
+
# @example Integration with EnumerableBehavior
|
28
|
+
# type = RangeType.new
|
29
|
+
# type.having_size(10) # Validates size (total elements in the range).
|
30
|
+
# type.containing_exactly(3, 7) # Validates specific values within the range.
|
31
|
+
#
|
32
|
+
# @example Flexible boundaries
|
33
|
+
# type = RangeType.new
|
34
|
+
# type.having_bounds(1, 10, exclusive: true) # Upper and lower bounds are exclusive.
|
35
|
+
#
|
36
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
37
|
+
# @since 0.1.0
|
38
|
+
class RangeType
|
39
|
+
# @rbs! extend Behavior::ClassMethods
|
40
|
+
|
41
|
+
include Behavior
|
42
|
+
include Behavior::EnumerableBehavior
|
43
|
+
|
44
|
+
intrinsically_constrain :self, :type, Range, abort_on_failure: true, description: :not_described
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'domainic/type/behavior'
|
4
|
+
require 'domainic/type/behavior/numeric_behavior'
|
5
|
+
|
6
|
+
module Domainic
|
7
|
+
module Type
|
8
|
+
# A type for validating Rational numbers.
|
9
|
+
#
|
10
|
+
# This type ensures values are of type `Rational` and supports a range of
|
11
|
+
# constraints for numerical validation, such as positivity, negativity,
|
12
|
+
# and divisibility. It integrates with `NumericBehavior` to provide a
|
13
|
+
# consistent and fluent interface for numeric constraints.
|
14
|
+
#
|
15
|
+
# @example Validating a positive Rational number
|
16
|
+
# type = Domainic::Type::RationalType.new
|
17
|
+
# type.being_positive.validate!(Rational(3, 4)) # => true
|
18
|
+
#
|
19
|
+
# @example Validating divisibility
|
20
|
+
# type = Domainic::Type::RationalType.new
|
21
|
+
# type.being_divisible_by(1).validate!(Rational(3, 1)) # => true
|
22
|
+
#
|
23
|
+
# @example Using constraints with chaining
|
24
|
+
# type = Domainic::Type::RationalType.new
|
25
|
+
# type.being_greater_than(0).being_less_than(1).validate!(Rational(1, 2)) # => true
|
26
|
+
#
|
27
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
28
|
+
# @since 0.1.0
|
29
|
+
class RationalType
|
30
|
+
# @rbs! extend Behavior::ClassMethods
|
31
|
+
|
32
|
+
include Behavior
|
33
|
+
include Behavior::NumericBehavior
|
34
|
+
|
35
|
+
intrinsically_constrain :self, :type, Rational, abort_on_failure: true, description: :not_described
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bigdecimal'
|
4
|
+
require 'domainic/type/behavior'
|
5
|
+
require 'domainic/type/behavior/numeric_behavior'
|
6
|
+
|
7
|
+
module Domainic
|
8
|
+
module Type
|
9
|
+
# A type for validating and constraining `BigDecimal` objects.
|
10
|
+
#
|
11
|
+
# This type extends `NumericBehavior` to provide a fluent interface for numeric-specific validations such as being
|
12
|
+
# positive, being within a range, or satisfying divisibility rules.
|
13
|
+
#
|
14
|
+
# @example Validating a `BigDecimal` value
|
15
|
+
# type = Domainic::Type::BigDecimalType.new
|
16
|
+
# type.validate!(BigDecimal('3.14')) # => true
|
17
|
+
#
|
18
|
+
# @example Enforcing constraints
|
19
|
+
# type = Domainic::Type::BigDecimalType.new
|
20
|
+
# type.being_positive.validate!(BigDecimal('42')) # => true
|
21
|
+
# type.being_positive.validate!(BigDecimal('-42')) # raises TypeError
|
22
|
+
#
|
23
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
24
|
+
# @since 0.1.0
|
25
|
+
class BigDecimalType
|
26
|
+
# @rbs! extend Behavior::ClassMethods
|
27
|
+
|
28
|
+
include Behavior
|
29
|
+
include Behavior::NumericBehavior
|
30
|
+
|
31
|
+
intrinsically_constrain :self, :type, BigDecimal, abort_on_failure: true, description: :not_described
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'domainic/type/behavior'
|
4
|
+
require 'domainic/type/behavior/enumerable_behavior'
|
5
|
+
require 'set'
|
6
|
+
|
7
|
+
module Domainic
|
8
|
+
module Type
|
9
|
+
# A type for validating and constraining `Set` objects.
|
10
|
+
#
|
11
|
+
# This type extends `EnumerableBehavior` to support validations and constraints
|
12
|
+
# such as being empty, containing specific elements, or having a minimum or maximum count.
|
13
|
+
#
|
14
|
+
# @example Validating a `Set` object
|
15
|
+
# type = Domainic::Type::SetType.new
|
16
|
+
# type.validate!(Set.new([1, 2, 3])) # => true
|
17
|
+
#
|
18
|
+
# @example Enforcing constraints
|
19
|
+
# type = Domainic::Type::SetType.new
|
20
|
+
# type.being_empty.validate!(Set.new) # => true
|
21
|
+
# type.being_empty.validate!(Set.new([1])) # raises TypeError
|
22
|
+
#
|
23
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
24
|
+
# @since 0.1.0
|
25
|
+
class SetType
|
26
|
+
# @rbs! extend Behavior::ClassMethods
|
27
|
+
|
28
|
+
include Behavior
|
29
|
+
include Behavior::EnumerableBehavior
|
30
|
+
|
31
|
+
intrinsically_constrain :self, :type, Set, abort_on_failure: true, description: :not_described
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'domainic/type/behavior'
|
4
|
+
require 'domainic/type/behavior/date_time_behavior'
|
5
|
+
|
6
|
+
module Domainic
|
7
|
+
module Type
|
8
|
+
# A type for validating and constraining strings to match various datetime formats.
|
9
|
+
#
|
10
|
+
# This class includes behaviors for handling datetime constraints and ensures that
|
11
|
+
# a string matches one of the predefined datetime formats (e.g., ISO 8601, RFC2822).
|
12
|
+
#
|
13
|
+
# @example Validating American format
|
14
|
+
# type = Domainic::Type::DateTimeStringType.new
|
15
|
+
# type.having_american_format.validate!("01/31/2024")
|
16
|
+
#
|
17
|
+
# @example Validating ISO 8601 format
|
18
|
+
# type = Domainic::Type::DateTimeStringType.new
|
19
|
+
# type.having_iso8601_format.validate!("2024-01-01T12:00:00Z")
|
20
|
+
#
|
21
|
+
# @example Validating RFC2822 format
|
22
|
+
# type = Domainic::Type::DateTimeStringType.new
|
23
|
+
# type.having_rfc2822_format.validate!("Thu, 31 Jan 2024 13:30:00 +0000")
|
24
|
+
#
|
25
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
26
|
+
# @since 0.1.0
|
27
|
+
class DateTimeStringType
|
28
|
+
# @rbs! extend Behavior::ClassMethods
|
29
|
+
|
30
|
+
include Behavior
|
31
|
+
include Behavior::DateTimeBehavior
|
32
|
+
|
33
|
+
# The `Format` module provides a set of regular expressions for validating
|
34
|
+
# various datetime formats, including ISO 8601, RFC2822, American, European,
|
35
|
+
# full month names, abbreviated month names, and 12-hour clock formats.
|
36
|
+
#
|
37
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
38
|
+
# @since 0.1.0
|
39
|
+
module Format
|
40
|
+
# Matches ISO 8601 datetime formats, including:
|
41
|
+
# - `2024-01-01T12:00:00.000+00:00`
|
42
|
+
# - `2024-01-01T12:00:00+00:00`
|
43
|
+
# - `2024-01-01T12:00:00.000`
|
44
|
+
# - `2024-01-01T12:00:00`
|
45
|
+
# - `20240101T120000+0000` (basic format)
|
46
|
+
# @return [Regexp]
|
47
|
+
ISO8601 = /\A\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[-+]\d{2}:?\d{2})?)?\z/ #: Regexp
|
48
|
+
|
49
|
+
# Matches RFC2822 datetime formats, including:
|
50
|
+
# - `Thu, 31 Jan 2024 13:30:00 +0000`
|
51
|
+
# - `31 Jan 2024 13:30:00 +0000`
|
52
|
+
# @return [Regexp]
|
53
|
+
RFC2822 = /\A
|
54
|
+
(?: # Optional day of the week
|
55
|
+
(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s+
|
56
|
+
)?
|
57
|
+
\d{1,2}\s+ # Day of the month
|
58
|
+
(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+ # Month
|
59
|
+
\d{2,4}\s+ # Year
|
60
|
+
\d{2}:\d{2} # Time in HH:MM
|
61
|
+
(?::\d{2})? # Optional seconds
|
62
|
+
\s+ # Space
|
63
|
+
(?: # Time zone
|
64
|
+
[+-]\d{4} | # Offset (e.g., +0000)
|
65
|
+
GMT | UTC # Literal GMT or UTC
|
66
|
+
)
|
67
|
+
\z/x #: Regexp
|
68
|
+
|
69
|
+
# Matches American-style dates with optional time in 12-hour or 24-hour formats, including:
|
70
|
+
# - `01/31/2024`
|
71
|
+
# - `01/31/2024 12:30:00`
|
72
|
+
# - `01/31/2024 12:30 PM`
|
73
|
+
# @return [Regexp]
|
74
|
+
AMERICAN = %r{\A\d{1,2}/\d{1,2}/\d{2,4}(?: \d{1,2}:\d{1,2}(?::\d{1,2})?(?:\s*[AaPp][Mm])?)?\z} #: Regexp
|
75
|
+
|
76
|
+
# Matches European-style dates with optional time in 24-hour format, including:
|
77
|
+
# - `31.01.2024`
|
78
|
+
# - `31.01.2024 12:30:00`
|
79
|
+
# @return [Regexp]
|
80
|
+
EUROPEAN = /\A\d{1,2}\.\d{1,2}\.\d{2,4}(?: \d{1,2}:\d{2}(?::\d{2})?)?\z/ #: Regexp
|
81
|
+
|
82
|
+
# Matches datetime formats with full month names, including:
|
83
|
+
# - `January 31, 2024 12:00:00`
|
84
|
+
# - `January 31, 2024 12:00`
|
85
|
+
# @return [Regexp]
|
86
|
+
FULL_MONTH_NAME = /\A
|
87
|
+
(?: # Full month name
|
88
|
+
(?:January|February|March|April|May|June|July|August|September|October|November|December)
|
89
|
+
)\s+\d{1,2},\s+\d{4}(?:\s+\d{1,2}:\d{2}(?::\d{2})?)?
|
90
|
+
\z/x #: Regexp
|
91
|
+
|
92
|
+
# Matches datetime formats with abbreviated month names, including:
|
93
|
+
# - `Jan 31, 2024 12:00:00`
|
94
|
+
# - `Jan 31, 2024 12:00`
|
95
|
+
# @return [Regexp]
|
96
|
+
ABBREVIATED_MONTH_NAME = /\A
|
97
|
+
\d{1,2}\s+(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec),\s+\d{4}(?:\s+\d{1,2}:\d{2}(?::\d{2})?)?
|
98
|
+
\z/x #: Regexp
|
99
|
+
|
100
|
+
# Matches datetime formats using a 12-hour clock with AM/PM, including:
|
101
|
+
# - `2024-01-01 01:30:00 PM`
|
102
|
+
# - `2024-01-01 01:30 PM`
|
103
|
+
# @return [Regexp]
|
104
|
+
TWELVE_HOUR_FORMAT = /\A\d{4}-\d{2}-\d{2}\s+\d{1,2}:\d{2}(?::\d{2})?\s+[APap][Mm]\z/x #: Regexp
|
105
|
+
end
|
106
|
+
private_constant :Format
|
107
|
+
|
108
|
+
intrinsically_constrain :self, :type, String, abort_on_failure: true, description: :not_described
|
109
|
+
intrinsically_constrain :self, :match_pattern,
|
110
|
+
Regexp.union(Format.constants.map { |name| Format.const_get(name) }),
|
111
|
+
abort_on_failure: true, concerning: :format, description: :not_described
|
112
|
+
|
113
|
+
# Constrain the type to match American-style datetime formats.
|
114
|
+
#
|
115
|
+
# @return [self] self for method chaining
|
116
|
+
# @rbs () -> self
|
117
|
+
def having_american_format
|
118
|
+
constrain :self, :match_pattern, Format::AMERICAN, concerning: :format
|
119
|
+
end
|
120
|
+
alias american having_american_format
|
121
|
+
alias having_us_format having_american_format
|
122
|
+
alias us_format having_american_format
|
123
|
+
|
124
|
+
# Constrain the type to match European-style datetime formats.
|
125
|
+
#
|
126
|
+
# @return [self] self for method chaining
|
127
|
+
# @rbs () -> self
|
128
|
+
def having_european_format
|
129
|
+
constrain :self, :match_pattern, Format::EUROPEAN, concerning: :format
|
130
|
+
end
|
131
|
+
alias european having_european_format
|
132
|
+
alias having_eu_format having_european_format
|
133
|
+
alias eu_format having_european_format
|
134
|
+
|
135
|
+
# Constrain the type to match ISO 8601 datetime formats.
|
136
|
+
#
|
137
|
+
# @return [self] self for method chaining
|
138
|
+
# @rbs () -> self
|
139
|
+
def having_iso8601_format
|
140
|
+
constrain :self, :match_pattern, Format::ISO8601, concerning: :format
|
141
|
+
end
|
142
|
+
alias iso8601 having_iso8601_format
|
143
|
+
alias iso8601_format having_iso8601_format
|
144
|
+
|
145
|
+
# Constrain the type to match RFC2822 datetime formats.
|
146
|
+
#
|
147
|
+
# @return [self] self for method chaining
|
148
|
+
# @rbs () -> self
|
149
|
+
def having_rfc2822_format
|
150
|
+
constrain :self, :match_pattern, Format::RFC2822, concerning: :format
|
151
|
+
end
|
152
|
+
alias rfc2822 having_rfc2822_format
|
153
|
+
alias rfc2822_format having_rfc2822_format
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'domainic/type/behavior'
|
4
|
+
require 'domainic/type/behavior/date_time_behavior'
|
5
|
+
|
6
|
+
module Domainic
|
7
|
+
module Type
|
8
|
+
# A type for validating Unix timestamps (seconds since the Unix epoch).
|
9
|
+
#
|
10
|
+
# This type ensures the value is an `Integer` representing a valid Unix
|
11
|
+
# timestamp. It integrates with `DateTimeBehavior` to provide a rich set of
|
12
|
+
# validation capabilities, including chronological constraints and range checks.
|
13
|
+
#
|
14
|
+
# Key features:
|
15
|
+
# - Ensures the value is an `Integer` representing a Unix timestamp.
|
16
|
+
# - Supports chronological relationship constraints (e.g., before, after).
|
17
|
+
# - Provides range, equality, and nilable checks.
|
18
|
+
#
|
19
|
+
# @example Basic usage
|
20
|
+
# type = TimestampType.new
|
21
|
+
# type.validate(Time.now.to_i) # => true
|
22
|
+
# type.validate(Date.today.to_time.to_i) # => true
|
23
|
+
# type.validate('invalid') # => false
|
24
|
+
#
|
25
|
+
# @example Range validation
|
26
|
+
# type = TimestampType.new
|
27
|
+
# .being_between(Time.now.to_i, (Time.now + 3600).to_i)
|
28
|
+
# type.validate((Time.now + 1800).to_i) # => true
|
29
|
+
# type.validate((Time.now + 7200).to_i) # => false
|
30
|
+
#
|
31
|
+
# @example Historical timestamps
|
32
|
+
# type = TimestampType.new
|
33
|
+
# type.validate(-1234567890) # => true (date before 1970-01-01)
|
34
|
+
#
|
35
|
+
# @example Nilable timestamp
|
36
|
+
# nilable_type = _Nilable(TimestampType.new)
|
37
|
+
# nilable_type.validate(nil) # => true
|
38
|
+
#
|
39
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
40
|
+
# @since 0.1.0
|
41
|
+
class TimestampType
|
42
|
+
# @rbs! extend Behavior::ClassMethods
|
43
|
+
|
44
|
+
include Behavior
|
45
|
+
include Behavior::DateTimeBehavior
|
46
|
+
|
47
|
+
intrinsically_constrain :self, :type, Integer, abort_on_failure: true, description: :not_described
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module Domainic
|
2
2
|
module Type
|
3
|
-
type accessor = :abs | :begin | :chars | :class | :count | :end | :entries | :first | :keys | :last | :length | :self | :size | :values
|
3
|
+
type accessor = :abs | :begin | :chars | :class | :count | :end | :entries | :first | :keys | :last | :length | :real | :self | :size | :values
|
4
4
|
|
5
5
|
# A list of valid access methods that can be used to retrieve values for constraint validation.
|
6
6
|
# These methods represent common Ruby interfaces for accessing collection sizes, ranges, and values.
|
7
7
|
#
|
8
|
-
# - :abs
|
8
|
+
# - :abs, :real - For absolute values
|
9
9
|
# - :begin, :end - For Range-like objects
|
10
10
|
# - :class - For type checking
|
11
11
|
# - :count, :length, :size - For measuring collections
|
@@ -24,73 +24,85 @@ module Domainic
|
|
24
24
|
# @author {https://aaronmallen.me Aaron Allen}
|
25
25
|
# @since 0.1.0
|
26
26
|
module DateTimeBehavior
|
27
|
-
|
27
|
+
# The supported date/time patterns for parsing date/time strings
|
28
|
+
#
|
29
|
+
# @note This list is ordered from most specific to least specific to ensure that the most specific patterns are
|
30
|
+
# tried first. This is important because some patterns are more lenient than others and may match a wider range
|
31
|
+
# of input strings.
|
32
|
+
#
|
33
|
+
# @return [Array<String>] the supported date/time patterns
|
34
|
+
DATETIME_PATTERNS: Array[String]
|
35
|
+
|
36
|
+
# Coerce a value to a Date, DateTime, or Time object
|
37
|
+
#
|
38
|
+
# @return [Proc] a lambda that coerces a value to a Date, DateTime, or Time object
|
39
|
+
TO_DATETIME_COERCER: ^(Date | DateTime | Integer | String | Time value) -> (Date | DateTime | Time)
|
28
40
|
|
29
41
|
# Parse arguments for being_between
|
30
42
|
#
|
31
43
|
# @note this in my opinion is better than polluting the namespace of the including class even with a private
|
32
44
|
# method. This way, the method is only available within the module itself. See {#being_between}.
|
33
45
|
#
|
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
|
46
|
+
# @param after [Date, DateTime, Integer, String, Time, nil] minimum size value from positional args
|
47
|
+
# @param before [Date, DateTime, Integer, String, Time, nil] maximum size value from positional args
|
36
48
|
# @param options [Hash] keyword arguments containing after/before values
|
37
49
|
#
|
38
50
|
# @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)?]
|
51
|
+
# @return [Array<Date, DateTime, Integer, String, Time, nil>] parsed [after, before] values
|
52
|
+
private def self.parse_being_between_arguments!: ((Date | DateTime | Integer | String | Time)? after, (Date | DateTime | Integer | String | Time)? before, Hash[Symbol, (Date | DateTime | Integer | String | Time)?] options) -> Array[(Date | DateTime | Integer | String | Time)?]
|
41
53
|
|
42
54
|
# Raise appropriate ArgumentError for being_between
|
43
55
|
#
|
44
56
|
# @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
|
57
|
+
# @param after [Date, DateTime, Integer, String, Time, nil] after value from positional args
|
58
|
+
# @param before [Date, DateTime, Integer, String, Time, nil] before value from positional args
|
47
59
|
# @param options [Hash] keyword arguments containing after/before values
|
48
60
|
#
|
49
61
|
# @raise [ArgumentError] with appropriate message
|
50
62
|
# @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
|
63
|
+
private def self.raise_being_between_argument_error!: (Array[String] original_caller, (Date | DateTime | Integer | String | Time)? after, (Date | DateTime | Integer | String | Time)? before, Hash[Symbol, (Date | DateTime | Integer | String | Time)?] options) -> void
|
52
64
|
|
53
65
|
# Constrain the value to be chronologically after a given date/time
|
54
66
|
#
|
55
|
-
# @param other [Date, DateTime, String, Time] the date/time to compare against
|
67
|
+
# @param other [Date, DateTime, Integer, String, Time] the date/time to compare against
|
56
68
|
# @return [self] self for method chaining
|
57
|
-
def being_after: (Date | DateTime | String | Time other) -> Behavior
|
69
|
+
def being_after: (Date | DateTime | Integer | String | Time other) -> Behavior
|
58
70
|
|
59
71
|
alias after being_after
|
60
72
|
|
61
73
|
# Constrain the value to be chronologically before a given date/time
|
62
74
|
#
|
63
|
-
# @param other [Date, DateTime, String, Time] the date/time to compare against
|
75
|
+
# @param other [Date, DateTime, Integer, String, Time] the date/time to compare against
|
64
76
|
# @return [self] self for method chaining
|
65
|
-
def being_before: (Date | DateTime | String | Time other) -> Behavior
|
77
|
+
def being_before: (Date | DateTime | Integer | String | Time other) -> Behavior
|
66
78
|
|
67
79
|
alias before being_before
|
68
80
|
|
69
81
|
# Constrain the value to be chronologically between two date/times
|
70
82
|
#
|
71
|
-
# @param after [Date, DateTime, String, Time] the earliest allowed date/time
|
72
|
-
# @param before [Date, DateTime, String, Time] the latest allowed date/time
|
83
|
+
# @param after [Date, DateTime, Integer, String, Time] the earliest allowed date/time
|
84
|
+
# @param before [Date, DateTime, Integer, String, Time] the latest allowed date/time
|
73
85
|
# @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
|
86
|
+
# @option options [Date, DateTime, Integer, String, Time] :after earliest allowed date/time
|
87
|
+
# @option options [Date, DateTime, Integer, String, Time] :before latest allowed date/time
|
76
88
|
# @return [self] self for method chaining
|
77
|
-
def being_between: (Date | DateTime | String | Time after, Date | DateTime | String | Time before) -> Behavior
|
89
|
+
def being_between: (Date | DateTime | Integer | String | Time after, Date | DateTime | Integer | String | Time before) -> Behavior
|
78
90
|
|
79
91
|
alias between being_between
|
80
92
|
|
81
93
|
# Constrain the value to be exactly equal to a given date/time
|
82
94
|
#
|
83
|
-
# @param other [Date, DateTime, String, Time] the date/time to compare against
|
95
|
+
# @param other [Date, DateTime, Integer, String, Time] the date/time to compare against
|
84
96
|
# @return [self] self for method chaining
|
85
|
-
def being_equal_to: (Date | DateTime | String | Time other) -> Behavior
|
97
|
+
def being_equal_to: (Date | DateTime | Integer | String | Time other) -> Behavior
|
86
98
|
|
87
99
|
alias at being_equal_to
|
88
100
|
|
89
101
|
# Constrain the value to be chronologically on or after a given date/time
|
90
102
|
#
|
91
|
-
# @param other [Date, DateTime, String, Time] the date/time to compare against
|
103
|
+
# @param other [Date, DateTime, Integer, String, Time] the date/time to compare against
|
92
104
|
# @return [self] self for method chaining
|
93
|
-
def being_on_or_after: (Date | DateTime | String | Time other) -> Behavior
|
105
|
+
def being_on_or_after: (Date | DateTime | Integer | String | Time other) -> Behavior
|
94
106
|
|
95
107
|
alias at_or_after being_on_or_after
|
96
108
|
|
@@ -100,9 +112,9 @@ module Domainic
|
|
100
112
|
|
101
113
|
# Constrain the value to be chronologically on or before a given date/time
|
102
114
|
#
|
103
|
-
# @param other [Date, DateTime, String, Time] the date/time to compare against
|
115
|
+
# @param other [Date, DateTime, Integer, String, Time] the date/time to compare against
|
104
116
|
# @return [self] self for method chaining
|
105
|
-
def being_on_or_before: (Date | DateTime | String | Time other) -> Behavior
|
117
|
+
def being_on_or_before: (Date | DateTime | Integer | String | Time other) -> Behavior
|
106
118
|
|
107
119
|
alias at_or_before being_on_or_before
|
108
120
|
|