greener 0.0.1.1
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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +61 -0
- data/bin/greener +5 -0
- data/config/defaults.yml +13 -0
- data/config/dogfood.yml +4 -0
- data/features/configuration.feature +49 -0
- data/features/lint.feature +44 -0
- data/features/support/env.rb +7 -0
- data/greener.gemspec +46 -0
- data/lib/greener/checker/base.rb +51 -0
- data/lib/greener/checker/style/feature_name.rb +25 -0
- data/lib/greener/checker/style/indentation_width.rb +44 -0
- data/lib/greener/cli.rb +20 -0
- data/lib/greener/config_store.rb +94 -0
- data/lib/greener/custom_error.rb +3 -0
- data/lib/greener/linter.rb +53 -0
- data/lib/greener/runner.rb +63 -0
- data/lib/greener/utils.rb +25 -0
- data/lib/greener/version.rb +4 -0
- data/lib/greener.rb +1 -0
- metadata +205 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a8ee00975481d14b7de2a3c4c5704e38e2ae7f8d
|
4
|
+
data.tar.gz: e3b4e819ebbd7dffba73cbcce66537534b5e1a3c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 018e3986bb9ae95dcaf5101190865c62d562b27724639b294990252d1e2b3ee7385f5f3b0e2f4a127ac10100b2eb5f8506d47bde510d6e62dd37af89f5be4ce2
|
7
|
+
data.tar.gz: f6ffa76be7bdfbca5ac6b1586691ee5ceaaa34f9d1090015388759d602d990e8c452dcd005903826ddfc79df3acac71caf1979007fffafd47376cf54a75b9b07
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 smoll
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
greener
|
2
|
+
===
|
3
|
+
|
4
|
+
[](https://circleci.com/gh/smoll/greener) [](https://codeclimate.com/github/smoll/greener) [](https://coveralls.io/r/smoll/greener?branch=master)
|
5
|
+
|
6
|
+
A Gherkin .feature file linter
|
7
|
+
|
8
|
+
**NOTE: This project is still under early-stage development**
|
9
|
+
|
10
|
+
## TODOs
|
11
|
+
|
12
|
+
0. Move "results printing" logic to configurable formatters
|
13
|
+
0. Implement some kind of hooks system for use by formatters, similar to [this](https://github.com/bbatsov/rubocop/blob/master/lib/rubocop/formatter/base_formatter.rb#L30-L41)
|
14
|
+
0. Create a RakeTask class for use in CI systems
|
15
|
+
0. Add coloring for formatters
|
16
|
+
|
17
|
+
## Contributing
|
18
|
+
|
19
|
+
Install dev dependencies locally
|
20
|
+
```
|
21
|
+
bundle install
|
22
|
+
```
|
23
|
+
|
24
|
+
Run tests
|
25
|
+
```
|
26
|
+
bundle exec rake test
|
27
|
+
```
|
28
|
+
|
29
|
+
View code coverage
|
30
|
+
```
|
31
|
+
rake && open coverage/index.html
|
32
|
+
```
|
33
|
+
|
34
|
+
View code coverage from RSpec unit tests only
|
35
|
+
```
|
36
|
+
rm -rf coverage && rspec && open coverage/index.html
|
37
|
+
```
|
38
|
+
|
39
|
+
## Testing
|
40
|
+
|
41
|
+
To test the `greener` binary locally, `cd` to the repo root and run
|
42
|
+
```
|
43
|
+
RUBYLIB=lib bundle exec ruby bin/greener
|
44
|
+
```
|
45
|
+
Based on [this answer](http://stackoverflow.com/a/23367196/3456726).
|
46
|
+
|
47
|
+
## References
|
48
|
+
|
49
|
+
#### CLI related
|
50
|
+
|
51
|
+
0. http://guides.rubygems.org/make-your-own-gem/
|
52
|
+
0. http://whatisthor.com/
|
53
|
+
0. https://github.com/erikhuda/thor/wiki/Integrating-with-Aruba-In-Process-Runs
|
54
|
+
|
55
|
+
#### Testing related
|
56
|
+
0. http://stackoverflow.com/questions/12673485/how-to-test-stdin-for-a-cli-using-rspec
|
57
|
+
0. https://github.com/livinginthepast/aruba-rspec
|
58
|
+
|
59
|
+
#### Lint related
|
60
|
+
|
61
|
+
0. https://github.com/bbatsov/rubocop/tree/master/lib/rubocop
|
data/bin/greener
ADDED
data/config/defaults.yml
ADDED
data/config/dogfood.yml
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
Feature: configuration
|
2
|
+
|
3
|
+
Scenario: disable checker via config file
|
4
|
+
Given a file named "foo/mismatched_feature_title.feature" with:
|
5
|
+
"""
|
6
|
+
Feature: mismatched feature title LOL
|
7
|
+
"""
|
8
|
+
And a file named "config/disabled.yml" with:
|
9
|
+
"""
|
10
|
+
Style/FeatureName:
|
11
|
+
Enabled: false
|
12
|
+
"""
|
13
|
+
When I run `greener --config config/disabled.yml`
|
14
|
+
Then the output should contain exactly "1 file(s) inspected, no offenses detected\n"
|
15
|
+
|
16
|
+
Scenario: invalid checker specified in config
|
17
|
+
Given a file named "foo/something.feature" with:
|
18
|
+
"""
|
19
|
+
Feature: something
|
20
|
+
"""
|
21
|
+
And a file named "config/invalid.yml" with:
|
22
|
+
"""
|
23
|
+
Style/NotEvenReal:
|
24
|
+
Enabled: false
|
25
|
+
"""
|
26
|
+
When I run `greener --config config/invalid.yml`
|
27
|
+
Then the output should contain exactly "Unknown checker specified: Style/NotEvenReal\n"
|
28
|
+
|
29
|
+
Scenario: complex configuration
|
30
|
+
Given a file named "foo/indentation_too.feature" with:
|
31
|
+
"""
|
32
|
+
Feature: BAD NAME LOL
|
33
|
+
|
34
|
+
Scenario: correctly indented
|
35
|
+
|
36
|
+
Scenario: correctly indented
|
37
|
+
Then boom
|
38
|
+
"""
|
39
|
+
And a file named "config/complex.yml" with:
|
40
|
+
"""
|
41
|
+
Style/FeatureName:
|
42
|
+
Enabled: false
|
43
|
+
|
44
|
+
Style/IndentationWidth:
|
45
|
+
Enabled: true
|
46
|
+
Width: 4
|
47
|
+
"""
|
48
|
+
When I run `greener --config config/complex.yml`
|
49
|
+
Then the output should contain exactly "1 file(s) inspected, no offenses detected\n"
|
@@ -0,0 +1,44 @@
|
|
1
|
+
Feature: lint
|
2
|
+
* TODO: eventually, move most, if not all, of these to unit tests
|
3
|
+
|
4
|
+
Scenario: feature text matches, with flexible capitalization
|
5
|
+
Given a file named "foo/valid_title.feature" with:
|
6
|
+
"""
|
7
|
+
Feature: Valid Title
|
8
|
+
"""
|
9
|
+
When I run `greener`
|
10
|
+
Then the output should contain exactly "1 file(s) inspected, no offenses detected\n"
|
11
|
+
|
12
|
+
Scenario: feature text does not match filename
|
13
|
+
Given a file named "foo/mismatched_feature_title.feature" with:
|
14
|
+
"""
|
15
|
+
Feature: mismatched feature title LOL
|
16
|
+
"""
|
17
|
+
When I run `greener`
|
18
|
+
Then the output should contain exactly "foo/mismatched_feature_title.feature:1\nFeature: mismatched feature title LOL\n^^^ feature title does not match file name\n\n1 file(s) inspected, 1 offense(s) detected\n"
|
19
|
+
|
20
|
+
Scenario: inconsistent indentation
|
21
|
+
Given a file named "foo/indentation.feature" with:
|
22
|
+
"""
|
23
|
+
Feature: indentation
|
24
|
+
|
25
|
+
Scenario: correctly indented
|
26
|
+
|
27
|
+
Scenario: poorly indented
|
28
|
+
Then nothing
|
29
|
+
"""
|
30
|
+
When I run `greener`
|
31
|
+
Then the output should contain exactly "foo/indentation.feature:5\n Scenario: poorly indented\n ^^^ inconsistent indentation detected\n\n1 file(s) inspected, 1 offense(s) detected\n"
|
32
|
+
|
33
|
+
Scenario: multiple issues
|
34
|
+
Given a file named "foo/multiple_issues.feature" with:
|
35
|
+
"""
|
36
|
+
Feature: multiple issues
|
37
|
+
|
38
|
+
Scenario: correctly indented
|
39
|
+
|
40
|
+
Scenario: poorly indented
|
41
|
+
Then nothing
|
42
|
+
"""
|
43
|
+
When I run `greener`
|
44
|
+
Then the output should contain "1 file(s) inspected, 2 offense(s) detected\n"
|
data/greener.gemspec
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "greener/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "greener"
|
8
|
+
spec.version = Greener::VERSION
|
9
|
+
spec.authors = ["smoll"]
|
10
|
+
spec.email = ["smollah@theorchard.com"]
|
11
|
+
|
12
|
+
spec.summary = "A Gherkin .feature file linter"
|
13
|
+
spec.description = "Keep your Gherkin readable with the greener linter..."
|
14
|
+
spec.homepage = "https://github.com/smoll/greener"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files`.split($RS).reject do |file|
|
18
|
+
file =~ %r{^(?:
|
19
|
+
spec/.*
|
20
|
+
|\.gitignore
|
21
|
+
|\.rspec
|
22
|
+
|\.rubocop.yml
|
23
|
+
|\.rubocop_todo.yml
|
24
|
+
|\.simplecov
|
25
|
+
|circle.yml
|
26
|
+
|cucumber.yml
|
27
|
+
|Gemfile
|
28
|
+
|Rakefile
|
29
|
+
)$}x
|
30
|
+
end
|
31
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
32
|
+
spec.require_paths = ["lib"]
|
33
|
+
|
34
|
+
spec.add_dependency "thor", "~> 0.19.1"
|
35
|
+
# TODO: rip this out when gherkin3 has a release
|
36
|
+
spec.add_dependency "gherkin3-pre-alpha", "~> 3.0.0.alpha.1"
|
37
|
+
|
38
|
+
spec.add_development_dependency "aruba", "~> 0.6.2"
|
39
|
+
spec.add_development_dependency "bundler", ">= 1.9.5"
|
40
|
+
spec.add_development_dependency "codeclimate-test-reporter", "~> 0.4.6"
|
41
|
+
spec.add_development_dependency "coveralls", "~> 0.7.9"
|
42
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
43
|
+
spec.add_development_dependency "rspec", "~> 3.3.0"
|
44
|
+
spec.add_development_dependency "rubocop", "~> 0.32.0"
|
45
|
+
spec.add_development_dependency "simplecov", "~> 0.9.1"
|
46
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Greener
|
2
|
+
module Checker
|
3
|
+
# Base checker class
|
4
|
+
class Base
|
5
|
+
attr_reader :violations
|
6
|
+
|
7
|
+
def initialize(ast, path, config)
|
8
|
+
@ast = ast
|
9
|
+
@path = path
|
10
|
+
@config = config
|
11
|
+
|
12
|
+
@violations = []
|
13
|
+
end
|
14
|
+
|
15
|
+
# Method invoked when a checker is applied to a file
|
16
|
+
def run
|
17
|
+
end
|
18
|
+
|
19
|
+
# Read the violation message text set in the checker subclass
|
20
|
+
def message
|
21
|
+
self.class::MSG
|
22
|
+
end
|
23
|
+
|
24
|
+
# Adds violation data to the @violations array
|
25
|
+
def log_violation(line, col, raw_txt = nil, msg = nil)
|
26
|
+
# Set defaults for last 2 params if not overridden
|
27
|
+
raw_txt ||= raw_line(line)
|
28
|
+
msg ||= message
|
29
|
+
|
30
|
+
violation = {}
|
31
|
+
violation[:file] = @path
|
32
|
+
violation[:line] = line
|
33
|
+
violation[:column] = col
|
34
|
+
violation[:text_of_line] = raw_txt
|
35
|
+
violation[:message] = msg
|
36
|
+
|
37
|
+
@violations << violation
|
38
|
+
end
|
39
|
+
|
40
|
+
# For readability in checker subclasses
|
41
|
+
def feature
|
42
|
+
@ast
|
43
|
+
end
|
44
|
+
|
45
|
+
# Given a num, returns the full text corresponding to that line number in the file
|
46
|
+
def raw_line(num)
|
47
|
+
open(@path).each_line.take(num).last
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "greener/checker/base"
|
2
|
+
|
3
|
+
module Greener
|
4
|
+
module Checker
|
5
|
+
module Style
|
6
|
+
# Ensure .feature filename matches feature name in the Gherkin
|
7
|
+
# e.g. filename_of_feature.feature => "Feature: filename of feature"
|
8
|
+
# TODO: add config option for toggling flexible capitalization
|
9
|
+
class FeatureName < Base
|
10
|
+
MSG = "feature title does not match file name"
|
11
|
+
|
12
|
+
# Returns nil if no violation, or if there is one:
|
13
|
+
# { line: 1, column: 1, text_of_line: "Feature: thing", message: "feature title does not match file name" }
|
14
|
+
def run
|
15
|
+
return unless @config["Enabled"]
|
16
|
+
|
17
|
+
filename_without_extension = File.basename(@path, ".*")
|
18
|
+
return if feature[:name].downcase.gsub(" ", "_") == filename_without_extension
|
19
|
+
|
20
|
+
log_violation(feature[:location][:line], feature[:location][:column])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "greener/checker/base"
|
2
|
+
|
3
|
+
module Greener
|
4
|
+
module Checker
|
5
|
+
module Style
|
6
|
+
# Ensure keywords are indented correctly
|
7
|
+
# Ref: https://github.com/bbatsov/rubocop/commit/44c1cdd5d9bc1c3588ea7841fc6e9543126306e8
|
8
|
+
class IndentationWidth < Base
|
9
|
+
MSG = "inconsistent indentation detected"
|
10
|
+
|
11
|
+
def run
|
12
|
+
return unless @config["Enabled"]
|
13
|
+
|
14
|
+
check_feature_line
|
15
|
+
check_every_scenario
|
16
|
+
end
|
17
|
+
|
18
|
+
def check_feature_line
|
19
|
+
return if feature[:location][:column] == 1
|
20
|
+
log_violation(feature[:location][:line], feature[:location][:column])
|
21
|
+
end
|
22
|
+
|
23
|
+
def check_every_scenario
|
24
|
+
feature[:scenarioDefinitions].each do |scenario|
|
25
|
+
scenario[:steps].each { |step| check_a_step(step) }
|
26
|
+
|
27
|
+
next if scenario[:location][:column] == (1 + configured_indentation_width)
|
28
|
+
log_violation(scenario[:location][:line], scenario[:location][:column])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def check_a_step(step) # nil if a scenario has no steps, or
|
33
|
+
# {:type=>:Step, :location=>{:line=>6, :column=>5}, :keyword=>"Then ", :text=>"nothing"}
|
34
|
+
return if step[:location][:column] == (1 + configured_indentation_width * 2)
|
35
|
+
log_violation(step[:location][:line], step[:location][:column])
|
36
|
+
end
|
37
|
+
|
38
|
+
def configured_indentation_width
|
39
|
+
@config["Width"]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/greener/cli.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "thor"
|
2
|
+
|
3
|
+
require "greener/linter"
|
4
|
+
|
5
|
+
module Greener
|
6
|
+
# Main Thor application
|
7
|
+
# Subcommands map directly to instance methods in this class.
|
8
|
+
# e.g. entering `greener whatever` in your terminal should invoke the App#whatever method
|
9
|
+
class CLI < Thor
|
10
|
+
desc "lint", "The default task to run when no command is given"
|
11
|
+
method_options %w( config -c ) => :string
|
12
|
+
def lint
|
13
|
+
linter = Linter.new(options[:config])
|
14
|
+
linter.lint
|
15
|
+
linter.print_results
|
16
|
+
end
|
17
|
+
|
18
|
+
default_task :lint
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
require "greener/utils"
|
4
|
+
require "greener/custom_error"
|
5
|
+
|
6
|
+
# Require all checker files here
|
7
|
+
require "greener/checker/style/feature_name"
|
8
|
+
require "greener/checker/style/indentation_width"
|
9
|
+
|
10
|
+
module Greener
|
11
|
+
# Read configs from a user-specified greener.yml or fallback to defaults
|
12
|
+
class ConfigStore
|
13
|
+
include Utils
|
14
|
+
|
15
|
+
attr_reader :checkers, :files
|
16
|
+
|
17
|
+
def initialize(path, default_path = nil)
|
18
|
+
@path = path
|
19
|
+
default_path ||= default_absolute_path
|
20
|
+
@default_path = default_path
|
21
|
+
|
22
|
+
@checkers = {}
|
23
|
+
@files = []
|
24
|
+
end
|
25
|
+
|
26
|
+
def resolve
|
27
|
+
if @path
|
28
|
+
fail CustomError, "No config file found at specified path: #{@path}" unless File.exist? @path
|
29
|
+
config = load_yml_file @path
|
30
|
+
end
|
31
|
+
defaults = load_yml_file @default_path
|
32
|
+
|
33
|
+
@all = @path ? defaults.merge(config) : defaults
|
34
|
+
|
35
|
+
validate
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
# Stub-able methods
|
40
|
+
def load_yml_file(path)
|
41
|
+
YAML.load_file(path)
|
42
|
+
end
|
43
|
+
|
44
|
+
def files_matching_glob(glob)
|
45
|
+
Dir.glob(glob).select { |e| File.file? e }
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def validate
|
51
|
+
set_checkers
|
52
|
+
set_files
|
53
|
+
|
54
|
+
@all.each do |k, _v|
|
55
|
+
fail CustomError, "Unknown option in config file: #{k}" # TODO: print warning instead of fail
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def set_checkers
|
60
|
+
@all.each do |k, v|
|
61
|
+
next unless %w( Style/ Lint/ ).any? { |prefix| k.start_with?(prefix) }
|
62
|
+
checker_klass = checker_from_string(k)
|
63
|
+
@checkers[checker_klass] = v
|
64
|
+
@all.delete(k)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def set_files
|
69
|
+
if @all["FileList"].nil?
|
70
|
+
# Default to all .feature files recursively
|
71
|
+
return @files = files_matching_glob("**/*.feature")
|
72
|
+
end
|
73
|
+
|
74
|
+
@all.each do |k, v|
|
75
|
+
next unless k == "FileList"
|
76
|
+
discover_files(v)
|
77
|
+
@all.delete(k)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def discover_files(hash)
|
82
|
+
includes = []
|
83
|
+
excludes = []
|
84
|
+
|
85
|
+
hash["Include"].each { |glob| includes += files_matching_glob(glob) } if hash["Include"]
|
86
|
+
hash["Exclude"].each { |glob| excludes += files_matching_glob(glob) } if hash["Exclude"]
|
87
|
+
@files = includes - excludes
|
88
|
+
end
|
89
|
+
|
90
|
+
def default_absolute_path
|
91
|
+
File.expand_path("../../../config/defaults.yml", __FILE__)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "gherkin3/parser"
|
2
|
+
require "gherkin3/token_scanner"
|
3
|
+
require "gherkin3/ast_builder"
|
4
|
+
require "gherkin3/token_matcher"
|
5
|
+
|
6
|
+
require "greener/config_store"
|
7
|
+
|
8
|
+
module Greener
|
9
|
+
# Parse then lint a collection of .feature files for violations
|
10
|
+
class Linter
|
11
|
+
def initialize(config_path)
|
12
|
+
@config = ConfigStore.new(config_path).resolve
|
13
|
+
@results = []
|
14
|
+
end
|
15
|
+
|
16
|
+
# Here we iterate through a list of files, then iterate through the list of
|
17
|
+
# desired checkers, passing each one the filename & parsed AST.
|
18
|
+
# This is fine for now, but to speed this up we could refactor this to do a
|
19
|
+
# single pass through the file, line-by-line, and flagging violations as
|
20
|
+
# they are encountered.
|
21
|
+
def lint
|
22
|
+
@config.files.each { |f| parse_single_file(f) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def print_results
|
26
|
+
@results.each do |violation|
|
27
|
+
puts "#{violation[:file]}:#{violation[:line]}"
|
28
|
+
puts "#{violation[:text_of_line]}"
|
29
|
+
puts "#{' ' * (violation[:column] - 1)}^^^ #{violation[:message]}"
|
30
|
+
puts ""
|
31
|
+
end
|
32
|
+
|
33
|
+
conclusion = @results.empty? ? "no offenses detected" : "#{@results.count} offense(s) detected"
|
34
|
+
|
35
|
+
puts "#{@config.files.count} file(s) inspected, #{conclusion}"
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def parse_single_file(fname)
|
41
|
+
parser = Gherkin3::Parser.new
|
42
|
+
scanner = Gherkin3::TokenScanner.new(fname)
|
43
|
+
builder = Gherkin3::AstBuilder.new
|
44
|
+
ast = parser.parse(scanner, builder, Gherkin3::TokenMatcher.new)
|
45
|
+
|
46
|
+
@config.checkers.each do |sc_klass, sc_opts|
|
47
|
+
checker = sc_klass.new(ast, fname, sc_opts)
|
48
|
+
checker.run
|
49
|
+
@results += checker.violations
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "greener/cli"
|
2
|
+
|
3
|
+
begin # require any dev dependencies here
|
4
|
+
require "byebug"
|
5
|
+
rescue LoadError # rubocop:disable Lint/HandleExceptions
|
6
|
+
end
|
7
|
+
|
8
|
+
module Greener
|
9
|
+
# Aruba In Process + Thor integration
|
10
|
+
# Based on https://github.com/erikhuda/thor/wiki/Integrating-with-Aruba-In-Process-Runs
|
11
|
+
class Runner
|
12
|
+
# Allow everything fun to be injected from the outside while defaulting to normal implementations.
|
13
|
+
def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = Kernel)
|
14
|
+
@argv = argv
|
15
|
+
@stdin = stdin
|
16
|
+
@stdout = stdout
|
17
|
+
@stderr = stderr
|
18
|
+
@kernel = kernel
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute!
|
22
|
+
exit_code = begin
|
23
|
+
# Thor accesses these streams directly rather than letting them be injected, so we replace them...
|
24
|
+
$stderr = @stderr
|
25
|
+
$stdin = @stdin
|
26
|
+
$stdout = @stdout
|
27
|
+
|
28
|
+
# Run our normal Thor app the way we know and love.
|
29
|
+
Greener::CLI.start(@argv)
|
30
|
+
|
31
|
+
# Thor::Base#start does not have a return value, assume success if no exception is raised.
|
32
|
+
0
|
33
|
+
rescue Greener::CustomError => e
|
34
|
+
@stderr.puts e.message
|
35
|
+
1
|
36
|
+
rescue StandardError => e
|
37
|
+
# The ruby interpreter would pipe this to STDERR and exit 1 in the case of an unhandled exception
|
38
|
+
b = e.backtrace
|
39
|
+
@stderr.puts("#{b.shift}: #{e.message} (#{e.class})")
|
40
|
+
@stderr.puts(b.map { |s| "\tfrom #{s}" }.join("\n"))
|
41
|
+
1
|
42
|
+
rescue SystemExit => e
|
43
|
+
e.status
|
44
|
+
ensure
|
45
|
+
# TODO: reset your app here, free up resources, etc.
|
46
|
+
# Examples:
|
47
|
+
# MyApp.logger.flush
|
48
|
+
# MyApp.logger.close
|
49
|
+
# MyApp.logger = nil
|
50
|
+
#
|
51
|
+
# MyApp.reset_singleton_instance_variables
|
52
|
+
|
53
|
+
# ...then we put the streams back.
|
54
|
+
$stderr = STDERR
|
55
|
+
$stdin = STDIN
|
56
|
+
$stdout = STDOUT
|
57
|
+
end
|
58
|
+
|
59
|
+
# Proxy our exit code back to the injected kernel.
|
60
|
+
@kernel.exit(exit_code)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Greener
|
2
|
+
# Useful shared utils
|
3
|
+
module Utils
|
4
|
+
def checker_from_string(str)
|
5
|
+
namespaced = str.gsub("/", "::")
|
6
|
+
constantize "Greener::Checker::#{namespaced}"
|
7
|
+
rescue NameError
|
8
|
+
raise CustomError, "Unknown checker specified: #{str}" # TODO: print warning instead of failing
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
# Borrowed from Rails
|
14
|
+
def constantize(camel_cased_word)
|
15
|
+
names = camel_cased_word.split("::")
|
16
|
+
names.shift if names.empty? || names.first.empty?
|
17
|
+
|
18
|
+
constant = Object
|
19
|
+
names.each do |name|
|
20
|
+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
21
|
+
end
|
22
|
+
constant
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/greener.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "greener/runner"
|
metadata
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: greener
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- smoll
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.19.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.19.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: gherkin3-pre-alpha
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.0.0.alpha.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.0.0.alpha.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: aruba
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.6.2
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.6.2
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.9.5
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.9.5
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: codeclimate-test-reporter
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.4.6
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.4.6
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: coveralls
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.7.9
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.7.9
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '10.0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '10.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 3.3.0
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 3.3.0
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rubocop
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 0.32.0
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 0.32.0
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: simplecov
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 0.9.1
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 0.9.1
|
153
|
+
description: Keep your Gherkin readable with the greener linter...
|
154
|
+
email:
|
155
|
+
- smollah@theorchard.com
|
156
|
+
executables:
|
157
|
+
- greener
|
158
|
+
extensions: []
|
159
|
+
extra_rdoc_files: []
|
160
|
+
files:
|
161
|
+
- LICENSE.txt
|
162
|
+
- README.md
|
163
|
+
- bin/greener
|
164
|
+
- config/defaults.yml
|
165
|
+
- config/dogfood.yml
|
166
|
+
- features/configuration.feature
|
167
|
+
- features/lint.feature
|
168
|
+
- features/support/env.rb
|
169
|
+
- greener.gemspec
|
170
|
+
- lib/greener.rb
|
171
|
+
- lib/greener/checker/base.rb
|
172
|
+
- lib/greener/checker/style/feature_name.rb
|
173
|
+
- lib/greener/checker/style/indentation_width.rb
|
174
|
+
- lib/greener/cli.rb
|
175
|
+
- lib/greener/config_store.rb
|
176
|
+
- lib/greener/custom_error.rb
|
177
|
+
- lib/greener/linter.rb
|
178
|
+
- lib/greener/runner.rb
|
179
|
+
- lib/greener/utils.rb
|
180
|
+
- lib/greener/version.rb
|
181
|
+
homepage: https://github.com/smoll/greener
|
182
|
+
licenses:
|
183
|
+
- MIT
|
184
|
+
metadata: {}
|
185
|
+
post_install_message:
|
186
|
+
rdoc_options: []
|
187
|
+
require_paths:
|
188
|
+
- lib
|
189
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
190
|
+
requirements:
|
191
|
+
- - ">="
|
192
|
+
- !ruby/object:Gem::Version
|
193
|
+
version: '0'
|
194
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
195
|
+
requirements:
|
196
|
+
- - ">="
|
197
|
+
- !ruby/object:Gem::Version
|
198
|
+
version: '0'
|
199
|
+
requirements: []
|
200
|
+
rubyforge_project:
|
201
|
+
rubygems_version: 2.2.2
|
202
|
+
signing_key:
|
203
|
+
specification_version: 4
|
204
|
+
summary: A Gherkin .feature file linter
|
205
|
+
test_files: []
|