data_model 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -2
- data/.solargraph.yml +22 -0
- data/Gemfile.lock +36 -3
- 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 +100 -35
- 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 +112 -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 +12 -25
- 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,32 @@ 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
137
|
keys = children.keys
|
89
|
-
extra =
|
138
|
+
extra = val.keys - keys
|
90
139
|
|
91
140
|
if !extra.empty?
|
92
141
|
errors.add(extra_keys_error(extra))
|
@@ -95,23 +144,39 @@ module DataModel
|
|
95
144
|
end
|
96
145
|
|
97
146
|
# process children
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
log.debug("
|
105
|
-
|
147
|
+
if @key_value
|
148
|
+
log.debug("processing hash children with {key => value} notation")
|
149
|
+
key = children[:key]
|
150
|
+
value = children[:value]
|
151
|
+
|
152
|
+
for (k, v) in val.dup
|
153
|
+
log.debug("processing #{k} -> #{v.inspect}")
|
154
|
+
k, key_errors = key.read(k, coerce:)
|
155
|
+
v, value_errors = value.read(v, coerce:)
|
156
|
+
|
157
|
+
if !key_errors.any? && !value_errors.any?
|
158
|
+
log.debug("no errors")
|
159
|
+
val[k] = v
|
160
|
+
end
|
161
|
+
|
162
|
+
errors.merge_child(k, key_errors)
|
163
|
+
errors.merge_child(k, value_errors)
|
164
|
+
end
|
165
|
+
else
|
166
|
+
log.debug("processing hash children with array notation")
|
167
|
+
for (name, child) in children
|
168
|
+
val[name], child_errors = child.read(val[name], coerce:)
|
169
|
+
log.debug("child #{name} -> #{val[name].inspect} #{child_errors.inspect}")
|
170
|
+
|
171
|
+
if !child_errors.any?
|
172
|
+
log.debug("no errors, skipping")
|
173
|
+
next
|
174
|
+
end
|
175
|
+
|
176
|
+
errors.merge_child(name, child_errors)
|
106
177
|
end
|
107
|
-
|
108
|
-
errors.merge_child(name, child_errors)
|
109
|
-
|
110
|
-
return [val, errors]
|
111
178
|
end
|
112
179
|
|
113
|
-
log.debug("hash check successful")
|
114
|
-
|
115
180
|
# done
|
116
181
|
return [val, errors]
|
117
182
|
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
|