data_model 0.4.0 → 0.6.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/.rubocop.yml +8 -2
- data/.solargraph.yml +22 -0
- data/Gemfile.lock +71 -29
- data/Rakefile +0 -6
- data/Steepfile +27 -0
- data/data_model.gemspec +1 -2
- data/lib/data_model/boolean.rb +0 -2
- data/lib/data_model/builtin/array.rb +32 -25
- data/lib/data_model/builtin/big_decimal.rb +15 -14
- data/lib/data_model/builtin/boolean.rb +10 -7
- data/lib/data_model/builtin/date.rb +15 -12
- data/lib/data_model/builtin/float.rb +14 -13
- data/lib/data_model/builtin/hash.rb +109 -36
- data/lib/data_model/builtin/integer.rb +14 -13
- data/lib/data_model/builtin/numeric.rb +35 -0
- data/lib/data_model/builtin/object.rb +28 -0
- data/lib/data_model/builtin/or.rb +73 -0
- data/lib/data_model/builtin/string.rb +15 -16
- data/lib/data_model/builtin/symbol.rb +14 -13
- data/lib/data_model/builtin/time.rb +17 -14
- data/lib/data_model/builtin.rb +9 -9
- data/lib/data_model/error.rb +30 -18
- data/lib/data_model/errors.rb +79 -55
- data/lib/data_model/fixtures/array.rb +22 -9
- data/lib/data_model/fixtures/big_decimal.rb +9 -7
- data/lib/data_model/fixtures/boolean.rb +5 -5
- data/lib/data_model/fixtures/date.rb +13 -11
- data/lib/data_model/fixtures/example.rb +7 -7
- data/lib/data_model/fixtures/float.rb +9 -7
- data/lib/data_model/fixtures/hash.rb +22 -10
- data/lib/data_model/fixtures/integer.rb +9 -7
- data/lib/data_model/fixtures/numeric.rb +31 -0
- data/lib/data_model/fixtures/object.rb +27 -0
- data/lib/data_model/fixtures/or.rb +29 -0
- data/lib/data_model/fixtures/string.rb +15 -32
- data/lib/data_model/fixtures/symbol.rb +9 -7
- data/lib/data_model/fixtures/time.rb +13 -11
- data/lib/data_model/logging.rb +5 -8
- data/lib/data_model/model.rb +11 -8
- data/lib/data_model/registry.rb +37 -22
- data/lib/data_model/scanner.rb +23 -28
- data/lib/data_model/struct.rb +116 -0
- data/lib/data_model/testing/minitest.rb +33 -9
- data/lib/data_model/testing.rb +0 -2
- data/lib/data_model/type.rb +38 -22
- data/lib/data_model/version.rb +1 -3
- data/lib/data_model.rb +8 -17
- metadata +13 -26
- data/sorbet/config +0 -4
- data/sorbet/rbi/annotations/rainbow.rbi +0 -269
- data/sorbet/rbi/gems/minitest@5.18.0.rbi +0 -1491
- data/sorbet/rbi/gems/zeitwerk.rbi +0 -196
- data/sorbet/rbi/gems/zeitwerk@2.6.7.rbi +0 -966
- data/sorbet/rbi/todo.rbi +0 -5
- data/sorbet/tapioca/config.yml +0 -13
- data/sorbet/tapioca/require.rb +0 -4
@@ -1,26 +1,73 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
1
|
module DataModel
|
4
|
-
# Hash type has a concept of "child types"
|
2
|
+
# Hash type has a concept of "child types". They can either be specified
|
3
|
+
# as params of nested child arrays, or in a hash notation if specific
|
4
|
+
# keys are not being specified
|
5
|
+
#
|
6
|
+
# This is by far the most complex built-in type
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
# [:hash, [:name, :string], [:email, :string]] # must have two specific keys
|
10
|
+
# [:hash, [symbol: :string] # key and values must be according to type
|
11
|
+
# [:hash, [symbol: [:string, {optional: true}]]] # types can be specified long hand
|
5
12
|
class Builtin::Hash < Type
|
6
13
|
include Errors
|
7
14
|
include Logging
|
8
15
|
|
9
|
-
class Arguments <
|
10
|
-
prop :optional,
|
11
|
-
prop :open,
|
16
|
+
class Arguments < Struct
|
17
|
+
prop :optional, :boolean, default: false
|
18
|
+
prop :open, :boolean, default: true
|
12
19
|
end
|
13
20
|
|
14
21
|
## Children
|
15
22
|
|
16
|
-
|
23
|
+
# configure how hash children will be read
|
24
|
+
# @param params [Array] the params to configure
|
25
|
+
# @return [void]
|
17
26
|
def configure(params)
|
18
|
-
result =
|
19
|
-
@
|
20
|
-
|
27
|
+
result = @children = {}
|
28
|
+
@key_value = false
|
21
29
|
log.debug("configuring hash children")
|
22
30
|
|
23
|
-
|
31
|
+
if params.length == 1 && params.first.length == 1 && params.first.first.is_a?(Hash)
|
32
|
+
@key_value = true
|
33
|
+
log.debug("configuring hash children with {key => value} notation")
|
34
|
+
params = params.first.first
|
35
|
+
|
36
|
+
if params.length != 1
|
37
|
+
raise "expected only one key in the {key => value} notation, got #{params.length} for #{params.inspect}"
|
38
|
+
end
|
39
|
+
|
40
|
+
key = params.keys.first
|
41
|
+
value = params.values.first
|
42
|
+
|
43
|
+
if key.nil?
|
44
|
+
raise "schema for key is missing"
|
45
|
+
end
|
46
|
+
|
47
|
+
if value.nil?
|
48
|
+
raise "schema for value is missing"
|
49
|
+
end
|
50
|
+
|
51
|
+
key_node = Scanner.scan(Array(key), type_registry)
|
52
|
+
value_node = Scanner.scan(Array(value), type_registry)
|
53
|
+
|
54
|
+
result[:key] = type_registry.type(
|
55
|
+
key_node.type,
|
56
|
+
args: key_node.args,
|
57
|
+
params: key_node.params,
|
58
|
+
)
|
59
|
+
|
60
|
+
result[:value] = type_registry.type(
|
61
|
+
value_node.type,
|
62
|
+
args: value_node.args,
|
63
|
+
params: value_node.params,
|
64
|
+
)
|
65
|
+
|
66
|
+
return
|
67
|
+
end
|
68
|
+
|
69
|
+
log.debug("configuring hash children with array notation")
|
70
|
+
for child in params
|
24
71
|
name, *schema = child
|
25
72
|
if !name.is_a?(Symbol)
|
26
73
|
raise "expected name as a symbol for the first element of child schemas, got #{name.inspect} for #{child.inspect}"
|
@@ -31,13 +78,14 @@ module DataModel
|
|
31
78
|
end
|
32
79
|
|
33
80
|
node = Scanner.scan(schema)
|
34
|
-
log.debug("adding hash child -> #{name}: #{node.
|
81
|
+
log.debug("adding hash child -> #{name}: #{node.to_h}")
|
35
82
|
|
36
83
|
result[name] = instantiate(node.type, args: node.args, params: node.params)
|
37
84
|
end
|
38
85
|
end
|
39
86
|
|
40
|
-
|
87
|
+
# get the children types of this hash
|
88
|
+
# @return [Hash] the children types of this hash
|
41
89
|
def children
|
42
90
|
if @children.nil?
|
43
91
|
raise "children not configured"
|
@@ -48,7 +96,10 @@ module DataModel
|
|
48
96
|
|
49
97
|
## Read
|
50
98
|
|
51
|
-
|
99
|
+
# read a value, and validate it
|
100
|
+
# @param val [Object] the value to read
|
101
|
+
# @param coerce [Boolean] whether to coerce the value
|
102
|
+
# @return [Array(Object, Error)] the result of reading the value
|
52
103
|
def read(val, coerce: false)
|
53
104
|
args = Arguments.new(type_args)
|
54
105
|
errors = Error.new
|
@@ -59,34 +110,40 @@ module DataModel
|
|
59
110
|
end
|
60
111
|
|
61
112
|
if !args.optional && val.nil?
|
62
|
-
errors.add(missing_error(
|
113
|
+
errors.add(missing_error(type_name))
|
63
114
|
return [val, errors]
|
64
115
|
end
|
65
116
|
|
66
117
|
# type error, early exit
|
67
118
|
if !val.is_a?(Hash) && !coerce
|
68
|
-
errors.add(type_error(
|
119
|
+
errors.add(type_error(type_name, val))
|
69
120
|
return [val, errors]
|
70
121
|
end
|
71
122
|
|
72
123
|
# attempt coercion
|
73
124
|
if !val.is_a?(Hash) && coerce
|
74
125
|
if val.respond_to?(:to_h)
|
75
|
-
val =
|
126
|
+
val = val.to_h
|
76
127
|
elsif val.respond_to?(:to_hash)
|
77
128
|
val = Hash(val)
|
78
129
|
else
|
79
|
-
errors.add(coerce_error(
|
130
|
+
errors.add(coerce_error(type_name, val))
|
80
131
|
return [val, errors]
|
81
132
|
end
|
82
133
|
end
|
83
134
|
|
84
|
-
hash = T.cast(val, T::Hash[Symbol, Object])
|
85
|
-
|
86
135
|
# detect extra keys then what is defined in the schema
|
87
136
|
if !args.open
|
88
|
-
|
89
|
-
|
137
|
+
found = []
|
138
|
+
for child_key in children.keys
|
139
|
+
for val_key in val.keys
|
140
|
+
if val_key.to_s == child_key.to_s # doing it this way to support symbols and strings
|
141
|
+
found << val_key
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
extra = val.keys - found
|
90
147
|
|
91
148
|
if !extra.empty?
|
92
149
|
errors.add(extra_keys_error(extra))
|
@@ -95,23 +152,39 @@ module DataModel
|
|
95
152
|
end
|
96
153
|
|
97
154
|
# process children
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
log.debug("
|
105
|
-
|
155
|
+
if @key_value
|
156
|
+
log.debug("processing hash children with {key => value} notation")
|
157
|
+
key = children[:key]
|
158
|
+
value = children[:value]
|
159
|
+
|
160
|
+
for (k, v) in val.dup
|
161
|
+
log.debug("processing #{k} -> #{v.inspect}")
|
162
|
+
k, key_errors = key.read(k, coerce:)
|
163
|
+
v, value_errors = value.read(v, coerce:)
|
164
|
+
|
165
|
+
if !key_errors.any? && !value_errors.any?
|
166
|
+
log.debug("no errors")
|
167
|
+
val[k] = v
|
168
|
+
end
|
169
|
+
|
170
|
+
errors.merge_child(k, key_errors)
|
171
|
+
errors.merge_child(k, value_errors)
|
172
|
+
end
|
173
|
+
else
|
174
|
+
log.debug("processing hash children with array notation")
|
175
|
+
for (name, child) in children
|
176
|
+
val[name], child_errors = child.read(val[name], coerce:)
|
177
|
+
log.debug("child #{name} -> #{val[name].inspect} #{child_errors.inspect}")
|
178
|
+
|
179
|
+
if !child_errors.any?
|
180
|
+
log.debug("no errors, skipping")
|
181
|
+
next
|
182
|
+
end
|
183
|
+
|
184
|
+
errors.merge_child(name, child_errors)
|
106
185
|
end
|
107
|
-
|
108
|
-
errors.merge_child(name, child_errors)
|
109
|
-
|
110
|
-
return [val, errors]
|
111
186
|
end
|
112
187
|
|
113
|
-
log.debug("hash check successful")
|
114
|
-
|
115
188
|
# done
|
116
189
|
return [val, errors]
|
117
190
|
end
|
@@ -1,16 +1,19 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
1
|
module DataModel
|
2
|
+
# Integer type
|
4
3
|
class Builtin::Integer < Type
|
5
4
|
include Errors
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
prop :
|
10
|
-
prop :
|
6
|
+
# Arguments for Array type.
|
7
|
+
class Arguments < Struct
|
8
|
+
prop :optional, :boolean, default: false
|
9
|
+
prop :min, :numeric, default: nil
|
10
|
+
prop :max, :numeric, default: nil
|
11
11
|
end
|
12
12
|
|
13
|
-
|
13
|
+
# read a value, and validate it
|
14
|
+
# @param val [Object] the value to read
|
15
|
+
# @param coerce [Boolean] whether to coerce the value
|
16
|
+
# @return [Array(Object, Error)] the result of reading the value
|
14
17
|
def read(val, coerce: false)
|
15
18
|
err = Error.new
|
16
19
|
args = Arguments.new(type_args)
|
@@ -20,12 +23,12 @@ module DataModel
|
|
20
23
|
end
|
21
24
|
|
22
25
|
if !args.optional && val.nil?
|
23
|
-
err.add(missing_error(
|
26
|
+
err.add(missing_error(type_name))
|
24
27
|
return [val, err]
|
25
28
|
end
|
26
29
|
|
27
30
|
if !val.is_a?(Integer) && !coerce
|
28
|
-
err.add(type_error(
|
31
|
+
err.add(type_error(type_name, val))
|
29
32
|
return [val, err]
|
30
33
|
end
|
31
34
|
|
@@ -33,17 +36,15 @@ module DataModel
|
|
33
36
|
if val.is_a?(String) || val.is_a?(Numeric)
|
34
37
|
val = Integer(val)
|
35
38
|
elsif val.respond_to?(:to_i)
|
36
|
-
val =
|
39
|
+
val = val.to_i
|
37
40
|
end
|
38
41
|
|
39
42
|
if !val.is_a?(Integer)
|
40
|
-
err.add(coerce_error(
|
43
|
+
err.add(coerce_error(type_name, val))
|
41
44
|
return [val, err]
|
42
45
|
end
|
43
46
|
end
|
44
47
|
|
45
|
-
val = T.cast(val, Integer)
|
46
|
-
|
47
48
|
min = args.min
|
48
49
|
if min && val <= min
|
49
50
|
err.add(min_error(min, val))
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module DataModel
|
2
|
+
# Numeric type is :integer | :float | :decimal
|
3
|
+
class Builtin::Numeric < Type
|
4
|
+
include Errors
|
5
|
+
|
6
|
+
# Arguments for this type
|
7
|
+
class Arguments < Struct
|
8
|
+
prop :optional, :boolean, default: false
|
9
|
+
end
|
10
|
+
|
11
|
+
# read a value, and validate it
|
12
|
+
# @param val [Object] the value to read
|
13
|
+
# @param coerce [Boolean] whether to coerce the value
|
14
|
+
# @return [Array(Object, Error)] the result of reading the value
|
15
|
+
def read(val, coerce: false)
|
16
|
+
args = Arguments.new(type_args)
|
17
|
+
err = Error.new
|
18
|
+
|
19
|
+
# optional and missing
|
20
|
+
if !args.optional && val.nil?
|
21
|
+
err.add(missing_error(type_name))
|
22
|
+
end
|
23
|
+
|
24
|
+
# when missing, return early
|
25
|
+
if val.nil?
|
26
|
+
return [val, err]
|
27
|
+
end
|
28
|
+
|
29
|
+
val, err = invoke(:or, val, params: [:integer, :float, :decimal], coerce:)
|
30
|
+
|
31
|
+
# done
|
32
|
+
return [val, err]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module DataModel
|
2
|
+
# acts as any type, the only way to fail validation is if it is nil and not present
|
3
|
+
class Builtin::Object < Type
|
4
|
+
include Errors
|
5
|
+
|
6
|
+
# Object arguments
|
7
|
+
class Arguments < Struct
|
8
|
+
# @!attribute optional
|
9
|
+
# @return [Boolean] whether or not this type is optional
|
10
|
+
prop :optional, :boolean, default: false
|
11
|
+
end
|
12
|
+
|
13
|
+
# read a value, and validate it
|
14
|
+
# @param val [Object] the value to read
|
15
|
+
# @param coerce [Boolean] whether to coerce the value
|
16
|
+
# @return [Array(Object, Error)] the result of reading the value
|
17
|
+
def read(val, coerce: false)
|
18
|
+
args = Arguments.new(type_args)
|
19
|
+
err = Error.new
|
20
|
+
|
21
|
+
if val.nil? && !args.optional
|
22
|
+
err.add(missing_error(type_name))
|
23
|
+
end
|
24
|
+
|
25
|
+
return [val, err]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module DataModel
|
2
|
+
# Or type, allows for value to be one of several types
|
3
|
+
# Types will be tried in order. The first type to succeed will be used.
|
4
|
+
# If all types fail, the or type fails
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# [:or, :string, [:array, :string]]
|
8
|
+
class Builtin::Or < Type
|
9
|
+
include Errors
|
10
|
+
include Logging
|
11
|
+
|
12
|
+
# Arguments for this type
|
13
|
+
class Arguments < Struct
|
14
|
+
prop :optional, :boolean, default: false
|
15
|
+
end
|
16
|
+
|
17
|
+
# support either :string shorthand or [:string, {optional: true}]
|
18
|
+
# @param params [Array<untyped>] the params to configure this type
|
19
|
+
# @return [void]
|
20
|
+
def configure(params)
|
21
|
+
if params.first.is_a?(Array)
|
22
|
+
params = params.first
|
23
|
+
end
|
24
|
+
|
25
|
+
@child_types = []
|
26
|
+
|
27
|
+
nodes = params.map { |p| Scanner.scan(Array(p)) }
|
28
|
+
for node in nodes
|
29
|
+
type = instantiate(node.type, args: node.args, params: node.params)
|
30
|
+
@child_types << type
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# read a value, and validate it
|
35
|
+
# @param val [Object] the value to read
|
36
|
+
# @param coerce [Boolean] whether to coerce the value
|
37
|
+
# @return [Array(Object, Error)] the result of reading the value
|
38
|
+
def read(val, coerce: false)
|
39
|
+
args = Arguments.new(type_args)
|
40
|
+
err = Error.new
|
41
|
+
child_names = @child_types.map(&:type_name)
|
42
|
+
|
43
|
+
log.debug("coerce: #{coerce} or type #{child_names} with value #{val}")
|
44
|
+
|
45
|
+
# optional and missing
|
46
|
+
if !args.optional && val.nil?
|
47
|
+
err.add(missing_error(child_names))
|
48
|
+
end
|
49
|
+
|
50
|
+
# when missing, return early
|
51
|
+
if val.nil?
|
52
|
+
return [val, err]
|
53
|
+
end
|
54
|
+
|
55
|
+
valid = false
|
56
|
+
|
57
|
+
for type in @child_types
|
58
|
+
val, err = type.read(val, coerce: coerce)
|
59
|
+
if err.empty?
|
60
|
+
valid = true
|
61
|
+
break
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
if !valid
|
66
|
+
err.add(type_error(child_names, val))
|
67
|
+
end
|
68
|
+
|
69
|
+
# done
|
70
|
+
return [val, err]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -1,20 +1,21 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
1
|
module DataModel
|
2
|
+
# Built-in type for String
|
4
3
|
class Builtin::String < Type
|
5
4
|
include Errors
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
prop :
|
11
|
-
prop :
|
12
|
-
prop :
|
13
|
-
prop :
|
14
|
-
prop :excluded, T::Array[String], default: []
|
6
|
+
# Arguments for this type
|
7
|
+
class Arguments < Struct
|
8
|
+
prop :optional, :boolean, default: false
|
9
|
+
prop :allow_blank, :boolean, default: true
|
10
|
+
prop :format, :string, default: nil
|
11
|
+
prop :included, [:array, :string], default: []
|
12
|
+
prop :excluded, [:array, :string], default: []
|
15
13
|
end
|
16
14
|
|
17
|
-
|
15
|
+
# read a value, and validate it
|
16
|
+
# @param val [Object] the value to read
|
17
|
+
# @param coerce [Boolean] whether to coerce the value
|
18
|
+
# @return [Array(Object, Error)] the result of reading the value
|
18
19
|
def read(val, coerce: false)
|
19
20
|
args = Arguments.new(type_args)
|
20
21
|
err = Error.new
|
@@ -25,13 +26,13 @@ module DataModel
|
|
25
26
|
end
|
26
27
|
|
27
28
|
if !args.optional && val.nil?
|
28
|
-
err.add(missing_error(
|
29
|
+
err.add(missing_error(type_name))
|
29
30
|
return [val, err]
|
30
31
|
end
|
31
32
|
|
32
33
|
# type error
|
33
34
|
if !val.is_a?(String) && !coerce
|
34
|
-
err.add(type_error(
|
35
|
+
err.add(type_error(type_name, val))
|
35
36
|
return [val, err]
|
36
37
|
end
|
37
38
|
|
@@ -40,13 +41,11 @@ module DataModel
|
|
40
41
|
begin
|
41
42
|
val = String(val)
|
42
43
|
rescue TypeError
|
43
|
-
err.add(coerce_error(
|
44
|
+
err.add(coerce_error(type_name, val))
|
44
45
|
return [val, err]
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
48
|
-
val = T.cast(val, String)
|
49
|
-
|
50
49
|
# format
|
51
50
|
fmt = args.format
|
52
51
|
if fmt
|
@@ -1,16 +1,19 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
1
|
module DataModel
|
2
|
+
# Symbol type
|
4
3
|
class Builtin::Symbol < Type
|
5
4
|
include Errors
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
prop :
|
10
|
-
prop :
|
6
|
+
# Arguments for this type
|
7
|
+
class Arguments < Struct
|
8
|
+
prop :optional, :boolean, default: false
|
9
|
+
prop :included, [:array, :symbol], default: []
|
10
|
+
prop :excluded, [:array, :symbol], default: []
|
11
11
|
end
|
12
12
|
|
13
|
-
|
13
|
+
# read a value, and validate it
|
14
|
+
# @param val [Object] the value to read
|
15
|
+
# @param coerce [Boolean] whether to coerce the value
|
16
|
+
# @return [Array(Object, Error)] the result of reading the value
|
14
17
|
def read(val, coerce: false)
|
15
18
|
args = Arguments.new(type_args)
|
16
19
|
err = Error.new
|
@@ -21,13 +24,13 @@ module DataModel
|
|
21
24
|
end
|
22
25
|
|
23
26
|
if !args.optional && val.nil?
|
24
|
-
err.add(missing_error(
|
27
|
+
err.add(missing_error(type_name))
|
25
28
|
return [val, err]
|
26
29
|
end
|
27
30
|
|
28
31
|
# type error
|
29
32
|
if !val.is_a?(Symbol) && !coerce
|
30
|
-
err.add(type_error(
|
33
|
+
err.add(type_error(type_name, val))
|
31
34
|
return [val, err]
|
32
35
|
end
|
33
36
|
|
@@ -36,15 +39,13 @@ module DataModel
|
|
36
39
|
if val.is_a?(String)
|
37
40
|
val = val.intern
|
38
41
|
elsif val.respond_to?(:to_sym)
|
39
|
-
val =
|
42
|
+
val = val.to_sym
|
40
43
|
else
|
41
|
-
err.add(coerce_error(
|
44
|
+
err.add(coerce_error(type_name, val))
|
42
45
|
return [val, err]
|
43
46
|
end
|
44
47
|
end
|
45
48
|
|
46
|
-
val = T.cast(val, Symbol)
|
47
|
-
|
48
49
|
# inclusion
|
49
50
|
if args.included.any? && !args.included.include?(val)
|
50
51
|
err.add(inclusion_error(args.included))
|
@@ -1,16 +1,19 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
1
|
module DataModel
|
2
|
+
# A time type
|
4
3
|
class Builtin::Time < Type
|
5
4
|
include Errors
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
prop :
|
10
|
-
prop :
|
6
|
+
# Arguments for this type
|
7
|
+
class Arguments < Struct
|
8
|
+
prop :optional, :boolean, default: false
|
9
|
+
prop :earliest, [:time, { optional: true }], default: nil
|
10
|
+
prop :latest, [:time, { optional: true }], default: nil
|
11
11
|
end
|
12
12
|
|
13
|
-
|
13
|
+
# read a value, and validate it
|
14
|
+
# @param val [Object] the value to read
|
15
|
+
# @param coerce [Boolean] whether to coerce the value
|
16
|
+
# @return [Array(Object, Error)] the result of reading the value
|
14
17
|
def read(val, coerce: false)
|
15
18
|
args = Arguments.new(type_args)
|
16
19
|
err = Error.new
|
@@ -22,7 +25,7 @@ module DataModel
|
|
22
25
|
|
23
26
|
# missing, but not allowed, don't do any more checks
|
24
27
|
if val.nil?
|
25
|
-
err.add(missing_error(
|
28
|
+
err.add(missing_error(type_name))
|
26
29
|
return [val, err]
|
27
30
|
end
|
28
31
|
|
@@ -31,26 +34,26 @@ module DataModel
|
|
31
34
|
begin
|
32
35
|
val = Time.parse(val)
|
33
36
|
rescue ArgumentError
|
34
|
-
err.add(type_error(
|
37
|
+
err.add(type_error(type_name, val))
|
35
38
|
return [val, err]
|
36
39
|
end
|
37
40
|
end
|
38
41
|
|
39
42
|
# not a date, don't do any more checks
|
40
43
|
if !val.is_a?(Time)
|
41
|
-
err.add(type_error(
|
44
|
+
err.add(type_error(type_name, val))
|
42
45
|
return [val, err]
|
43
46
|
end
|
44
47
|
|
45
48
|
# date is before the earliest point allowed
|
46
|
-
if args.earliest && (val <
|
47
|
-
error = earliest_error(
|
49
|
+
if args.earliest && (val < args.earliest)
|
50
|
+
error = earliest_error(args.earliest, val)
|
48
51
|
err.add(error)
|
49
52
|
end
|
50
53
|
|
51
54
|
# date is after the latest point allowed
|
52
|
-
if args.latest && (val >
|
53
|
-
error = latest_error(
|
55
|
+
if args.latest && (val > args.latest)
|
56
|
+
error = latest_error(args.latest, val)
|
54
57
|
err.add(error)
|
55
58
|
end
|
56
59
|
|
data/lib/data_model/builtin.rb
CHANGED
@@ -1,22 +1,22 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
1
|
module DataModel
|
4
2
|
module Builtin
|
5
|
-
|
6
|
-
|
7
|
-
sig { returns(TTypeMap) }
|
3
|
+
# Hash of all builtin types, useful when instanciating a Registry
|
4
|
+
# @return [Hash{Symbol => Type}] the builtin types
|
8
5
|
def self.types
|
9
6
|
{
|
10
7
|
hash: Builtin::Hash,
|
8
|
+
array: Builtin::Array,
|
9
|
+
or: Builtin::Or,
|
10
|
+
object: Builtin::Object,
|
11
|
+
boolean: Builtin::Boolean,
|
12
|
+
date: Builtin::Date,
|
13
|
+
time: Builtin::Time,
|
11
14
|
string: Builtin::String,
|
12
15
|
symbol: Builtin::Symbol,
|
13
16
|
integer: Builtin::Integer,
|
14
17
|
decimal: Builtin::BigDecimal,
|
15
18
|
float: Builtin::Float,
|
16
|
-
|
17
|
-
array: Builtin::Array,
|
18
|
-
date: Builtin::Date,
|
19
|
-
time: Builtin::Time
|
19
|
+
numeric: Builtin::Numeric
|
20
20
|
}
|
21
21
|
end
|
22
22
|
end
|