mirror of
https://gitea.invidious.io/iv-org/shard-ameba.git
synced 2024-08-15 00:53:29 +00:00
Document configuration properties & specs
This commit is contained in:
parent
822a69b81c
commit
06855816d7
22 changed files with 325 additions and 8 deletions
|
@ -41,7 +41,7 @@ Or just compile it from sources `make install`.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Run `ameba` binary to catch code issues within you project:
|
Run `ameba` binary within your project directory to catch code issues:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ ameba
|
$ ameba
|
||||||
|
@ -63,6 +63,12 @@ Finished in 10.53 milliseconds
|
||||||
52 inspected, 3 failures.
|
52 inspected, 3 failures.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
It is possible to configure or even disable specific rules using YAML configuration file.
|
||||||
|
By default Ameba is looking for `.ameba.yml` in a project root directory.
|
||||||
|
Copy and adjust [existed example](config/ameba.yml).
|
||||||
|
|
||||||
## Write a new Rule
|
## Write a new Rule
|
||||||
|
|
||||||
Adding a new rule is as simple as inheriting from `Rule::Base` struct and implementing
|
Adding a new rule is as simple as inheriting from `Rule::Base` struct and implementing
|
||||||
|
|
67
config/ameba.yml
Normal file
67
config/ameba.yml
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
ComaprisonToBoolean:
|
||||||
|
# Disallows comparison to booleans in conditions.
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
ConstantNames:
|
||||||
|
# Enforces constant names to be in a screaming case.
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
DebuggerStatement:
|
||||||
|
# Disallows calls to debugger.
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
EmptyExpression:
|
||||||
|
# Disallows empty expressions.
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
LargeNumbers:
|
||||||
|
# A rule that disallows usage of large numbers without underscore.
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
LineLength:
|
||||||
|
# Disallows lines longer that MaxLength number of symbols.
|
||||||
|
Enabled: true
|
||||||
|
MaxLength: 80
|
||||||
|
|
||||||
|
LiteralInCondition:
|
||||||
|
# Disallows useless conditional statements that contain a literal in place
|
||||||
|
# of a variable or predicate function.
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
LiteralInInterpolation:
|
||||||
|
# Disallows useless string interpolations that contain a literal value
|
||||||
|
# instead of a variable or function.
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
MethodNames:
|
||||||
|
# Enforces method names to be in the underscored case.
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
NegatedConditionsInUnless:
|
||||||
|
# Disallows negated conditions in unless.
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
PredicateName:
|
||||||
|
# Disallows tautological predicate names, meaning those that start with
|
||||||
|
# the prefix `has_` or the prefix `is_`.
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
TrailingBlankLines:
|
||||||
|
# Disallows trailing blank lines at the end of the source file.
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
TrailingWhitespace:
|
||||||
|
# Disallows trailing whitespaces.
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
TypeNames:
|
||||||
|
# Enforces type names in a camelcase manner.
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
UnlessElse:
|
||||||
|
# Disallows the use of an `else` block with `unless`.
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
VariableNames:
|
||||||
|
# Enforces variable names to be in the underscored case.
|
||||||
|
Enabled: true
|
58
spec/ameba/config_spec.cr
Normal file
58
spec/ameba/config_spec.cr
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
require "../spec_helper"
|
||||||
|
|
||||||
|
module Ameba
|
||||||
|
describe Config do
|
||||||
|
config_sample = "config/ameba.yml"
|
||||||
|
|
||||||
|
describe ".load" do
|
||||||
|
it "loads custom config" do
|
||||||
|
config = Config.load config_sample
|
||||||
|
config.should_not be_nil
|
||||||
|
config.files.should_not be_nil
|
||||||
|
config.formatter.should_not be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "loads default config" do
|
||||||
|
config = Config.load
|
||||||
|
config.should_not be_nil
|
||||||
|
config.files.should_not be_nil
|
||||||
|
config.formatter.should_not be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#files, #files=" do
|
||||||
|
config = Config.load config_sample
|
||||||
|
|
||||||
|
it "holds source files" do
|
||||||
|
config.files.should contain "spec/ameba/config_spec.cr"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows to set files" do
|
||||||
|
config.files = ["file.cr"]
|
||||||
|
config.files.should eq ["file.cr"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#formatter, formatter=" do
|
||||||
|
config = Config.load config_sample
|
||||||
|
formatter = DummyFormatter.new
|
||||||
|
|
||||||
|
it "contains default formatter" do
|
||||||
|
config.formatter.should_not be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows to set formatter" do
|
||||||
|
config.formatter = formatter
|
||||||
|
config.formatter.should eq formatter
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#subconfig" do
|
||||||
|
it "returns a specific subconfig" do
|
||||||
|
config = Config.load config_sample
|
||||||
|
config.subconfig("NoSuchConfig").should be_nil
|
||||||
|
config.subconfig(Rule::LineLength.new.name).should_not be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
58
spec/ameba/runner_spec.cr
Normal file
58
spec/ameba/runner_spec.cr
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
require "../spec_helper"
|
||||||
|
|
||||||
|
module Ameba
|
||||||
|
private def runner(files = [__FILE__], formatter = DummyFormatter.new)
|
||||||
|
config = Config.load "config/ameba.yml"
|
||||||
|
config.formatter = formatter
|
||||||
|
config.files = files
|
||||||
|
|
||||||
|
Runner.new(config)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe Runner do
|
||||||
|
formatter = DummyFormatter.new
|
||||||
|
|
||||||
|
describe "#run" do
|
||||||
|
it "returns self" do
|
||||||
|
runner.run.should_not be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "calls started callback" do
|
||||||
|
runner(formatter: formatter).run
|
||||||
|
formatter.started_sources.should_not be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "calls finished callback" do
|
||||||
|
runner(formatter: formatter).run
|
||||||
|
formatter.finished_sources.should_not be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "calls source_started callback" do
|
||||||
|
runner(formatter: formatter).run
|
||||||
|
formatter.started_source.should_not be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "calls source_finished callback" do
|
||||||
|
runner(formatter: formatter).run
|
||||||
|
formatter.finished_source.should_not be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#success?" do
|
||||||
|
it "returns true if runner has not been run" do
|
||||||
|
runner.success?.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns true if all sources are valid" do
|
||||||
|
runner.run.success?.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns false if there are invalid sources" do
|
||||||
|
s = Source.new %q(
|
||||||
|
WrongConstant = 5
|
||||||
|
)
|
||||||
|
Runner.new([s], formatter).run.success?.should be_false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,6 +7,29 @@ module Ameba
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class DummyFormatter < Formatter::BaseFormatter
|
||||||
|
property started_sources : Array(Source)?
|
||||||
|
property finished_sources : Array(Source)?
|
||||||
|
property started_source : Source?
|
||||||
|
property finished_source : Source?
|
||||||
|
|
||||||
|
def started(sources)
|
||||||
|
@started_sources = sources
|
||||||
|
end
|
||||||
|
|
||||||
|
def source_finished(source : Source)
|
||||||
|
@started_source = source
|
||||||
|
end
|
||||||
|
|
||||||
|
def source_started(source : Source)
|
||||||
|
@finished_source = source
|
||||||
|
end
|
||||||
|
|
||||||
|
def finished(sources)
|
||||||
|
@finished_sources = sources
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
struct BeValidExpectation
|
struct BeValidExpectation
|
||||||
def match(source)
|
def match(source)
|
||||||
source.valid?
|
source.valid?
|
||||||
|
|
|
@ -8,10 +8,18 @@ module Ameba::Rule
|
||||||
# bar != false
|
# bar != false
|
||||||
# false === baz
|
# false === baz
|
||||||
# ```
|
# ```
|
||||||
|
#
|
||||||
# This is because these expressions evaluate to `true` or `false`, so you
|
# This is because these expressions evaluate to `true` or `false`, so you
|
||||||
# could get the same result by using either the variable directly,
|
# could get the same result by using either the variable directly,
|
||||||
# or negating the variable.
|
# or negating the variable.
|
||||||
#
|
#
|
||||||
|
# YAML configuration example:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# ComparisonToBoolean:
|
||||||
|
# Enabled: true
|
||||||
|
# ```
|
||||||
|
#
|
||||||
struct ComparisonToBoolean < Base
|
struct ComparisonToBoolean < Base
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
|
|
|
@ -15,6 +15,13 @@ module Ameba::Rule
|
||||||
# Wrong_NAME = 2
|
# Wrong_NAME = 2
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
|
# YAML configuration example:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# ConstantNames:
|
||||||
|
# Enabled: true
|
||||||
|
# ```
|
||||||
|
#
|
||||||
struct ConstantNames < Base
|
struct ConstantNames < Base
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
|
|
|
@ -4,6 +4,13 @@ module Ameba::Rule
|
||||||
# This is because we don't want debugger breakpoints accidentally being
|
# This is because we don't want debugger breakpoints accidentally being
|
||||||
# committed into our codebase.
|
# committed into our codebase.
|
||||||
#
|
#
|
||||||
|
# YAML configuration example:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# DebuggerStatement:
|
||||||
|
# Enabled: true
|
||||||
|
# ```
|
||||||
|
#
|
||||||
struct DebuggerStatement < Base
|
struct DebuggerStatement < Base
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
|
|
|
@ -21,6 +21,13 @@ module Ameba::Rule
|
||||||
# end
|
# end
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
|
# YAML configuration example:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# EmptyExpression:
|
||||||
|
# Enabled: true
|
||||||
|
# ```
|
||||||
|
#
|
||||||
struct EmptyExpression < Base
|
struct EmptyExpression < Base
|
||||||
include AST::Util
|
include AST::Util
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,13 @@ module Ameba::Rule
|
||||||
# 5.123_45
|
# 5.123_45
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
|
# YAML configuration example:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# LargeNumbers:
|
||||||
|
# Enabled: true
|
||||||
|
# ```
|
||||||
|
#
|
||||||
struct LargeNumbers < Base
|
struct LargeNumbers < Base
|
||||||
def test(source)
|
def test(source)
|
||||||
Tokenizer.new(source).run do |token|
|
Tokenizer.new(source).run do |token|
|
||||||
|
|
|
@ -8,6 +8,7 @@ module Ameba::Rule
|
||||||
# Enabled: true
|
# Enabled: true
|
||||||
# MaxLength: 100
|
# MaxLength: 100
|
||||||
# ```
|
# ```
|
||||||
|
#
|
||||||
struct LineLength < Base
|
struct LineLength < Base
|
||||||
prop max_length = 80
|
prop max_length = 80
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,13 @@ module Ameba::Rule
|
||||||
# end
|
# end
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
|
# YAML configuration example:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# LiteralInCondition:
|
||||||
|
# Enabled: true
|
||||||
|
# ```
|
||||||
|
#
|
||||||
struct LiteralInCondition < Base
|
struct LiteralInCondition < Base
|
||||||
include AST::Util
|
include AST::Util
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,13 @@ module Ameba::Rule
|
||||||
# "There are #{4} cats"
|
# "There are #{4} cats"
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
|
# YAML configuration example:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# LiteralInInterpolation
|
||||||
|
# Enabled: true
|
||||||
|
# ```
|
||||||
|
#
|
||||||
struct LiteralInInterpolation < Base
|
struct LiteralInInterpolation < Base
|
||||||
include AST::Util
|
include AST::Util
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,14 @@ module Ameba::Rule
|
||||||
# end
|
# end
|
||||||
# end
|
# end
|
||||||
# ```
|
# ```
|
||||||
|
#
|
||||||
|
# YAML configuration example:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# MethodNames:
|
||||||
|
# Enabled: true
|
||||||
|
# ```
|
||||||
|
#
|
||||||
struct MethodNames < Base
|
struct MethodNames < Base
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
|
|
|
@ -20,6 +20,13 @@ module Ameba::Rule
|
||||||
# It is pretty difficult to wrap your head around a block of code
|
# It is pretty difficult to wrap your head around a block of code
|
||||||
# that is executed if a negated condition is NOT met.
|
# that is executed if a negated condition is NOT met.
|
||||||
#
|
#
|
||||||
|
# YAML configuration example:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# NegatedConditionsInUnless:
|
||||||
|
# Enabled: true
|
||||||
|
# ```
|
||||||
|
#
|
||||||
struct NegatedConditionsInUnless < Base
|
struct NegatedConditionsInUnless < Base
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
|
|
|
@ -22,6 +22,13 @@ module Ameba::Rule
|
||||||
# end
|
# end
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
|
# YAML configuration example:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# PredicateName:
|
||||||
|
# Enabled: true
|
||||||
|
# ```
|
||||||
|
#
|
||||||
struct PredicateName < Base
|
struct PredicateName < Base
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
module Ameba::Rule
|
module Ameba::Rule
|
||||||
# A rule that disallows trailing blank lines at the end of the source file.
|
# A rule that disallows trailing blank lines at the end of the source file.
|
||||||
#
|
#
|
||||||
|
# YAML configuration example:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# TrailingBlankLines:
|
||||||
|
# Enabled: true
|
||||||
|
# ```
|
||||||
|
#
|
||||||
struct TrailingBlankLines < Base
|
struct TrailingBlankLines < Base
|
||||||
def test(source)
|
def test(source)
|
||||||
if source.lines.size > 1 && source.lines[-2, 2].join.strip.empty?
|
if source.lines.size > 1 && source.lines[-2, 2].join.strip.empty?
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
module Ameba::Rule
|
module Ameba::Rule
|
||||||
# A rule that disallows trailing whitespaces.
|
# A rule that disallows trailing whitespaces.
|
||||||
#
|
#
|
||||||
|
# YAML configuration example:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# TrailingWhitespace:
|
||||||
|
# Enabled: true
|
||||||
|
# ```
|
||||||
|
#
|
||||||
struct TrailingWhitespace < Base
|
struct TrailingWhitespace < Base
|
||||||
def test(source)
|
def test(source)
|
||||||
source.lines.each_with_index do |line, index|
|
source.lines.each_with_index do |line, index|
|
||||||
|
|
|
@ -45,6 +45,13 @@ module Ameba::Rule
|
||||||
# end
|
# end
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
|
# YAML configuration example:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# TypeNames:
|
||||||
|
# Enabled: true
|
||||||
|
# ```
|
||||||
|
#
|
||||||
struct TypeNames < Base
|
struct TypeNames < Base
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
|
|
|
@ -36,6 +36,13 @@ module Ameba::Rule
|
||||||
# end
|
# end
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
|
# YAML configuration example:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# UnlessElse:
|
||||||
|
# Enabled: true
|
||||||
|
# ```
|
||||||
|
#
|
||||||
struct UnlessElse < Base
|
struct UnlessElse < Base
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
|
|
|
@ -24,6 +24,13 @@ module Ameba::Rule
|
||||||
# wrong_Name = 2
|
# wrong_Name = 2
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
|
# YAML configuration example:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# VariableNames:
|
||||||
|
# Enabled: true
|
||||||
|
# ```
|
||||||
|
#
|
||||||
struct VariableNames < Base
|
struct VariableNames < Base
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
|
|
|
@ -10,6 +10,10 @@ module Ameba
|
||||||
@formatter = config.formatter
|
@formatter = config.formatter
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def initialize(@sources, @formatter)
|
||||||
|
@rules = load_rules nil
|
||||||
|
end
|
||||||
|
|
||||||
def run
|
def run
|
||||||
@formatter.started @sources
|
@formatter.started @sources
|
||||||
@sources.each do |source|
|
@sources.each do |source|
|
||||||
|
@ -28,13 +32,6 @@ module Ameba
|
||||||
@sources.all? &.valid?
|
@sources.all? &.valid?
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_source(source)
|
|
||||||
@formatter.source_started source
|
|
||||||
@rules.each &.test(source)
|
|
||||||
ensure
|
|
||||||
@formatter.source_finished source
|
|
||||||
end
|
|
||||||
|
|
||||||
private def load_sources(config)
|
private def load_sources(config)
|
||||||
config.files
|
config.files
|
||||||
.map { |wildcard| Dir[wildcard] }
|
.map { |wildcard| Dir[wildcard] }
|
||||||
|
|
Loading…
Reference in a new issue