jiffy 1.0.1 → 1.0.3
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/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
|