jsonlint 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9440424c5ce5284432200d4275be3ef815c55a87
4
+ data.tar.gz: b85712a8defcd3df4d0846d4993e0a967cc2283d
5
+ SHA512:
6
+ metadata.gz: d2f64c73f7f4e8512dee6abc20228ad68de118a339fd45313766828c118e9bb0f370b067575017566d3138915ca0e6cce644b5db46e0bcba85f47f422585dea3
7
+ data.tar.gz: 1dd3cea2cca3437c3dc766b57d63d79690cdb5fcdb7588c2d071ab2c281431492c18ef079439b596527fb3b844509a81a4113de1a8ffdf73404d3fcd365767fd
@@ -1,5 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.9.3
4
- - 2.0.0
5
- - 2.1.0
3
+ - 2.0.0-p648
4
+ - 2.1.9
5
+ - 2.2.5
6
+ - 2.3.1
data/README.md CHANGED
@@ -1,6 +1,12 @@
1
- # JsonLint
1
+ # jsonlint
2
2
 
3
- TODO: Write a gem description
3
+ [![Build Status](https://travis-ci.org/PagerDuty/jsonlint.svg?branch=master)](https://travis-ci.org/PagerDuty/jsonlint)
4
+ [![Gem Version](http://img.shields.io/gem/v/jsonlint.svg)](https://rubygems.org/gems/jsonlint)
5
+
6
+ jsonlint checks your JSON files for syntax errors or silly mistakes. Currently it checks for:
7
+
8
+ * Valid JSON syntax
9
+ * Overlapping key definitions in JSON files, where the last definition would win
4
10
 
5
11
  ## Installation
6
12
 
@@ -16,13 +22,52 @@ Or install it yourself as:
16
22
 
17
23
  $ gem install jsonlint
18
24
 
19
- ## Usage
25
+ ## CLI Usage
26
+
27
+ You can run jsonlint against a set of files in the command line. Any errors will be printed and the process will exit with a non-zero exit code.
28
+
29
+ ```
30
+ $ jsonlint spec/data/*
31
+ spec/data/deep_overlap.json
32
+ The same key is defined twice: foo.bar
33
+ spec/data/missing_brace.json
34
+ Hash/Object not terminated at line 5, column 2 [sparse.c:782]
35
+ spec/data/missing_comma.json
36
+ expected comma, not a string at line 3, column 8 [sparse.c:386]
37
+ spec/data/overlapping_keys.json
38
+ The same key is defined twice: foo
39
+ ```
40
+
41
+ ## Rake task
42
+
43
+ You can integrate jsonlint into your build process by adding a Rake task to your project
44
+
45
+ ```ruby
46
+ require 'jsonlint/rake_task'
47
+ JsonLint::RakeTask.new do |t|
48
+ t.paths = %w(
49
+ spec/**/*.json
50
+ )
51
+ end
52
+ ```
53
+
54
+ Then run the rake task.
20
55
 
21
- TODO: Write usage instructions here
56
+ ```
57
+ $ rake jsonlint
58
+ spec/data/deep_overlap.json
59
+ The same key is defined twice: foo.bar
60
+ spec/data/missing_brace.json
61
+ Hash/Object not terminated at line 5, column 2 [sparse.c:782]
62
+ spec/data/missing_comma.json
63
+ expected comma, not a string at line 3, column 8 [sparse.c:386]
64
+ spec/data/overlapping_keys.json
65
+ The same key is defined twice: foo
66
+ ```
22
67
 
23
68
  ## Contributing
24
69
 
25
- 1. Fork it ( https://github.com/[my-github-username]/jsonlint/fork )
70
+ 1. Fork it ( https://github.com/PagerDuty/jsonlint/fork )
26
71
  2. Create your feature branch (`git checkout -b my-new-feature`)
27
72
  3. Commit your changes (`git commit -am 'Add some feature'`)
28
73
  4. Push to the branch (`git push origin my-new-feature`)
data/Rakefile CHANGED
@@ -1,4 +1,4 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
2
 
3
3
  require 'rspec/core/rake_task'
4
4
 
@@ -18,8 +18,10 @@ Gem::Specification.new do |spec|
18
18
  spec.require_paths = ["lib"]
19
19
 
20
20
  spec.add_dependency 'oj', '~> 2'
21
+ spec.add_dependency 'trollop', '~> 2'
21
22
 
22
23
  spec.add_development_dependency "bundler", "~> 1.6"
23
24
  spec.add_development_dependency "rake"
24
25
  spec.add_development_dependency "rspec"
26
+ spec.add_development_dependency "aruba"
25
27
  end
@@ -4,7 +4,6 @@ require 'jsonlint/version'
4
4
  require 'jsonlint/linter'
5
5
 
6
6
  module JsonLint
7
- # Your code goes here...
8
7
  def self.logger
9
8
  @logger ||= Logger.new(STDOUT).tap do |l|
10
9
  l.level = Logger::INFO
@@ -1,3 +1,5 @@
1
+ require 'trollop'
2
+
1
3
  module JsonLint
2
4
  class CLI
3
5
  def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = Kernel)
@@ -5,14 +7,40 @@ module JsonLint
5
7
  end
6
8
 
7
9
  def execute!
10
+ parse_options
11
+
8
12
  files_to_check = @argv
9
13
 
14
+ Trollop::die 'need at least one JSON file to check' if files_to_check.empty?
15
+
10
16
  linter = JsonLint::Linter.new
11
- linter.check_all(files_to_check)
17
+ begin
18
+ if files_to_check == ['-']
19
+ linter.check_stream(STDIN)
20
+ else
21
+ linter.check_all(files_to_check)
22
+ end
23
+ rescue JsonLint::FileNotFoundError => e
24
+ @stderr.puts e.message
25
+ exit(1)
26
+ rescue => e
27
+ @stderr.puts e.message
28
+ exit(1)
29
+ end
30
+
31
+ return unless linter.errors?
32
+ linter.display_errors
33
+ @kernel.exit(1)
34
+ end
35
+
36
+ private
12
37
 
13
- if linter.has_errors?
14
- linter.display_errors
15
- @kernel.exit(1)
38
+ def parse_options
39
+ @opts = Trollop.options(@argv) do
40
+ banner 'Usage: jsonlint [options] file1.json [file2.json ...]'
41
+ version(JsonLint::VERSION)
42
+ banner ''
43
+ banner 'Options:'
16
44
  end
17
45
  end
18
46
  end
@@ -8,21 +8,38 @@ module JsonLint
8
8
  attr_reader :errors
9
9
 
10
10
  def initialize
11
- @errors = Hash.new {|h,k| h[k] = [] }
11
+ @errors = {}
12
12
  end
13
13
 
14
14
  def check_all(*files_to_check)
15
- files_to_check.flatten.each {|f| check(f) }
15
+ files_to_check.flatten.each { |f| check(f) }
16
16
  end
17
17
 
18
18
  def check(path)
19
- raise FileNotFoundError, "#{path} does not exist" unless File.exist?(path)
19
+ fail FileNotFoundError, "#{path}: no such file" unless File.exist?(path)
20
20
 
21
- check_syntax_valid(path) && check_overlapping_keys(path)
21
+ valid = false
22
+ File.open(path, 'r') do |f|
23
+ error_array = []
24
+ valid = check_data(f.read, error_array)
25
+ errors[path] = error_array unless error_array.empty?
26
+ end
27
+
28
+ valid
22
29
  end
23
30
 
24
- def has_errors?
25
- ! errors.empty?
31
+ def check_stream(io_stream)
32
+ json_data = io_stream.read
33
+ error_array = []
34
+
35
+ valid = check_data(json_data, error_array)
36
+ errors[''] = error_array unless error_array.empty?
37
+
38
+ valid
39
+ end
40
+
41
+ def errors?
42
+ !errors.empty?
26
43
  end
27
44
 
28
45
  def display_errors
@@ -36,11 +53,31 @@ module JsonLint
36
53
 
37
54
  private
38
55
 
39
- def check_syntax_valid(path)
40
- Oj.load_file(path, nilnil: false)
56
+ def check_data(json_data, errors_array)
57
+ valid = check_not_empty?(json_data, errors_array)
58
+ valid &&= check_syntax_valid?(json_data, errors_array)
59
+ valid &&= check_overlapping_keys?(json_data, errors_array)
60
+
61
+ valid
62
+ end
63
+
64
+ def check_not_empty?(json_data, errors_array)
65
+ if json_data.empty?
66
+ errors_array << 'The JSON should not be an empty string'
67
+ false
68
+ elsif json_data.strip.empty?
69
+ errors_array << 'The JSON should not just be spaces'
70
+ false
71
+ else
72
+ true
73
+ end
74
+ end
75
+
76
+ def check_syntax_valid?(json_data, errors_array)
77
+ Oj.load(json_data, nilnil: false)
41
78
  true
42
79
  rescue Oj::ParseError => e
43
- errors[path] << e.message
80
+ errors_array << e.message
44
81
  false
45
82
  end
46
83
 
@@ -120,28 +157,26 @@ module JsonLint
120
157
  end
121
158
 
122
159
  private
160
+
123
161
  def check_for_overlap!
124
162
  full_key = @key_components.dup
125
163
  JsonLint.logger.debug { "Checking #{full_key.join('.')} for overlap" }
126
164
 
127
- unless @seen_keys.add?(full_key)
128
- JsonLint.logger.debug { "Overlapping key #{full_key.join('.')}" }
129
- @overlapping_keys << full_key
130
- end
165
+ return if @seen_keys.add?(full_key)
166
+ JsonLint.logger.debug { "Overlapping key #{full_key.join('.')}" }
167
+ @overlapping_keys << full_key
131
168
  end
132
169
  end
133
170
 
134
- def check_overlapping_keys(path)
171
+ def check_overlapping_keys?(json_data, errors_array)
135
172
  overlap_detector = KeyOverlapDetector.new
136
- File.open(path, 'r') do |f|
137
- Oj.saj_parse(overlap_detector, f)
138
- end
173
+ Oj.saj_parse(overlap_detector, StringIO.new(json_data))
139
174
 
140
175
  overlap_detector.overlapping_keys.each do |key|
141
- errors[path] << "The same key is defined twice: #{key.join('.')}"
176
+ errors_array << "The same key is defined more than once: #{key.join('.')}"
142
177
  end
143
178
 
144
- !! overlap_detector.overlapping_keys.empty?
179
+ !!overlap_detector.overlapping_keys.empty?
145
180
  end
146
181
  end
147
182
  end
@@ -19,15 +19,15 @@ module JsonLint
19
19
  private
20
20
 
21
21
  def define_task
22
- desc 'Run jsonlint' unless ::Rake.application.last_comment
22
+ desc 'Run jsonlint' unless ::Rake.application.last_description
23
23
 
24
- task(self.name) do
25
- files_to_check = Rake::FileList.new(self.paths)
24
+ task(name) do
25
+ files_to_check = Rake::FileList.new(paths)
26
26
 
27
27
  linter = ::JsonLint::Linter.new
28
28
  linter.check_all(files_to_check)
29
29
 
30
- if linter.has_errors?
30
+ if linter.errors?
31
31
  linter.display_errors
32
32
  abort('JSON lint found')
33
33
  end
@@ -1,3 +1,3 @@
1
1
  module JsonLint
2
- VERSION = "0.1.0"
2
+ VERSION = '0.2.0'
3
3
  end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'jsonlint' do
4
+ it 'should print usage if run with no args' do
5
+ jsonlint
6
+ expect(last_command_started).to_not be_successfully_executed
7
+ expect(last_command_started).to have_output(/Error: need at least one JSON file to check./)
8
+ end
9
+
10
+ it '-h should print usage' do
11
+ jsonlint '-h'
12
+ expect(last_command_started).to be_successfully_executed
13
+ expect(last_command_started).to have_output(/Usage: jsonlint/)
14
+ end
15
+
16
+ it '--help should print usage' do
17
+ jsonlint '--help'
18
+ expect(last_command_started).to be_successfully_executed
19
+ expect(last_command_started).to have_output(/Usage: jsonlint/)
20
+ end
21
+
22
+ it '-v should print its version' do
23
+ jsonlint '-v'
24
+ expect(last_command_started).to be_successfully_executed
25
+ expect(last_command_started).to have_output JsonLint::VERSION
26
+ end
27
+
28
+ it '--version should print its version' do
29
+ jsonlint '--version'
30
+ expect(last_command_started).to be_successfully_executed
31
+ expect(last_command_started).to have_output JsonLint::VERSION
32
+ end
33
+
34
+ it 'should exit successfully with good JSON' do
35
+ jsonlint spec_data('valid.json')
36
+ expect(last_command_started).to be_successfully_executed
37
+ end
38
+
39
+ it 'should fail with bad JSON' do
40
+ jsonlint spec_data('missing_comma.json')
41
+ expect(last_command_started).to_not be_successfully_executed
42
+ end
43
+
44
+ it 'should fail with a path that does not exist' do
45
+ jsonlint '/does/not/exist'
46
+ expect(last_command_started).to_not be_successfully_executed
47
+ expect(last_command_started).to have_output(/no such file/)
48
+ end
49
+
50
+ it 'should fail with a path that is unreadable' do
51
+ run_simple('mkdir -p tmp')
52
+ run_simple('touch tmp/unreadable_file.json')
53
+ run_simple('chmod -r tmp/unreadable_file.json')
54
+
55
+ jsonlint 'tmp/unreadable_file.json'
56
+ expect(last_command_started).to_not be_successfully_executed
57
+ expect(last_command_started).to have_output(/Permission denied/)
58
+ end
59
+
60
+ it 'should be able to lint good JSON from STDIN' do
61
+ run "#{jsonlint_bin} -"
62
+ pipe_in_file('../../spec/data/valid.json') and close_input
63
+ expect(last_command_started).to be_successfully_executed
64
+ end
65
+
66
+ it 'should be able to lint bad JSON from STDIN' do
67
+ run "#{jsonlint_bin} -"
68
+ pipe_in_file('../../spec/data/missing_comma.json') and close_input
69
+ expect(last_command_started).to_not be_successfully_executed
70
+ end
71
+ end
File without changes
@@ -5,7 +5,7 @@ describe 'JsonLint::Linter' do
5
5
  let(:linter) { JsonLint::Linter.new }
6
6
 
7
7
  it 'should throw an exception if given a bogus path' do
8
- expect { linter.check('/does/not/exist') }.to raise_error
8
+ expect { linter.check('/does/not/exist') }.to raise_error(JsonLint::FileNotFoundError)
9
9
  end
10
10
 
11
11
  it 'should be happy with a valid JSON file' do
@@ -22,4 +22,20 @@ describe 'JsonLint::Linter' do
22
22
  expect(linter.check(spec_data('overlapping_keys.json'))).to be(false)
23
23
  expect(linter.check(spec_data('deep_overlap.json'))).to be(false)
24
24
  end
25
+
26
+ it 'should be able to check an IO stream' do
27
+ valid_stream = File.open(spec_data('valid.json'))
28
+ expect(linter.check_stream(valid_stream)).to be(true)
29
+
30
+ invalid_stream = File.open(spec_data('missing_comma.json'))
31
+ expect(linter.check_stream(invalid_stream)).to be(false)
32
+ end
33
+
34
+ it 'should be unhappy with an empty JSON file' do
35
+ expect(linter.check(spec_data('empty_file.json'))).to be(false)
36
+ end
37
+
38
+ it 'should be unhapy with a JSON file full of spaces' do
39
+ expect(linter.check(spec_data('lots_of_spaces.json'))).to be(false)
40
+ end
25
41
  end
@@ -1,5 +1,8 @@
1
1
  require 'rspec'
2
2
 
3
+ require 'aruba'
4
+ require 'aruba/rspec'
5
+
3
6
  require 'jsonlint'
4
7
 
5
8
  module SpecHelpers
@@ -8,6 +11,21 @@ module SpecHelpers
8
11
  end
9
12
  end
10
13
 
14
+ module CliSpecHelpers
15
+ def jsonlint(args = nil)
16
+ run_simple("#{jsonlint_bin} #{args}", fail_on_error: false)
17
+ end
18
+
19
+ def jsonlint_bin
20
+ File.expand_path('../../bin/jsonlint', __FILE__)
21
+ end
22
+ end
23
+
11
24
  RSpec.configure do |config|
12
25
  config.include SpecHelpers
26
+ config.include CliSpecHelpers
27
+ config.include Aruba::Api
28
+
29
+ config.run_all_when_everything_filtered = true
30
+ config.filter_run :focus
13
31
  end
metadata CHANGED
@@ -1,78 +1,97 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonlint
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
5
- prerelease:
4
+ version: 0.2.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Doug Barth
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2014-07-03 00:00:00.000000000 Z
11
+ date: 2016-08-07 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: oj
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ~>
17
+ - - "~>"
20
18
  - !ruby/object:Gem::Version
21
19
  version: '2'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ~>
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: trollop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
28
39
  - !ruby/object:Gem::Version
29
40
  version: '2'
30
41
  - !ruby/object:Gem::Dependency
31
42
  name: bundler
32
43
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
44
  requirements:
35
- - - ~>
45
+ - - "~>"
36
46
  - !ruby/object:Gem::Version
37
47
  version: '1.6'
38
48
  type: :development
39
49
  prerelease: false
40
50
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
51
  requirements:
43
- - - ~>
52
+ - - "~>"
44
53
  - !ruby/object:Gem::Version
45
54
  version: '1.6'
46
55
  - !ruby/object:Gem::Dependency
47
56
  name: rake
48
57
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
58
  requirements:
51
- - - ! '>='
59
+ - - ">="
52
60
  - !ruby/object:Gem::Version
53
61
  version: '0'
54
62
  type: :development
55
63
  prerelease: false
56
64
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
65
  requirements:
59
- - - ! '>='
66
+ - - ">="
60
67
  - !ruby/object:Gem::Version
61
68
  version: '0'
62
69
  - !ruby/object:Gem::Dependency
63
70
  name: rspec
64
71
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
72
  requirements:
67
- - - ! '>='
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: aruba
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
68
88
  - !ruby/object:Gem::Version
69
89
  version: '0'
70
90
  type: :development
71
91
  prerelease: false
72
92
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
93
  requirements:
75
- - - ! '>='
94
+ - - ">="
76
95
  - !ruby/object:Gem::Version
77
96
  version: '0'
78
97
  description: Checks JSON files for correct syntax and no silly mistakes
@@ -83,8 +102,8 @@ executables:
83
102
  extensions: []
84
103
  extra_rdoc_files: []
85
104
  files:
86
- - .gitignore
87
- - .travis.yml
105
+ - ".gitignore"
106
+ - ".travis.yml"
88
107
  - Gemfile
89
108
  - LICENSE.txt
90
109
  - README.md
@@ -97,7 +116,10 @@ files:
97
116
  - lib/jsonlint/linter.rb
98
117
  - lib/jsonlint/rake_task.rb
99
118
  - lib/jsonlint/version.rb
119
+ - spec/cli_spec.rb
100
120
  - spec/data/deep_overlap.json
121
+ - spec/data/empty_file.json
122
+ - spec/data/lots_of_spaces.json
101
123
  - spec/data/missing_brace.json
102
124
  - spec/data/missing_comma.json
103
125
  - spec/data/overlapping_keys.json
@@ -109,36 +131,32 @@ files:
109
131
  homepage:
110
132
  licenses:
111
133
  - MIT
134
+ metadata: {}
112
135
  post_install_message:
113
136
  rdoc_options: []
114
137
  require_paths:
115
138
  - lib
116
139
  required_ruby_version: !ruby/object:Gem::Requirement
117
- none: false
118
140
  requirements:
119
- - - ! '>='
141
+ - - ">="
120
142
  - !ruby/object:Gem::Version
121
143
  version: '0'
122
- segments:
123
- - 0
124
- hash: -4326837317699917705
125
144
  required_rubygems_version: !ruby/object:Gem::Requirement
126
- none: false
127
145
  requirements:
128
- - - ! '>='
146
+ - - ">="
129
147
  - !ruby/object:Gem::Version
130
148
  version: '0'
131
- segments:
132
- - 0
133
- hash: -4326837317699917705
134
149
  requirements: []
135
150
  rubyforge_project:
136
- rubygems_version: 1.8.24
151
+ rubygems_version: 2.5.1
137
152
  signing_key:
138
- specification_version: 3
153
+ specification_version: 4
139
154
  summary: JSON lint checker
140
155
  test_files:
156
+ - spec/cli_spec.rb
141
157
  - spec/data/deep_overlap.json
158
+ - spec/data/empty_file.json
159
+ - spec/data/lots_of_spaces.json
142
160
  - spec/data/missing_brace.json
143
161
  - spec/data/missing_comma.json
144
162
  - spec/data/overlapping_keys.json
@@ -147,3 +165,4 @@ test_files:
147
165
  - spec/data/valid_array_of_objects.json
148
166
  - spec/linter_spec.rb
149
167
  - spec/spec_helper.rb
168
+ has_rdoc: