diff --git a/README.md b/README.md index d4cd0fa5..4083d15d 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,18 @@ It allows to configure rule properties, disable specific rules and exclude sourc Generate new file by running `ameba --gen-config`. +### Only/Except + +One or more rules, or a one or more group of rules can be included or excluded +via command line arguments: + +``` +$ ameba --only Lint/Syntax # runs only Lint/Syntax rule +$ ameba --only Style,Lint # runs only rules from Style and Lint groups +$ ameba --except Lint/Syntax # runs all rules except Lint/Syntax +$ ameba --except Style,Lint # runs all rules except rules in Style and Lint groups +``` + ### Inline disabling One or more rules can be disabled using inline directives: diff --git a/spec/ameba/config_spec.cr b/spec/ameba/config_spec.cr index 75d6337a..f2ad3709 100644 --- a/spec/ameba/config_spec.cr +++ b/spec/ameba/config_spec.cr @@ -80,5 +80,39 @@ module Ameba rule.excluded.should eq excluded end end + + describe "#update_rules" do + config = Config.load config_sample + + it "updates multiple rules by enabled property" do + name = DummyRule.rule_name + config.update_rules [name], enabled: false + rule = config.rules.find(&.name.== name).not_nil! + rule.enabled.should be_false + end + + it "updates multiple rules by excluded property" do + name = DummyRule.rule_name + excluded = %w(spec/source.cr) + config.update_rules [name], excluded: excluded + rule = config.rules.find(&.name.== name).not_nil! + rule.excluded.should eq excluded + end + + it "updates a group of rules by enabled property" do + group = DummyRule.group_name + config.update_rules [group], enabled: false + rule = config.rules.find(&.name.== DummyRule.rule_name).not_nil! + rule.enabled.should be_false + end + + it "updates a group by excluded property" do + name = DummyRule.group_name + excluded = %w(spec/source.cr) + config.update_rules [name], excluded: excluded + rule = config.rules.find(&.name.== DummyRule.rule_name).not_nil! + rule.excluded.should eq excluded + end + end end end diff --git a/spec/ameba/rule/base_spec.cr b/spec/ameba/rule/base_spec.cr index d038f6fd..166e1e26 100644 --- a/spec/ameba/rule/base_spec.cr +++ b/spec/ameba/rule/base_spec.cr @@ -14,6 +14,12 @@ module Ameba DummyRule.new.name.should eq "Ameba/DummyRule" end end + + describe "#group" do + it "returns a group rule belongs to" do + DummyRule.new.group.should eq "Ameba" + end + end end describe Rule do diff --git a/src/ameba/cli/cmd.cr b/src/ameba/cli/cmd.cr index 3849030f..8613a17e 100644 --- a/src/ameba/cli/cmd.cr +++ b/src/ameba/cli/cmd.cr @@ -47,12 +47,12 @@ module Ameba::Cli end parser.on("--only RULE1,RULE2,...", - "Run only given rules") do |rules| + "Run only given rules (or groups)") do |rules| opts.only = rules.split "," end parser.on("--except RULE1,RULE2,...", - "Disable the given rules") do |rules| + "Disable the given rules (or groups)") do |rules| opts.except = rules.split "," end @@ -69,14 +69,10 @@ module Ameba::Cli private def configure_rules(config, opts) if only = opts.only config.rules.map! { |r| r.enabled = false; r } - only.each do |rule_name| - config.update_rule(rule_name, enabled: true) - end + config.update_rules(only, enabled: true) end - opts.except.try &.each do |rule_name| - config.update_rule(rule_name, enabled: false) - end + config.update_rules(opts.except, enabled: false) end private def configure_formatter(config, opts) diff --git a/src/ameba/config.cr b/src/ameba/config.cr index cf95dacc..0891b230 100644 --- a/src/ameba/config.cr +++ b/src/ameba/config.cr @@ -26,11 +26,14 @@ class Ameba::Config setter files : Array(String)? getter rules : Array(Rule::Base) + @rule_groups: Hash(String, Array(Rule::Base)) + # Creates a new instance of `Ameba::Config` based on YAML parameters. # # `Config.load` uses this constructor to instantiate new config by YAML file. protected def initialize(@config : YAML::Any) @rules = Rule.rules.map &.new(config).as(Rule::Base) + @rule_groups = @rules.group_by &.group if @config.as_h? && (name = @config["Formatter"]?.try &.["Name"]?) self.formatter = name.to_s @@ -113,6 +116,29 @@ class Ameba::Config @rules[index] = rule end + # Updates rules properties. + # + # ``` + # config = Ameba::Config.load + # config.update_rules %w(Rule1 Rule2), enabled: true + # ``` + # + # also it allows to update groups of rules: + # + # ``` + # config.update_rules %w(Group1 Group2), enabled: true + # ``` + # + def update_rules(names, **args) + names.try &.each do |name| + if group = @rule_groups[name]? + group.each { |rule| update_rule(rule.name, **args) } + else + update_rule name, **args + end + end + end + private def default_files Dir["**/*.cr"].reject(&.starts_with? "lib/") end diff --git a/src/ameba/rule/base.cr b/src/ameba/rule/base.cr index 3b79cfd0..8951fa51 100644 --- a/src/ameba/rule/base.cr +++ b/src/ameba/rule/base.cr @@ -65,6 +65,20 @@ module Ameba::Rule {{@type}}.rule_name end + # Returns a group this rule belong to. + # + # ``` + # struct MyGroup::MyRule < Ameba::Rule::Base + # # ... + # end + # + # MyGroup::MyRule.new.group # => "MyGroup" + # ``` + # + def group + {{@type}}.group_name + end + # Checks whether the source is excluded from this rule. # It searches for a path in `excluded` property which matches # the one of the given source. @@ -99,16 +113,19 @@ module Ameba::Rule name.gsub("Ameba::Rule::", "").gsub("::", "/") end + protected def self.group_name + rule_name.split("/")[0...-1].join("/") + end + protected def self.subclasses {{ @type.subclasses }} end end - # Returns a list of all available rules - # (except a `Rule::Syntax` which is a special rule). + # Returns a list of all available rules. # # ``` - # Ameba::Rule.rules # => [LineLength, ConstantNames, ....] + # Ameba::Rule.rules # => [Rule1, Rule2, ....] # ``` # def self.rules