mighty_json 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/LICENSE.txt.original +24 -0
- data/README.md +46 -0
- data/Rakefile +9 -0
- data/benchmarks/large.rb +47 -0
- data/benchmarks/small.rb +60 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/mighty_json/builder.rb +48 -0
- data/lib/mighty_json/errors.rb +45 -0
- data/lib/mighty_json/type.rb +241 -0
- data/lib/mighty_json/types.rb +87 -0
- data/lib/mighty_json/variable.rb +14 -0
- data/lib/mighty_json/version.rb +3 -0
- data/lib/mighty_json.rb +12 -0
- data/mighty_json.gemspec +31 -0
- metadata +119 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 15d2f5dff6d3f941811d37ee84d8e789e3aa0160
|
4
|
+
data.tar.gz: 18d8272bff2142be7f5842be8c32af429280ad5a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6bb832dac8163b344f5be5eda23f24ff0f901242af6886b5cffcbceabd1938248867adb4c75539ab8eb666348a5f97cfc94451f4ffb6350cefad86c1d9a319a0
|
7
|
+
data.tar.gz: c4b59eeeedab80b5964c544e05e566fec55e21269b0d68c2c68efab95c42e2bb162b2d8073b2fbeac7339fd0e52034cf315834cec33d9ec7baa53aff667a63b3
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2017 Masataka Kuwabara
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
@@ -0,0 +1,24 @@
|
|
1
|
+
https://github.com/soutaro/strong_json
|
2
|
+
|
3
|
+
Copyright (c) 2014 Soutaro Matsumoto
|
4
|
+
|
5
|
+
MIT License
|
6
|
+
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
a copy of this software and associated documentation files (the
|
9
|
+
"Software"), to deal in the Software without restriction, including
|
10
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
the following conditions:
|
14
|
+
|
15
|
+
The above copyright notice and this permission notice shall be
|
16
|
+
included in all copies or substantial portions of the Software.
|
17
|
+
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# MightyJSON
|
2
|
+
|
3
|
+
A faster implementation of [soutaro/strong_json](https://github.com/soutaro/strong_json).
|
4
|
+
|
5
|
+
## Benchmarking
|
6
|
+
|
7
|
+
- In complex case, MightyJSON is faster 2.8x than StrongJSON ([code](https://github.com/pocke/mighty_json/blob/master/benchmarks/large.rb)).
|
8
|
+
- In simple case, MightyJSON is faster around 2x ~ 10x than StrongJSON ([code](https://github.com/pocke/mighty_json/blob/master/benchmarks/small.rb)).
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'mighty_json'
|
16
|
+
```
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install mighty_json
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
See [soutaro/strong_json](https://github.com/soutaro/strong_json/blob/master/README.md).
|
29
|
+
|
30
|
+
## Compatibility
|
31
|
+
|
32
|
+
MightyJSON does not have the following methods.
|
33
|
+
|
34
|
+
- `Type::Object#merge`
|
35
|
+
- `Type::Object#except`
|
36
|
+
- `Type::*#coerce`
|
37
|
+
- `Type::*#===`
|
38
|
+
- `Type::*#=~`
|
39
|
+
|
40
|
+
MightyJSON does not support literal type that is not serializable by `Object#inspect` method.
|
41
|
+
|
42
|
+
|
43
|
+
## License
|
44
|
+
|
45
|
+
See [LICENSE.txt](https://github.com/pocke/mighty_json/blob/master/LICENSE.txt) and [LICENSE.txt.original](https://github.com/pocke/mighty_json/blob/master/LICENSE.txt.original).
|
46
|
+
This code bases [soutaro/strong_json](https://github.com/soutaro/strong_json).
|
data/Rakefile
ADDED
data/benchmarks/large.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Usage:
|
2
|
+
# # The json is result of RuboCop in Readmine project
|
3
|
+
# $ ruby large.rb json
|
4
|
+
#
|
5
|
+
# Result:
|
6
|
+
# user system total real
|
7
|
+
# StrongJSON 11.140000 0.020000 11.160000 ( 11.152806)
|
8
|
+
# MightyJSON 4.340000 0.010000 4.350000 ( 4.355610)
|
9
|
+
#
|
10
|
+
# Environment:
|
11
|
+
# - StrongJSON: v0.4.0
|
12
|
+
# - MightyJSON: 7c4c34fff3e24a9573bc8397985d4080eee87359
|
13
|
+
# - Ruby 2.4.1
|
14
|
+
# - Arch Linux
|
15
|
+
# - Core i7-6700K
|
16
|
+
|
17
|
+
require 'json'
|
18
|
+
require 'mighty_json'
|
19
|
+
require 'strong_json'
|
20
|
+
require 'benchmark'
|
21
|
+
|
22
|
+
s = StrongJSON.new do
|
23
|
+
let :metadata, object(rubocop_version: string, ruby_engine: string, ruby_version: string, ruby_patchlevel: string, ruby_platform: string)
|
24
|
+
let :offense, object(severity: string, message: string, cop_name: string, corrected: boolean, location: object(line: number, column: number, length: number))
|
25
|
+
let :file, object(path: string, offenses: array(offense))
|
26
|
+
let :summary, object(offense_count: number, target_file_count: number, inspected_file_count: number)
|
27
|
+
|
28
|
+
let :o, object(metadata: metadata, files: array(file), summary: summary)
|
29
|
+
end
|
30
|
+
|
31
|
+
m = MightyJSON.new do
|
32
|
+
let :metadata, object(rubocop_version: string, ruby_engine: string, ruby_version: string, ruby_patchlevel: string, ruby_platform: string)
|
33
|
+
let :offense, object(severity: string, message: string, cop_name: string, corrected: boolean, location: object(line: number, column: number, length: number))
|
34
|
+
let :file, object(path: string, offenses: array(offense))
|
35
|
+
let :summary, object(offense_count: number, target_file_count: number, inspected_file_count: number)
|
36
|
+
|
37
|
+
let :o, object(metadata: metadata, files: array(file), summary: summary)
|
38
|
+
end
|
39
|
+
|
40
|
+
data = JSON.parse ARGF.read, symbolize_names: true
|
41
|
+
|
42
|
+
raise 'Invalid' if s.o.coerce(data) != m.o.coerce(data)
|
43
|
+
|
44
|
+
Benchmark.bm(20) do |x|
|
45
|
+
x.report('StrongJSON'){20.times{s.o.coerce(data)}}
|
46
|
+
x.report('MightyJSON'){20.times{m.o.coerce(data)}}
|
47
|
+
end
|
data/benchmarks/small.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# Usage:
|
2
|
+
# $ ruby small.rb
|
3
|
+
#
|
4
|
+
# Result:
|
5
|
+
# user system total real
|
6
|
+
# StrongJSON:object 3.500000 0.000000 3.500000 ( 3.498456)
|
7
|
+
# MightyJSON:object 1.740000 0.000000 1.740000 ( 1.737220)
|
8
|
+
# StrongJSON:string 1.960000 0.000000 1.960000 ( 1.962529)
|
9
|
+
# MightyJSON:string 0.450000 0.000000 0.450000 ( 0.447002)
|
10
|
+
# StrongJSON:array 2.290000 0.000000 2.290000 ( 2.295061)
|
11
|
+
# MightyJSON:array 1.130000 0.000000 1.130000 ( 1.127846)
|
12
|
+
# StrongJSON:enum 5.560000 0.000000 5.560000 ( 5.564537)
|
13
|
+
# MightyJSON:enum 2.920000 0.000000 2.920000 ( 2.913830)
|
14
|
+
# StrongJSON:optional 2.080000 0.000000 2.080000 ( 2.081410)
|
15
|
+
# MightyJSON:optional 0.180000 0.000000 0.180000 ( 0.185282)
|
16
|
+
#
|
17
|
+
# Environment:
|
18
|
+
# - StrongJSON: v0.4.0
|
19
|
+
# - MightyJSON: 7c4c34fff3e24a9573bc8397985d4080eee87359
|
20
|
+
# - Ruby 2.4.1
|
21
|
+
# - Arch Linux
|
22
|
+
# - Core i7-6700K
|
23
|
+
|
24
|
+
require 'mighty_json'
|
25
|
+
require 'strong_json'
|
26
|
+
require 'benchmark'
|
27
|
+
|
28
|
+
s = StrongJSON.new do
|
29
|
+
let :obj, object(a: string, b: number)
|
30
|
+
let :str, string
|
31
|
+
let :arr, array(string)
|
32
|
+
let :enm, enum(string, number)
|
33
|
+
let :opt, string?
|
34
|
+
end
|
35
|
+
|
36
|
+
m = MightyJSON.new do
|
37
|
+
let :obj, object(a: string, b: number)
|
38
|
+
let :str, string
|
39
|
+
let :arr, array(string)
|
40
|
+
let :enm, enum(string, number)
|
41
|
+
let :opt, string?
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
Benchmark.bm(20) do |x|
|
46
|
+
x.report('StrongJSON:object') {2000000.times{s.obj.coerce({a: 'foo', b: 42})}}
|
47
|
+
x.report('MightyJSON:object') {2000000.times{m.obj.coerce({a: 'foo', b: 42})}}
|
48
|
+
|
49
|
+
x.report('StrongJSON:string') {5000000.times{s.str.coerce('foo')}}
|
50
|
+
x.report('MightyJSON:string') {5000000.times{m.str.coerce('foo')}}
|
51
|
+
|
52
|
+
x.report('StrongJSON:array') {2000000.times{s.arr.coerce(['foo', 'bar'])}}
|
53
|
+
x.report('MightyJSON:array') {2000000.times{m.arr.coerce(['foo', 'bar'])}}
|
54
|
+
|
55
|
+
x.report('StrongJSON:enum') {2000000.times{s.enm.coerce(1)}}
|
56
|
+
x.report('MightyJSON:enum') {2000000.times{m.enm.coerce(1)}}
|
57
|
+
|
58
|
+
x.report('StrongJSON:optional'){2000000.times{s.enm.coerce('foo')}}
|
59
|
+
x.report('MightyJSON:optional'){2000000.times{m.enm.coerce('foo')}}
|
60
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "mighty_json"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require "mighty_json/version"
|
2
|
+
|
3
|
+
module MightyJSON
|
4
|
+
class Builder
|
5
|
+
NONE = Type::NONE
|
6
|
+
def initialize(&block)
|
7
|
+
@lets = {}
|
8
|
+
instance_eval(&block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def compile💪💪💪
|
12
|
+
Struct.new(*@lets.keys).new(
|
13
|
+
*@lets.values.map do |type|
|
14
|
+
Object.new.tap do |this|
|
15
|
+
var = Variable.new
|
16
|
+
v = var.cur
|
17
|
+
eval <<~END
|
18
|
+
def this.coerce(#{v})
|
19
|
+
#{type.compile(var: var, path: [])}
|
20
|
+
end
|
21
|
+
|
22
|
+
def this.=~(value)
|
23
|
+
coerce(value)
|
24
|
+
true
|
25
|
+
rescue Error, IllegalTypeError, UnexpectedFieldError
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
def this.===(value)
|
30
|
+
coerce(value)
|
31
|
+
true
|
32
|
+
rescue Error, IllegalTypeError, UnexpectedFieldError
|
33
|
+
false
|
34
|
+
end
|
35
|
+
END
|
36
|
+
end
|
37
|
+
end
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def let(name, type)
|
42
|
+
@lets[name] = type
|
43
|
+
define_singleton_method(name) { type }
|
44
|
+
end
|
45
|
+
|
46
|
+
include MightyJSON::Types
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module MightyJSON
|
2
|
+
class Error < StandardError
|
3
|
+
attr_reader :path, :type, :value
|
4
|
+
|
5
|
+
def initialize(path:, type:, value:)
|
6
|
+
@path = path
|
7
|
+
@type = type
|
8
|
+
@value = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
position = path.empty? ? "" : " at .#{path.join('.')}"
|
13
|
+
"Expected type of value #{value}#{position} is #{type}"
|
14
|
+
end
|
15
|
+
alias message to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
class IllegalTypeError < StandardError
|
19
|
+
attr_reader :type
|
20
|
+
|
21
|
+
def initialize(type:)
|
22
|
+
@type = type
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
"#{type} can not be put on toplevel"
|
27
|
+
end
|
28
|
+
alias message to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
class UnexpectedFieldError < StandardError
|
32
|
+
attr_reader :path, :value
|
33
|
+
|
34
|
+
def initialize(path: , value:)
|
35
|
+
@path = path
|
36
|
+
@value = value
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
position = "#{path.join('.')}"
|
41
|
+
"Unexpected field of #{position} (#{value})"
|
42
|
+
end
|
43
|
+
alias message to_s
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
module MightyJSON
|
2
|
+
module Type
|
3
|
+
NONE = Object.new
|
4
|
+
|
5
|
+
class Base
|
6
|
+
def initialize(type)
|
7
|
+
@type = type
|
8
|
+
end
|
9
|
+
|
10
|
+
def compile(var:, path:)
|
11
|
+
v = var.cur
|
12
|
+
err = "(raise Error.new(value: #{v}, type: #{self.to_s.inspect}, path: #{path.inspect}))"
|
13
|
+
case @type
|
14
|
+
when :ignored
|
15
|
+
if path == []
|
16
|
+
'raise IllegalTypeError.new(type: :ignored)'
|
17
|
+
else
|
18
|
+
'NONE'
|
19
|
+
end
|
20
|
+
when :any
|
21
|
+
v
|
22
|
+
when :number
|
23
|
+
<<~END
|
24
|
+
#{v}.is_a?(Numeric) ?
|
25
|
+
#{v} :
|
26
|
+
#{err}
|
27
|
+
END
|
28
|
+
when :string
|
29
|
+
<<~END
|
30
|
+
#{v}.is_a?(String) ?
|
31
|
+
#{v} :
|
32
|
+
#{err}
|
33
|
+
END
|
34
|
+
when :boolean
|
35
|
+
<<~END
|
36
|
+
#{v} == true || #{v} == false ?
|
37
|
+
#{v} :
|
38
|
+
#{err}
|
39
|
+
END
|
40
|
+
when :numeric
|
41
|
+
<<~END
|
42
|
+
#{v}.is_a?(Numeric) || (#{v}.is_a?(String) && /\\A[-+]?[\\d.]+\\Z/ =~ #{v}) ?
|
43
|
+
#{v} :
|
44
|
+
#{err}
|
45
|
+
END
|
46
|
+
when :symbol
|
47
|
+
<<~END
|
48
|
+
#{v}.is_a?(Symbol) ?
|
49
|
+
#{v} :
|
50
|
+
#{err}
|
51
|
+
END
|
52
|
+
else
|
53
|
+
err
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_s
|
58
|
+
@type.to_s
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Optional
|
63
|
+
def initialize(type)
|
64
|
+
@type = type
|
65
|
+
end
|
66
|
+
|
67
|
+
def compile(var:, path:)
|
68
|
+
v = var.cur
|
69
|
+
<<~END
|
70
|
+
if #{v}.nil? || NONE.equal?(#{v})
|
71
|
+
nil
|
72
|
+
else
|
73
|
+
#{@type.compile(var: var, path: path)}
|
74
|
+
end
|
75
|
+
END
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_s
|
79
|
+
"optinal(#{@type})"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class Literal
|
84
|
+
def initialize(value)
|
85
|
+
@value = value
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_s
|
89
|
+
"literal(#{@value})"
|
90
|
+
end
|
91
|
+
|
92
|
+
def compile(var:, path:)
|
93
|
+
v = var.cur
|
94
|
+
<<~END
|
95
|
+
raise Error.new(path: #{path.inspect}, type: #{self.to_s.inspect}, value: #{v}) unless #{@value.inspect} == #{v}
|
96
|
+
#{v}
|
97
|
+
END
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class Array
|
102
|
+
def initialize(type)
|
103
|
+
@type = type
|
104
|
+
end
|
105
|
+
|
106
|
+
def compile(var:, path:)
|
107
|
+
v = var.cur
|
108
|
+
child = var.next
|
109
|
+
<<~END
|
110
|
+
begin
|
111
|
+
raise Error.new(path: #{path.inspect}, type: #{self.to_s.inspect}, value: #{v}) unless #{v}.is_a?(::Array)
|
112
|
+
#{v}.map.with_index do |#{child}, i|
|
113
|
+
#{@type.compile(var: var, path: path + [:array_index])} # TODO: optimize path
|
114
|
+
end
|
115
|
+
end
|
116
|
+
END
|
117
|
+
end
|
118
|
+
|
119
|
+
def to_s
|
120
|
+
"array(#{@type})"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class Object
|
125
|
+
def initialize(fields)
|
126
|
+
@fields = fields
|
127
|
+
end
|
128
|
+
|
129
|
+
def compile(var:, path:)
|
130
|
+
v = var.cur
|
131
|
+
keys = @fields.keys.map(&:inspect)
|
132
|
+
|
133
|
+
result = var.next
|
134
|
+
<<~END
|
135
|
+
begin
|
136
|
+
raise Error.new(path: #{path}, type: #{self.to_s.inspect}, value: object) unless #{v}.is_a?(Hash)
|
137
|
+
|
138
|
+
{}.tap do |#{result}|
|
139
|
+
#{v}.each do |key, value|
|
140
|
+
next if #{keys.map{|key| "#{key} == key"}.join('||')}
|
141
|
+
raise UnexpectedFieldError.new(path: #{path.inspect} + [key], value: #{v}) # TOOD: optimize path
|
142
|
+
end
|
143
|
+
|
144
|
+
#{
|
145
|
+
@fields.map do |key, type|
|
146
|
+
new_var = var.next
|
147
|
+
<<~END2
|
148
|
+
#{new_var} = #{v}.key?(#{key.inspect}) ? #{v}[#{key.inspect}] : NONE
|
149
|
+
v = #{type.compile(var: var, path: path + [key])}
|
150
|
+
if !NONE.equal?(v) &&
|
151
|
+
#{!NONE.equal?(type)} &&
|
152
|
+
!(#{type.is_a?(Optional)} && NONE.equal?(#{new_var}))
|
153
|
+
#{result}[#{key.inspect}] = v
|
154
|
+
end
|
155
|
+
END2
|
156
|
+
end.join("\n")
|
157
|
+
}
|
158
|
+
end
|
159
|
+
end
|
160
|
+
END
|
161
|
+
end
|
162
|
+
|
163
|
+
def to_s
|
164
|
+
fields = []
|
165
|
+
|
166
|
+
@fields.each do |name, type|
|
167
|
+
fields << "#{name}: #{type}"
|
168
|
+
end
|
169
|
+
|
170
|
+
"object(#{fields.join(', ')})"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
class Enum
|
175
|
+
attr_reader :types
|
176
|
+
|
177
|
+
def initialize(types)
|
178
|
+
@types = types
|
179
|
+
end
|
180
|
+
|
181
|
+
def compile(var:, path:)
|
182
|
+
v = var.cur
|
183
|
+
<<~END
|
184
|
+
#{@types.map do |type|
|
185
|
+
<<~END2.chomp
|
186
|
+
begin
|
187
|
+
#{type.compile(var: var, path: path)}
|
188
|
+
rescue Error, UnexpectedFieldError, IllegalTypeError
|
189
|
+
end
|
190
|
+
END2
|
191
|
+
end.join(' || ')} || (raise Error.new(path: #{path.inspect}, type: #{self.to_s.inspect}, value: #{v}))
|
192
|
+
END
|
193
|
+
end
|
194
|
+
|
195
|
+
def to_s
|
196
|
+
"enum(#{types.map(&:to_s).join(", ")})"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
class UnexpectedFieldError < StandardError
|
201
|
+
attr_reader :path, :value
|
202
|
+
|
203
|
+
def initialize(path: , value:)
|
204
|
+
@path = path
|
205
|
+
@value = value
|
206
|
+
end
|
207
|
+
|
208
|
+
def to_s
|
209
|
+
position = "#{path.join('.')}"
|
210
|
+
"Unexpected field of #{position} (#{value})"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
class IllegalTypeError < StandardError
|
215
|
+
attr_reader :type
|
216
|
+
|
217
|
+
def initialize(type:)
|
218
|
+
@type = type
|
219
|
+
end
|
220
|
+
|
221
|
+
def to_s
|
222
|
+
"#{type} can not be put on toplevel"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
class Error < StandardError
|
227
|
+
attr_reader :path, :type, :value
|
228
|
+
|
229
|
+
def initialize(path:, type:, value:)
|
230
|
+
@path = path
|
231
|
+
@type = type
|
232
|
+
@value = value
|
233
|
+
end
|
234
|
+
|
235
|
+
def to_s
|
236
|
+
position = path.empty? ? "" : " at .#{path.join('.')}"
|
237
|
+
"Expected type of value #{value}#{position} is #{type}"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module MightyJSON
|
2
|
+
module Types
|
3
|
+
def object(fields = {})
|
4
|
+
Type::Object.new(fields)
|
5
|
+
end
|
6
|
+
|
7
|
+
def array(type = any)
|
8
|
+
Type::Array.new(type)
|
9
|
+
end
|
10
|
+
|
11
|
+
def optional(type = any)
|
12
|
+
Type::Optional.new(type)
|
13
|
+
end
|
14
|
+
|
15
|
+
def string
|
16
|
+
MightyJSON::Type::Base.new(:string)
|
17
|
+
end
|
18
|
+
|
19
|
+
def numeric
|
20
|
+
MightyJSON::Type::Base.new(:numeric)
|
21
|
+
end
|
22
|
+
|
23
|
+
def number
|
24
|
+
MightyJSON::Type::Base.new(:number)
|
25
|
+
end
|
26
|
+
|
27
|
+
def boolean
|
28
|
+
MightyJSON::Type::Base.new(:boolean)
|
29
|
+
end
|
30
|
+
|
31
|
+
def any
|
32
|
+
MightyJSON::Type::Base.new(:any)
|
33
|
+
end
|
34
|
+
|
35
|
+
def prohibited
|
36
|
+
MightyJSON::Type::Base.new(:prohibited)
|
37
|
+
end
|
38
|
+
|
39
|
+
def symbol
|
40
|
+
MightyJSON::Type::Base.new(:symbol)
|
41
|
+
end
|
42
|
+
|
43
|
+
def literal(value)
|
44
|
+
MightyJSON::Type::Literal.new(value)
|
45
|
+
end
|
46
|
+
|
47
|
+
def enum(*types)
|
48
|
+
MightyJSON::Type::Enum.new(types)
|
49
|
+
end
|
50
|
+
|
51
|
+
def string?
|
52
|
+
optional(string)
|
53
|
+
end
|
54
|
+
|
55
|
+
def numeric?
|
56
|
+
optional(numeric)
|
57
|
+
end
|
58
|
+
|
59
|
+
def number?
|
60
|
+
optional(number)
|
61
|
+
end
|
62
|
+
|
63
|
+
def boolean?
|
64
|
+
optional(boolean)
|
65
|
+
end
|
66
|
+
|
67
|
+
def symbol?
|
68
|
+
optional(symbol)
|
69
|
+
end
|
70
|
+
|
71
|
+
def ignored
|
72
|
+
MightyJSON::Type::Base.new(:ignored)
|
73
|
+
end
|
74
|
+
|
75
|
+
def array?(ty)
|
76
|
+
optional(array(ty))
|
77
|
+
end
|
78
|
+
|
79
|
+
def object?(fields)
|
80
|
+
optional(object(fields))
|
81
|
+
end
|
82
|
+
|
83
|
+
def literal?(value)
|
84
|
+
optional(literal(value))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/mighty_json.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "mighty_json/version"
|
2
|
+
require 'mighty_json/errors'
|
3
|
+
require 'mighty_json/type'
|
4
|
+
require 'mighty_json/types'
|
5
|
+
require 'mighty_json/builder'
|
6
|
+
require 'mighty_json/variable'
|
7
|
+
|
8
|
+
module MightyJSON
|
9
|
+
def self.new(&block)
|
10
|
+
Builder.new(&block).compile💪💪💪
|
11
|
+
end
|
12
|
+
end
|
data/mighty_json.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "mighty_json/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "mighty_json"
|
8
|
+
spec.version = MightyJSON::VERSION
|
9
|
+
spec.authors = ["Masataka Kuwabara"]
|
10
|
+
spec.email = ["kuwabara@pocke.me"]
|
11
|
+
|
12
|
+
spec.summary = %q{A faster implementation of soutaro/strong_json}
|
13
|
+
spec.description = %q{A faster implementation of soutaro/strong_json}
|
14
|
+
spec.homepage = "https://github.com/pocke/mighty_json"
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.required_ruby_version = '>= 2.3.0'
|
18
|
+
|
19
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
20
|
+
f.match(%r{^(test|spec|features)/})
|
21
|
+
end
|
22
|
+
spec.bindir = "exe"
|
23
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.15"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
|
29
|
+
spec.add_development_dependency 'minitest'
|
30
|
+
spec.add_development_dependency 'minitest-power_assert'
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mighty_json
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Masataka Kuwabara
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-05-25 00:00:00.000000000 Z
|
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.15'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.15'
|
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: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest-power_assert
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: A faster implementation of soutaro/strong_json
|
70
|
+
email:
|
71
|
+
- kuwabara@pocke.me
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".travis.yml"
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE.txt
|
80
|
+
- LICENSE.txt.original
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- benchmarks/large.rb
|
84
|
+
- benchmarks/small.rb
|
85
|
+
- bin/console
|
86
|
+
- bin/setup
|
87
|
+
- lib/mighty_json.rb
|
88
|
+
- lib/mighty_json/builder.rb
|
89
|
+
- lib/mighty_json/errors.rb
|
90
|
+
- lib/mighty_json/type.rb
|
91
|
+
- lib/mighty_json/types.rb
|
92
|
+
- lib/mighty_json/variable.rb
|
93
|
+
- lib/mighty_json/version.rb
|
94
|
+
- mighty_json.gemspec
|
95
|
+
homepage: https://github.com/pocke/mighty_json
|
96
|
+
licenses:
|
97
|
+
- MIT
|
98
|
+
metadata: {}
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: 2.3.0
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
requirements: []
|
114
|
+
rubyforge_project:
|
115
|
+
rubygems_version: 2.6.10
|
116
|
+
signing_key:
|
117
|
+
specification_version: 4
|
118
|
+
summary: A faster implementation of soutaro/strong_json
|
119
|
+
test_files: []
|