data_model 0.4.0 → 0.6.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 +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,12 +1,11 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
1
|
module DataModel
|
2
|
+
# Test data for float schemas
|
4
3
|
module Fixtures::Float
|
5
4
|
include Fixtures
|
6
|
-
extend T::Sig
|
7
5
|
extend self
|
8
6
|
|
9
|
-
|
7
|
+
# a simple float example
|
8
|
+
# @return [Example] the example
|
10
9
|
def simple
|
11
10
|
Example.new(
|
12
11
|
[:float],
|
@@ -18,7 +17,8 @@ module DataModel
|
|
18
17
|
)
|
19
18
|
end
|
20
19
|
|
21
|
-
|
20
|
+
# a float example that is optional
|
21
|
+
# @return [Example] the example
|
22
22
|
def optional
|
23
23
|
Example.new(
|
24
24
|
[:float, { optional: true }],
|
@@ -28,7 +28,8 @@ module DataModel
|
|
28
28
|
)
|
29
29
|
end
|
30
30
|
|
31
|
-
|
31
|
+
# a float example where the minimum value is 5
|
32
|
+
# @return [Example] the example
|
32
33
|
def min
|
33
34
|
Example.new(
|
34
35
|
[:float, { min: 5 }],
|
@@ -39,7 +40,8 @@ module DataModel
|
|
39
40
|
)
|
40
41
|
end
|
41
42
|
|
42
|
-
|
43
|
+
# a float example where the maximum value is 5
|
44
|
+
# @return [Example] the example
|
43
45
|
def max
|
44
46
|
Example.new(
|
45
47
|
[:float, { max: 5.0 }],
|
@@ -1,14 +1,11 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
1
|
module DataModel
|
2
|
+
# Test data for hash schemas
|
4
3
|
module Fixtures::Hash
|
5
4
|
include Fixtures
|
6
|
-
extend T::Sig
|
7
5
|
extend self
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
sig { returns(TContact) }
|
7
|
+
# hash data conforming to the contact schema
|
8
|
+
# @return [Hash{Symbol => String}] the hash
|
12
9
|
def example_contact
|
13
10
|
{
|
14
11
|
first_name: "foo",
|
@@ -17,7 +14,20 @@ module DataModel
|
|
17
14
|
}
|
18
15
|
end
|
19
16
|
|
20
|
-
|
17
|
+
# alternate hash syntax for when you want to type keys and values
|
18
|
+
# @return [Example] the example
|
19
|
+
def dictionary
|
20
|
+
Example.new(
|
21
|
+
[:hash, [symbol: :string]],
|
22
|
+
variants: {
|
23
|
+
valid: { foo: "bar" },
|
24
|
+
invalid: { foo: 123 }
|
25
|
+
},
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
# hash contact example
|
30
|
+
# @return [Example] the example
|
21
31
|
def contact
|
22
32
|
Example.new(
|
23
33
|
[:hash,
|
@@ -28,14 +38,15 @@ module DataModel
|
|
28
38
|
valid: example_contact,
|
29
39
|
missing: nil,
|
30
40
|
coercible: example_contact.to_a,
|
31
|
-
missing_email: example_contact.tap { |h|
|
41
|
+
missing_email: example_contact.tap { |h| h.delete(:email) },
|
32
42
|
invalid_field: example_contact.merge(email: 123),
|
33
43
|
other_type: []
|
34
44
|
},
|
35
45
|
)
|
36
46
|
end
|
37
47
|
|
38
|
-
|
48
|
+
# hash contact example that is optional
|
49
|
+
# @return [Example] the example
|
39
50
|
def optional_contact
|
40
51
|
Example.new(
|
41
52
|
[:hash, { optional: true },
|
@@ -49,7 +60,8 @@ module DataModel
|
|
49
60
|
)
|
50
61
|
end
|
51
62
|
|
52
|
-
|
63
|
+
# hash contact example that is closed to extra keys
|
64
|
+
# @return [Example] the example
|
53
65
|
def closed_contact
|
54
66
|
Example.new(
|
55
67
|
[:hash, { open: false },
|
@@ -1,12 +1,11 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
1
|
module DataModel
|
2
|
+
# Test data for integer schemas
|
4
3
|
module Fixtures::Integer
|
5
4
|
include Fixtures
|
6
|
-
extend T::Sig
|
7
5
|
extend self
|
8
6
|
|
9
|
-
|
7
|
+
# simple integer example
|
8
|
+
# @return [Hash{Symbol => untyped}] the variants used by each example
|
10
9
|
def simple
|
11
10
|
Example.new(
|
12
11
|
[:integer],
|
@@ -18,7 +17,8 @@ module DataModel
|
|
18
17
|
)
|
19
18
|
end
|
20
19
|
|
21
|
-
|
20
|
+
# integer example that is optional
|
21
|
+
# @return [Example] the example
|
22
22
|
def optional
|
23
23
|
Example.new(
|
24
24
|
[:integer, { optional: true }],
|
@@ -28,7 +28,8 @@ module DataModel
|
|
28
28
|
)
|
29
29
|
end
|
30
30
|
|
31
|
-
|
31
|
+
# integer example that has a minimum value
|
32
|
+
# @return [Example] the example
|
32
33
|
def min
|
33
34
|
Example.new(
|
34
35
|
[:integer, { min: 5 }],
|
@@ -39,7 +40,8 @@ module DataModel
|
|
39
40
|
)
|
40
41
|
end
|
41
42
|
|
42
|
-
|
43
|
+
# integer example that has a maximum value
|
44
|
+
# @return [Example] the example
|
43
45
|
def max
|
44
46
|
Example.new(
|
45
47
|
[:integer, { max: 5 }],
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "bigdecimal/util"
|
2
|
+
|
3
|
+
module DataModel
|
4
|
+
# test fixtures for object type
|
5
|
+
module Fixtures::Numeric
|
6
|
+
extend self
|
7
|
+
include Fixtures
|
8
|
+
|
9
|
+
def variants
|
10
|
+
{
|
11
|
+
missing: nil,
|
12
|
+
integer: 1,
|
13
|
+
float: 1.0,
|
14
|
+
decimal: 1.0.to_d,
|
15
|
+
string: ["1", 1]
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
# a simple numeric example
|
20
|
+
# @return [Example] the example
|
21
|
+
def simple
|
22
|
+
Example.new([:numeric], variants:)
|
23
|
+
end
|
24
|
+
|
25
|
+
# a numeric example that is optional
|
26
|
+
# @return [Example] the example
|
27
|
+
def optional
|
28
|
+
Example.new([:numeric, { optional: true }], variants:)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module DataModel
|
2
|
+
# test fixtures for object type
|
3
|
+
module Fixtures::Object
|
4
|
+
extend self
|
5
|
+
include Fixtures
|
6
|
+
|
7
|
+
def variants
|
8
|
+
{
|
9
|
+
missing: nil,
|
10
|
+
integer: 1,
|
11
|
+
string: "string"
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
# a simple object example
|
16
|
+
# @return [Example] the example
|
17
|
+
def simple
|
18
|
+
Example.new([:object], variants:)
|
19
|
+
end
|
20
|
+
|
21
|
+
# a object example that is optional
|
22
|
+
# @return [Example] the example
|
23
|
+
def optional
|
24
|
+
Example.new([:object, { optional: true }], variants:)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module DataModel
|
2
|
+
# test fixtures for object type
|
3
|
+
module Fixtures::Or
|
4
|
+
extend self
|
5
|
+
include Fixtures
|
6
|
+
|
7
|
+
def variants
|
8
|
+
{
|
9
|
+
missing: nil,
|
10
|
+
integer: 1,
|
11
|
+
int_array: [1],
|
12
|
+
string: ["1", 1],
|
13
|
+
float: 1.0
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
# a simple numeric example, integer or integer array
|
18
|
+
# @return [Example] the example
|
19
|
+
def simple
|
20
|
+
Example.new([:or, :integer, [:array, :integer]], variants:)
|
21
|
+
end
|
22
|
+
|
23
|
+
# a numeric example that is optional
|
24
|
+
# @return [Example] the example
|
25
|
+
def optional
|
26
|
+
Example.new([:or, { optional: true }, :integer, [:array, :integer]], variants:)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,12 +1,11 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
1
|
module DataModel
|
2
|
+
# Test data for string schemas
|
4
3
|
module Fixtures::String
|
5
4
|
extend self
|
6
|
-
extend T::Sig
|
7
5
|
include Fixtures
|
8
6
|
|
9
|
-
|
7
|
+
# a simple string example
|
8
|
+
# @return [Example] the example
|
10
9
|
def simple
|
11
10
|
Example.new(
|
12
11
|
[:string],
|
@@ -18,7 +17,8 @@ module DataModel
|
|
18
17
|
)
|
19
18
|
end
|
20
19
|
|
21
|
-
|
20
|
+
# an email string example
|
21
|
+
# @return [Example] the example
|
22
22
|
def email
|
23
23
|
Example.new(
|
24
24
|
[:string, { format: "@" }],
|
@@ -29,29 +29,8 @@ module DataModel
|
|
29
29
|
)
|
30
30
|
end
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
Example.new(
|
35
|
-
[:string, { format: /@/ }],
|
36
|
-
variants: {
|
37
|
-
valid: "foo@bar.com",
|
38
|
-
invalid: "invalid"
|
39
|
-
},
|
40
|
-
)
|
41
|
-
end
|
42
|
-
|
43
|
-
sig { returns(Example) }
|
44
|
-
def email_proc
|
45
|
-
Example.new(
|
46
|
-
[:string, { format: ->(val) { val.match?(/@/) } }],
|
47
|
-
variants: {
|
48
|
-
valid: "foo@bar.com",
|
49
|
-
invalid: "invalid"
|
50
|
-
},
|
51
|
-
)
|
52
|
-
end
|
53
|
-
|
54
|
-
sig { returns(Example) }
|
32
|
+
# a string example that is optional
|
33
|
+
# @return [Example] the example
|
55
34
|
def optional
|
56
35
|
Example.new(
|
57
36
|
[:string, { optional: true }],
|
@@ -63,7 +42,8 @@ module DataModel
|
|
63
42
|
)
|
64
43
|
end
|
65
44
|
|
66
|
-
|
45
|
+
# a string example where "valid" is the only allowed String
|
46
|
+
# @return [Example] the example
|
67
47
|
def inclusion
|
68
48
|
Example.new(
|
69
49
|
[:string, { included: ["valid"] }],
|
@@ -74,7 +54,8 @@ module DataModel
|
|
74
54
|
)
|
75
55
|
end
|
76
56
|
|
77
|
-
|
57
|
+
# a string example where "invalid" is the only disallowed String
|
58
|
+
# @return [Example] the example
|
78
59
|
def exclusion
|
79
60
|
Example.new(
|
80
61
|
[:string, { excluded: ["invalid"] }],
|
@@ -85,7 +66,8 @@ module DataModel
|
|
85
66
|
)
|
86
67
|
end
|
87
68
|
|
88
|
-
|
69
|
+
# a string example where blank strings are allowed
|
70
|
+
# @return [Example] the example
|
89
71
|
def allow_blank
|
90
72
|
Example.new(
|
91
73
|
[:string, { allow_blank: true }],
|
@@ -97,7 +79,8 @@ module DataModel
|
|
97
79
|
)
|
98
80
|
end
|
99
81
|
|
100
|
-
|
82
|
+
# a string example where blank strings are not allowed
|
83
|
+
# @return [Example] the example
|
101
84
|
def dont_allow_blank
|
102
85
|
Example.new(
|
103
86
|
[:string, { allow_blank: false }],
|
@@ -1,12 +1,11 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
1
|
module DataModel
|
2
|
+
# Test data around symbol schemas
|
4
3
|
module Fixtures::Symbol
|
5
4
|
extend self
|
6
5
|
include Fixtures
|
7
|
-
extend T::Sig
|
8
6
|
|
9
|
-
|
7
|
+
# a simple symbol example
|
8
|
+
# @return [Example] the example
|
10
9
|
def simple
|
11
10
|
Example.new(
|
12
11
|
[:symbol],
|
@@ -19,7 +18,8 @@ module DataModel
|
|
19
18
|
)
|
20
19
|
end
|
21
20
|
|
22
|
-
|
21
|
+
# a symbol example that is optional
|
22
|
+
# @return [Example] the example
|
23
23
|
def optional
|
24
24
|
Example.new(
|
25
25
|
[:symbol, { optional: true }],
|
@@ -31,7 +31,8 @@ module DataModel
|
|
31
31
|
)
|
32
32
|
end
|
33
33
|
|
34
|
-
|
34
|
+
# a symbol example where :valid is the only allowed Symbol
|
35
|
+
# @return [Example] the example
|
35
36
|
def inclusion
|
36
37
|
Example.new(
|
37
38
|
[:symbol, { included: [:valid] }],
|
@@ -42,7 +43,8 @@ module DataModel
|
|
42
43
|
)
|
43
44
|
end
|
44
45
|
|
45
|
-
|
46
|
+
# a symbol example where :invalid is the only disallowed Symbol
|
47
|
+
# @return [Example] the example
|
46
48
|
def exclusion
|
47
49
|
Example.new(
|
48
50
|
[:symbol, { excluded: [:invalid] }],
|
@@ -1,22 +1,20 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
1
|
module DataModel
|
2
|
+
# Test data around time schemas
|
4
3
|
module Fixtures::Time
|
5
|
-
extend T::Sig
|
6
|
-
extend self
|
7
4
|
include Fixtures
|
5
|
+
extend self
|
8
6
|
|
9
|
-
|
7
|
+
# @return [Time] a time that is used by the #earliest example
|
10
8
|
def earliest_time
|
11
9
|
return ::Time.now - 1
|
12
10
|
end
|
13
11
|
|
14
|
-
|
12
|
+
# @return [Time] a time that is used by the #latest example
|
15
13
|
def latest_time
|
16
14
|
return ::Time.now + 1
|
17
15
|
end
|
18
16
|
|
19
|
-
|
17
|
+
# @return [Hash{Symbol => untyped}] the variants used by each example
|
20
18
|
def variants
|
21
19
|
now = ::Time.now
|
22
20
|
|
@@ -30,22 +28,26 @@ module DataModel
|
|
30
28
|
}
|
31
29
|
end
|
32
30
|
|
33
|
-
|
31
|
+
# A simple time schema
|
32
|
+
# @return [Example] the example
|
34
33
|
def simple
|
35
34
|
Example.new([:time], variants:)
|
36
35
|
end
|
37
36
|
|
38
|
-
|
37
|
+
# A time schema that is optional
|
38
|
+
# @return [Example] the example
|
39
39
|
def optional
|
40
40
|
Example.new([:time, { optional: true }], variants:)
|
41
41
|
end
|
42
42
|
|
43
|
-
|
43
|
+
# A time schema that has a restriction on the earliest time
|
44
|
+
# @return [Example] the example
|
44
45
|
def earliest
|
45
46
|
Example.new([:time, { earliest: earliest_time }], variants:)
|
46
47
|
end
|
47
48
|
|
48
|
-
|
49
|
+
# A time schema that has a restriction on the latest time
|
50
|
+
# @return [Example] the example
|
49
51
|
def latest
|
50
52
|
Example.new([:time, { latest: latest_time }], variants:)
|
51
53
|
end
|
data/lib/data_model/logging.rb
CHANGED
@@ -1,15 +1,12 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
1
|
require "logger"
|
4
2
|
|
5
3
|
module DataModel
|
4
|
+
# Provides a logger for classes that include it
|
6
5
|
module Logging
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
sig { returns(Logger) }
|
6
|
+
# Get a logger
|
7
|
+
# @return [Logger] the logger for this class
|
11
8
|
def log
|
12
|
-
target =
|
9
|
+
target = respond_to?(:name) ? self : self.class
|
13
10
|
|
14
11
|
logger = Logger.new(
|
15
12
|
STDERR,
|
@@ -17,7 +14,7 @@ module DataModel
|
|
17
14
|
progname: target.name,
|
18
15
|
)
|
19
16
|
|
20
|
-
return @log ||=
|
17
|
+
return @log ||= logger
|
21
18
|
end
|
22
19
|
end
|
23
20
|
end
|
data/lib/data_model/model.rb
CHANGED
@@ -1,28 +1,31 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
1
|
module DataModel
|
2
|
+
# A model is a schema and a type. It is the primary interface for interacting
|
3
|
+
# with the data_model gem.
|
4
4
|
class Model
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
# Create a new model.
|
6
|
+
# @param schema [Array] the schema to define
|
7
|
+
# @param type [Type] the type to use
|
8
|
+
# @return [void]
|
8
9
|
def initialize(schema, type)
|
9
10
|
@schema = schema
|
10
11
|
@type = type
|
11
12
|
end
|
12
13
|
|
13
|
-
|
14
|
+
# @return [Array] the schema configured
|
14
15
|
attr_reader :schema
|
15
16
|
|
16
17
|
# Validate data against the model. This will return true if the data is valid,
|
17
18
|
# or false if it is not. If it is not valid, it will raise an exception.
|
18
|
-
|
19
|
+
# @param data [Hash] the data to validate
|
20
|
+
# @return [Boolean] true if the data is valid, false if it is not
|
19
21
|
def validate(data)
|
20
22
|
_, err = @type.read(data)
|
21
23
|
return err
|
22
24
|
end
|
23
25
|
|
24
26
|
# Read data with the model. This will return a tuple of [data, error]
|
25
|
-
|
27
|
+
# @param data [Hash] the data to read
|
28
|
+
# @return [Array] a tuple of [data, error]
|
26
29
|
def coerce(data)
|
27
30
|
result = @type.read(data, coerce: true)
|
28
31
|
return result
|
data/lib/data_model/registry.rb
CHANGED
@@ -1,61 +1,71 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
1
|
module DataModel
|
4
2
|
# Registry allows for different type implementations to be used by the scanner.
|
5
3
|
# It also acts as an error message registry, mostly for pragmatic reasons.
|
6
4
|
class Registry
|
7
|
-
extend T::Sig
|
8
|
-
|
9
5
|
# Default types that will be used if alternative type map is not given
|
10
|
-
|
6
|
+
# @return [Hash] the default type map
|
11
7
|
def self.default_types
|
12
8
|
Builtin.types
|
13
9
|
end
|
14
10
|
|
15
|
-
|
11
|
+
# Default error messages that will be used if alternative error messages are not given
|
12
|
+
# @return [Hash] the default error messages
|
16
13
|
def self.default_error_messages
|
17
14
|
Errors.error_messages
|
18
15
|
end
|
19
16
|
|
20
17
|
# Singleton instance that will be used globally unless instances given
|
21
|
-
|
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
|
22
21
|
def self.instance(types: default_types, errors: default_error_messages)
|
23
|
-
@instance ||=
|
22
|
+
@instance ||= new(types:, errors:)
|
24
23
|
end
|
25
24
|
|
26
25
|
# Register a type on the global instance
|
27
|
-
|
26
|
+
# @param name [Symbol] the name of the type
|
27
|
+
# @param type [Type] the type to register
|
28
|
+
# @return [void]
|
28
29
|
def self.register(name, type)
|
29
30
|
instance.register(name, type)
|
30
31
|
end
|
31
32
|
|
32
33
|
# Instanciate a new type registry. Default errors will always be used, but additional
|
33
34
|
# errors can be registered.
|
34
|
-
|
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
|
35
38
|
def initialize(types: self.class.default_types, errors: self.class.default_error_messages)
|
36
|
-
@error_messages =
|
39
|
+
@error_messages = nil
|
40
|
+
|
37
41
|
if errors
|
38
42
|
errors.each { |type, builder| register_error_message(type, &builder) }
|
39
43
|
end
|
40
44
|
|
41
|
-
@types =
|
45
|
+
@types = {}
|
42
46
|
types.each { |(name, type)| register(name, type) }
|
43
47
|
end
|
44
48
|
|
45
49
|
# Register a type on this instance
|
46
|
-
|
50
|
+
# @param name [Symbol] the name of the Type
|
51
|
+
# @param type [Type] the type to register
|
52
|
+
# @return [void]
|
47
53
|
def register(name, type)
|
48
54
|
@types[name] = type
|
49
55
|
end
|
50
56
|
|
51
57
|
# Check if a type is registered
|
52
|
-
|
58
|
+
# @param name [Symbol] the name of the type
|
59
|
+
# @return [Boolean] whether the type is registered
|
53
60
|
def type?(name)
|
54
61
|
@types.key?(name)
|
55
62
|
end
|
56
63
|
|
57
64
|
# Access and configure registered type
|
58
|
-
|
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
|
59
69
|
def type(name, args: {}, params: nil)
|
60
70
|
if !type?(name)
|
61
71
|
raise "#{name} is not registered as a type"
|
@@ -73,26 +83,30 @@ module DataModel
|
|
73
83
|
## API
|
74
84
|
|
75
85
|
# Register a custom error message for use with custom errors
|
76
|
-
|
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]
|
77
89
|
def register_error_message(type, &block)
|
78
90
|
error_message_builders[type] = block
|
79
91
|
end
|
80
92
|
|
81
93
|
# Get the error message builders
|
82
|
-
|
94
|
+
# @return [Hash] the error message builders
|
83
95
|
def error_message_builders
|
84
96
|
if @error_messages.nil?
|
85
|
-
@error_messages ||=
|
97
|
+
@error_messages ||= {}
|
86
98
|
end
|
87
99
|
|
88
100
|
@error_messages
|
89
101
|
end
|
90
102
|
|
91
103
|
# Build the error message for a given error
|
92
|
-
|
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
|
93
107
|
def error_message(error)
|
94
|
-
type =
|
95
|
-
ctx =
|
108
|
+
type = error[0]
|
109
|
+
ctx = error[1]
|
96
110
|
|
97
111
|
builder = error_message_builders[type]
|
98
112
|
|
@@ -104,7 +118,8 @@ module DataModel
|
|
104
118
|
end
|
105
119
|
|
106
120
|
# Build error messages from error object
|
107
|
-
|
121
|
+
# @param error [Error] the error to build the messages for
|
122
|
+
# @return [Hash] the error messages
|
108
123
|
def error_messages(error)
|
109
124
|
error.to_h.transform_values do |error_list|
|
110
125
|
error_list.map { |e| error_message(e) }
|