jiffy 1.0.1 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +15 -3
- data/bin/jiffy +7 -12
- data/jiffy.gemspec +2 -1
- data/lib/jiffy/array_mimicking_io.rb +37 -0
- data/lib/jiffy/parsers/json.rb +10 -12
- data/lib/jiffy/parsers/json.rl +7 -9
- data/lib/jiffy/version.rb +1 -1
- data/lib/jiffy.rb +49 -6
- data/test/jiffy_test.rb +103 -17
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dff94c8019e36bd2ba0d85e47a6dc95a31d701c5
|
4
|
+
data.tar.gz: 0e018b5041a4dd2da6748e546524ccd795132b0e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1b8f39061d4c55c291d56a3c074b29adcbbc8843fd67c82034f6ef6f1b5df6a98d1d676c66b684416e5beaae384a853a14ad6858ed302c85d81df2467fe1adae
|
7
|
+
data.tar.gz: edd9b3fe0cb7b7b1bf1db304e8106d953a7b12f0de66879f05ded532e9e137efd28ad30ce1bd5fd95c1dceb13467a50862417fe8b50db7a8afe337677c059dab
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -20,13 +20,13 @@ $ gem install jiffy
|
|
20
20
|
It can be installed system-wide using the following options.
|
21
21
|
|
22
22
|
```
|
23
|
-
$ gem install --no-user-install -i "$(ruby -e'puts Gem.default_dir')" -n /usr/bin jiffy
|
23
|
+
$ gem install --no-user-install -i "$(ruby -e'puts Gem.default_dir')" -n /usr/local/bin jiffy
|
24
24
|
```
|
25
25
|
|
26
26
|
### Arch Linux
|
27
27
|
|
28
28
|
```
|
29
|
-
$ yaourt -
|
29
|
+
$ yaourt -S ruby-jiffy
|
30
30
|
```
|
31
31
|
|
32
32
|
## Usage
|
@@ -104,11 +104,23 @@ Tested against the following Ruby versions.
|
|
104
104
|
|
105
105
|
## Changelog
|
106
106
|
|
107
|
+
### 1.0.3
|
108
|
+
|
109
|
+
* Fixed an issue with the gemspec.
|
110
|
+
|
111
|
+
### 1.0.2
|
112
|
+
|
113
|
+
* Missing read permissions is now handled properly.
|
114
|
+
* Non-existing file as argument is now handled properly.
|
115
|
+
* Directory as argument is now handled properly.
|
116
|
+
* Output now always ends with a newline.
|
117
|
+
* #read is switched out in favor of #readpartial.
|
118
|
+
|
107
119
|
### 1.0.1
|
108
120
|
|
109
121
|
* The application is renamed Jiffy.
|
110
122
|
* The executable may now read from standard input.
|
111
|
-
* SIGTERM and SIGINT is now handled properly and a ruby stacktrace is
|
123
|
+
* SIGTERM and SIGINT is now handled properly and a ruby stacktrace is not shown.
|
112
124
|
|
113
125
|
### 1.0.0
|
114
126
|
|
data/bin/jiffy
CHANGED
@@ -32,15 +32,10 @@ Signal.trap("TERM") do
|
|
32
32
|
exit 1
|
33
33
|
end
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
$stderr.puts e
|
43
|
-
exit! 1
|
44
|
-
rescue Errno::EPIPE
|
45
|
-
# Do nothing
|
46
|
-
end
|
35
|
+
result = if ARGV.empty?
|
36
|
+
Jiffy.new(in: STDIN).cl_format(err: STDERR)
|
37
|
+
else
|
38
|
+
Jiffy.new(in: ARGF).cl_format(err: STDERR)
|
39
|
+
end
|
40
|
+
|
41
|
+
exit 1 unless result
|
data/jiffy.gemspec
CHANGED
@@ -4,7 +4,7 @@ Gem::Specification.new do |s|
|
|
4
4
|
s.name = 'jiffy'
|
5
5
|
s.version = Jiffy::VERSION
|
6
6
|
s.license = 'MIT'
|
7
|
-
s.date = '2014-12-
|
7
|
+
s.date = '2014-12-14'
|
8
8
|
|
9
9
|
s.summary = 'A streaming-based JSON formatter in Ruby.'
|
10
10
|
s.description = 'Jiffy utilizes Ragel in order to parse and continuously format JSON data. This allows it to achieve a constant memory usage, independent of the input size.'
|
@@ -30,6 +30,7 @@ Gem::Specification.new do |s|
|
|
30
30
|
lib/jiffy/parsers/json_string.rl
|
31
31
|
lib/jiffy/parsers/json_value.rb
|
32
32
|
lib/jiffy/parsers/json_value.rl
|
33
|
+
lib/jiffy/array_mimicking_io.rb
|
33
34
|
lib/jiffy/json_outputter.rb
|
34
35
|
lib/jiffy/version.rb
|
35
36
|
lib/jiffy.rb
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class Jiffy
|
2
|
+
class ArrayMimickingIO
|
3
|
+
CHUNK_SIZE = 1_000_000 # 1 MB
|
4
|
+
|
5
|
+
attr_accessor :io, :chunk, :bytes_read
|
6
|
+
|
7
|
+
def initialize(io)
|
8
|
+
@io = io
|
9
|
+
@chunk = []
|
10
|
+
@bytes_read = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](nth_byte)
|
14
|
+
read_chunk unless has_nth_byte?(nth_byte)
|
15
|
+
|
16
|
+
fetch_nth_byte(nth_byte)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def fetch_nth_byte(nth_byte)
|
22
|
+
index = nth_byte - bytes_read + chunk.length
|
23
|
+
|
24
|
+
chunk[index]
|
25
|
+
end
|
26
|
+
|
27
|
+
def has_nth_byte?(nth_byte)
|
28
|
+
bytes_read > nth_byte
|
29
|
+
end
|
30
|
+
|
31
|
+
def read_chunk
|
32
|
+
@chunk = @io.readpartial(CHUNK_SIZE).codepoints
|
33
|
+
|
34
|
+
@bytes_read += @chunk.length
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/jiffy/parsers/json.rb
CHANGED
@@ -121,9 +121,9 @@ self.json_en_main = 1;
|
|
121
121
|
def parse_json
|
122
122
|
pe = :ignored
|
123
123
|
eof = :ignored
|
124
|
-
leftover = []
|
125
124
|
|
126
|
-
|
125
|
+
begin
|
126
|
+
|
127
127
|
# line 128 "json.rb"
|
128
128
|
begin
|
129
129
|
p ||= 0
|
@@ -132,14 +132,8 @@ begin
|
|
132
132
|
end
|
133
133
|
|
134
134
|
# line 43 "json.rl"
|
135
|
-
|
136
|
-
while chunk = io.read(1_000_000)
|
137
|
-
self.data = leftover + chunk.unpack("c*")
|
138
|
-
p ||= 0
|
139
|
-
pe = data.length
|
140
|
-
|
141
135
|
|
142
|
-
# line
|
136
|
+
# line 137 "json.rb"
|
143
137
|
begin
|
144
138
|
_klen, _trans, _keys, _acts, _nacts = nil
|
145
139
|
_goto_level = 0
|
@@ -256,7 +250,7 @@ when 1 then
|
|
256
250
|
|
257
251
|
end
|
258
252
|
end
|
259
|
-
# line
|
253
|
+
# line 254 "json.rb"
|
260
254
|
end # action switch
|
261
255
|
end
|
262
256
|
end
|
@@ -283,10 +277,14 @@ when 1 then
|
|
283
277
|
end
|
284
278
|
end
|
285
279
|
|
286
|
-
# line
|
280
|
+
# line 44 "json.rl"
|
281
|
+
rescue EOFError
|
282
|
+
if p < data.bytes_read || data.bytes_read == 0
|
283
|
+
raise UnexpectedEndError, 'Unexpected end of input'
|
284
|
+
end
|
287
285
|
end
|
288
286
|
|
289
|
-
raise_unparseable p unless p ==
|
287
|
+
raise_unparseable p unless p == data.bytes_read
|
290
288
|
end
|
291
289
|
end
|
292
290
|
end
|
data/lib/jiffy/parsers/json.rl
CHANGED
@@ -37,19 +37,17 @@ class Jiffy
|
|
37
37
|
def parse_json
|
38
38
|
pe = :ignored
|
39
39
|
eof = :ignored
|
40
|
-
leftover = []
|
41
|
-
|
42
|
-
%% write init;
|
43
|
-
|
44
|
-
while chunk = io.read(1_000_000)
|
45
|
-
self.data = leftover + chunk.unpack("c*")
|
46
|
-
p ||= 0
|
47
|
-
pe = data.length
|
48
40
|
|
41
|
+
begin
|
42
|
+
%% write init;
|
49
43
|
%% write exec;
|
44
|
+
rescue EOFError
|
45
|
+
if p < data.bytes_read || data.bytes_read == 0
|
46
|
+
raise UnexpectedEndError, 'Unexpected end of input'
|
47
|
+
end
|
50
48
|
end
|
51
49
|
|
52
|
-
raise_unparseable p unless p ==
|
50
|
+
raise_unparseable p unless p == data.bytes_read
|
53
51
|
end
|
54
52
|
end
|
55
53
|
end
|
data/lib/jiffy/version.rb
CHANGED
data/lib/jiffy.rb
CHANGED
@@ -4,6 +4,7 @@ require 'jiffy/parsers/json_float'
|
|
4
4
|
require 'jiffy/parsers/json_object'
|
5
5
|
require 'jiffy/parsers/json_string'
|
6
6
|
require 'jiffy/parsers/json_value'
|
7
|
+
require 'jiffy/array_mimicking_io'
|
7
8
|
require 'jiffy/json_outputter'
|
8
9
|
|
9
10
|
class Jiffy
|
@@ -29,22 +30,64 @@ class Jiffy
|
|
29
30
|
def initialize(options = {})
|
30
31
|
if options[:in].is_a?(String)
|
31
32
|
@io = File.open(options[:in])
|
32
|
-
elsif options[:in].respond_to?(:
|
33
|
+
elsif options[:in].respond_to?(:readpartial)
|
33
34
|
@io = options[:in]
|
34
35
|
else
|
35
36
|
raise ArgumentError, 'Invalid input source'
|
36
37
|
end
|
37
38
|
|
39
|
+
@data = ArrayMimickingIO.new(@io)
|
40
|
+
|
38
41
|
@outputter = JsonOutputter.new(options)
|
39
42
|
end
|
40
43
|
|
41
|
-
|
44
|
+
def cl_format(options = {})
|
45
|
+
format
|
42
46
|
|
43
|
-
|
44
|
-
|
45
|
-
|
47
|
+
@outputter.t :char, "\n"
|
48
|
+
|
49
|
+
true
|
50
|
+
rescue Errno::EACCES
|
51
|
+
err = options[:err] || $stderr
|
52
|
+
|
53
|
+
if @io.respond_to? :filename
|
54
|
+
err.write "jiffy: #{@io.filename}: Permission denied\n"
|
46
55
|
else
|
47
|
-
|
56
|
+
err.write "jiffy: Permission denied\n"
|
48
57
|
end
|
58
|
+
|
59
|
+
false
|
60
|
+
rescue Errno::ENOENT
|
61
|
+
err = options[:err] || $stderr
|
62
|
+
|
63
|
+
if @io.respond_to? :filename
|
64
|
+
err.write "jiffy: #{@io.filename}: No such file or directory\n"
|
65
|
+
else
|
66
|
+
err.write "jiffy: No such file or directory\n"
|
67
|
+
end
|
68
|
+
|
69
|
+
false
|
70
|
+
rescue Errno::EISDIR
|
71
|
+
err = options[:err] || $stderr
|
72
|
+
|
73
|
+
if @io.respond_to? :filename
|
74
|
+
err.write "jiffy: #{@io.filename}: Is a directory\n"
|
75
|
+
else
|
76
|
+
err.write "jiffy: Is a directory\n"
|
77
|
+
end
|
78
|
+
|
79
|
+
false
|
80
|
+
rescue UnexpectedEndError, UnparseableError => e
|
81
|
+
err = options[:err] || $stderr
|
82
|
+
|
83
|
+
err.write e.message << "\n"
|
84
|
+
|
85
|
+
false
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def raise_unparseable(p)
|
91
|
+
raise UnparseableError, "Unexpected token at position #{p}"
|
49
92
|
end
|
50
93
|
end
|
data/test/jiffy_test.rb
CHANGED
@@ -4,33 +4,85 @@ require 'stringio'
|
|
4
4
|
require 'minitest/autorun'
|
5
5
|
require 'jiffy'
|
6
6
|
|
7
|
+
valid_json = '["Valid JSON"]'
|
8
|
+
invalid_json = '["Invalid" "JSON"]'
|
9
|
+
incomplete_json = '["Incomplete JSON'
|
10
|
+
|
11
|
+
def it_should_properly_handle(exception, options)
|
12
|
+
io = Object.new.tap do |io|
|
13
|
+
io.define_singleton_method :readpartial do |*|
|
14
|
+
raise exception
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should return false upon #{exception.inspect}" do
|
19
|
+
assert_equal false, Jiffy.new(in: io, out: StringIO.new).cl_format(err: StringIO.new)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should write #{options[:with]} to :stderr upon #{exception.inspect}" do
|
23
|
+
err = StringIO.new
|
24
|
+
|
25
|
+
Jiffy.new(in: io, out: StringIO.new).cl_format(err: err)
|
26
|
+
|
27
|
+
assert_includes err.string, options[:with]
|
28
|
+
end
|
29
|
+
|
30
|
+
it ":stderr should end with a newline upon #{exception.inspect}" do
|
31
|
+
err = StringIO.new
|
32
|
+
|
33
|
+
Jiffy.new(in: io, out: StringIO.new).cl_format(err: err)
|
34
|
+
|
35
|
+
assert_equal "\n", err.string[-1]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
7
39
|
describe Jiffy do
|
8
40
|
positive_examples = Dir[File.join(File.dirname(__FILE__), 'positive-examples', '*')]
|
9
41
|
|
10
|
-
|
11
|
-
|
12
|
-
|
42
|
+
describe '#format' do
|
43
|
+
positive_examples.each do |example|
|
44
|
+
it "should format #{File.basename(example)} without raising an exception" do
|
45
|
+
out = StringIO.new
|
13
46
|
|
14
|
-
|
47
|
+
Jiffy.new(in: example, out: out).format
|
15
48
|
|
16
|
-
|
49
|
+
assert_equal(out.string, File.read(example).strip)
|
50
|
+
end
|
17
51
|
end
|
18
|
-
end
|
19
52
|
|
20
|
-
|
53
|
+
negative_examples = Dir[File.join(File.dirname(__FILE__), 'negative-examples', '*')]
|
21
54
|
|
22
|
-
|
23
|
-
|
24
|
-
|
55
|
+
negative_examples.each do |example|
|
56
|
+
it "should raise an exception when formatting #{File.basename(example)}" do
|
57
|
+
out = StringIO.new
|
58
|
+
|
59
|
+
assert_raises Jiffy::UnexpectedEndError, Jiffy::UnparseableError do
|
60
|
+
Jiffy.new(in: example, out: out).format
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
25
64
|
|
26
|
-
|
27
|
-
|
28
|
-
else
|
29
|
-
Jiffy::UnparseableError
|
30
|
-
end
|
65
|
+
it 'should raise UnexpectedEndError on valid, but incomplete JSON input' do
|
66
|
+
example = StringIO.new incomplete_json
|
31
67
|
|
32
|
-
assert_raises
|
33
|
-
Jiffy.new(in: example, out:
|
68
|
+
assert_raises Jiffy::UnexpectedEndError do
|
69
|
+
Jiffy.new(in: example, out: StringIO.new).format
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should raise UnexpectedEndError on empty input' do
|
74
|
+
example = StringIO.new ""
|
75
|
+
|
76
|
+
assert_raises Jiffy::UnexpectedEndError do
|
77
|
+
Jiffy.new(in: example, out: StringIO.new).format
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should raise UnparseableError on invalid JSON input' do
|
82
|
+
example = StringIO.new invalid_json
|
83
|
+
|
84
|
+
assert_raises Jiffy::UnparseableError do
|
85
|
+
Jiffy.new(in: example, out: StringIO.new).format
|
34
86
|
end
|
35
87
|
end
|
36
88
|
end
|
@@ -44,4 +96,38 @@ describe Jiffy do
|
|
44
96
|
end
|
45
97
|
end
|
46
98
|
end
|
99
|
+
|
100
|
+
describe '#cl_format' do
|
101
|
+
it_should_properly_handle Jiffy::UnexpectedEndError.new('Unexpected end of input'), with: 'Unexpected end of input'
|
102
|
+
it_should_properly_handle Jiffy::UnparseableError.new('Unexpected token at position'), with: 'Unexpected token at position'
|
103
|
+
it_should_properly_handle Errno::EACCES, with: 'jiffy: Permission denied'
|
104
|
+
it_should_properly_handle Errno::ENOENT, with: 'jiffy: No such file or directory'
|
105
|
+
it_should_properly_handle Errno::EISDIR, with: 'jiffy: Is a directory'
|
106
|
+
|
107
|
+
it 'should return true upon valid input' do
|
108
|
+
example = StringIO.new valid_json
|
109
|
+
|
110
|
+
assert_equal true, Jiffy.new(in: example, out: StringIO.new).cl_format
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should not write to :stderr upon valid input' do
|
114
|
+
example = StringIO.new valid_json
|
115
|
+
|
116
|
+
err = StringIO.new
|
117
|
+
|
118
|
+
Jiffy.new(in: example, out: StringIO.new).cl_format(err: err)
|
119
|
+
|
120
|
+
assert_equal "", err.string
|
121
|
+
end
|
122
|
+
|
123
|
+
it ':stdout should end with a newline upon valid input' do
|
124
|
+
example = StringIO.new valid_json
|
125
|
+
|
126
|
+
out = StringIO.new
|
127
|
+
|
128
|
+
Jiffy.new(in: example, out: out).cl_format
|
129
|
+
|
130
|
+
assert_equal "\n", out.string[-1]
|
131
|
+
end
|
132
|
+
end
|
47
133
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jiffy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonas Amundsen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -54,6 +54,7 @@ files:
|
|
54
54
|
- bin/jiffy
|
55
55
|
- jiffy.gemspec
|
56
56
|
- lib/jiffy.rb
|
57
|
+
- lib/jiffy/array_mimicking_io.rb
|
57
58
|
- lib/jiffy/json_outputter.rb
|
58
59
|
- lib/jiffy/parsers/json.rb
|
59
60
|
- lib/jiffy/parsers/json.rl
|