strong_json 1.0.1 → 2.1.2
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/.github/workflows/ruby.yml +29 -0
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/.travis.yml +3 -1
- data/CHANGELOG.md +20 -0
- data/Gemfile +4 -0
- data/README.md +16 -15
- data/Rakefile +2 -2
- data/Steepfile +7 -0
- data/example/Steepfile +8 -0
- data/example/{example.rb → lib/example.rb} +1 -1
- data/example/sig/example.rbs +11 -0
- data/lib/strong_json/error_reporter.rb +2 -4
- data/lib/strong_json/type.rb +82 -52
- data/lib/strong_json/types.rb +18 -6
- data/lib/strong_json/version.rb +2 -2
- data/pp.rb +27 -0
- data/sig/polyfill.rbs +13 -0
- data/sig/strong_json.rbs +67 -0
- data/sig/type.rbs +132 -0
- data/spec/basetype_spec.rb +12 -0
- data/spec/enum_spec.rb +8 -8
- data/spec/hash_spec.rb +32 -0
- data/spec/json_spec.rb +10 -10
- data/spec/object_spec.rb +108 -80
- data/strong_json.gemspec +0 -5
- metadata +18 -68
- data/example/example.rbi +0 -11
- data/sig/strong_json.rbi +0 -64
- data/sig/type.rbi +0 -116
data/lib/strong_json/types.rb
CHANGED
@@ -1,20 +1,17 @@
|
|
1
1
|
class StrongJSON
|
2
2
|
module Types
|
3
|
-
# @type method object: (?Hash<Symbol, ty>) -> Type::Object<any>
|
4
3
|
def object(fields = {})
|
5
4
|
if fields.empty?
|
6
|
-
Type::Object.new(fields,
|
5
|
+
Type::Object.new(fields, on_unknown: :ignore, exceptions: Set.new)
|
7
6
|
else
|
8
|
-
Type::Object.new(fields,
|
7
|
+
Type::Object.new(fields, on_unknown: :reject, exceptions: Set.new)
|
9
8
|
end
|
10
9
|
end
|
11
10
|
|
12
|
-
# @type method array: (?ty) -> Type::Array<any>
|
13
11
|
def array(type = any)
|
14
12
|
Type::Array.new(type)
|
15
13
|
end
|
16
14
|
|
17
|
-
# @type method optional: (?ty) -> Type::Optional<any>
|
18
15
|
def optional(type = any)
|
19
16
|
Type::Optional.new(type)
|
20
17
|
end
|
@@ -31,6 +28,10 @@ class StrongJSON
|
|
31
28
|
StrongJSON::Type::Base.new(:number)
|
32
29
|
end
|
33
30
|
|
31
|
+
def integer
|
32
|
+
StrongJSON::Type::Base.new(:integer)
|
33
|
+
end
|
34
|
+
|
34
35
|
def boolean
|
35
36
|
StrongJSON::Type::Base.new(:boolean)
|
36
37
|
end
|
@@ -63,6 +64,10 @@ class StrongJSON
|
|
63
64
|
optional(numeric)
|
64
65
|
end
|
65
66
|
|
67
|
+
def integer?
|
68
|
+
optional(integer)
|
69
|
+
end
|
70
|
+
|
66
71
|
def number?
|
67
72
|
optional(number)
|
68
73
|
end
|
@@ -79,7 +84,6 @@ class StrongJSON
|
|
79
84
|
optional(array(ty))
|
80
85
|
end
|
81
86
|
|
82
|
-
# @type method object?: (?Hash<Symbol, ty>) -> Type::Optional<any>
|
83
87
|
def object?(fields={})
|
84
88
|
optional(object(fields))
|
85
89
|
end
|
@@ -91,5 +95,13 @@ class StrongJSON
|
|
91
95
|
def enum?(*types, detector: nil)
|
92
96
|
optional(enum(*types, detector: detector))
|
93
97
|
end
|
98
|
+
|
99
|
+
def hash(type)
|
100
|
+
StrongJSON::Type::Hash.new(type)
|
101
|
+
end
|
102
|
+
|
103
|
+
def hash?(type)
|
104
|
+
optional(hash(type))
|
105
|
+
end
|
94
106
|
end
|
95
107
|
end
|
data/lib/strong_json/version.rb
CHANGED
data/pp.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require "prettyprint"
|
2
|
+
|
3
|
+
pp = PrettyPrint.new
|
4
|
+
|
5
|
+
pp.group 0 do
|
6
|
+
pp.text "hello = "
|
7
|
+
|
8
|
+
pp.group 0, "enum_____(", ")" do
|
9
|
+
pp.nest(2) do
|
10
|
+
pp.breakable ""
|
11
|
+
count = 7
|
12
|
+
count.times do |i|
|
13
|
+
pp.text "hello #{i}"
|
14
|
+
|
15
|
+
if i < count - 1
|
16
|
+
pp.text ","
|
17
|
+
pp.breakable " "
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
pp.breakable ""
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
pp.flush
|
27
|
+
puts pp.output
|
data/sig/polyfill.rbs
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
class PrettyPrint
|
2
|
+
def text: (String) -> void
|
3
|
+
|
4
|
+
def group: (?Integer, ?String, ?String) { () -> void } -> void
|
5
|
+
|
6
|
+
def nest: (Integer) { () -> void } -> void
|
7
|
+
|
8
|
+
def breakable: (String) -> void
|
9
|
+
|
10
|
+
def self.format: { (instance) -> void } -> String
|
11
|
+
|
12
|
+
def self.singleline_format: { (instance) -> void } -> String
|
13
|
+
end
|
data/sig/strong_json.rbs
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
class StrongJSON
|
2
|
+
def initialize: { (StrongJSON) -> void } -> untyped
|
3
|
+
def let: (Symbol, ty) -> void
|
4
|
+
include Types
|
5
|
+
|
6
|
+
VERSION: String
|
7
|
+
|
8
|
+
interface _Schema[T]
|
9
|
+
def coerce: (untyped, ?path: Type::ErrorPath) -> T
|
10
|
+
def =~: (untyped) -> bool
|
11
|
+
def to_s: -> String
|
12
|
+
def is_a?: (untyped) -> bool
|
13
|
+
def `alias`: -> Symbol?
|
14
|
+
def with_alias: (Symbol) -> self
|
15
|
+
def ==: (untyped) -> bool
|
16
|
+
def yield_self: [X] () { (self) -> X } -> X
|
17
|
+
end
|
18
|
+
|
19
|
+
type ty = _Schema[untyped]
|
20
|
+
|
21
|
+
module Types
|
22
|
+
def object: [X] (Hash[Symbol, ty]) -> Type::Object[X]
|
23
|
+
| () -> Type::Object[bot]
|
24
|
+
def object?: [X] (Hash[Symbol, ty]) -> Type::Optional[X]
|
25
|
+
| () -> Type::Optional[bot]
|
26
|
+
def any: () -> Type::Base[untyped]
|
27
|
+
def any?: () -> Type::Optional[untyped]
|
28
|
+
def optional: [X] (_Schema[X]) -> Type::Optional[X]
|
29
|
+
| () -> Type::Optional[untyped]
|
30
|
+
def string: () -> Type::Base[String]
|
31
|
+
def string?: () -> Type::Optional[String]
|
32
|
+
def number: () -> Type::Base[Numeric]
|
33
|
+
def number?: () -> Type::Optional[Numeric]
|
34
|
+
def numeric: () -> Type::Base[Numeric]
|
35
|
+
def numeric?: () -> Type::Optional[Numeric]
|
36
|
+
def integer: () -> Type::Base[Integer]
|
37
|
+
def integer?: () -> Type::Optional[Integer]
|
38
|
+
def boolean: () -> Type::Base[bool]
|
39
|
+
def boolean?: () -> Type::Optional[bool]
|
40
|
+
def symbol: () -> Type::Base[Symbol]
|
41
|
+
def symbol?: () -> Type::Optional[Symbol]
|
42
|
+
def array: [X] (_Schema[X]) -> Type::Array[X]
|
43
|
+
| () -> Type::Array[untyped]
|
44
|
+
def array?: [X] (_Schema[X]) -> Type::Optional[::Array[X]]
|
45
|
+
def literal: [X] (X) -> Type::Literal[X]
|
46
|
+
def literal?: [X] (X) -> Type::Optional[X]
|
47
|
+
def enum: [X] (*_Schema[untyped], ?detector: Type::detector?) -> Type::Enum[X]
|
48
|
+
def enum?: [X] (*_Schema[untyped], ?detector: Type::detector?) -> Type::Optional[X]
|
49
|
+
def hash: [X] (_Schema[X]) -> Type::Hash[X]
|
50
|
+
def hash?: [X] (_Schema[X]) -> Type::Optional[Hash[Symbol, X]]
|
51
|
+
end
|
52
|
+
|
53
|
+
class ErrorReporter
|
54
|
+
attr_reader path: Type::ErrorPath
|
55
|
+
@string: String
|
56
|
+
|
57
|
+
def initialize: (path: Type::ErrorPath) -> untyped
|
58
|
+
def format: -> void
|
59
|
+
def pretty_str: (ty, ?expand_alias: bool) -> ::String
|
60
|
+
|
61
|
+
private
|
62
|
+
def format_trace: (path: Type::ErrorPath, ?index: Integer) -> void
|
63
|
+
def format_aliases: (path: Type::ErrorPath, where: ::Array[String]) -> ::Array[String]
|
64
|
+
def format_single_alias: (Symbol, ty) -> String
|
65
|
+
def pretty: (ty, PrettyPrint, ?expand_alias: bool) -> void
|
66
|
+
end
|
67
|
+
end
|
data/sig/type.rbs
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
class StrongJSON
|
2
|
+
module Type
|
3
|
+
module Match : _Schema[untyped]
|
4
|
+
def =~: (untyped) -> bool
|
5
|
+
def ===: (untyped) -> bool
|
6
|
+
end
|
7
|
+
|
8
|
+
module WithAlias : ::Object
|
9
|
+
@alias: Symbol?
|
10
|
+
def `alias`: -> Symbol?
|
11
|
+
def with_alias: (Symbol) -> self
|
12
|
+
end
|
13
|
+
|
14
|
+
type base_type_name = :any | :number | :string | :boolean | :numeric | :symbol | :integer
|
15
|
+
|
16
|
+
class Base[A]
|
17
|
+
include Match
|
18
|
+
include WithAlias
|
19
|
+
|
20
|
+
attr_reader type: base_type_name
|
21
|
+
|
22
|
+
def initialize: (base_type_name) -> untyped
|
23
|
+
def test: (untyped) -> bool
|
24
|
+
def coerce: (untyped, ?path: ErrorPath) -> A
|
25
|
+
end
|
26
|
+
|
27
|
+
class Optional[T]
|
28
|
+
include Match
|
29
|
+
include WithAlias
|
30
|
+
|
31
|
+
attr_reader type: _Schema[T]
|
32
|
+
|
33
|
+
def initialize: (_Schema[T]) -> untyped
|
34
|
+
def coerce: (untyped, ?path: ErrorPath) -> (T | nil)
|
35
|
+
end
|
36
|
+
|
37
|
+
class Literal[T]
|
38
|
+
include Match
|
39
|
+
include WithAlias
|
40
|
+
|
41
|
+
attr_reader value: T
|
42
|
+
|
43
|
+
def initialize: (T) -> untyped
|
44
|
+
def coerce: (untyped, ?path: ErrorPath) -> T
|
45
|
+
end
|
46
|
+
|
47
|
+
class Array[T]
|
48
|
+
include Match
|
49
|
+
include WithAlias
|
50
|
+
|
51
|
+
attr_reader type: _Schema[T]
|
52
|
+
|
53
|
+
def initialize: (_Schema[T]) -> untyped
|
54
|
+
def coerce: (untyped, ?path: ErrorPath) -> ::Array[T]
|
55
|
+
end
|
56
|
+
|
57
|
+
class Object[T]
|
58
|
+
include Match
|
59
|
+
include WithAlias
|
60
|
+
|
61
|
+
attr_reader fields: ::Hash[Symbol, _Schema[untyped]]
|
62
|
+
attr_reader on_unknown: :ignore | :reject
|
63
|
+
attr_reader exceptions: Set[Symbol]
|
64
|
+
|
65
|
+
def initialize: (::Hash[Symbol, _Schema[T]], on_unknown: :ignore | :reject, exceptions: Set[Symbol]) -> untyped
|
66
|
+
def coerce: (untyped, ?path: ErrorPath) -> T
|
67
|
+
|
68
|
+
# If no argument is given, it ignores all unknown attributes.
|
69
|
+
# If `Symbol`s are given, it ignores the listed attributes, but rejects if other unknown attributes are detected.
|
70
|
+
# If `except:` is specified, it rejects attributes listed in `except` are detected, but ignores other unknown attributes.
|
71
|
+
def ignore: (*Symbol ignores, ?except: Set[Symbol]?) -> Object[T]
|
72
|
+
|
73
|
+
# If no argument is given, it rejects on untyped unknown attribute.
|
74
|
+
# If `Symbol`s are given, it rejects the listed attributes are detected, but ignores other unknown attributes.
|
75
|
+
# If `except:` is specified, it ignores given attributes, but rejects if other unknown attributes are detected.
|
76
|
+
def reject: (*Symbol rejecteds, ?except: Set[Symbol]?) -> Object[T]
|
77
|
+
|
78
|
+
def update_fields: [X] { (::Hash[Symbol, _Schema[untyped]]) -> void } -> Object[X]
|
79
|
+
end
|
80
|
+
|
81
|
+
type detector = ^(untyped) -> _Schema[untyped]?
|
82
|
+
|
83
|
+
class Enum[T]
|
84
|
+
include Match
|
85
|
+
include WithAlias
|
86
|
+
|
87
|
+
attr_reader types: ::Array[_Schema[untyped]]
|
88
|
+
attr_reader detector: detector?
|
89
|
+
|
90
|
+
def initialize: (::Array[_Schema[untyped]], ?detector?) -> untyped
|
91
|
+
def coerce: (untyped, ?path: ErrorPath) -> T
|
92
|
+
end
|
93
|
+
|
94
|
+
class ErrorPath
|
95
|
+
attr_reader type: _Schema[untyped]
|
96
|
+
attr_reader parent: [Symbol | Integer | nil, ErrorPath]?
|
97
|
+
|
98
|
+
def initialize: (type: _Schema[untyped], parent: [Symbol | Integer | nil, ErrorPath]?) -> untyped
|
99
|
+
def dig: (key: Symbol | Integer, type: _Schema[untyped]) -> ErrorPath
|
100
|
+
def expand: (type: _Schema[untyped]) -> ErrorPath
|
101
|
+
|
102
|
+
def self.root: (_Schema[untyped]) -> ErrorPath
|
103
|
+
def root?: -> bool
|
104
|
+
end
|
105
|
+
|
106
|
+
class TypeError < StandardError
|
107
|
+
attr_reader path: ErrorPath
|
108
|
+
attr_reader value: untyped
|
109
|
+
|
110
|
+
def initialize: (path: ErrorPath, value: untyped) -> untyped
|
111
|
+
def type: -> _Schema[untyped]
|
112
|
+
end
|
113
|
+
|
114
|
+
class UnexpectedAttributeError < StandardError
|
115
|
+
attr_reader path: ErrorPath
|
116
|
+
attr_reader attribute: Symbol
|
117
|
+
|
118
|
+
def initialize: (path: ErrorPath, attribute: Symbol) -> untyped
|
119
|
+
def type: -> _Schema[untyped]
|
120
|
+
end
|
121
|
+
|
122
|
+
class Hash[T]
|
123
|
+
include Match
|
124
|
+
include WithAlias
|
125
|
+
|
126
|
+
attr_reader type: _Schema[T]
|
127
|
+
|
128
|
+
def initialize: (_Schema[T]) -> untyped
|
129
|
+
def coerce: (untyped, ?path: ErrorPath) -> ::Hash[Symbol, T]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/spec/basetype_spec.rb
CHANGED
@@ -94,6 +94,18 @@ describe StrongJSON::Type::Base do
|
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
97
|
+
context ":integer" do
|
98
|
+
let (:type) { StrongJSON::Type::Base.new(:integer) }
|
99
|
+
|
100
|
+
it "accepts integer" do
|
101
|
+
expect(type.test(123)).to be_truthy
|
102
|
+
end
|
103
|
+
|
104
|
+
it "rejects float" do
|
105
|
+
expect(type.test(1.23)).to be_falsey
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
97
109
|
context ":numeric" do
|
98
110
|
let (:type) { StrongJSON::Type::Base.new(:numeric) }
|
99
111
|
|
data/spec/enum_spec.rb
CHANGED
@@ -29,16 +29,16 @@ describe StrongJSON::Type::Enum do
|
|
29
29
|
id: StrongJSON::Type::Literal.new("id1"),
|
30
30
|
value: StrongJSON::Type::Base.new(:string)
|
31
31
|
},
|
32
|
-
|
33
|
-
|
32
|
+
on_unknown: :reject,
|
33
|
+
exceptions: Set[]
|
34
34
|
),
|
35
35
|
StrongJSON::Type::Object.new(
|
36
36
|
{
|
37
37
|
id: StrongJSON::Type::Base.new(:string),
|
38
38
|
value: StrongJSON::Type::Base.new(:symbol)
|
39
39
|
},
|
40
|
-
|
41
|
-
|
40
|
+
on_unknown: :reject,
|
41
|
+
exceptions: Set[]
|
42
42
|
),
|
43
43
|
StrongJSON::Type::Optional.new(StrongJSON::Type::Literal.new(3)),
|
44
44
|
StrongJSON::Type::Literal.new(false),
|
@@ -73,8 +73,8 @@ describe StrongJSON::Type::Enum do
|
|
73
73
|
regexp: StrongJSON::Type::Base.new(:string),
|
74
74
|
option: StrongJSON::Type::Base.new(:string),
|
75
75
|
},
|
76
|
-
|
77
|
-
|
76
|
+
on_unknown: :reject,
|
77
|
+
exceptions: Set[]
|
78
78
|
)
|
79
79
|
}
|
80
80
|
|
@@ -83,8 +83,8 @@ describe StrongJSON::Type::Enum do
|
|
83
83
|
{
|
84
84
|
literal: StrongJSON::Type::Base.new(:string)
|
85
85
|
},
|
86
|
-
|
87
|
-
|
86
|
+
on_unknown: :reject,
|
87
|
+
exceptions: Set[]
|
88
88
|
)
|
89
89
|
}
|
90
90
|
|
data/spec/hash_spec.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require "strong_json"
|
2
|
+
|
3
|
+
describe StrongJSON::Type::Hash do
|
4
|
+
let(:number) { StrongJSON::Type::Base.new(:number) }
|
5
|
+
|
6
|
+
describe "#coerce" do
|
7
|
+
it "returns a hash" do
|
8
|
+
type = StrongJSON::Type::Hash.new(number)
|
9
|
+
expect(type.coerce({ foo: 123, bar: 234 })).to eq({ foo: 123, bar: 234 })
|
10
|
+
end
|
11
|
+
|
12
|
+
it "raises an error if number is given" do
|
13
|
+
type = StrongJSON::Type::Hash.new(number)
|
14
|
+
|
15
|
+
expect {
|
16
|
+
type.coerce(1)
|
17
|
+
}.to raise_error(StrongJSON::Type::TypeError) {|error|
|
18
|
+
expect(error.path.to_s).to eq("$")
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
it "raises an error if hash value is unexpected" do
|
23
|
+
type = StrongJSON::Type::Hash.new(number)
|
24
|
+
|
25
|
+
expect {
|
26
|
+
type.coerce({ foo: "hello" })
|
27
|
+
}.to raise_error(StrongJSON::Type::TypeError) {|error|
|
28
|
+
expect(error.path.to_s).to eq("$.foo")
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/spec/json_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require "strong_json"
|
|
3
3
|
describe "StrongJSON.new" do
|
4
4
|
it "tests the structure of a JSON object" do
|
5
5
|
s = StrongJSON.new do
|
6
|
-
let :item, object(name: string, count: numeric, price: numeric).ignore(
|
6
|
+
let :item, object(name: string, count: numeric, price: numeric).ignore(:comment)
|
7
7
|
let :items, array(item)
|
8
8
|
let :checkout,
|
9
9
|
object(items: items,
|
@@ -20,11 +20,11 @@ describe "StrongJSON.new" do
|
|
20
20
|
end
|
21
21
|
|
22
22
|
expect(
|
23
|
-
s.checkout.coerce(items: [{ name: "test", count: 1, price: "2.33", comment: "dummy" }], type: 1)
|
23
|
+
s.checkout.coerce({ items: [{ name: "test", count: 1, price: "2.33", comment: "dummy" }], type: 1 })
|
24
24
|
).to eq(items: [ { name: "test", count: 1, price: "2.33" }], type: 1, change: nil, customer: nil)
|
25
25
|
|
26
26
|
expect {
|
27
|
-
s.checkout.coerce(items: [{ name: "test", count: 1, price: [], comment: "dummy" }], type: 1)
|
27
|
+
s.checkout.coerce({ items: [{ name: "test", count: 1, price: [], comment: "dummy" }], type: 1 })
|
28
28
|
}.to raise_error(StrongJSON::Type::TypeError) {|e|
|
29
29
|
expect(e.path.to_s).to eq("$.items[0].price")
|
30
30
|
expect(e.type).to be_a(StrongJSON::Type::Base)
|
@@ -58,7 +58,7 @@ MSG
|
|
58
58
|
}
|
59
59
|
|
60
60
|
expect {
|
61
|
-
s.checkout.coerce(items: [], change: "", type: 1)
|
61
|
+
s.checkout.coerce({ items: [], change: "", type: 1 })
|
62
62
|
}.to raise_error(StrongJSON::Type::TypeError) {|e|
|
63
63
|
expect(e.path.to_s).to eq("$.change")
|
64
64
|
expect(e.type).to be_a(StrongJSON::Type::Base)
|
@@ -94,14 +94,14 @@ MSG
|
|
94
94
|
let :enum, object(e1: enum(boolean, number), e2: enum?(literal(1), literal(2)))
|
95
95
|
end
|
96
96
|
|
97
|
-
expect(s.enum.coerce(e1: false)).to eq(e1: false, e2: nil)
|
98
|
-
expect(s.enum.coerce(e1: 0)).to eq(e1: 0, e2: nil)
|
99
|
-
expect(s.enum.coerce(e1: 0, e2: 1)).to eq(e1: 0, e2: 1)
|
100
|
-
expect(s.enum.coerce(e1: 0, e2: 2)).to eq(e1: 0, e2: 2)
|
101
|
-
expect{ s.enum.coerce(e1: "", e2: 3) }.to raise_error(StrongJSON::Type::TypeError) {|e|
|
97
|
+
expect(s.enum.coerce({ e1: false })).to eq(e1: false, e2: nil)
|
98
|
+
expect(s.enum.coerce({ e1: 0 })).to eq(e1: 0, e2: nil)
|
99
|
+
expect(s.enum.coerce({ e1: 0, e2: 1 })).to eq(e1: 0, e2: 1)
|
100
|
+
expect(s.enum.coerce({ e1: 0, e2: 2 })).to eq(e1: 0, e2: 2)
|
101
|
+
expect{ s.enum.coerce({ e1: "", e2: 3 }) }.to raise_error(StrongJSON::Type::TypeError) {|e|
|
102
102
|
expect(e.path.to_s).to eq("$.e1")
|
103
103
|
}
|
104
|
-
expect{ s.enum.coerce(e1: false, e2: "") }.to raise_error(StrongJSON::Type::TypeError) {|e|
|
104
|
+
expect{ s.enum.coerce({ e1: false, e2: ""} ) }.to raise_error(StrongJSON::Type::TypeError) {|e|
|
105
105
|
expect(e.path.to_s).to eq("$.e2")
|
106
106
|
}
|
107
107
|
end
|