Skip to content

Commit

Permalink
Merge pull request #471 from crystal-ameba/feature/rule-since-version
Browse files Browse the repository at this point in the history
Implement rule versioning
  • Loading branch information
Sija authored Nov 26, 2024
2 parents a21dea0 + 3552fd0 commit 26cfa07
Show file tree
Hide file tree
Showing 91 changed files with 337 additions and 63 deletions.
7 changes: 7 additions & 0 deletions spec/ameba/cli/cmd_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ module Ameba::Cli
end
end

%w[-u --up-to-version].each do |flag|
it "accepts #{flag} flag" do
c = Cli.parse_args [flag, "1.5.0"]
c.version.should eq "1.5.0"
end
end

it "accepts --stdin-filename flag" do
c = Cli.parse_args %w[--stdin-filename foo.cr]
c.stdin_filename.should eq "foo.cr"
Expand Down
27 changes: 27 additions & 0 deletions spec/ameba/config_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ module Ameba
it "loads custom config" do
config = Config.load config_sample
config.should_not be_nil
config.version.should_not be_nil
config.globs.should_not be_nil
config.formatter.should_not be_nil
end
Expand All @@ -108,6 +109,7 @@ module Ameba
it "loads default config" do
config = Config.load
config.should_not be_nil
config.version.should be_nil
config.globs.should_not be_nil
config.formatter.should_not be_nil
end
Expand Down Expand Up @@ -184,6 +186,31 @@ module Ameba
end
end

describe "#version, version=" do
config = Config.load config_sample
version = SemanticVersion.parse("1.5.0")

it "contains default version" do
config.version.should_not be_nil
end

it "allows to set version" do
config.version = version
config.version.should eq version
end

it "allows to set version using a string" do
config.version = version.to_s
config.version.should eq version
end

it "raises an error if version is not valid" do
expect_raises(Exception) do
config.version = "foo"
end
end
end

describe "#update_rule" do
config = Config.load config_sample

Expand Down
8 changes: 2 additions & 6 deletions spec/ameba/presenter/rule_collection_presenter_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@ require "../../spec_helper"

module Ameba
private def with_rule_collection_presenter(&)
with_presenter(Presenter::RuleCollectionPresenter) do |presenter, io|
rules = Config.load.rules
presenter.run(rules)

output = io.to_s
output = Formatter::Util.deansify(output).to_s
rules = Config.load.rules

with_presenter(Presenter::RuleCollectionPresenter, rules) do |presenter, output|
yield rules, output, presenter
end
end
Expand Down
10 changes: 3 additions & 7 deletions spec/ameba/presenter/rule_presenter_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@ require "../../spec_helper"

module Ameba
private def rule_presenter_each_rule(&)
with_presenter(Presenter::RulePresenter) do |presenter, io|
rules = Config.load.rules
rules.each do |rule|
presenter.run(rule)

output = io.to_s
output = Formatter::Util.deansify(output).to_s
rules = Config.load.rules

rules.each do |rule|
with_presenter(Presenter::RulePresenter, rule) do |presenter, output|
yield rule, output, presenter
end
end
Expand Down
29 changes: 29 additions & 0 deletions spec/ameba/presenter/rule_versions_presenter_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require "../../spec_helper"

module Ameba
private def with_rule_versions_presenter(&)
rules = Config.load.rules

with_presenter(Presenter::RuleVersionsPresenter, rules) do |presenter, output|
yield rules, output, presenter
end
end

describe Presenter::RuleVersionsPresenter do
it "outputs rule versions" do
with_rule_versions_presenter do |_rules, output|
output.should contain <<-TEXT
- 0.1.0
- Layout/LineLength
- Layout/TrailingBlankLines
- Layout/TrailingWhitespace
- Lint/ComparisonToBoolean
- Lint/DebuggerStatement
- Lint/LiteralInCondition
- Lint/LiteralInInterpolation
- Style/UnlessElse
TEXT
end
end
end
end
15 changes: 15 additions & 0 deletions spec/ameba/runner_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module Ameba
config.formatter = formatter
config.globs = files

config.update_rule VersionedRule.rule_name, enabled: false
config.update_rule ErrorRule.rule_name, enabled: false
config.update_rule PerfRule.rule_name, enabled: false
config.update_rule AtoAA.rule_name, enabled: false
Expand Down Expand Up @@ -48,6 +49,20 @@ module Ameba
formatter.finished_source.should_not be_nil
end

it "checks accordingly to the rule #since_version" do
rules = [VersionedRule.new] of Rule::Base
source = Source.new "", "source.cr"

v1_0_0 = SemanticVersion.parse("1.0.0")
Runner.new(rules, [source], formatter, default_severity, false, v1_0_0).run.success?.should be_true

v1_5_0 = SemanticVersion.parse("1.5.0")
Runner.new(rules, [source], formatter, default_severity, false, v1_5_0).run.success?.should be_false

v1_10_0 = SemanticVersion.parse("1.10.0")
Runner.new(rules, [source], formatter, default_severity, false, v1_10_0).run.success?.should be_false
end

it "skips rule check if source is excluded" do
path = "source.cr"
source = Source.new "", path
Expand Down
2 changes: 2 additions & 0 deletions spec/fixtures/config.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
Version: "1.5.0"

Lint/ComparisonToBoolean:
Enabled: true
20 changes: 18 additions & 2 deletions spec/spec_helper.cr
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ module Ameba
end
end

class VersionedRule < Rule::Base
properties do
since_version "1.5.0"
description "Rule with a custom version."
end

def test(source)
issue_for({1, 1}, "This rule always adds an error")
end
end

# Rule extended description
class ErrorRule < Rule::Base
properties do
Expand Down Expand Up @@ -283,11 +294,16 @@ module Ameba
end
end

def with_presenter(klass, &)
def with_presenter(klass, *args, deansify = true, **kwargs, &)
io = IO::Memory.new

presenter = klass.new(io)
presenter.run(*args, **kwargs)

output = io.to_s
output = Ameba::Formatter::Util.deansify(output).to_s if deansify

yield presenter, io
yield presenter, output
end

def as_node(source)
Expand Down
98 changes: 60 additions & 38 deletions src/ameba/cli/cmd.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,47 @@ require "option_parser"
module Ameba::Cli
extend self

# ameba:disable Metrics/CyclomaticComplexity
private class Opts
property config : Path?
property version : String?
property formatter : Symbol | String | Nil
property globs : Array(String)?
property only : Array(String)?
property except : Array(String)?
property describe_rule : String?
property location_to_explain : NamedTuple(file: String, line: Int32, column: Int32)?
property fail_level : Severity?
property stdin_filename : String?
property? skip_reading_config = false
property? rules = false
property? rule_versions = false
property? all = false
property? colors = true
property? without_affected_code = false
property? autocorrect = false
end

def run(args = ARGV) : Nil
opts = parse_args args
location_to_explain = opts.location_to_explain
stdin_filename = opts.stdin_filename
autocorrect = opts.autocorrect?
opts = parse_args(args)

if location_to_explain && autocorrect
if (location_to_explain = opts.location_to_explain) && opts.autocorrect?
raise "Invalid usage: Cannot explain an issue and autocorrect at the same time."
end

if stdin_filename && autocorrect
if opts.stdin_filename && opts.autocorrect?
raise "Invalid usage: Cannot autocorrect from stdin."
end

config = Config.load opts.config, opts.colors?, opts.skip_reading_config?
config.autocorrect = autocorrect
config.stdin_filename = stdin_filename

if globs = opts.globs
config.globs = globs
end
if fail_level = opts.fail_level
config.severity = fail_level
end

configure_formatter(config, opts)
configure_rules(config, opts)
config = config_from_opts(opts)

if opts.rules?
print_rules(config.rules)
end

if opts.rule_versions?
print_rule_versions(config.rules)
end

if describe_rule_name = opts.describe_rule
unless rule = config.rules.find(&.name.== describe_rule_name)
raise "Unknown rule"
Expand All @@ -57,31 +65,14 @@ module Ameba::Cli
exit 255
end

private class Opts
property config : Path?
property formatter : Symbol | String | Nil
property globs : Array(String)?
property only : Array(String)?
property except : Array(String)?
property describe_rule : String?
property location_to_explain : NamedTuple(file: String, line: Int32, column: Int32)?
property fail_level : Severity?
property stdin_filename : String?
property? skip_reading_config = false
property? rules = false
property? all = false
property? colors = true
property? without_affected_code = false
property? autocorrect = false
end

def parse_args(args, opts = Opts.new)
OptionParser.parse(args) do |parser|
parser.banner = "Usage: ameba [options] [file1 file2 ...]"

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

parser.on("-u", "--up-to-version VERSION",
"Choose a version") do |version|
opts.version = version
end

parser.on("-f", "--format FORMATTER",
"Choose an output formatter: #{Config.formatter_names}") do |formatter|
opts.formatter = formatter
Expand Down Expand Up @@ -160,6 +156,27 @@ module Ameba::Cli
opts
end

private def config_from_opts(opts)
config = Config.load opts.config, opts.colors?, opts.skip_reading_config?
config.autocorrect = opts.autocorrect?
config.stdin_filename = opts.stdin_filename

if version = opts.version
config.version = version
end
if globs = opts.globs
config.globs = globs
end
if fail_level = opts.fail_level
config.severity = fail_level
end

configure_formatter(config, opts)
configure_rules(config, opts)

config
end

private def configure_rules(config, opts) : Nil
case
when only = opts.only
Expand Down Expand Up @@ -225,4 +242,9 @@ module Ameba::Cli
Presenter::RuleCollectionPresenter.new.run(rules)
exit 0
end

private def print_rule_versions(rules)
Presenter::RuleVersionsPresenter.new.run(rules)
exit 0
end
end
Loading

0 comments on commit 26cfa07

Please sign in to comment.