data_model 0.3.0 → 0.5.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 +11 -2
- data/.shadowenv.d/.gitignore +2 -0
- data/.shadowenv.d/550-ruby.lisp +37 -0
- data/.solargraph.yml +22 -0
- data/Gemfile.lock +38 -3
- data/Rakefile +0 -6
- data/Steepfile +27 -0
- data/data_model.gemspec +2 -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 +33 -33
- data/lib/data_model/errors.rb +107 -143
- 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 +129 -0
- data/lib/data_model/scanner.rb +24 -29
- 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 +39 -23
- data/lib/data_model/version.rb +1 -3
- data/lib/data_model.rb +10 -19
- metadata +24 -21
- data/lib/data_model/type_registry.rb +0 -68
- 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
@@ -0,0 +1,129 @@
|
|
1
|
+
module DataModel
|
2
|
+
# Registry allows for different type implementations to be used by the scanner.
|
3
|
+
# It also acts as an error message registry, mostly for pragmatic reasons.
|
4
|
+
class Registry
|
5
|
+
# Default types that will be used if alternative type map is not given
|
6
|
+
# @return [Hash] the default type map
|
7
|
+
def self.default_types
|
8
|
+
Builtin.types
|
9
|
+
end
|
10
|
+
|
11
|
+
# Default error messages that will be used if alternative error messages are not given
|
12
|
+
# @return [Hash] the default error messages
|
13
|
+
def self.default_error_messages
|
14
|
+
Errors.error_messages
|
15
|
+
end
|
16
|
+
|
17
|
+
# Singleton instance that will be used globally unless instances given
|
18
|
+
# @param types [Hash] the type map to use
|
19
|
+
# @param errors [Hash] the error message map to use
|
20
|
+
# @return [Registry] the singleton instance
|
21
|
+
def self.instance(types: default_types, errors: default_error_messages)
|
22
|
+
@instance ||= new(types:, errors:)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Register a type on the global instance
|
26
|
+
# @param name [Symbol] the name of the type
|
27
|
+
# @param type [Type] the type to register
|
28
|
+
# @return [void]
|
29
|
+
def self.register(name, type)
|
30
|
+
instance.register(name, type)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Instanciate a new type registry. Default errors will always be used, but additional
|
34
|
+
# errors can be registered.
|
35
|
+
# @param types [Hash] the type map to use
|
36
|
+
# @param errors [Hash] the error message map to use
|
37
|
+
# @return [Registry] the new instance
|
38
|
+
def initialize(types: self.class.default_types, errors: self.class.default_error_messages)
|
39
|
+
@error_messages = nil
|
40
|
+
|
41
|
+
if errors
|
42
|
+
errors.each { |type, builder| register_error_message(type, &builder) }
|
43
|
+
end
|
44
|
+
|
45
|
+
@types = {}
|
46
|
+
types.each { |(name, type)| register(name, type) }
|
47
|
+
end
|
48
|
+
|
49
|
+
# Register a type on this instance
|
50
|
+
# @param name [Symbol] the name of the Type
|
51
|
+
# @param type [Type] the type to register
|
52
|
+
# @return [void]
|
53
|
+
def register(name, type)
|
54
|
+
@types[name] = type
|
55
|
+
end
|
56
|
+
|
57
|
+
# Check if a type is registered
|
58
|
+
# @param name [Symbol] the name of the type
|
59
|
+
# @return [Boolean] whether the type is registered
|
60
|
+
def type?(name)
|
61
|
+
@types.key?(name)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Access and configure registered type
|
65
|
+
# @param name [Symbol] the name of the Type
|
66
|
+
# @param args [Hash] the arguments to pass to the Type
|
67
|
+
# @param params [Array] the parameters to configure the Type with
|
68
|
+
# @return [Type] the configured type
|
69
|
+
def type(name, args: {}, params: nil)
|
70
|
+
if !type?(name)
|
71
|
+
raise "#{name} is not registered as a type"
|
72
|
+
end
|
73
|
+
|
74
|
+
t = @types.fetch(name).new(args, registry: self)
|
75
|
+
|
76
|
+
if params
|
77
|
+
t.configure(params)
|
78
|
+
end
|
79
|
+
|
80
|
+
return t
|
81
|
+
end
|
82
|
+
|
83
|
+
## API
|
84
|
+
|
85
|
+
# Register a custom error message for use with custom errors
|
86
|
+
# @param type [Symbol] the type of error to register
|
87
|
+
# @param block [Proc] the block to use to build the error message, shoudl take the error context and return a string
|
88
|
+
# @return [void]
|
89
|
+
def register_error_message(type, &block)
|
90
|
+
error_message_builders[type] = block
|
91
|
+
end
|
92
|
+
|
93
|
+
# Get the error message builders
|
94
|
+
# @return [Hash] the error message builders
|
95
|
+
def error_message_builders
|
96
|
+
if @error_messages.nil?
|
97
|
+
@error_messages ||= {}
|
98
|
+
end
|
99
|
+
|
100
|
+
@error_messages
|
101
|
+
end
|
102
|
+
|
103
|
+
# Build the error message for a given error
|
104
|
+
# @param error [Error] the error to build the message for
|
105
|
+
# @return [String] the error message
|
106
|
+
# @raise [RuntimeError] if no error message builder is registered for the error type
|
107
|
+
def error_message(error)
|
108
|
+
type = error[0]
|
109
|
+
ctx = error[1]
|
110
|
+
|
111
|
+
builder = error_message_builders[type]
|
112
|
+
|
113
|
+
if builder.nil?
|
114
|
+
raise "no error message builder for #{type}"
|
115
|
+
end
|
116
|
+
|
117
|
+
builder.call(ctx)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Build error messages from error object
|
121
|
+
# @param error [Error] the error to build the messages for
|
122
|
+
# @return [Hash] the error messages
|
123
|
+
def error_messages(error)
|
124
|
+
error.to_h.transform_values do |error_list|
|
125
|
+
error_list.map { |e| error_message(e) }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/lib/data_model/scanner.rb
CHANGED
@@ -1,43 +1,37 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
|
-
# Scan a schema into a struct that can be inspected to construct a model validator
|
4
|
-
#
|
5
|
-
# schema eg:
|
6
|
-
# [:string, { min: 1, max: 10}]
|
7
|
-
# [:tuple, { title: "coordinates" }, :double, :double]
|
8
|
-
# [:hash, { open: false },
|
9
|
-
# [:first_name, :string]
|
10
|
-
# [:last_name, :string]]
|
11
|
-
#
|
12
|
-
# first param is type, which is a key lookup in the registry
|
13
|
-
# second param is args, this is optional, but is a way to configure a type
|
14
|
-
# rest are type params. these are used to configure a type at the point of instantiation. Think of them as generics.
|
15
|
-
#
|
16
|
-
# params are either
|
17
|
-
# symbol, for example tuple types
|
18
|
-
# array, for object types to configure child properties.
|
19
1
|
module DataModel
|
2
|
+
# The scanner is responsible for scanning a schema into a data structure that is easier to work with.
|
3
|
+
#
|
4
|
+
# schema eg:
|
5
|
+
# [:string, { min: 1, max: 10}]
|
6
|
+
# [:tuple, { title: "coordinates" }, :double, :double]
|
7
|
+
# [:hash, { open: false },
|
8
|
+
# [:first_name, :string]
|
9
|
+
# [:last_name, :string]]
|
10
|
+
#
|
11
|
+
# first param is type, which is a key lookup in the registry
|
12
|
+
# second param is args, this is optional, but is a way to configure a type
|
13
|
+
# rest are type params. these are used to configure a type at the point of instantiation. Think of them as generics.
|
14
|
+
#
|
15
|
+
# params are either
|
16
|
+
# symbol, for example tuple types
|
17
|
+
# array, for object types to configure child properties.
|
20
18
|
module Scanner
|
21
|
-
include Kernel
|
22
19
|
include Logging
|
23
|
-
|
24
|
-
extend T::Sig
|
25
20
|
extend self
|
26
21
|
|
27
|
-
|
28
|
-
|
29
|
-
prop :args, T::Hash[Symbol, Object], default: {}
|
30
|
-
prop :params, T::Array[Object], default: []
|
31
|
-
end
|
22
|
+
# cant use DataModel::Struct because it would be a circular dependency
|
23
|
+
Node = ::Struct.new(:type, :args, :params)
|
32
24
|
|
33
25
|
# Scan a schema, which is defined as a data structure, into a struct that is easier to work with.
|
34
26
|
# "Syntax" validations will be enforced at this level.
|
35
|
-
|
36
|
-
|
27
|
+
# @param schema [Array] the schema to scan
|
28
|
+
# @param registry [Registry] the registry to use
|
29
|
+
# @return [Node] the scanned node
|
30
|
+
def scan(schema, registry = Registry.instance)
|
37
31
|
# state:
|
38
32
|
# nil (start) -> :type (we have a type) -> :args (we have arguments)
|
39
33
|
scanned = Node.new
|
40
|
-
state =
|
34
|
+
state = nil
|
41
35
|
|
42
36
|
log.debug("scanning schema: #{schema.inspect}")
|
43
37
|
|
@@ -61,6 +55,7 @@ module DataModel
|
|
61
55
|
raise "expected type params at (#{dbg}), which should be either a symbol or an array"
|
62
56
|
end
|
63
57
|
|
58
|
+
scanned.params ||= []
|
64
59
|
scanned.params << token
|
65
60
|
log.debug("collecting params at (#{dbg})")
|
66
61
|
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module DataModel
|
2
|
+
# A struct that has typed properties, which will raise when the wrong type is assigned
|
3
|
+
class Struct
|
4
|
+
class << self
|
5
|
+
# types configured by the prop macro
|
6
|
+
# @return [Hash] the types configured
|
7
|
+
attr_reader :types
|
8
|
+
|
9
|
+
# required types configured by the prop macro
|
10
|
+
# @return [Array<Symbol>] the required types configured
|
11
|
+
attr_reader :required_types
|
12
|
+
end
|
13
|
+
|
14
|
+
# Define a property on the struct.
|
15
|
+
# @param name [Symbol] the name of the property
|
16
|
+
# @param schema [Array] the schema to define
|
17
|
+
# @option opts [Boolean] :default the default value for the property
|
18
|
+
# @return [void]
|
19
|
+
def self.prop(name, schema, opts = {})
|
20
|
+
has_default = opts.key?(:default)
|
21
|
+
default = opts[:default]
|
22
|
+
|
23
|
+
# ensure values are initialized
|
24
|
+
@types ||= {}
|
25
|
+
@required_types ||= []
|
26
|
+
|
27
|
+
# if a prop does not have a default, it is required
|
28
|
+
if !has_default
|
29
|
+
@required_types << name
|
30
|
+
end
|
31
|
+
|
32
|
+
# store the data model for the prop
|
33
|
+
schema = Array(schema)
|
34
|
+
@types[name] = DataModel.define(schema)
|
35
|
+
|
36
|
+
# field getter
|
37
|
+
define_method(name) do
|
38
|
+
@values ||= {}
|
39
|
+
types = self.class.types
|
40
|
+
|
41
|
+
if !types.key?(name)
|
42
|
+
raise "No prop configured for #{name}"
|
43
|
+
end
|
44
|
+
|
45
|
+
if !@values.key?(name) && has_default
|
46
|
+
return default
|
47
|
+
end
|
48
|
+
|
49
|
+
return @values[name]
|
50
|
+
end
|
51
|
+
|
52
|
+
# field setter
|
53
|
+
define_method("#{name}=") do |val|
|
54
|
+
@values ||= {}
|
55
|
+
types = self.class.types
|
56
|
+
|
57
|
+
if !types.key?(name)
|
58
|
+
raise "No prop configured for #{name}"
|
59
|
+
end
|
60
|
+
|
61
|
+
model = types.fetch(name)
|
62
|
+
|
63
|
+
if @coerce
|
64
|
+
val, err = model.coerce(val)
|
65
|
+
else
|
66
|
+
err = model.validate(val)
|
67
|
+
end
|
68
|
+
|
69
|
+
if @strict && err.any?
|
70
|
+
errors.merge_child(prop, err)
|
71
|
+
raise "Invalid value for #{name}: #{err.to_messages.inspect}"
|
72
|
+
end
|
73
|
+
|
74
|
+
@values[name] = val
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [Error] the errors for this struct
|
79
|
+
attr_reader :errors
|
80
|
+
|
81
|
+
# @param data [Hash] the data to initialize the struct with
|
82
|
+
# @param coerce [Boolean] whether to coerce the data if it is not correctly typed
|
83
|
+
# @param strict [Boolean] whether to raise if a required field is missing
|
84
|
+
# @return [void]
|
85
|
+
def initialize(data = {}, coerce: false, strict: true)
|
86
|
+
@coerce = coerce
|
87
|
+
@strict = strict
|
88
|
+
@values = {}
|
89
|
+
@errors = Error.new
|
90
|
+
|
91
|
+
if data.nil?
|
92
|
+
return
|
93
|
+
end
|
94
|
+
|
95
|
+
for k, v in data
|
96
|
+
send("#{k}=", v)
|
97
|
+
end
|
98
|
+
|
99
|
+
for req in self.class.required_types
|
100
|
+
if !data.key?(req)
|
101
|
+
raise "Missing required field #{req}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# convert struct to a hash
|
107
|
+
# @return [Hash] the struct as a hash
|
108
|
+
def to_hash
|
109
|
+
@values
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -1,15 +1,18 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
1
|
require "minitest/assertions"
|
4
2
|
|
5
3
|
module DataModel
|
4
|
+
# Provides assertions for minitest
|
6
5
|
module Testing::Minitest
|
7
|
-
extend T::Sig
|
8
6
|
include Minitest::Assertions
|
9
|
-
include Kernel
|
10
7
|
|
11
|
-
|
8
|
+
# Assert that a child model error was found.
|
9
|
+
# @param err [Error] the error to check
|
10
|
+
# @param type [Symbol] the type of error to check for
|
11
|
+
# @param key [Array(Symbol)] limit checking to a specific child key
|
12
|
+
# @return [void]
|
12
13
|
def assert_child_model_error(err, type, key = nil)
|
14
|
+
refute_nil(err)
|
15
|
+
|
13
16
|
assert(err.children.any?, "validate was successful, but should not have been")
|
14
17
|
|
15
18
|
for k in key ? [key] : err.children.keys
|
@@ -18,8 +21,13 @@ module DataModel
|
|
18
21
|
end
|
19
22
|
end
|
20
23
|
|
21
|
-
|
24
|
+
# Assert that a model error was found.
|
25
|
+
# @param err [Error] the error to check
|
26
|
+
# @param type [Symbol] the type of error to check for
|
27
|
+
# @return [void]
|
22
28
|
def assert_model_error(err, type)
|
29
|
+
refute_nil err
|
30
|
+
|
23
31
|
assert(err.base.any?, "validate was successful, but should not have been")
|
24
32
|
|
25
33
|
found = err.base.any? { |(t, _ctx)| t == type }
|
@@ -27,8 +35,14 @@ module DataModel
|
|
27
35
|
assert(found, "validation was not successful, but #{type} error was not found #{err.inspect}")
|
28
36
|
end
|
29
37
|
|
30
|
-
|
38
|
+
# Assert that no child error is found
|
39
|
+
# @param err [Error] the error to check
|
40
|
+
# @param type [Symbol] the type of error to check for
|
41
|
+
# @param key [Symbol, Array<Symbol>] limit checking to a specific child key
|
42
|
+
# @return [void]
|
31
43
|
def refute_child_model_error(err, type = nil, key = nil)
|
44
|
+
refute_nil(err)
|
45
|
+
|
32
46
|
if !err.any?
|
33
47
|
return
|
34
48
|
end
|
@@ -44,8 +58,13 @@ module DataModel
|
|
44
58
|
end
|
45
59
|
end
|
46
60
|
|
47
|
-
|
61
|
+
# Assert that no base error is present
|
62
|
+
# @param err [Error] the error to check
|
63
|
+
# @param type [Symbol] the type of error to check for
|
64
|
+
# @return [void]
|
48
65
|
def refute_model_error(err, type = nil)
|
66
|
+
refute_nil(err)
|
67
|
+
|
49
68
|
if !err.any?
|
50
69
|
return
|
51
70
|
end
|
@@ -60,8 +79,13 @@ module DataModel
|
|
60
79
|
refute(found, "#{type} error was found #{err.inspect}")
|
61
80
|
end
|
62
81
|
|
63
|
-
|
82
|
+
# Assert that no errors are present
|
83
|
+
# @param err [Error] the error to check
|
84
|
+
# @param type [Symbol] the type of error to check for
|
85
|
+
# @return [void]
|
64
86
|
def refute_all_errors(err, type = nil)
|
87
|
+
refute_nil(err)
|
88
|
+
|
65
89
|
if !err.any?
|
66
90
|
return
|
67
91
|
end
|
data/lib/data_model/testing.rb
CHANGED
data/lib/data_model/type.rb
CHANGED
@@ -1,32 +1,36 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
|
-
# Mixin included on every type. Type::Generic and Type::Parent are higher level specializations.
|
4
1
|
module DataModel
|
2
|
+
# @abstract Base class for all types.
|
3
|
+
# Types have arguments, which configure the act of reading / validating / coercing data.
|
4
|
+
# They also have parameters, which configure the type itself, such as generic or child specification
|
5
|
+
# Arguments are passed to the type when it is invoked, and parameters are passed to the type when it is configured.
|
6
|
+
# Parameters are really only used in complex types, such as Array or Hash. If you don't need them, you can ignore them.
|
5
7
|
class Type
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
TArguments = T.type_alias { T::Hash[Symbol, T.untyped] }
|
12
|
-
TTypeParams = T.type_alias { T::Array[Object] }
|
13
|
-
TTypeResult = T.type_alias { [Object, Error] }
|
14
|
-
|
15
|
-
sig { params(args: TArguments, registry: TypeRegistry).void }
|
16
|
-
def initialize(args, registry: TypeRegistry.instance)
|
8
|
+
# @param args [Hash] type arguments, configures the reading process
|
9
|
+
# @param registry [Registry] the registry to use
|
10
|
+
# @return [void]
|
11
|
+
def initialize(args, registry: Registry.instance)
|
17
12
|
@type_args = args
|
18
13
|
@type_registry = registry
|
19
14
|
end
|
20
15
|
|
21
|
-
|
16
|
+
# @return [Hash] the type arguments
|
22
17
|
attr_reader :type_args
|
23
18
|
|
24
|
-
#
|
25
|
-
|
19
|
+
# @return [Registry] the type Registry
|
20
|
+
attr_reader :type_registry
|
21
|
+
|
22
|
+
# configure must be overridden to use params. If you don't need params, you can ignore this.
|
23
|
+
# @param params [Array] type parameters, configures the type itself
|
24
|
+
# @return [void]
|
26
25
|
def configure(params); end
|
27
26
|
|
28
|
-
# invoke another type by name
|
29
|
-
|
27
|
+
# invoke another type by name. This is useful for specifying types like UUIDs which are specialized strings
|
28
|
+
# @param name [Symbol] the name of the type to invoke
|
29
|
+
# @param val [Object] the value to read
|
30
|
+
# @param coerce [Boolean] whether to coerce the value
|
31
|
+
# @param args [Hash] type arguments, configures the reading process
|
32
|
+
# @param params [Array] type parameters, configures the type itself
|
33
|
+
# @return [Array(Object, Error)] the result of reading the value
|
30
34
|
def invoke(name, val, coerce: false, args: {}, params: nil)
|
31
35
|
t = instantiate(name, args:, params:)
|
32
36
|
|
@@ -35,16 +39,28 @@ module DataModel
|
|
35
39
|
return result
|
36
40
|
end
|
37
41
|
|
38
|
-
# instanciate another type
|
39
|
-
|
42
|
+
# instanciate another type by name
|
43
|
+
# @param name [Symbol] the name of the type to instantiate
|
44
|
+
# @param args [Hash] type arguments, configures the reading Process
|
45
|
+
# @param params [Array] type parameters, configures the type itself
|
46
|
+
# @return [Type] the instantiated type
|
40
47
|
def instantiate(name, args: {}, params: nil)
|
41
48
|
t = @type_registry.type(name, args:, params:)
|
42
49
|
|
43
50
|
return t
|
44
51
|
end
|
45
52
|
|
46
|
-
# default reader
|
47
|
-
|
53
|
+
# @abstract default reader, must be overridden for a type to be useful
|
54
|
+
# @param data [untyped] the data to read
|
55
|
+
# @param coerce [Boolean] whether to coerce the value
|
56
|
+
# @return [Array(Object, Error)] the result of reading the value
|
48
57
|
def read(data, coerce: false); end
|
58
|
+
|
59
|
+
# name of the type without module prefix as a string
|
60
|
+
# useful for generating error messages
|
61
|
+
# @return [String] the type name
|
62
|
+
def type_name
|
63
|
+
@type_name ||= self.class.name.split("::").last
|
64
|
+
end
|
49
65
|
end
|
50
66
|
end
|
data/lib/data_model/version.rb
CHANGED
data/lib/data_model.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
1
|
require "logger"
|
4
2
|
require "bigdecimal"
|
5
3
|
require "date"
|
@@ -7,28 +5,18 @@ require "time"
|
|
7
5
|
|
8
6
|
require "bundler/setup"
|
9
7
|
require "zeitwerk"
|
10
|
-
require "sorbet-runtime"
|
11
8
|
|
12
|
-
loader =
|
9
|
+
loader = Zeitwerk::Loader.for_gem
|
13
10
|
loader.setup
|
14
11
|
|
15
12
|
module DataModel
|
16
|
-
extend T::Sig
|
17
13
|
extend self
|
18
14
|
|
19
|
-
TSchema = T.type_alias { T::Array[Object] }
|
20
|
-
TData = T.type_alias { Object }
|
21
|
-
|
22
|
-
# an error is a tuple of [error_type, error_context], where context
|
23
|
-
# provides additional information about the error
|
24
|
-
TError = T.type_alias { [Symbol, Object] }
|
25
|
-
|
26
|
-
# a map of symbol => type, suitable for sending to a TypeRegistry
|
27
|
-
TTypeMap = T.type_alias { T::Hash[Symbol, T.class_of(Type)] }
|
28
|
-
|
29
15
|
# Scan a schema and create a data model, which is a configured type.
|
30
|
-
|
31
|
-
|
16
|
+
# @param schema [Array] the schema to define
|
17
|
+
# @param registry [Registry] the registry to use
|
18
|
+
# @return [Model] the model built from the schema
|
19
|
+
def define(schema, registry: Registry.instance)
|
32
20
|
scanned = Scanner.scan(schema, registry)
|
33
21
|
|
34
22
|
type = registry.type(
|
@@ -42,8 +30,11 @@ module DataModel
|
|
42
30
|
return model
|
43
31
|
end
|
44
32
|
|
45
|
-
|
33
|
+
# Register a global type, which is available to all models.
|
34
|
+
# @param name [Symbol] the name of the Type
|
35
|
+
# @param type [Type] the type to register
|
36
|
+
# @return [void]
|
46
37
|
def register_global_type(name, type)
|
47
|
-
|
38
|
+
Registry.register(name, type)
|
48
39
|
end
|
49
40
|
end
|