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,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) }
|