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/spec/object_spec.rb
CHANGED
@@ -8,26 +8,11 @@ describe StrongJSON::Type::Object do
|
|
8
8
|
a: StrongJSON::Type::Base.new(:numeric),
|
9
9
|
b: StrongJSON::Type::Base.new(:string)
|
10
10
|
},
|
11
|
-
|
12
|
-
|
11
|
+
on_unknown: :reject,
|
12
|
+
exceptions: Set.new
|
13
13
|
)
|
14
14
|
|
15
|
-
expect(type.coerce(a: 123, b: "test")).to eq(a: 123, b: "test")
|
16
|
-
end
|
17
|
-
|
18
|
-
it "rejects unspecified fields" do
|
19
|
-
type = StrongJSON::Type::Object.new(
|
20
|
-
{
|
21
|
-
a: StrongJSON::Type::Base.new(:numeric)
|
22
|
-
},
|
23
|
-
ignored_attributes: nil,
|
24
|
-
prohibited_attributes: Set.new
|
25
|
-
)
|
26
|
-
|
27
|
-
expect { type.coerce(a:123, b:true) }.to raise_error(StrongJSON::Type::UnexpectedAttributeError) {|e|
|
28
|
-
expect(e.path.to_s).to eq("$")
|
29
|
-
expect(e.attribute).to eq(:b)
|
30
|
-
}
|
15
|
+
expect(type.coerce({ a: 123, b: "test" })).to eq(a: 123, b: "test")
|
31
16
|
end
|
32
17
|
|
33
18
|
it "rejects objects with missing fields" do
|
@@ -35,87 +20,61 @@ describe StrongJSON::Type::Object do
|
|
35
20
|
{
|
36
21
|
a: StrongJSON::Type::Base.new(:numeric)
|
37
22
|
},
|
38
|
-
|
39
|
-
|
23
|
+
on_unknown: :reject,
|
24
|
+
exceptions: Set.new
|
40
25
|
)
|
41
26
|
|
42
|
-
expect{ type.coerce(b: "test") }.to raise_error(StrongJSON::Type::UnexpectedAttributeError) {|e|
|
27
|
+
expect{ type.coerce({ a: 123, b: "test" }) }.to raise_error(StrongJSON::Type::UnexpectedAttributeError) {|e|
|
43
28
|
expect(e.path.to_s).to eq("$")
|
44
29
|
expect(e.attribute).to eq(:b)
|
45
30
|
}
|
46
31
|
end
|
47
32
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
}
|
59
|
-
|
60
|
-
it "ignores field with any value" do
|
61
|
-
expect(type.coerce(a: 123, b: true)).to eq(a: 123)
|
62
|
-
end
|
63
|
-
|
64
|
-
it "accepts if it does not contains the field" do
|
65
|
-
expect(type.coerce(a: 123)).to eq(a: 123)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
context "when ignored_attributes is nil" do
|
70
|
-
let(:type) {
|
71
|
-
StrongJSON::Type::Object.new(
|
72
|
-
{
|
73
|
-
a: StrongJSON::Type::Base.new(:numeric)
|
74
|
-
},
|
75
|
-
ignored_attributes: nil,
|
76
|
-
prohibited_attributes: Set.new
|
77
|
-
)
|
78
|
-
}
|
33
|
+
context "when on_unknown is :ignore" do
|
34
|
+
let(:type) {
|
35
|
+
StrongJSON::Type::Object.new(
|
36
|
+
{
|
37
|
+
a: StrongJSON::Type::Base.new(:numeric)
|
38
|
+
},
|
39
|
+
on_unknown: :ignore,
|
40
|
+
exceptions: Set[:x]
|
41
|
+
)
|
42
|
+
}
|
79
43
|
|
80
|
-
|
81
|
-
|
82
|
-
type.coerce(a: 123, b: true)
|
83
|
-
}.to raise_error(StrongJSON::Type::UnexpectedAttributeError)
|
84
|
-
end
|
44
|
+
it "ignores field with any value" do
|
45
|
+
expect(type.coerce({ a: 123, b: true })).to eq(a: 123)
|
85
46
|
end
|
86
47
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
},
|
93
|
-
ignored_attributes: :any,
|
94
|
-
prohibited_attributes: Set.new
|
95
|
-
)
|
48
|
+
it "raises error on attributes listed in exceptions" do
|
49
|
+
expect {
|
50
|
+
type.coerce({ a: 123, x: false })
|
51
|
+
}.to raise_error(StrongJSON::Type::UnexpectedAttributeError) {|error|
|
52
|
+
expect(error.attribute).to eq(:x)
|
96
53
|
}
|
97
|
-
|
98
|
-
it "ignores field with any value" do
|
99
|
-
expect(type.coerce(a: 123, b: true)).to eq(a: 123)
|
100
|
-
end
|
101
54
|
end
|
102
55
|
end
|
103
56
|
|
104
|
-
|
57
|
+
context "when on_unknown is :reject" do
|
105
58
|
let(:type) {
|
106
59
|
StrongJSON::Type::Object.new(
|
107
60
|
{
|
108
61
|
a: StrongJSON::Type::Base.new(:numeric)
|
109
62
|
},
|
110
|
-
|
111
|
-
|
63
|
+
on_unknown: :reject,
|
64
|
+
exceptions: Set[:c]
|
112
65
|
)
|
113
66
|
}
|
114
67
|
|
115
|
-
it "raises
|
68
|
+
it "raises with unknown attribute" do
|
116
69
|
expect {
|
117
|
-
type.coerce(a:123, b:true
|
118
|
-
}.to raise_error(StrongJSON::Type::UnexpectedAttributeError)
|
70
|
+
type.coerce({ a: 123, b: true })
|
71
|
+
}.to raise_error(StrongJSON::Type::UnexpectedAttributeError) {|error|
|
72
|
+
expect(error.attribute).to eq(:b)
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
it "ignores attributes listed in exceptions" do
|
77
|
+
expect(type.coerce({ a: 123, c: false })).to eq(a:123)
|
119
78
|
end
|
120
79
|
end
|
121
80
|
end
|
@@ -126,8 +85,8 @@ describe StrongJSON::Type::Object do
|
|
126
85
|
{
|
127
86
|
a: StrongJSON::Type::Optional.new(StrongJSON::Type::Base.new(:numeric))
|
128
87
|
},
|
129
|
-
|
130
|
-
|
88
|
+
on_unknown: :reject,
|
89
|
+
exceptions: Set[]
|
131
90
|
)
|
132
91
|
}
|
133
92
|
|
@@ -151,8 +110,8 @@ describe StrongJSON::Type::Object do
|
|
151
110
|
a: StrongJSON::Type::Base.new(:numeric),
|
152
111
|
b: StrongJSON::Type::Base.new(:string)
|
153
112
|
},
|
154
|
-
|
155
|
-
|
113
|
+
on_unknown: :reject,
|
114
|
+
exceptions: Set[]
|
156
115
|
)
|
157
116
|
}
|
158
117
|
|
@@ -164,4 +123,73 @@ describe StrongJSON::Type::Object do
|
|
164
123
|
expect(type =~ {}).to be_falsey
|
165
124
|
end
|
166
125
|
end
|
126
|
+
|
127
|
+
describe "#ignore" do
|
128
|
+
let (:type) {
|
129
|
+
StrongJSON::Type::Object.new(
|
130
|
+
{ a: StrongJSON::Type::Base.new(:numeric) },
|
131
|
+
on_unknown: :reject,
|
132
|
+
exceptions: Set[]
|
133
|
+
)
|
134
|
+
}
|
135
|
+
|
136
|
+
context "if no argument is given" do
|
137
|
+
it "ignores all unknown attributes" do
|
138
|
+
updated_type = type.ignore()
|
139
|
+
expect(updated_type.on_unknown).to eq(:ignore)
|
140
|
+
expect(updated_type.exceptions).to eq(Set[])
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
context "if list of Symbol is given" do
|
146
|
+
it "ignores specified attributes but raises unknowns" do
|
147
|
+
updated_type = type.ignore(:x, :y)
|
148
|
+
expect(updated_type.on_unknown).to eq(:reject)
|
149
|
+
expect(updated_type.exceptions).to eq(Set[:x, :y])
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "if except keyword is specified" do
|
154
|
+
it "raises unknowns but ignores specified attributes" do
|
155
|
+
updated_type = type.ignore(except: Set[:x, :y])
|
156
|
+
expect(updated_type.on_unknown).to eq(:ignore)
|
157
|
+
expect(updated_type.exceptions).to eq(Set[:x, :y])
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "#reject" do
|
163
|
+
let (:type) {
|
164
|
+
StrongJSON::Type::Object.new(
|
165
|
+
{ a: StrongJSON::Type::Base.new(:numeric) },
|
166
|
+
on_unknown: :reject,
|
167
|
+
exceptions: Set[]
|
168
|
+
)
|
169
|
+
}
|
170
|
+
|
171
|
+
context "if no argument is given" do
|
172
|
+
it "raises on any unknown attribute" do
|
173
|
+
updated_type = type.reject()
|
174
|
+
expect(updated_type.on_unknown).to eq(:reject)
|
175
|
+
expect(updated_type.exceptions).to eq(Set[])
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context "if list of Symbol is given" do
|
180
|
+
it "raises unknowns but ignores specified attributes" do
|
181
|
+
updated_type = type.reject(:x, :y)
|
182
|
+
expect(updated_type.on_unknown).to eq(:ignore)
|
183
|
+
expect(updated_type.exceptions).to eq(Set[:x, :y])
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context "if except keyword is specified" do
|
188
|
+
it "ignores specified attributes but raises unknowns" do
|
189
|
+
updated_type = type.reject(except: Set[:x, :y])
|
190
|
+
expect(updated_type.on_unknown).to eq(:reject)
|
191
|
+
expect(updated_type.exceptions).to eq(Set[:x, :y])
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
167
195
|
end
|
data/strong_json.gemspec
CHANGED
@@ -18,9 +18,4 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
spec.metadata = { "steep_types" => "sig" }
|
21
|
-
|
22
|
-
spec.add_development_dependency "bundler", ">= 1.6"
|
23
|
-
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
-
spec.add_development_dependency "rspec", "~> 3.0"
|
25
|
-
spec.add_development_dependency "steep", "~> 0.10"
|
26
21
|
end
|
metadata
CHANGED
@@ -1,71 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: strong_json
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 2.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Soutaro Matsumoto
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.6'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.6'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '10.0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '10.0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rspec
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '3.0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '3.0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: steep
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0.10'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0.10'
|
11
|
+
date: 2020-10-09 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
69
13
|
description: Type check JSON objects
|
70
14
|
email:
|
71
15
|
- matsumoto@soutaro.com
|
@@ -73,6 +17,7 @@ executables: []
|
|
73
17
|
extensions: []
|
74
18
|
extra_rdoc_files: []
|
75
19
|
files:
|
20
|
+
- ".github/workflows/ruby.yml"
|
76
21
|
- ".gitignore"
|
77
22
|
- ".ruby-version"
|
78
23
|
- ".travis.yml"
|
@@ -81,20 +26,25 @@ files:
|
|
81
26
|
- LICENSE.txt
|
82
27
|
- README.md
|
83
28
|
- Rakefile
|
84
|
-
-
|
85
|
-
- example/
|
29
|
+
- Steepfile
|
30
|
+
- example/Steepfile
|
31
|
+
- example/lib/example.rb
|
32
|
+
- example/sig/example.rbs
|
86
33
|
- lib/strong_json.rb
|
87
34
|
- lib/strong_json/error_reporter.rb
|
88
35
|
- lib/strong_json/type.rb
|
89
36
|
- lib/strong_json/types.rb
|
90
37
|
- lib/strong_json/version.rb
|
91
|
-
-
|
92
|
-
- sig/
|
38
|
+
- pp.rb
|
39
|
+
- sig/polyfill.rbs
|
40
|
+
- sig/strong_json.rbs
|
41
|
+
- sig/type.rbs
|
93
42
|
- spec/array_spec.rb
|
94
43
|
- spec/basetype_spec.rb
|
95
44
|
- spec/case_subsumption_operator_spec.rb
|
96
45
|
- spec/enum_spec.rb
|
97
46
|
- spec/error_spec.rb
|
47
|
+
- spec/hash_spec.rb
|
98
48
|
- spec/json_spec.rb
|
99
49
|
- spec/literal_spec.rb
|
100
50
|
- spec/object_spec.rb
|
@@ -105,7 +55,7 @@ licenses:
|
|
105
55
|
- MIT
|
106
56
|
metadata:
|
107
57
|
steep_types: sig
|
108
|
-
post_install_message:
|
58
|
+
post_install_message:
|
109
59
|
rdoc_options: []
|
110
60
|
require_paths:
|
111
61
|
- lib
|
@@ -120,9 +70,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
70
|
- !ruby/object:Gem::Version
|
121
71
|
version: '0'
|
122
72
|
requirements: []
|
123
|
-
|
124
|
-
|
125
|
-
signing_key:
|
73
|
+
rubygems_version: 3.0.8
|
74
|
+
signing_key:
|
126
75
|
specification_version: 4
|
127
76
|
summary: Type check JSON objects
|
128
77
|
test_files:
|
@@ -131,6 +80,7 @@ test_files:
|
|
131
80
|
- spec/case_subsumption_operator_spec.rb
|
132
81
|
- spec/enum_spec.rb
|
133
82
|
- spec/error_spec.rb
|
83
|
+
- spec/hash_spec.rb
|
134
84
|
- spec/json_spec.rb
|
135
85
|
- spec/literal_spec.rb
|
136
86
|
- spec/object_spec.rb
|
data/example/example.rbi
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
type address = { address: String, country: Symbol? }
|
2
|
-
type email = { email: String }
|
3
|
-
|
4
|
-
class AddressSchema < StrongJSON
|
5
|
-
def address: -> StrongJSON::Type::Object<address>
|
6
|
-
def email: -> StrongJSON::Type::Object<email>
|
7
|
-
def contact: -> StrongJSON::Type::Object<email | address>
|
8
|
-
def person: -> StrongJSON::Type::Object<{ name: String, contacts: Array<email | address> }>
|
9
|
-
end
|
10
|
-
|
11
|
-
Schema: AddressSchema
|
data/sig/strong_json.rbi
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
class StrongJSON
|
2
|
-
def initialize: { (self) -> void } -> any
|
3
|
-
def let: (Symbol, ty) -> void
|
4
|
-
include StrongJSON::Types
|
5
|
-
end
|
6
|
-
|
7
|
-
StrongJSON::VERSION: String
|
8
|
-
|
9
|
-
class StandardError
|
10
|
-
def initialize: (String) -> any
|
11
|
-
end
|
12
|
-
|
13
|
-
interface StrongJSON::_Schema<'type>
|
14
|
-
def coerce: (any, ?path: Type::ErrorPath) -> 'type
|
15
|
-
def =~: (any) -> bool
|
16
|
-
def to_s: -> String
|
17
|
-
def is_a?: (any) -> bool
|
18
|
-
def alias: -> Symbol?
|
19
|
-
def with_alias: (Symbol) -> self
|
20
|
-
def ==: (any) -> bool
|
21
|
-
def yield_self: <'a> () { (self) -> 'a } -> 'a
|
22
|
-
end
|
23
|
-
|
24
|
-
type StrongJSON::ty = _Schema<any>
|
25
|
-
|
26
|
-
module StrongJSON::Types
|
27
|
-
def object: <'x> (Hash<Symbol, ty>) -> Type::Object<'x>
|
28
|
-
| () -> Type::Object<{}>
|
29
|
-
def object?: <'x> (Hash<Symbol, ty>) -> Type::Optional<'x>
|
30
|
-
| () -> Type::Optional<{}>
|
31
|
-
def any: () -> Type::Base<any>
|
32
|
-
def any?: () -> Type::Optional<any>
|
33
|
-
def optional: <'x> (_Schema<'x>) -> Type::Optional<'x>
|
34
|
-
| () -> Type::Optional<any>
|
35
|
-
def string: () -> Type::Base<String>
|
36
|
-
def string?: () -> Type::Optional<String>
|
37
|
-
def number: () -> Type::Base<Numeric>
|
38
|
-
def number?: () -> Type::Optional<Numeric>
|
39
|
-
def numeric: () -> Type::Base<Numeric>
|
40
|
-
def numeric?: () -> Type::Optional<Numeric>
|
41
|
-
def boolean: () -> Type::Base<bool>
|
42
|
-
def boolean?: () -> Type::Optional<bool>
|
43
|
-
def symbol: () -> Type::Base<Symbol>
|
44
|
-
def symbol?: () -> Type::Optional<Symbol>
|
45
|
-
def array: <'x> (_Schema<'x>) -> Type::Array<'x>
|
46
|
-
| () -> Type::Array<any>
|
47
|
-
def array?: <'x> (_Schema<'x>) -> Type::Optional<::Array<'x>>
|
48
|
-
def literal: <'x> ('x) -> Type::Literal<'x>
|
49
|
-
def literal?: <'x> ('x) -> Type::Optional<'x>
|
50
|
-
def enum: <'x> (*_Schema<any>, ?detector: Type::detector?) -> Type::Enum<'x>
|
51
|
-
def enum?: <'x> (*_Schema<any>, ?detector: Type::detector?) -> Type::Optional<'x>
|
52
|
-
end
|
53
|
-
|
54
|
-
class StrongJSON::ErrorReporter
|
55
|
-
attr_reader path: Type::ErrorPath
|
56
|
-
@string: String
|
57
|
-
def initialize: (path: Type::ErrorPath) -> any
|
58
|
-
def format: -> void
|
59
|
-
def (private) format_trace: (path: Type::ErrorPath, ?index: Integer) -> void
|
60
|
-
def (private) format_aliases: (path: Type::ErrorPath, where: ::Array<String>) -> ::Array<String>
|
61
|
-
def (private) format_single_alias: (Symbol, ty) -> String
|
62
|
-
def (private) pretty: (ty, any, ?expand_alias: bool) -> void
|
63
|
-
def pretty_str: (ty, ?expand_alias: bool) -> ::String
|
64
|
-
end
|