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