literal 1.0.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/literal/array.rb +428 -0
- data/lib/literal/data_structure.rb +0 -10
- data/lib/literal/flags.rb +245 -0
- data/lib/literal/hash.rb +48 -0
- data/lib/literal/properties/schema.rb +5 -3
- data/lib/literal/properties.rb +5 -0
- data/lib/literal/rails/flags_type.rb +41 -0
- data/lib/literal/rails.rb +1 -0
- data/lib/literal/railtie.rb +8 -0
- data/lib/literal/set.rb +48 -0
- data/lib/literal/struct.rb +26 -0
- data/lib/literal/type.rb +7 -0
- data/lib/literal/types/any_type.rb +13 -3
- data/lib/literal/types/array_type.rb +18 -1
- data/lib/literal/types/boolean_type.rb +18 -3
- data/lib/literal/types/callable_type.rb +22 -3
- data/lib/literal/types/class_type.rb +20 -1
- data/lib/literal/types/constraint_type.rb +55 -1
- data/lib/literal/types/descendant_type.rb +18 -1
- data/lib/literal/types/enumerable_type.rb +18 -1
- data/lib/literal/types/falsy_type.rb +22 -3
- data/lib/literal/types/hash_type.rb +17 -0
- data/lib/literal/types/interface_type.rb +2 -0
- data/lib/literal/types/intersection_type.rb +10 -0
- data/lib/literal/types/json_data_type.rb +16 -0
- data/lib/literal/types/map_type.rb +9 -3
- data/lib/literal/types/nilable_type.rb +6 -0
- data/lib/literal/types/set_type.rb +10 -0
- data/lib/literal/types/tuple_type.rb +16 -0
- data/lib/literal/types/union_type.rb +7 -0
- data/lib/literal/types.rb +13 -13
- data/lib/literal/version.rb +1 -1
- data/lib/literal.rb +97 -0
- metadata +8 -2
data/lib/literal/hash.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Literal::Hash
|
4
|
+
class Generic
|
5
|
+
def initialize(key_type, value_type)
|
6
|
+
@key_type = key_type
|
7
|
+
@value_type = value_type
|
8
|
+
end
|
9
|
+
|
10
|
+
def new(**value)
|
11
|
+
Literal::Hash.new(value, key_type: @key_type, value_type: @value_type)
|
12
|
+
end
|
13
|
+
|
14
|
+
def ===(value)
|
15
|
+
Literal::Hash === value && @type == value.__type__
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
"Literal::Hash(#{@type.inspect})"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
include Enumerable
|
24
|
+
|
25
|
+
def initialize(value, key_type:, value_type:)
|
26
|
+
collection_type = Literal::Types::HashType.new(key_type, value_type)
|
27
|
+
|
28
|
+
Literal.check(actual: value, expected: collection_type) do |c|
|
29
|
+
c.fill_receiver(receiver: self, method: "#initialize")
|
30
|
+
end
|
31
|
+
|
32
|
+
@__key_type__ = key_type
|
33
|
+
@__value_type__ = value_type
|
34
|
+
@__value__ = value
|
35
|
+
@__collection_type__ = collection_type
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :__key_type__, :__value_type__, :__value__
|
39
|
+
|
40
|
+
def freeze
|
41
|
+
@__value__.freeze
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def each(...)
|
46
|
+
@__value__.each(...)
|
47
|
+
end
|
48
|
+
end
|
@@ -47,8 +47,10 @@ class Literal::Properties::Schema
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def generate_initializer(buffer = +"")
|
50
|
-
buffer << "alias initialize initialize\n"
|
51
|
-
|
50
|
+
buffer << "alias initialize initialize\n" \
|
51
|
+
"def initialize("
|
52
|
+
generate_initializer_params(buffer)
|
53
|
+
buffer << ")\n"
|
52
54
|
generate_initializer_body(buffer)
|
53
55
|
buffer << "" \
|
54
56
|
"rescue Literal::TypeError => error\n" \
|
@@ -100,7 +102,7 @@ class Literal::Properties::Schema
|
|
100
102
|
i, n = 0, sorted_properties.size
|
101
103
|
while i < n
|
102
104
|
property = sorted_properties[i]
|
103
|
-
buffer << " @" << property.name.name << " == other
|
105
|
+
buffer << " @" << property.name.name << " == other." << property.name.name
|
104
106
|
buffer << " &&\n " if i < n - 1
|
105
107
|
i += 1
|
106
108
|
end
|
data/lib/literal/properties.rb
CHANGED
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Literal::Rails::FlagsType < ActiveModel::Type::Value
|
4
|
+
def initialize(flags_class)
|
5
|
+
@flags_class = flags_class
|
6
|
+
super()
|
7
|
+
end
|
8
|
+
|
9
|
+
def cast(value)
|
10
|
+
case value
|
11
|
+
when @flags_class
|
12
|
+
value
|
13
|
+
else
|
14
|
+
deserialize(value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def serialize(value)
|
19
|
+
case value
|
20
|
+
when nil
|
21
|
+
nil
|
22
|
+
when @flags_class
|
23
|
+
value.to_bit_string
|
24
|
+
else
|
25
|
+
raise Literal::ArgumentError.new(
|
26
|
+
"Invalid value: #{value.inspect}. Expected an #{@flags_class.inspect}.",
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def deserialize(value)
|
32
|
+
case value
|
33
|
+
when nil
|
34
|
+
nil
|
35
|
+
else
|
36
|
+
@flags_class.from_bit_string(value) || raise(
|
37
|
+
ArgumentError.new("Invalid value: #{value.inspect} for #{@flags_class}"),
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/literal/rails.rb
CHANGED
data/lib/literal/railtie.rb
CHANGED
@@ -6,8 +6,16 @@ class Literal::Railtie < Rails::Railtie
|
|
6
6
|
Literal::Rails::EnumType.new(type)
|
7
7
|
end
|
8
8
|
|
9
|
+
ActiveRecord::Type.register(:literal_flags) do |name, type:|
|
10
|
+
Literal::Rails::FlagsType.new(type)
|
11
|
+
end
|
12
|
+
|
9
13
|
ActiveModel::Type.register(:literal_enum) do |name, type:|
|
10
14
|
Literal::Rails::EnumType.new(type)
|
11
15
|
end
|
16
|
+
|
17
|
+
ActiveModel::Type.register(:literal_flags) do |name, type:|
|
18
|
+
Literal::Rails::FlagsType.new(type)
|
19
|
+
end
|
12
20
|
end
|
13
21
|
end
|
data/lib/literal/set.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Literal::Set
|
4
|
+
class Generic
|
5
|
+
def initialize(type)
|
6
|
+
@type = type
|
7
|
+
end
|
8
|
+
|
9
|
+
def new(*value)
|
10
|
+
Literal::Set.new(value.to_set, type: @type)
|
11
|
+
end
|
12
|
+
|
13
|
+
alias_method :[], :new
|
14
|
+
|
15
|
+
def ===(value)
|
16
|
+
Literal::Set === value && @type == value.__type__
|
17
|
+
end
|
18
|
+
|
19
|
+
def inspect
|
20
|
+
"Literal::Set(#{@type.inspect})"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
include Enumerable
|
25
|
+
|
26
|
+
def initialize(value, type:)
|
27
|
+
collection_type = Literal::Types::SetType.new(type)
|
28
|
+
|
29
|
+
Literal.check(actual: value, expected: collection_type) do |c|
|
30
|
+
c.fill_receiver(receiver: self, method: "#initialize")
|
31
|
+
end
|
32
|
+
|
33
|
+
@__type__ = type
|
34
|
+
@__value__ = value
|
35
|
+
@__collection_type__ = collection_type
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :__type__, :__value__
|
39
|
+
|
40
|
+
def freeze
|
41
|
+
@__value__.freeze
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def each(...)
|
46
|
+
@__value__.each(...)
|
47
|
+
end
|
48
|
+
end
|
data/lib/literal/struct.rb
CHANGED
@@ -6,4 +6,30 @@ class Literal::Struct < Literal::DataStructure
|
|
6
6
|
super
|
7
7
|
end
|
8
8
|
end
|
9
|
+
|
10
|
+
def [](key)
|
11
|
+
case key
|
12
|
+
when Symbol
|
13
|
+
when String
|
14
|
+
key = key.intern
|
15
|
+
else
|
16
|
+
raise TypeError.new("expected a string or symbol, got #{key.inspect.class}")
|
17
|
+
end
|
18
|
+
|
19
|
+
prop = self.class.literal_properties[key] || raise(NameError.new("unknown attribute: #{key.inspect} for #{self.class}"))
|
20
|
+
__send__(prop.name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def []=(key, value)
|
24
|
+
case key
|
25
|
+
when Symbol
|
26
|
+
when String
|
27
|
+
key = key.intern
|
28
|
+
else
|
29
|
+
raise TypeError.new("expected a string or symbol, got #{key.inspect.class}")
|
30
|
+
end
|
31
|
+
|
32
|
+
prop = self.class.literal_properties[key] || raise(NameError.new("unknown attribute: #{key.inspect} for #{self.class}"))
|
33
|
+
__send__(:"#{prop.name}=", value)
|
34
|
+
end
|
9
35
|
end
|
data/lib/literal/type.rb
ADDED
@@ -1,12 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
|
5
|
-
|
4
|
+
class Literal::Types::AnyType
|
5
|
+
include Literal::Type
|
6
6
|
|
7
|
-
def
|
7
|
+
def inspect
|
8
|
+
"_Any"
|
9
|
+
end
|
10
|
+
|
11
|
+
def ===(value)
|
8
12
|
!(nil === value)
|
9
13
|
end
|
10
14
|
|
15
|
+
def >=(other)
|
16
|
+
!(other === nil)
|
17
|
+
end
|
18
|
+
|
19
|
+
Instance = new.freeze
|
20
|
+
|
11
21
|
freeze
|
12
22
|
end
|
@@ -2,16 +2,31 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::ArrayType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(type)
|
6
8
|
@type = type
|
7
9
|
end
|
8
10
|
|
9
|
-
|
11
|
+
attr_reader :type
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"_Array(#{@type.inspect})"
|
15
|
+
end
|
10
16
|
|
11
17
|
def ===(value)
|
12
18
|
Array === value && value.all?(@type)
|
13
19
|
end
|
14
20
|
|
21
|
+
def >=(other)
|
22
|
+
case other
|
23
|
+
when Literal::Types::ArrayType
|
24
|
+
@type >= other.type
|
25
|
+
else
|
26
|
+
false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
15
30
|
def record_literal_type_errors(context)
|
16
31
|
unless Array === context.actual
|
17
32
|
return
|
@@ -23,4 +38,6 @@ class Literal::Types::ArrayType
|
|
23
38
|
end
|
24
39
|
end
|
25
40
|
end
|
41
|
+
|
42
|
+
freeze
|
26
43
|
end
|
@@ -1,12 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
|
5
|
-
|
4
|
+
class Literal::Types::BooleanType
|
5
|
+
include Literal::Type
|
6
6
|
|
7
|
-
def
|
7
|
+
def inspect
|
8
|
+
"_Boolean"
|
9
|
+
end
|
10
|
+
|
11
|
+
def ===(value)
|
8
12
|
true == value || false == value
|
9
13
|
end
|
10
14
|
|
15
|
+
def >=(other)
|
16
|
+
case other
|
17
|
+
when true, false, Literal::Types::BooleanType
|
18
|
+
true
|
19
|
+
else
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Instance = new.freeze
|
25
|
+
|
11
26
|
freeze
|
12
27
|
end
|
@@ -1,12 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
|
5
|
-
|
4
|
+
class Literal::Types::CallableType
|
5
|
+
include Literal::Type
|
6
6
|
|
7
|
-
def
|
7
|
+
def inspect
|
8
|
+
"_Callable"
|
9
|
+
end
|
10
|
+
|
11
|
+
def ===(value)
|
8
12
|
value.respond_to?(:call)
|
9
13
|
end
|
10
14
|
|
15
|
+
def >=(other)
|
16
|
+
(self == other) || (Proc == other) || (Method == other) || case other
|
17
|
+
when Literal::Types::IntersectionType
|
18
|
+
other.types.any? { |type| Literal.subtype?(type, of: self) }
|
19
|
+
when Literal::Types::ConstraintType
|
20
|
+
other.object_constraints.any? { |type| Literal.subtype?(type, of: self) }
|
21
|
+
when Literal::Types::InterfaceType
|
22
|
+
other.methods.include?(:call)
|
23
|
+
else
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Instance = new.freeze
|
29
|
+
|
11
30
|
freeze
|
12
31
|
end
|
@@ -2,13 +2,32 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::ClassType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(type)
|
6
8
|
@type = type
|
7
9
|
end
|
8
10
|
|
9
|
-
|
11
|
+
attr_reader :type
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"_Class(#{@type.name})"
|
15
|
+
end
|
10
16
|
|
11
17
|
def ===(value)
|
12
18
|
Class === value && (value == @type || value < @type)
|
13
19
|
end
|
20
|
+
|
21
|
+
def >=(other)
|
22
|
+
case other
|
23
|
+
when Literal::Types::ClassType
|
24
|
+
Literal.subtype?(other.type, of: @type)
|
25
|
+
when Literal::Types::DescendantType
|
26
|
+
(Class === other.type) && Literal.subtype?(other.type, of: @type)
|
27
|
+
else
|
28
|
+
false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
freeze
|
14
33
|
end
|
@@ -2,12 +2,19 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::ConstraintType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(*object_constraints, **property_constraints)
|
6
8
|
@object_constraints = object_constraints
|
7
9
|
@property_constraints = property_constraints
|
8
10
|
end
|
9
11
|
|
10
|
-
|
12
|
+
attr_reader :object_constraints
|
13
|
+
attr_reader :property_constraints
|
14
|
+
|
15
|
+
def inspect
|
16
|
+
"_Constraint(#{inspect_constraints})"
|
17
|
+
end
|
11
18
|
|
12
19
|
def ===(value)
|
13
20
|
object_constraints = @object_constraints
|
@@ -20,11 +27,56 @@ class Literal::Types::ConstraintType
|
|
20
27
|
|
21
28
|
@property_constraints.each do |a, t|
|
22
29
|
return false unless t === value.public_send(a)
|
30
|
+
rescue NoMethodError => e
|
31
|
+
raise unless e.name == a && e.receiver == value
|
32
|
+
return false
|
23
33
|
end
|
24
34
|
|
25
35
|
true
|
26
36
|
end
|
27
37
|
|
38
|
+
def >=(other)
|
39
|
+
case other
|
40
|
+
when Literal::Types::ConstraintType
|
41
|
+
other_object_constraints = other.object_constraints
|
42
|
+
return false unless @object_constraints.all? do |constraint|
|
43
|
+
other_object_constraints.all? { |c| Literal.subtype?(c, of: constraint) }
|
44
|
+
end
|
45
|
+
|
46
|
+
other_property_constraints = other.property_constraints
|
47
|
+
return false unless @property_constraints.all? do |k, v|
|
48
|
+
Literal.subtype?(other_property_constraints[k], of: v)
|
49
|
+
end
|
50
|
+
|
51
|
+
true
|
52
|
+
when Literal::Types::IntersectionType
|
53
|
+
other_object_constraints = other.types
|
54
|
+
return false unless @object_constraints.all? do |constraint|
|
55
|
+
other_object_constraints.all? { |c| Literal.subtype?(c, of: constraint) }
|
56
|
+
end
|
57
|
+
|
58
|
+
true
|
59
|
+
else
|
60
|
+
false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def record_literal_type_errors(context)
|
65
|
+
@object_constraints.each do |constraint|
|
66
|
+
next if constraint === context.actual
|
67
|
+
|
68
|
+
context.add_child(label: inspect, expected: constraint, actual: context.actual)
|
69
|
+
end
|
70
|
+
|
71
|
+
@property_constraints.each do |property, constraint|
|
72
|
+
next unless context.actual.respond_to?(property)
|
73
|
+
actual = context.actual.public_send(property)
|
74
|
+
next if constraint === actual
|
75
|
+
|
76
|
+
context.add_child(label: ".#{property}", expected: constraint, actual:)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
28
80
|
private
|
29
81
|
|
30
82
|
def inspect_constraints
|
@@ -42,4 +94,6 @@ class Literal::Types::ConstraintType
|
|
42
94
|
@property_constraints.map { |k, t| "#{k}: #{t.inspect}" }.join(", ")
|
43
95
|
end
|
44
96
|
end
|
97
|
+
|
98
|
+
freeze
|
45
99
|
end
|
@@ -1,13 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Literal::Types::DescendantType
|
4
|
+
include Literal::Type
|
5
|
+
|
4
6
|
def initialize(type)
|
5
7
|
@type = type
|
6
8
|
end
|
7
9
|
|
8
|
-
|
10
|
+
attr_reader :type
|
11
|
+
|
12
|
+
def inspect
|
13
|
+
"_Descendant(#{@type})"
|
14
|
+
end
|
9
15
|
|
10
16
|
def ===(value)
|
11
17
|
Module === value && value < @type
|
12
18
|
end
|
19
|
+
|
20
|
+
def >=(other)
|
21
|
+
case other
|
22
|
+
when Literal::Types::DescendantType, Literal::Types::ClassType
|
23
|
+
Literal.subtype?(other.type, of: @type)
|
24
|
+
else
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
freeze
|
13
30
|
end
|
@@ -2,13 +2,30 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::EnumerableType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(type)
|
6
8
|
@type = type
|
7
9
|
end
|
8
10
|
|
9
|
-
|
11
|
+
attr_reader :type
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"_Enumerable(#{@type.inspect})"
|
15
|
+
end
|
10
16
|
|
11
17
|
def ===(value)
|
12
18
|
Enumerable === value && value.all?(@type)
|
13
19
|
end
|
20
|
+
|
21
|
+
def >=(other)
|
22
|
+
case other
|
23
|
+
when Literal::Types::EnumerableType
|
24
|
+
Literal.subtype?(other.type, of: @type)
|
25
|
+
else
|
26
|
+
false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
freeze
|
14
31
|
end
|
@@ -1,12 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
|
5
|
-
|
4
|
+
class Literal::Types::FalsyType
|
5
|
+
include Literal::Type
|
6
6
|
|
7
|
-
def
|
7
|
+
def initialize
|
8
|
+
freeze
|
9
|
+
end
|
10
|
+
|
11
|
+
def inspect
|
12
|
+
"_Falsy"
|
13
|
+
end
|
14
|
+
|
15
|
+
def ===(value)
|
8
16
|
!value
|
9
17
|
end
|
10
18
|
|
19
|
+
def >=(other)
|
20
|
+
case other
|
21
|
+
when Literal::Types::FalsyType, nil, false
|
22
|
+
true
|
23
|
+
else
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Instance = new.freeze
|
29
|
+
|
11
30
|
freeze
|
12
31
|
end
|
@@ -18,4 +18,21 @@ class Literal::Types::HashType
|
|
18
18
|
|
19
19
|
true
|
20
20
|
end
|
21
|
+
|
22
|
+
def record_literal_type_errors(context)
|
23
|
+
unless Hash === context.actual
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
context.actual.each do |key, item|
|
28
|
+
unless @key_type === key
|
29
|
+
context.add_child(label: "[]", expected: @key_type, actual: key)
|
30
|
+
next
|
31
|
+
end
|
32
|
+
|
33
|
+
unless @value_type === item
|
34
|
+
context.add_child(label: "[#{key.inspect}]", expected: @value_type, actual: item)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
21
38
|
end
|
@@ -8,6 +8,8 @@ class Literal::Types::IntersectionType
|
|
8
8
|
@types = types
|
9
9
|
end
|
10
10
|
|
11
|
+
attr_reader :types
|
12
|
+
|
11
13
|
def inspect = "_Intersection(#{@types.map(&:inspect).join(', ')})"
|
12
14
|
|
13
15
|
def ===(value)
|
@@ -17,4 +19,12 @@ class Literal::Types::IntersectionType
|
|
17
19
|
def nil?
|
18
20
|
@types.all?(&:nil?)
|
19
21
|
end
|
22
|
+
|
23
|
+
def record_literal_type_errors(context)
|
24
|
+
@types.each do |type|
|
25
|
+
next if type === context.actual
|
26
|
+
|
27
|
+
context.add_child(label: inspect, expected: type, actual: context.actual)
|
28
|
+
end
|
29
|
+
end
|
20
30
|
end
|
@@ -19,5 +19,21 @@ module Literal::Types::JSONDataType
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
+
def self.record_literal_type_errors(context)
|
23
|
+
case value = context.actual
|
24
|
+
when String, Integer, Float, true, false, nil
|
25
|
+
# nothing to do
|
26
|
+
when Hash
|
27
|
+
value.each do |k, v|
|
28
|
+
context.add_child(label: "[]", expected: String, actual: k) unless String === k
|
29
|
+
context.add_child(label: "[#{k.inspect}]", expected: self, actual: v) unless self === v
|
30
|
+
end
|
31
|
+
when Array
|
32
|
+
value.each_with_index do |item, index|
|
33
|
+
context.add_child(label: "[#{index}]", expected: self, actual: item) unless self === item
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
22
38
|
freeze
|
23
39
|
end
|
@@ -21,9 +21,15 @@ class Literal::Types::MapType
|
|
21
21
|
return
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
unless
|
26
|
-
context.add_child(label: "[
|
24
|
+
@shape.each do |key, expected|
|
25
|
+
unless context.actual.key?(key) || expected === nil
|
26
|
+
context.add_child(label: "[]", expected: key, actual: nil)
|
27
|
+
next
|
28
|
+
end
|
29
|
+
|
30
|
+
actual = context.actual[key]
|
31
|
+
unless expected === actual
|
32
|
+
context.add_child(label: "[#{key.inspect}]", expected:, actual:)
|
27
33
|
end
|
28
34
|
end
|
29
35
|
end
|