Document configuration properties & specs

This commit is contained in:
Vitalii Elenhaupt 2017-11-14 09:24:36 +02:00
parent 822a69b81c
commit 06855816d7
No known key found for this signature in database
GPG key ID: 7558EF3A4056C706
22 changed files with 325 additions and 8 deletions

View file

@ -41,7 +41,7 @@ Or just compile it from sources `make install`.
## Usage
Run `ameba` binary to catch code issues within you project:
Run `ameba` binary within your project directory to catch code issues:
```sh
$ ameba
@ -63,6 +63,12 @@ Finished in 10.53 milliseconds
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
Adding a new rule is as simple as inheriting from `Rule::Base` struct and implementing

67
config/ameba.yml Normal file
View 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
View 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
View 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

View file

@ -7,6 +7,29 @@ module Ameba
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
def match(source)
source.valid?

View file

@ -8,10 +8,18 @@ module Ameba::Rule
# bar != false
# false === baz
# ```
#
# This is because these expressions evaluate to `true` or `false`, so you
# could get the same result by using either the variable directly,
# or negating the variable.
#
# YAML configuration example:
#
# ```
# ComparisonToBoolean:
# Enabled: true
# ```
#
struct ComparisonToBoolean < Base
def test(source)
AST::Visitor.new self, source

View file

@ -15,6 +15,13 @@ module Ameba::Rule
# Wrong_NAME = 2
# ```
#
# YAML configuration example:
#
# ```
# ConstantNames:
# Enabled: true
# ```
#
struct ConstantNames < Base
def test(source)
AST::Visitor.new self, source

View file

@ -4,6 +4,13 @@ module Ameba::Rule
# This is because we don't want debugger breakpoints accidentally being
# committed into our codebase.
#
# YAML configuration example:
#
# ```
# DebuggerStatement:
# Enabled: true
# ```
#
struct DebuggerStatement < Base
def test(source)
AST::Visitor.new self, source

View file

@ -21,6 +21,13 @@ module Ameba::Rule
# end
# ```
#
# YAML configuration example:
#
# ```
# EmptyExpression:
# Enabled: true
# ```
#
struct EmptyExpression < Base
include AST::Util

View file

@ -19,6 +19,13 @@ module Ameba::Rule
# 5.123_45
# ```
#
# YAML configuration example:
#
# ```
# LargeNumbers:
# Enabled: true
# ```
#
struct LargeNumbers < Base
def test(source)
Tokenizer.new(source).run do |token|

View file

@ -8,6 +8,7 @@ module Ameba::Rule
# Enabled: true
# MaxLength: 100
# ```
#
struct LineLength < Base
prop max_length = 80

View file

@ -13,6 +13,13 @@ module Ameba::Rule
# end
# ```
#
# YAML configuration example:
#
# ```
# LiteralInCondition:
# Enabled: true
# ```
#
struct LiteralInCondition < Base
include AST::Util

View file

@ -9,6 +9,13 @@ module Ameba::Rule
# "There are #{4} cats"
# ```
#
# YAML configuration example:
#
# ```
# LiteralInInterpolation
# Enabled: true
# ```
#
struct LiteralInInterpolation < Base
include AST::Util

View file

@ -30,6 +30,14 @@ module Ameba::Rule
# end
# end
# ```
#
# YAML configuration example:
#
# ```
# MethodNames:
# Enabled: true
# ```
#
struct MethodNames < Base
def test(source)
AST::Visitor.new self, source

View file

@ -20,6 +20,13 @@ module Ameba::Rule
# It is pretty difficult to wrap your head around a block of code
# that is executed if a negated condition is NOT met.
#
# YAML configuration example:
#
# ```
# NegatedConditionsInUnless:
# Enabled: true
# ```
#
struct NegatedConditionsInUnless < Base
def test(source)
AST::Visitor.new self, source

View file

@ -22,6 +22,13 @@ module Ameba::Rule
# end
# ```
#
# YAML configuration example:
#
# ```
# PredicateName:
# Enabled: true
# ```
#
struct PredicateName < Base
def test(source)
AST::Visitor.new self, source

View file

@ -1,6 +1,13 @@
module Ameba::Rule
# A rule that disallows trailing blank lines at the end of the source file.
#
# YAML configuration example:
#
# ```
# TrailingBlankLines:
# Enabled: true
# ```
#
struct TrailingBlankLines < Base
def test(source)
if source.lines.size > 1 && source.lines[-2, 2].join.strip.empty?

View file

@ -1,6 +1,13 @@
module Ameba::Rule
# A rule that disallows trailing whitespaces.
#
# YAML configuration example:
#
# ```
# TrailingWhitespace:
# Enabled: true
# ```
#
struct TrailingWhitespace < Base
def test(source)
source.lines.each_with_index do |line, index|

View file

@ -45,6 +45,13 @@ module Ameba::Rule
# end
# ```
#
# YAML configuration example:
#
# ```
# TypeNames:
# Enabled: true
# ```
#
struct TypeNames < Base
def test(source)
AST::Visitor.new self, source

View file

@ -36,6 +36,13 @@ module Ameba::Rule
# end
# ```
#
# YAML configuration example:
#
# ```
# UnlessElse:
# Enabled: true
# ```
#
struct UnlessElse < Base
def test(source)
AST::Visitor.new self, source

View file

@ -24,6 +24,13 @@ module Ameba::Rule
# wrong_Name = 2
# ```
#
# YAML configuration example:
#
# ```
# VariableNames:
# Enabled: true
# ```
#
struct VariableNames < Base
def test(source)
AST::Visitor.new self, source

View file

@ -10,6 +10,10 @@ module Ameba
@formatter = config.formatter
end
def initialize(@sources, @formatter)
@rules = load_rules nil
end
def run
@formatter.started @sources
@sources.each do |source|
@ -28,13 +32,6 @@ module Ameba
@sources.all? &.valid?
end
def test_source(source)
@formatter.source_started source
@rules.each &.test(source)
ensure
@formatter.source_finished source
end
private def load_sources(config)
config.files
.map { |wildcard| Dir[wildcard] }