Base YAML config loader & Ameba runner

This commit is contained in:
Vitalii Elenhaupt 2017-11-13 23:20:22 +02:00
parent 80e2ab4f55
commit 822a69b81c
No known key found for this signature in database
GPG key ID: 7558EF3A4056C706
7 changed files with 158 additions and 32 deletions

View file

@ -1,7 +1,7 @@
require "../src/ameba"
require "benchmark"
private def get_sources(n)
private def get_files(n)
Dir["src/**/*.cr"].first(n)
end
@ -16,10 +16,11 @@ Benchmark.ips do |x|
30,
40,
].each do |n|
sources = get_sources(n)
formatter = Ameba::Formatter::BaseFormatter.new
config = Ameba::Config.load
config.formatter = Ameba::Formatter::BaseFormatter.new
config.files = get_files(n)
s = n == 1 ? "" : "s"
x.report("#{n} source#{s}") { Ameba.run sources, formatter }
x.report("#{n} source#{s}") { Ameba.run config }
end
end

View file

@ -6,26 +6,7 @@ require "./ameba/formatter/*"
module Ameba
extend self
def run(formatter = Formatter::BaseFormatter.new)
run Dir["**/*.cr"].reject(&.starts_with? "lib/"), formatter
end
def run(files, formatter : Formatter::BaseFormatter)
sources = files.map { |path| Source.new(File.read(path), path) }
formatter.started sources
sources.each do |source|
formatter.source_started source
catch(source)
formatter.source_finished source
end
formatter.finished sources
sources
end
def catch(source : Source)
Rule.rules.each do |rule|
rule.new.test(source)
end
def run(config = Config.load)
Runner.new(config).run
end
end

69
src/ameba/config.cr Normal file
View file

@ -0,0 +1,69 @@
require "yaml"
class Ameba::Config
setter formatter : Formatter::BaseFormatter?
setter files : Array(String)?
def initialize(@config : YAML::Any)
end
def self.load(path = ".ameba.yml")
content = (path && File.exists? path) ? File.read path : "{}"
Config.new YAML.parse(content)
end
def files
@files ||= default_files
end
def formatter
@formatter ||= default_formatter
end
def subconfig(name)
@config[name]?
end
private def default_files
Dir["**/*.cr"].reject(&.starts_with? "lib/")
end
private def default_formatter
Formatter::DotFormatter.new
end
module Rule
getter config : YAML::Any?
macro prop(assign)
# Rule configuration property.
def {{assign.target}}
{% prop_name = assign.target.id.camelcase.gsub(/\?/, "") %}
{% if assign.value.is_a? NumberLiteral %}
int_prop "{{prop_name}}", {{assign.value}}
{% elsif assign.value.is_a? BoolLiteral %}
bool_prop "{{prop_name}}", {{assign.value}}
{% elsif assign.value.is_a? StringLiteral %}
str_prop "{{prop_name}}", {{assign.value}}
{% end %}
end
end
def initialize(config = nil)
@config = config.try &.subconfig(name)
end
protected def int_prop(name, default : Number)
str_prop(name, default).to_i
end
protected def bool_prop(name, default : Bool)
str_prop(name, default.to_s) == "true"
end
protected def str_prop(name, default)
config.try &.[name]?.try &.as_s || default
end
end
end

View file

@ -1,7 +1,11 @@
module Ameba::Rule
abstract struct Base
include Config::Rule
abstract def test(source : Source)
prop enabled? = true
def test(source : Source, node : Crystal::ASTNode)
# can't be abstract
end

View file

@ -1,10 +1,19 @@
module Ameba::Rule
# A rule that disallows lines longer than 80 symbols.
# A rule that disallows lines longer than `max_length` number of symbols.
#
# YAML configuration example:
#
# ```
# LineLength:
# Enabled: true
# MaxLength: 100
# ```
struct LineLength < Base
prop max_length = 80
def test(source)
source.lines.each_with_index do |line, index|
next unless line.size > 80
next unless line.size > max_length
source.error self, source.location(index + 1, line.size),
"Line too long"

49
src/ameba/runner.cr Normal file
View file

@ -0,0 +1,49 @@
module Ameba
class Runner
@rules : Array(Rule::Base)
@sources : Array(Source)
@formatter : Formatter::BaseFormatter
def initialize(config : Config)
@rules = load_rules(config)
@sources = load_sources(config)
@formatter = config.formatter
end
def run
@formatter.started @sources
@sources.each do |source|
@formatter.source_started source
@rules.each &.test(source)
@formatter.source_finished source
end
self
ensure
@formatter.finished @sources
end
def success?
@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] }
.flatten
.map { |path| Source.new File.read(path), path }
end
private def load_rules(config)
Rule.rules.map { |r| r.new config }.select &.enabled?
end
end
end

View file

@ -1,10 +1,10 @@
require "option_parser"
require "./ameba"
formatter = Ameba::Formatter::DotFormatter
files, formatter, config_path = nil, nil, nil
OptionParser.parse(ARGV) do |parser|
parser.banner = "Usage: ameba [options]"
parser.banner = "Usage: ameba [options] [file1 file2 ...]"
parser.on("-v", "--version", "Print version") do
puts Ameba::VERSION
@ -17,10 +17,23 @@ OptionParser.parse(ARGV) do |parser|
end
parser.on("-s", "--silent", "Disable output") do
formatter = Ameba::Formatter::BaseFormatter
formatter = Ameba::Formatter::BaseFormatter.new
end
# parser.on("-f FORMATTER", "--format FORMATTER", "Specify formatter") do |f|
# end
parser.on("-c PATH", "--config PATH", "Specify configuration file") do |f|
config_path = f
end
parser.unknown_args do |f|
files = f if f.any?
end
end
files = Dir["**/*.cr"]
config = Ameba::Config.load config_path
config.formatter = formatter
config.files = files
exit(1) unless Ameba.run(files, formatter.new).all? &.valid?
exit(1) unless Ameba.run(config).success?