Skip to content

Commit 26cfa07

Browse files
authored
Merge pull request #471 from crystal-ameba/feature/rule-since-version
Implement rule versioning
2 parents a21dea0 + 3552fd0 commit 26cfa07

File tree

91 files changed

+337
-63
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+337
-63
lines changed

spec/ameba/cli/cmd_spec.cr

+7
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ module Ameba::Cli
3232
end
3333
end
3434

35+
%w[-u --up-to-version].each do |flag|
36+
it "accepts #{flag} flag" do
37+
c = Cli.parse_args [flag, "1.5.0"]
38+
c.version.should eq "1.5.0"
39+
end
40+
end
41+
3542
it "accepts --stdin-filename flag" do
3643
c = Cli.parse_args %w[--stdin-filename foo.cr]
3744
c.stdin_filename.should eq "foo.cr"

spec/ameba/config_spec.cr

+27
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ module Ameba
9595
it "loads custom config" do
9696
config = Config.load config_sample
9797
config.should_not be_nil
98+
config.version.should_not be_nil
9899
config.globs.should_not be_nil
99100
config.formatter.should_not be_nil
100101
end
@@ -108,6 +109,7 @@ module Ameba
108109
it "loads default config" do
109110
config = Config.load
110111
config.should_not be_nil
112+
config.version.should be_nil
111113
config.globs.should_not be_nil
112114
config.formatter.should_not be_nil
113115
end
@@ -184,6 +186,31 @@ module Ameba
184186
end
185187
end
186188

189+
describe "#version, version=" do
190+
config = Config.load config_sample
191+
version = SemanticVersion.parse("1.5.0")
192+
193+
it "contains default version" do
194+
config.version.should_not be_nil
195+
end
196+
197+
it "allows to set version" do
198+
config.version = version
199+
config.version.should eq version
200+
end
201+
202+
it "allows to set version using a string" do
203+
config.version = version.to_s
204+
config.version.should eq version
205+
end
206+
207+
it "raises an error if version is not valid" do
208+
expect_raises(Exception) do
209+
config.version = "foo"
210+
end
211+
end
212+
end
213+
187214
describe "#update_rule" do
188215
config = Config.load config_sample
189216

spec/ameba/presenter/rule_collection_presenter_spec.cr

+2-6
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,9 @@ require "../../spec_helper"
22

33
module Ameba
44
private def with_rule_collection_presenter(&)
5-
with_presenter(Presenter::RuleCollectionPresenter) do |presenter, io|
6-
rules = Config.load.rules
7-
presenter.run(rules)
8-
9-
output = io.to_s
10-
output = Formatter::Util.deansify(output).to_s
5+
rules = Config.load.rules
116

7+
with_presenter(Presenter::RuleCollectionPresenter, rules) do |presenter, output|
128
yield rules, output, presenter
139
end
1410
end

spec/ameba/presenter/rule_presenter_spec.cr

+3-7
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,10 @@ require "../../spec_helper"
22

33
module Ameba
44
private def rule_presenter_each_rule(&)
5-
with_presenter(Presenter::RulePresenter) do |presenter, io|
6-
rules = Config.load.rules
7-
rules.each do |rule|
8-
presenter.run(rule)
9-
10-
output = io.to_s
11-
output = Formatter::Util.deansify(output).to_s
5+
rules = Config.load.rules
126

7+
rules.each do |rule|
8+
with_presenter(Presenter::RulePresenter, rule) do |presenter, output|
139
yield rule, output, presenter
1410
end
1511
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
require "../../spec_helper"
2+
3+
module Ameba
4+
private def with_rule_versions_presenter(&)
5+
rules = Config.load.rules
6+
7+
with_presenter(Presenter::RuleVersionsPresenter, rules) do |presenter, output|
8+
yield rules, output, presenter
9+
end
10+
end
11+
12+
describe Presenter::RuleVersionsPresenter do
13+
it "outputs rule versions" do
14+
with_rule_versions_presenter do |_rules, output|
15+
output.should contain <<-TEXT
16+
- 0.1.0
17+
- Layout/LineLength
18+
- Layout/TrailingBlankLines
19+
- Layout/TrailingWhitespace
20+
- Lint/ComparisonToBoolean
21+
- Lint/DebuggerStatement
22+
- Lint/LiteralInCondition
23+
- Lint/LiteralInInterpolation
24+
- Style/UnlessElse
25+
TEXT
26+
end
27+
end
28+
end
29+
end

spec/ameba/runner_spec.cr

+15
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module Ameba
66
config.formatter = formatter
77
config.globs = files
88

9+
config.update_rule VersionedRule.rule_name, enabled: false
910
config.update_rule ErrorRule.rule_name, enabled: false
1011
config.update_rule PerfRule.rule_name, enabled: false
1112
config.update_rule AtoAA.rule_name, enabled: false
@@ -48,6 +49,20 @@ module Ameba
4849
formatter.finished_source.should_not be_nil
4950
end
5051

52+
it "checks accordingly to the rule #since_version" do
53+
rules = [VersionedRule.new] of Rule::Base
54+
source = Source.new "", "source.cr"
55+
56+
v1_0_0 = SemanticVersion.parse("1.0.0")
57+
Runner.new(rules, [source], formatter, default_severity, false, v1_0_0).run.success?.should be_true
58+
59+
v1_5_0 = SemanticVersion.parse("1.5.0")
60+
Runner.new(rules, [source], formatter, default_severity, false, v1_5_0).run.success?.should be_false
61+
62+
v1_10_0 = SemanticVersion.parse("1.10.0")
63+
Runner.new(rules, [source], formatter, default_severity, false, v1_10_0).run.success?.should be_false
64+
end
65+
5166
it "skips rule check if source is excluded" do
5267
path = "source.cr"
5368
source = Source.new "", path

spec/fixtures/config.yml

+2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1+
Version: "1.5.0"
2+
13
Lint/ComparisonToBoolean:
24
Enabled: true

spec/spec_helper.cr

+18-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ module Ameba
2424
end
2525
end
2626

27+
class VersionedRule < Rule::Base
28+
properties do
29+
since_version "1.5.0"
30+
description "Rule with a custom version."
31+
end
32+
33+
def test(source)
34+
issue_for({1, 1}, "This rule always adds an error")
35+
end
36+
end
37+
2738
# Rule extended description
2839
class ErrorRule < Rule::Base
2940
properties do
@@ -283,11 +294,16 @@ module Ameba
283294
end
284295
end
285296

286-
def with_presenter(klass, &)
297+
def with_presenter(klass, *args, deansify = true, **kwargs, &)
287298
io = IO::Memory.new
299+
288300
presenter = klass.new(io)
301+
presenter.run(*args, **kwargs)
302+
303+
output = io.to_s
304+
output = Ameba::Formatter::Util.deansify(output).to_s if deansify
289305

290-
yield presenter, io
306+
yield presenter, output
291307
end
292308

293309
def as_node(source)

src/ameba/cli/cmd.cr

+60-38
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,47 @@ require "option_parser"
55
module Ameba::Cli
66
extend self
77

8-
# ameba:disable Metrics/CyclomaticComplexity
8+
private class Opts
9+
property config : Path?
10+
property version : String?
11+
property formatter : Symbol | String | Nil
12+
property globs : Array(String)?
13+
property only : Array(String)?
14+
property except : Array(String)?
15+
property describe_rule : String?
16+
property location_to_explain : NamedTuple(file: String, line: Int32, column: Int32)?
17+
property fail_level : Severity?
18+
property stdin_filename : String?
19+
property? skip_reading_config = false
20+
property? rules = false
21+
property? rule_versions = false
22+
property? all = false
23+
property? colors = true
24+
property? without_affected_code = false
25+
property? autocorrect = false
26+
end
27+
928
def run(args = ARGV) : Nil
10-
opts = parse_args args
11-
location_to_explain = opts.location_to_explain
12-
stdin_filename = opts.stdin_filename
13-
autocorrect = opts.autocorrect?
29+
opts = parse_args(args)
1430

15-
if location_to_explain && autocorrect
31+
if (location_to_explain = opts.location_to_explain) && opts.autocorrect?
1632
raise "Invalid usage: Cannot explain an issue and autocorrect at the same time."
1733
end
1834

19-
if stdin_filename && autocorrect
35+
if opts.stdin_filename && opts.autocorrect?
2036
raise "Invalid usage: Cannot autocorrect from stdin."
2137
end
2238

23-
config = Config.load opts.config, opts.colors?, opts.skip_reading_config?
24-
config.autocorrect = autocorrect
25-
config.stdin_filename = stdin_filename
26-
27-
if globs = opts.globs
28-
config.globs = globs
29-
end
30-
if fail_level = opts.fail_level
31-
config.severity = fail_level
32-
end
33-
34-
configure_formatter(config, opts)
35-
configure_rules(config, opts)
39+
config = config_from_opts(opts)
3640

3741
if opts.rules?
3842
print_rules(config.rules)
3943
end
4044

45+
if opts.rule_versions?
46+
print_rule_versions(config.rules)
47+
end
48+
4149
if describe_rule_name = opts.describe_rule
4250
unless rule = config.rules.find(&.name.== describe_rule_name)
4351
raise "Unknown rule"
@@ -57,31 +65,14 @@ module Ameba::Cli
5765
exit 255
5866
end
5967

60-
private class Opts
61-
property config : Path?
62-
property formatter : Symbol | String | Nil
63-
property globs : Array(String)?
64-
property only : Array(String)?
65-
property except : Array(String)?
66-
property describe_rule : String?
67-
property location_to_explain : NamedTuple(file: String, line: Int32, column: Int32)?
68-
property fail_level : Severity?
69-
property stdin_filename : String?
70-
property? skip_reading_config = false
71-
property? rules = false
72-
property? all = false
73-
property? colors = true
74-
property? without_affected_code = false
75-
property? autocorrect = false
76-
end
77-
7868
def parse_args(args, opts = Opts.new)
7969
OptionParser.parse(args) do |parser|
8070
parser.banner = "Usage: ameba [options] [file1 file2 ...]"
8171

8272
parser.on("-v", "--version", "Print version") { print_version }
8373
parser.on("-h", "--help", "Show this help") { print_help(parser) }
8474
parser.on("-r", "--rules", "Show all available rules") { opts.rules = true }
75+
parser.on("-R", "--rule-versions", "Show all available rule versions") { opts.rule_versions = true }
8576
parser.on("-s", "--silent", "Disable output") { opts.formatter = :silent }
8677
parser.unknown_args do |arr|
8778
case
@@ -99,6 +90,11 @@ module Ameba::Cli
9990
opts.config = Path[path] unless path.empty?
10091
end
10192

93+
parser.on("-u", "--up-to-version VERSION",
94+
"Choose a version") do |version|
95+
opts.version = version
96+
end
97+
10298
parser.on("-f", "--format FORMATTER",
10399
"Choose an output formatter: #{Config.formatter_names}") do |formatter|
104100
opts.formatter = formatter
@@ -160,6 +156,27 @@ module Ameba::Cli
160156
opts
161157
end
162158

159+
private def config_from_opts(opts)
160+
config = Config.load opts.config, opts.colors?, opts.skip_reading_config?
161+
config.autocorrect = opts.autocorrect?
162+
config.stdin_filename = opts.stdin_filename
163+
164+
if version = opts.version
165+
config.version = version
166+
end
167+
if globs = opts.globs
168+
config.globs = globs
169+
end
170+
if fail_level = opts.fail_level
171+
config.severity = fail_level
172+
end
173+
174+
configure_formatter(config, opts)
175+
configure_rules(config, opts)
176+
177+
config
178+
end
179+
163180
private def configure_rules(config, opts) : Nil
164181
case
165182
when only = opts.only
@@ -225,4 +242,9 @@ module Ameba::Cli
225242
Presenter::RuleCollectionPresenter.new.run(rules)
226243
exit 0
227244
end
245+
246+
private def print_rule_versions(rules)
247+
Presenter::RuleVersionsPresenter.new.run(rules)
248+
exit 0
249+
end
228250
end

0 commit comments

Comments
 (0)