TODO formatter

This commit is contained in:
Vitalii Elenhaupt 2017-11-30 11:27:52 +02:00 committed by V. Elenhaupt
parent bc552d0730
commit a1854c0aa3
5 changed files with 122 additions and 28 deletions

View file

@ -0,0 +1,44 @@
require "../../spec_helper"
module Ameba
private def create_todo(formatter)
s = Source.new "a = 1"
s.error DummyRule.new, s.location(1, 2), "message"
formatter.finished [s]
end
describe Formatter::TODOFormatter do
file = IO::Memory.new
subject = Formatter::TODOFormatter.new IO::Memory.new, file
context "problems not reported" do
it "does not create todo" do
subject.finished [Source.new ""]
file.to_s.empty?.should be_true
end
end
context "problems reported" do
it "creates a todo with header" do
create_todo subject
file.to_s.should contain "# This configuration file was generated by"
file.to_s.should contain "Ameba version #{VERSION}"
end
it "creates a todo with rule name" do
create_todo subject
file.to_s.should contain "DummyRule"
end
it "creates a todo with problems count" do
create_todo subject
file.to_s.should contain "Problems found: 1"
end
it "creates a valid YAML document" do
create_todo subject
YAML.parse(file.to_s).should_not be_nil
end
end
end
end

View file

@ -46,13 +46,11 @@ module Ameba::Cli
def run_ameba(opts) def run_ameba(opts)
config = Ameba::Config.load opts.config config = Ameba::Config.load opts.config
config.files = opts.files config.files = opts.files
config.formatter = Ameba::Formatter::BaseFormatter.new if opts.silent
configure_formatter(config, opts)
configure_rules(config, opts) configure_rules(config, opts)
runner = Ameba.run(config) exit 1 unless Ameba.run(config).success?
generate_config_file(config, runner, opts) if opts.generate
exit 1 unless runner.success?
rescue e rescue e
puts "Error: #{e.message}" puts "Error: #{e.message}"
exit 255 exit 255
@ -71,14 +69,14 @@ module Ameba::Cli
end end
end end
private def generate_config_file(config, runner, opts) private def configure_formatter(config, opts)
failed_rules = if opts.silent
runner.sources config.formatter = Ameba::Formatter::BaseFormatter.new
.map { |s| s.errors.map &.rule.name } elsif opts.generate
.flatten config.formatter = Ameba::Formatter::TODOFormatter.new
.uniq! else
failed_rules.each { |rule| config.update_rule rule, enabled: false } config.formatter = Ameba::Formatter::DotFormatter.new
File.write(opts.config, config.to_yaml) end
end end
private def print_version private def print_version

View file

@ -20,7 +20,7 @@ class Ameba::Config
# Creates a new instance of `Ameba::Config` based on YAML parameters. # Creates a new instance of `Ameba::Config` based on YAML parameters.
# #
# `Config.load` uses this constructor to instantiate new config by YAML file. # `Config.load` uses this constructor to instantiate new config by YAML file.
protected def initialize(@config : Hash(YAML::Type, YAML::Type)) protected def initialize(@config : YAML::Any)
@rules = Rule.rules.map &.new(config) @rules = Rule.rules.map &.new(config)
end end
@ -31,9 +31,9 @@ class Ameba::Config
# ``` # ```
# #
def self.load(path = PATH) def self.load(path = PATH)
content = File.exists?(path) ? File.read path : "{}" content = File.exists?(path) ? File.read path : ""
Config.new YAML.parse(content).as_h Config.new YAML.parse(content)
rescue e rescue
raise "Config file is invalid" raise "Config file is invalid"
end end
@ -80,15 +80,6 @@ class Ameba::Config
@rules[index] = rule @rules[index] = rule
end end
def to_yaml(yaml : YAML::Builder)
yaml.mapping do
rules.each do |rule|
rule.name.to_yaml(yaml)
rule.to_yaml(yaml)
end
end
end
private def default_files private def default_files
Dir["**/*.cr"].reject(&.starts_with? "lib/") Dir["**/*.cr"].reject(&.starts_with? "lib/")
end end
@ -158,8 +149,10 @@ class Ameba::Config
properties {} properties {}
def self.new(config = nil) def self.new(config = nil)
yaml = config.try &.[class_name]?.try &.to_yaml || "{}" if (raw = config.try &.raw).is_a? Hash
from_yaml yaml yaml = raw[class_name]?.try &.to_yaml
end
from_yaml yaml || "{}"
end end
end end
end end

View file

@ -21,7 +21,7 @@ module Ameba::Formatter
# Reports a message when inspection is finished. # Reports a message when inspection is finished.
def finished(sources) def finished(sources)
output << "\n\n" output << "\n\n"
failed_sources = sources.reject { |s| s.valid? } failed_sources = sources.reject &.valid?
failed_sources.each do |source| failed_sources.each do |source|
source.errors.each do |error| source.errors.each do |error|

View file

@ -0,0 +1,59 @@
module Ameba::Formatter
# A formatter that creates a todo config.
# Basically, it takes all errors reported and disables corresponding rules
# or excludes failed sources from these rules.
class TODOFormatter < DotFormatter
@io : IO::FileDescriptor | IO::Memory
def initialize(@output = STDOUT, @io = File.new(Config::PATH, mode: "w"))
end
def finished(sources)
super
errors = sources.map(&.errors).flatten
generate_todo_config errors if errors.any?
if (io = @io).is_a?(File)
@output << "Created #{io.path}\n"
end
end
private def generate_todo_config(errors)
@io << header
rule_errors_map(errors).each do |rule, rule_errors|
@io << "\n# Problems found: #{rule_errors.size}"
@io << rule_todo(rule, rule_errors).gsub("---", "")
end
ensure
@io.flush
end
private def rule_errors_map(errors)
Hash(Rule::Base, Array(Source::Error)).new.tap do |h|
errors.each do |error|
h[error.rule] ||= Array(Source::Error).new
h[error.rule] << error
end
end
end
private def header
<<-HEADER
# This configuration file was generated by `ameba --gen-config`
# on #{Time.now} using Ameba version #{VERSION}.
# The point is for the user to remove these configuration records
# one by one as the reported problems are removed from the code base.
HEADER
end
private def rule_todo(rule, errors)
rule.enabled = false
YAML.build do |yaml|
yaml.mapping do
rule.name.to_yaml(yaml)
rule.to_yaml(yaml)
end
end
end
end
end