greener2 0.0.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/CHANGELOG.md +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +67 -0
- data/bin/greener +5 -0
- data/config/defaults.yml +20 -0
- data/config/dogfood.yml +4 -0
- data/features/checker_feature_name.feature +74 -0
- data/features/checker_indentation_width.feature +23 -0
- data/features/configuration.feature +59 -0
- data/features/formatter_progress.feature +34 -0
- data/features/lint.feature +27 -0
- data/features/support/env.rb +9 -0
- data/greener.gemspec +46 -0
- data/lib/greener/checker/base.rb +51 -0
- data/lib/greener/checker/style/feature_name.rb +48 -0
- data/lib/greener/checker/style/indentation_width.rb +44 -0
- data/lib/greener/cli.rb +19 -0
- data/lib/greener/config_store.rb +124 -0
- data/lib/greener/error.rb +7 -0
- data/lib/greener/formatter/base_formatter.rb +35 -0
- data/lib/greener/formatter/progress.rb +21 -0
- data/lib/greener/formatter/simple_text.rb +17 -0
- data/lib/greener/formatter/summary.rb +16 -0
- data/lib/greener/formatter_set.rb +20 -0
- data/lib/greener/linter.rb +49 -0
- data/lib/greener/output_buffer.rb +35 -0
- data/lib/greener/parser.rb +18 -0
- data/lib/greener/runner.rb +63 -0
- data/lib/greener/utils.rb +31 -0
- data/lib/greener/version.rb +4 -0
- data/lib/greener.rb +1 -0
- metadata +230 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 21659a58c5605652857c5e13e450b645e5be355e
|
4
|
+
data.tar.gz: fb62aaca67ca27327e2dd2cb8ccf34dc65063540
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 75c81d38126470eae8e0dd52ca3577c3e42c4a01f67d414d948577b074b269855dc61b4b1645792e45f624c7cfa8f7ce01fa94b84269208a516d5c16ca9ed24a
|
7
|
+
data.tar.gz: 270a125694816a97cd10549d7b034d29abe763fbe605c84a65676174065adcbb9784b9b406f45bb672d1b808a49fe5325e752fc454db5c271314458b9c96b336
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
## 0.1.0 (June 29, 2015)
|
2
|
+
|
3
|
+
Features:
|
4
|
+
|
5
|
+
- First minor version release
|
6
|
+
- 2 checkers are available, `Style/FeatureName` and `Style/IndentationWidth`
|
7
|
+
- By default, `greener` checks for .feature files recursively from the current working directory. This can be overridden via the `AllCheckers:` > `Include:` or `Exclude:` keys in the yml.
|
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,67 @@
|
|
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) [](http://badge.fury.io/rb/greener) [](http://inch-ci.org/github/smoll/greener)
|
5
|
+
|
6
|
+
Keep your Cucumber and Spinach feature files greener :four_leaf_clover: with this configurable 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
|
+
## Usage
|
18
|
+
|
19
|
+
Install the gem
|
20
|
+
```
|
21
|
+
gem install greener
|
22
|
+
```
|
23
|
+
|
24
|
+
The `greener` binary takes a single argument, `-c path/to/config/greener.yml`. See the [defaults](./config/defaults.yml) for an example of this file.
|
25
|
+
|
26
|
+
View the [changelog](./CHANGELOG.md) for recent changes.
|
27
|
+
|
28
|
+
## Contributing
|
29
|
+
|
30
|
+
Install dev dependencies locally
|
31
|
+
```
|
32
|
+
bundle install
|
33
|
+
```
|
34
|
+
|
35
|
+
Run tests
|
36
|
+
```
|
37
|
+
bundle exec rake test
|
38
|
+
```
|
39
|
+
|
40
|
+
View code coverage
|
41
|
+
```
|
42
|
+
rake && open coverage/index.html
|
43
|
+
```
|
44
|
+
|
45
|
+
## Testing
|
46
|
+
|
47
|
+
To test the `greener` binary locally, `cd` to the repo root and run
|
48
|
+
```
|
49
|
+
RUBYLIB=lib bundle exec ruby bin/greener
|
50
|
+
```
|
51
|
+
Based on [this answer](http://stackoverflow.com/a/23367196/3456726).
|
52
|
+
|
53
|
+
## References
|
54
|
+
|
55
|
+
#### CLI related
|
56
|
+
|
57
|
+
0. http://guides.rubygems.org/make-your-own-gem/
|
58
|
+
0. http://whatisthor.com/
|
59
|
+
0. https://github.com/erikhuda/thor/wiki/Integrating-with-Aruba-In-Process-Runs
|
60
|
+
|
61
|
+
#### Testing related
|
62
|
+
0. http://stackoverflow.com/questions/12673485/how-to-test-stdin-for-a-cli-using-rspec
|
63
|
+
0. https://github.com/livinginthepast/aruba-rspec
|
64
|
+
|
65
|
+
#### Lint related
|
66
|
+
|
67
|
+
0. https://github.com/bbatsov/rubocop/tree/master/lib/rubocop
|
data/bin/greener
ADDED
data/config/defaults.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
AllCheckers:
|
2
|
+
Formatters:
|
3
|
+
# Summary does not need to be added here explicitly
|
4
|
+
- Progress
|
5
|
+
- SimpleText
|
6
|
+
Include:
|
7
|
+
- "**/*.feature"
|
8
|
+
# Exclude:
|
9
|
+
# - "some/excluded/path/*.feature"
|
10
|
+
|
11
|
+
Style/FeatureName:
|
12
|
+
Enabled: true
|
13
|
+
# Allow non-alphanumeric characters, e.g. filename test_thing.feature with text "Feature: (test) thing"
|
14
|
+
AllowPunctuation: true
|
15
|
+
EnforceTitleCase: false
|
16
|
+
|
17
|
+
Style/IndentationWidth:
|
18
|
+
Enabled: true
|
19
|
+
# Number of spaces for each indentation level.
|
20
|
+
Width: 2
|
data/config/dogfood.yml
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
Feature: (checker) feature name
|
2
|
+
|
3
|
+
Scenario: feature text does not match filename
|
4
|
+
Given a file named "foo/mismatched_feature_title.feature" with:
|
5
|
+
"""
|
6
|
+
Feature: mismatched feature title LOL
|
7
|
+
"""
|
8
|
+
When I run `greener`
|
9
|
+
Then the output should contain:
|
10
|
+
"""
|
11
|
+
F
|
12
|
+
|
13
|
+
foo/mismatched_feature_title.feature:1
|
14
|
+
Feature: mismatched feature title LOL
|
15
|
+
^^^ feature title does not match file name
|
16
|
+
|
17
|
+
1 file(s) inspected, 1 offense(s) detected
|
18
|
+
"""
|
19
|
+
|
20
|
+
Scenario: capitalization allowed
|
21
|
+
Given a file named "foo/valid_title.feature" with:
|
22
|
+
"""
|
23
|
+
Feature: Valid Title
|
24
|
+
"""
|
25
|
+
When I run `greener`
|
26
|
+
Then the output should contain:
|
27
|
+
"""
|
28
|
+
.
|
29
|
+
|
30
|
+
1 file(s) inspected, no offenses detected
|
31
|
+
"""
|
32
|
+
|
33
|
+
Scenario: punctuation allowed
|
34
|
+
Given a file named "foo/some_punctuation.feature" with:
|
35
|
+
"""
|
36
|
+
Feature: (some) punctuation
|
37
|
+
"""
|
38
|
+
And a file named "config/punctuation_allowed.yml" with:
|
39
|
+
"""
|
40
|
+
Style/FeatureName:
|
41
|
+
Enabled: true
|
42
|
+
AllowPunctuation: true
|
43
|
+
"""
|
44
|
+
When I run `greener --config config/punctuation_allowed.yml`
|
45
|
+
Then the output should contain:
|
46
|
+
"""
|
47
|
+
.
|
48
|
+
|
49
|
+
1 file(s) inspected, no offenses detected
|
50
|
+
"""
|
51
|
+
|
52
|
+
Scenario: title case enforced
|
53
|
+
Given a file named "foo/this_isnt_title_case_yo.feature" with:
|
54
|
+
"""
|
55
|
+
Feature: this isn't Title Case, yo!
|
56
|
+
"""
|
57
|
+
And a file named "config/enforce_title_case.yml" with:
|
58
|
+
"""
|
59
|
+
Style/FeatureName:
|
60
|
+
Enabled: true
|
61
|
+
AllowPunctuation: true
|
62
|
+
EnforceTitleCase: true
|
63
|
+
"""
|
64
|
+
When I run `greener --config config/enforce_title_case.yml`
|
65
|
+
Then the output should contain:
|
66
|
+
"""
|
67
|
+
F
|
68
|
+
|
69
|
+
foo/this_isnt_title_case_yo.feature:1
|
70
|
+
Feature: this isn't Title Case, yo!
|
71
|
+
^^^ feature title is not title case. expected: This Isn't Title Case, Yo!
|
72
|
+
|
73
|
+
1 file(s) inspected, 1 offense(s) detected
|
74
|
+
"""
|
@@ -0,0 +1,23 @@
|
|
1
|
+
Feature: (checker) indentation width
|
2
|
+
|
3
|
+
Scenario: inconsistent indentation
|
4
|
+
Given a file named "foo/indentation.feature" with:
|
5
|
+
"""
|
6
|
+
Feature: indentation
|
7
|
+
|
8
|
+
Scenario: correctly indented
|
9
|
+
|
10
|
+
Scenario: poorly indented
|
11
|
+
Then nothing
|
12
|
+
"""
|
13
|
+
When I run `greener`
|
14
|
+
Then the output should contain:
|
15
|
+
"""
|
16
|
+
F
|
17
|
+
|
18
|
+
foo/indentation.feature:5
|
19
|
+
Scenario: poorly indented
|
20
|
+
^^^ inconsistent indentation detected
|
21
|
+
|
22
|
+
1 file(s) inspected, 1 offense(s) detected
|
23
|
+
"""
|
@@ -0,0 +1,59 @@
|
|
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:
|
15
|
+
"""
|
16
|
+
.
|
17
|
+
|
18
|
+
1 file(s) inspected, no offenses detected
|
19
|
+
"""
|
20
|
+
|
21
|
+
Scenario: invalid checker specified in config
|
22
|
+
Given a file named "foo/something.feature" with:
|
23
|
+
"""
|
24
|
+
Feature: something
|
25
|
+
"""
|
26
|
+
And a file named "config/invalid.yml" with:
|
27
|
+
"""
|
28
|
+
Style/NotEvenReal:
|
29
|
+
Enabled: false
|
30
|
+
"""
|
31
|
+
When I run `greener --config config/invalid.yml`
|
32
|
+
Then the output should contain exactly "Unknown checker specified: Style/NotEvenReal\n"
|
33
|
+
|
34
|
+
Scenario: complex configuration
|
35
|
+
Given a file named "foo/indentation_too.feature" with:
|
36
|
+
"""
|
37
|
+
Feature: BAD NAME LOL
|
38
|
+
|
39
|
+
Scenario: correctly indented
|
40
|
+
|
41
|
+
Scenario: correctly indented
|
42
|
+
Then boom
|
43
|
+
"""
|
44
|
+
And a file named "config/complex.yml" with:
|
45
|
+
"""
|
46
|
+
Style/FeatureName:
|
47
|
+
Enabled: false
|
48
|
+
|
49
|
+
Style/IndentationWidth:
|
50
|
+
Enabled: true
|
51
|
+
Width: 4
|
52
|
+
"""
|
53
|
+
When I run `greener --config config/complex.yml`
|
54
|
+
Then the output should contain:
|
55
|
+
"""
|
56
|
+
.
|
57
|
+
|
58
|
+
1 file(s) inspected, no offenses detected
|
59
|
+
"""
|
@@ -0,0 +1,34 @@
|
|
1
|
+
Feature: (formatter) progress
|
2
|
+
|
3
|
+
Scenario: happy path
|
4
|
+
Given a file named "foo/good.feature" with:
|
5
|
+
"""
|
6
|
+
Feature: good
|
7
|
+
|
8
|
+
Scenario: correctly indented
|
9
|
+
|
10
|
+
Scenario: correctly indented
|
11
|
+
Then boom
|
12
|
+
"""
|
13
|
+
Given a file named "foo/good_too.feature" with:
|
14
|
+
"""
|
15
|
+
Feature: good too
|
16
|
+
|
17
|
+
Scenario: correctly indented
|
18
|
+
|
19
|
+
Scenario: correctly indented
|
20
|
+
Then boom
|
21
|
+
"""
|
22
|
+
And a file named "config/good.yml" with:
|
23
|
+
"""
|
24
|
+
AllCheckers:
|
25
|
+
Formatters:
|
26
|
+
- Progress
|
27
|
+
"""
|
28
|
+
When I run `greener --config config/good.yml`
|
29
|
+
Then the output should contain:
|
30
|
+
"""
|
31
|
+
..
|
32
|
+
|
33
|
+
2 file(s) inspected, no offenses detected
|
34
|
+
"""
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Feature: lint
|
2
|
+
|
3
|
+
Scenario: multiple issues
|
4
|
+
Given a file named "foo/multiple_issues.feature" with:
|
5
|
+
"""
|
6
|
+
Feature: multiple issues
|
7
|
+
|
8
|
+
Scenario: correctly indented
|
9
|
+
|
10
|
+
Scenario: poorly indented
|
11
|
+
Then nothing
|
12
|
+
"""
|
13
|
+
When I run `greener`
|
14
|
+
Then the output should contain:
|
15
|
+
"""
|
16
|
+
F
|
17
|
+
|
18
|
+
foo/multiple_issues.feature:1
|
19
|
+
Feature: multiple issues
|
20
|
+
^^^ inconsistent indentation detected
|
21
|
+
|
22
|
+
foo/multiple_issues.feature:5
|
23
|
+
Scenario: poorly indented
|
24
|
+
^^^ inconsistent indentation detected
|
25
|
+
|
26
|
+
1 file(s) inspected, 2 offense(s) detected\n
|
27
|
+
"""
|
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 = "greener2"
|
8
|
+
spec.version = Greener::VERSION
|
9
|
+
spec.authors = ["ikozakov"]
|
10
|
+
spec.email = ["ivankozakov0@gmail.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/ikozakov/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 "gherkin3", "~> 3.1.1"
|
35
|
+
spec.add_dependency "thor", "~> 0.19.1"
|
36
|
+
spec.add_dependency "titleize", "~> 1.3.0"
|
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, msg = nil, raw_txt = 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,48 @@
|
|
1
|
+
require "greener/checker/base"
|
2
|
+
require "titleize"
|
3
|
+
|
4
|
+
module Greener
|
5
|
+
module Checker
|
6
|
+
module Style
|
7
|
+
# Ensure .feature filename matches feature name in the Gherkin
|
8
|
+
# e.g. filename_of_feature.feature => "Feature: filename of feature"
|
9
|
+
class FeatureName < Base
|
10
|
+
MSG = "feature title does not match file name"
|
11
|
+
|
12
|
+
def run
|
13
|
+
return unless @config["Enabled"]
|
14
|
+
|
15
|
+
run_against_filename
|
16
|
+
run_against_titlecase
|
17
|
+
end
|
18
|
+
|
19
|
+
def run_against_filename
|
20
|
+
filename_without_extension = File.basename(@path, ".*")
|
21
|
+
expected = feature[:name]
|
22
|
+
expected = expected.gsub(/[^0-9a-z ]/i, "") if allow_punctuation?
|
23
|
+
expected = expected.downcase.gsub(" ", "_")
|
24
|
+
return if filename_without_extension == expected
|
25
|
+
log_violation(feature[:location][:line], feature[:location][:column])
|
26
|
+
end
|
27
|
+
|
28
|
+
def run_against_titlecase
|
29
|
+
return unless enforce_title_case?
|
30
|
+
return if feature[:name].titlecase == feature[:name]
|
31
|
+
log_violation(
|
32
|
+
feature[:location][:line],
|
33
|
+
feature[:location][:column],
|
34
|
+
"feature title is not title case. expected: #{feature[:name].titlecase}"
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def allow_punctuation?
|
39
|
+
@config["AllowPunctuation"]
|
40
|
+
end
|
41
|
+
|
42
|
+
def enforce_title_case?
|
43
|
+
@config["EnforceTitleCase"]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
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,19 @@
|
|
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
|
+
end
|
16
|
+
|
17
|
+
default_task :lint
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
require "greener/utils"
|
4
|
+
require "greener/error"
|
5
|
+
|
6
|
+
# Require all checker files here
|
7
|
+
require "greener/checker/style/feature_name"
|
8
|
+
require "greener/checker/style/indentation_width"
|
9
|
+
|
10
|
+
# And formatters
|
11
|
+
require "greener/formatter/progress"
|
12
|
+
require "greener/formatter/simple_text"
|
13
|
+
require "greener/formatter/summary"
|
14
|
+
|
15
|
+
module Greener
|
16
|
+
# Read configs from a user-specified greener.yml or fallback to defaults
|
17
|
+
class ConfigStore
|
18
|
+
include Utils
|
19
|
+
|
20
|
+
attr_reader :checkers, :files, :formatters
|
21
|
+
|
22
|
+
def initialize(path, default_path = nil)
|
23
|
+
@path = path
|
24
|
+
default_path ||= default_absolute_path
|
25
|
+
@default_path = default_path
|
26
|
+
|
27
|
+
@checkers = {}
|
28
|
+
@files = []
|
29
|
+
@formatters = []
|
30
|
+
end
|
31
|
+
|
32
|
+
def resolve
|
33
|
+
if @path
|
34
|
+
fail Error::Standard, "No config file found at specified path: #{@path}" unless File.exist? @path
|
35
|
+
config = load_yml_file @path
|
36
|
+
end
|
37
|
+
|
38
|
+
config ||= {}
|
39
|
+
defaults = load_yml_file @default_path
|
40
|
+
@all = merge_hashes(defaults, config)
|
41
|
+
|
42
|
+
validate
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
# Stub-able methods
|
47
|
+
def load_yml_file(path)
|
48
|
+
YAML.load_file(path)
|
49
|
+
end
|
50
|
+
|
51
|
+
def files_matching_glob(glob)
|
52
|
+
Dir.glob(glob).select { |e| File.file? e }
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# Deep merge, with a few post-merge checks
|
58
|
+
def merge_hashes(default, opt)
|
59
|
+
result = deep_merge(default, opt)
|
60
|
+
# Change nils to empty hashes/arrays so this class isn't littered with #nil? checks
|
61
|
+
result["AllCheckers"]["Exclude"] = [] if result["AllCheckers"]["Exclude"].nil?
|
62
|
+
|
63
|
+
result
|
64
|
+
end
|
65
|
+
|
66
|
+
def deep_merge(first, second)
|
67
|
+
merger = proc { |_key, v1, v2| v1.is_a?(Hash) && v2.is_a?(Hash) ? v1.merge(v2, &merger) : v2 }
|
68
|
+
first.merge(second, &merger)
|
69
|
+
end
|
70
|
+
|
71
|
+
def validate
|
72
|
+
set_formatters
|
73
|
+
set_checkers
|
74
|
+
set_files
|
75
|
+
|
76
|
+
@all.delete("AllCheckers") if @all["AllCheckers"] && @all["AllCheckers"].empty?
|
77
|
+
|
78
|
+
@all.each do |k, _v|
|
79
|
+
fail Error::Standard, "Unknown option in config file: #{k}" # TODO: print warning instead of fail
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def set_formatters
|
84
|
+
formatters = @all["AllCheckers"]["Formatters"].uniq.compact
|
85
|
+
# Ensure "Summary" formatter is in last position
|
86
|
+
if formatters.include?("Summary")
|
87
|
+
formatters << formatters.delete("Summary")
|
88
|
+
else
|
89
|
+
formatters << "Summary"
|
90
|
+
end
|
91
|
+
formatters.each do |f_string|
|
92
|
+
@formatters << formatter_from_string(f_string)
|
93
|
+
end
|
94
|
+
|
95
|
+
@all["AllCheckers"].delete "Formatters"
|
96
|
+
end
|
97
|
+
|
98
|
+
def set_checkers
|
99
|
+
@all.each do |k, v|
|
100
|
+
next unless %w( Style/ Lint/ ).any? { |prefix| k.start_with?(prefix) }
|
101
|
+
checker_klass = checker_from_string(k)
|
102
|
+
@checkers[checker_klass] = v
|
103
|
+
@all.delete(k)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def set_files
|
108
|
+
includes = []
|
109
|
+
excludes = []
|
110
|
+
|
111
|
+
@all["AllCheckers"]["Include"].each { |glob| includes += files_matching_glob(glob) }
|
112
|
+
@all["AllCheckers"].delete "Include"
|
113
|
+
|
114
|
+
@all["AllCheckers"]["Exclude"].each { |glob| excludes += files_matching_glob(glob) }
|
115
|
+
@all["AllCheckers"].delete "Exclude"
|
116
|
+
|
117
|
+
@files = includes.uniq - excludes.uniq
|
118
|
+
end
|
119
|
+
|
120
|
+
def default_absolute_path
|
121
|
+
File.expand_path("../../../config/defaults.yml", __FILE__)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Greener
|
2
|
+
module Formatter
|
3
|
+
# Abstract base class for formatter, implements all public API methods.
|
4
|
+
#
|
5
|
+
# ## Method Invocation Order
|
6
|
+
#
|
7
|
+
# For example, when Greener inspects 2 files,
|
8
|
+
# the invocation order should be like this:
|
9
|
+
#
|
10
|
+
# * `#initialize`
|
11
|
+
# * `#started`
|
12
|
+
# * `#file_started`
|
13
|
+
# * `#file_finished`
|
14
|
+
# * `#file_started`
|
15
|
+
# * `#file_finished`
|
16
|
+
# * `#finished`
|
17
|
+
class BaseFormatter
|
18
|
+
def initialize(files)
|
19
|
+
@files = files
|
20
|
+
end
|
21
|
+
|
22
|
+
def started
|
23
|
+
end
|
24
|
+
|
25
|
+
def file_started
|
26
|
+
end
|
27
|
+
|
28
|
+
def file_finished(_violations)
|
29
|
+
end
|
30
|
+
|
31
|
+
def finished(_violations)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "greener/formatter/base_formatter"
|
2
|
+
|
3
|
+
module Greener
|
4
|
+
module Formatter
|
5
|
+
# Print progress in real-time
|
6
|
+
class Progress < BaseFormatter
|
7
|
+
def file_finished(violations)
|
8
|
+
if violations.empty?
|
9
|
+
print "."
|
10
|
+
else
|
11
|
+
print "F"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def finished(_violations)
|
16
|
+
puts ""
|
17
|
+
puts ""
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "greener/formatter/base_formatter"
|
2
|
+
|
3
|
+
module Greener
|
4
|
+
module Formatter
|
5
|
+
# Prints violation info that includes file, line number, text of the line, and message
|
6
|
+
class SimpleText < BaseFormatter
|
7
|
+
def finished(violations)
|
8
|
+
violations.each do |violation|
|
9
|
+
puts "#{violation[:file]}:#{violation[:line]}"
|
10
|
+
puts "#{violation[:text_of_line]}"
|
11
|
+
puts "#{' ' * (violation[:column] - 1)}^^^ #{violation[:message]}"
|
12
|
+
puts ""
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "greener/formatter/base_formatter"
|
2
|
+
|
3
|
+
module Greener
|
4
|
+
module Formatter
|
5
|
+
# Prints summary line, e.g. "10 file(s) inspected, no offenses detected"
|
6
|
+
class Summary < BaseFormatter
|
7
|
+
def finished(violations)
|
8
|
+
conclusion = violations.empty? ? "no offenses detected" : "#{violations.count} offense(s) detected"
|
9
|
+
|
10
|
+
res = "#{@files.count} file(s) inspected, #{conclusion}"
|
11
|
+
return puts(res) if violations.empty?
|
12
|
+
fail Greener::Error::LintFailed, res
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Greener
|
2
|
+
# A set of Greener::Formatter::SomeFormatter objects
|
3
|
+
class FormatterSet
|
4
|
+
def initialize(formatters)
|
5
|
+
@formatters = formatters
|
6
|
+
end
|
7
|
+
|
8
|
+
# Note the "d"
|
9
|
+
def initialized(*args)
|
10
|
+
@formatters = @formatters.map { |formatter| formatter.new(*args) }
|
11
|
+
end
|
12
|
+
|
13
|
+
# All these instance methods have the same method signature as those of the Formatter classes
|
14
|
+
[:started, :file_started, :file_finished, :finished].each do |method_name|
|
15
|
+
define_method method_name do |*args|
|
16
|
+
@formatters.each { |formatter| formatter.send(method_name, *args) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "greener/config_store"
|
2
|
+
require "greener/formatter_set"
|
3
|
+
require "greener/parser"
|
4
|
+
|
5
|
+
module Greener
|
6
|
+
# Parse then lint a collection of .feature files for violations
|
7
|
+
class Linter
|
8
|
+
def initialize(config_path)
|
9
|
+
@config = ConfigStore.new(config_path).resolve
|
10
|
+
@results = []
|
11
|
+
@formatter_set = FormatterSet.new @config.formatters
|
12
|
+
end
|
13
|
+
|
14
|
+
def lint
|
15
|
+
@formatter_set.initialized(@config.files)
|
16
|
+
@formatter_set.started
|
17
|
+
|
18
|
+
# Here we iterate through a list of files, then iterate through the list of
|
19
|
+
# desired checkers, passing each one the filename & parsed AST.
|
20
|
+
# This is fine for now, but to speed this up we could refactor this to do a
|
21
|
+
# single pass through the file, line-by-line, and flagging violations as
|
22
|
+
# they are encountered.
|
23
|
+
@config.files.each do |f|
|
24
|
+
process_file(f) # TODO: move this to its own class
|
25
|
+
end
|
26
|
+
|
27
|
+
@formatter_set.finished @results
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def process_file(fname)
|
33
|
+
@formatter_set.file_started
|
34
|
+
|
35
|
+
ast = Parser.new(fname).ast
|
36
|
+
|
37
|
+
violations_in_file = []
|
38
|
+
|
39
|
+
@config.checkers.each do |sc_klass, sc_opts|
|
40
|
+
checker = sc_klass.new(ast, fname, sc_opts)
|
41
|
+
checker.run
|
42
|
+
violations_in_file += checker.violations
|
43
|
+
@results += checker.violations
|
44
|
+
end
|
45
|
+
|
46
|
+
@formatter_set.file_finished(violations_in_file)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "stringio"
|
2
|
+
|
3
|
+
module Greener
|
4
|
+
# Initialize this class to delay output, https://gist.github.com/macek/596007
|
5
|
+
class OutputBuffer
|
6
|
+
def initialize
|
7
|
+
@buffer = StringIO.new
|
8
|
+
activate
|
9
|
+
end
|
10
|
+
|
11
|
+
def activate
|
12
|
+
return if @activated
|
13
|
+
self.class.original_stdout = $stdout
|
14
|
+
$stdout = @buffer
|
15
|
+
@activated = true
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
@buffer.rewind
|
20
|
+
@buffer.read
|
21
|
+
end
|
22
|
+
|
23
|
+
def stop
|
24
|
+
self.class.restore_default
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
attr_accessor :original_stdout
|
29
|
+
|
30
|
+
def restore_default
|
31
|
+
$stdout = original_stdout
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "gherkin3/parser"
|
2
|
+
require "gherkin3/token_scanner"
|
3
|
+
|
4
|
+
module Greener
|
5
|
+
# Wrapper around Gherkin3's Parser
|
6
|
+
class Parser
|
7
|
+
def initialize(feature)
|
8
|
+
@feature = feature # filepath or String
|
9
|
+
end
|
10
|
+
|
11
|
+
# Return an Abstract Syntax Tree from a feature file
|
12
|
+
def ast
|
13
|
+
parser = Gherkin3::Parser.new
|
14
|
+
scanner = Gherkin3::TokenScanner.new(@feature)
|
15
|
+
parser.parse(scanner)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
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::Error::Standard => 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,31 @@
|
|
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 Error::Standard, "Unknown checker specified: #{str}" # TODO: print warning instead of failing
|
9
|
+
end
|
10
|
+
|
11
|
+
def formatter_from_string(str)
|
12
|
+
constantize "Greener::Formatter::#{str}"
|
13
|
+
rescue NameError
|
14
|
+
raise Error::Standard, "Unknown formatter specified: #{str}" # TODO: print warning instead of failing
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Borrowed from Rails
|
20
|
+
def constantize(camel_cased_word)
|
21
|
+
names = camel_cased_word.split("::")
|
22
|
+
names.shift if names.empty? || names.first.empty?
|
23
|
+
|
24
|
+
constant = Object
|
25
|
+
names.each do |name|
|
26
|
+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
27
|
+
end
|
28
|
+
constant
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/greener.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "greener/runner"
|
metadata
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: greener2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- ikozakov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-03-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: gherkin3
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.1.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.1.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: thor
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.19.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.19.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: titleize
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.3.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.3.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: aruba
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.6.2
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.6.2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.9.5
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.9.5
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: codeclimate-test-reporter
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.4.6
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.4.6
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: coveralls
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.7.9
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.7.9
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rake
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '10.0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '10.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 3.3.0
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 3.3.0
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rubocop
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 0.32.0
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 0.32.0
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: simplecov
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: 0.9.1
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: 0.9.1
|
167
|
+
description: Keep your Gherkin readable with the greener linter...
|
168
|
+
email:
|
169
|
+
- ivankozakov0@gmail.com
|
170
|
+
executables:
|
171
|
+
- greener
|
172
|
+
extensions: []
|
173
|
+
extra_rdoc_files: []
|
174
|
+
files:
|
175
|
+
- CHANGELOG.md
|
176
|
+
- LICENSE.txt
|
177
|
+
- README.md
|
178
|
+
- bin/greener
|
179
|
+
- config/defaults.yml
|
180
|
+
- config/dogfood.yml
|
181
|
+
- features/checker_feature_name.feature
|
182
|
+
- features/checker_indentation_width.feature
|
183
|
+
- features/configuration.feature
|
184
|
+
- features/formatter_progress.feature
|
185
|
+
- features/lint.feature
|
186
|
+
- features/support/env.rb
|
187
|
+
- greener.gemspec
|
188
|
+
- lib/greener.rb
|
189
|
+
- lib/greener/checker/base.rb
|
190
|
+
- lib/greener/checker/style/feature_name.rb
|
191
|
+
- lib/greener/checker/style/indentation_width.rb
|
192
|
+
- lib/greener/cli.rb
|
193
|
+
- lib/greener/config_store.rb
|
194
|
+
- lib/greener/error.rb
|
195
|
+
- lib/greener/formatter/base_formatter.rb
|
196
|
+
- lib/greener/formatter/progress.rb
|
197
|
+
- lib/greener/formatter/simple_text.rb
|
198
|
+
- lib/greener/formatter/summary.rb
|
199
|
+
- lib/greener/formatter_set.rb
|
200
|
+
- lib/greener/linter.rb
|
201
|
+
- lib/greener/output_buffer.rb
|
202
|
+
- lib/greener/parser.rb
|
203
|
+
- lib/greener/runner.rb
|
204
|
+
- lib/greener/utils.rb
|
205
|
+
- lib/greener/version.rb
|
206
|
+
homepage: https://github.com/ikozakov/greener
|
207
|
+
licenses:
|
208
|
+
- MIT
|
209
|
+
metadata: {}
|
210
|
+
post_install_message:
|
211
|
+
rdoc_options: []
|
212
|
+
require_paths:
|
213
|
+
- lib
|
214
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
215
|
+
requirements:
|
216
|
+
- - ">="
|
217
|
+
- !ruby/object:Gem::Version
|
218
|
+
version: '0'
|
219
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
220
|
+
requirements:
|
221
|
+
- - ">="
|
222
|
+
- !ruby/object:Gem::Version
|
223
|
+
version: '0'
|
224
|
+
requirements: []
|
225
|
+
rubyforge_project:
|
226
|
+
rubygems_version: 2.6.11
|
227
|
+
signing_key:
|
228
|
+
specification_version: 4
|
229
|
+
summary: A Gherkin .feature file linter
|
230
|
+
test_files: []
|