literal 1.0.0 → 1.2.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/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
|