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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fe32595cfa71cdbb4d6ab8aadab3da303ced671f
4
- data.tar.gz: 3db1aeb55a7e6f78ce15a19d2240e4bcb530134c
3
+ metadata.gz: dff94c8019e36bd2ba0d85e47a6dc95a31d701c5
4
+ data.tar.gz: 0e018b5041a4dd2da6748e546524ccd795132b0e
5
5
  SHA512:
6
- metadata.gz: 4a759a9452c53cdefe2e9d724a53eddb415491104ec8af2c9f1ed1e85d94a6186fdc157352467d61af87052bb283a179d7aaad29bb53e284de0468a19f8f0ac9
7
- data.tar.gz: e51d267574c1953e73b56d9c96050a2b59ef3d121142900306cfb66a420bc87c5fb3feb39917d8e413be4cfba9bc4d098e32f1872d94a0f7c71a32003b911d0b
6
+ metadata.gz: 1b8f39061d4c55c291d56a3c074b29adcbbc8843fd67c82034f6ef6f1b5df6a98d1d676c66b684416e5beaae384a853a14ad6858ed302c85d81df2467fe1adae
7
+ data.tar.gz: edd9b3fe0cb7b7b1bf1db304e8106d953a7b12f0de66879f05ded532e9e137efd28ad30ce1bd5fd95c1dceb13467a50862417fe8b50db7a8afe337677c059dab
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- jiffy (1.0.1)
4
+ jiffy (1.0.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
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 -Syua ruby-jiffy
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 now shown.
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
- begin
36
- if ARGV.empty?
37
- Jiffy.new(in: STDIN).format
38
- else
39
- Jiffy.new(in: ARGF).format
40
- end
41
- rescue Jiffy::UnparseableError => e
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-03'
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
@@ -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 143 "json.rb"
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 260 "json.rb"
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 50 "json.rl"
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 == pe
287
+ raise_unparseable p unless p == data.bytes_read
290
288
  end
291
289
  end
292
290
  end
@@ -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 == pe
50
+ raise_unparseable p unless p == data.bytes_read
53
51
  end
54
52
  end
55
53
  end
data/lib/jiffy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Jiffy
2
- VERSION = '1.0.1'
2
+ VERSION = '1.0.3'
3
3
  end
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?(:read)
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
- private
44
+ def cl_format(options = {})
45
+ format
42
46
 
43
- def raise_unparseable(p)
44
- if !@io.closed? && @io.eof? && @data.length == p
45
- raise UnexpectedEndError, 'Unexpected end of input'
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
- raise UnparseableError, "Unexpected token at position #{p}"
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
- positive_examples.each do |example|
11
- it "should correctly format #{File.basename(example)}" do
12
- out = StringIO.new
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
- Jiffy.new(in: example, out: out).format
47
+ Jiffy.new(in: example, out: out).format
15
48
 
16
- assert_equal(out.string, File.read(example).strip)
49
+ assert_equal(out.string, File.read(example).strip)
50
+ end
17
51
  end
18
- end
19
52
 
20
- negative_examples = Dir[File.join(File.dirname(__FILE__), 'negative-examples', '*')]
53
+ negative_examples = Dir[File.join(File.dirname(__FILE__), 'negative-examples', '*')]
21
54
 
22
- negative_examples.each do |example|
23
- it "should not format #{File.basename(example)}" do
24
- out = StringIO.new
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
- expected_exception = if /unclosed/ =~ example
27
- Jiffy::UnexpectedEndError
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 expected_exception do
33
- Jiffy.new(in: example, out: out).format
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.1
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-03 00:00:00.000000000 Z
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