diff --git a/CHANGELOG.md b/CHANGELOG.md index 37a695dd..2231c68e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - Ignore `vendor` directories from files to be checked. This allows rufo to be run without any arguments against a directory both locally and on common CI tools without needing any configuration. +- Allow logging level to be configured with `--loglevel[=LEVEL]`. ### Changed - Dropped support for ruby 2.3 as it is end of life. diff --git a/lib/rufo.rb b/lib/rufo.rb index 9802da91..98ac6ccd 100644 --- a/lib/rufo.rb +++ b/lib/rufo.rb @@ -11,6 +11,7 @@ def self.format(code, **options) end require_relative "rufo/command" +require_relative "rufo/logger" require_relative "rufo/dot_file" require_relative "rufo/settings" require_relative "rufo/formatter" diff --git a/lib/rufo/command.rb b/lib/rufo/command.rb index 5d60efe7..7340f9ca 100644 --- a/lib/rufo/command.rb +++ b/lib/rufo/command.rb @@ -8,16 +8,17 @@ class Rufo::Command CODE_CHANGE = 3 def self.run(argv) - want_check, exit_code, filename_for_dot_rufo = parse_options(argv) - new(want_check, exit_code, filename_for_dot_rufo).run(argv) + want_check, exit_code, filename_for_dot_rufo, loglevel = parse_options(argv) + new(want_check, exit_code, filename_for_dot_rufo, loglevel).run(argv) end - def initialize(want_check, exit_code, filename_for_dot_rufo) + def initialize(want_check, exit_code, filename_for_dot_rufo, loglevel) @want_check = want_check @exit_code = exit_code @filename_for_dot_rufo = filename_for_dot_rufo @dot_file = Rufo::DotFile.new @squiggly_warning_files = [] + @logger = Rufo::Logger.new(loglevel) end def exit_code(status_code) @@ -51,12 +52,11 @@ def format_stdin code == result ? CODE_OK : CODE_CHANGE rescue Rufo::SyntaxError - STDERR.puts "Error: the given text is not a valid ruby program (it has syntax errors)" + logger.error("Error: the given text is not a valid ruby program (it has syntax errors)") CODE_ERROR rescue => ex - STDERR.puts "You've found a bug!" - STDERR.puts "Please report it to https://github.com/ruby-formatter/rufo/issues with code that triggers it" - STDERR.puts + logger.error("You've found a bug!") + logger.error("Please report it to https://github.com/ruby-formatter/rufo/issues with code that triggers it\n") raise ex end @@ -72,7 +72,7 @@ def format_args(args) if exists files_exist = true else - STDERR.puts "Error: file or directory not found: #{file}" + logger.warn("Error: file or directory not found: #{file}") next end result = format_file(file) @@ -91,6 +91,7 @@ def format_args(args) end def format_file(filename) + logger.debug("Formatting: #{filename}") code = File.read(filename) begin @@ -98,28 +99,27 @@ def format_file(filename) rescue Rufo::SyntaxError # We ignore syntax errors as these might be template files # with .rb extension - STDERR.puts "Error: #{filename} has syntax errors" + logger.warn("Error: #{filename} has syntax errors") return CODE_ERROR end if code.force_encoding(result.encoding) != result if @want_check - STDERR.puts "Formatting #{filename} produced changes" + logger.warn("Formatting #{filename} produced changes") else File.write(filename, result) - puts "Format: #{filename}" + logger.log("Format: #{filename}") end return CODE_CHANGE end rescue Rufo::SyntaxError - STDERR.puts "Error: the given text in #{filename} is not a valid ruby program (it has syntax errors)" + logger.error("Error: the given text in #{filename} is not a valid ruby program (it has syntax errors)") CODE_ERROR rescue => ex - STDERR.puts "You've found a bug!" - STDERR.puts "It happened while trying to format the file #{filename}" - STDERR.puts "Please report it to https://github.com/ruby-formatter/rufo/issues with code that triggers it" - STDERR.puts + logger.error("You've found a bug!") + logger.error("It happened while trying to format the file #{filename}") + logger.error("Please report it to https://github.com/ruby-formatter/rufo/issues with code that triggers it\n") raise ex end @@ -139,6 +139,7 @@ def format(code, dir) def self.parse_options(argv) exit_code, want_check = true, false filename_for_dot_rufo = nil + loglevel = :log OptionParser.new do |opts| opts.version = Rufo::VERSION @@ -156,12 +157,20 @@ def self.parse_options(argv) exit_code = false end + opts.on(Rufo::Logger::LEVELS, "--loglevel[=LEVEL]", "Change the level of logging for the CLI. Options are: error, warn, log (default), debug, silent") do |value| + loglevel = value.to_sym + end + opts.on("-h", "--help", "Show this help") do puts opts exit end end.parse!(argv) - [want_check, exit_code, filename_for_dot_rufo] + [want_check, exit_code, filename_for_dot_rufo, loglevel] end + + private + + attr_reader :logger end diff --git a/lib/rufo/logger.rb b/lib/rufo/logger.rb new file mode 100644 index 00000000..3cc0f212 --- /dev/null +++ b/lib/rufo/logger.rb @@ -0,0 +1,39 @@ +module Rufo + class Logger + LEVELS = { + silent: 0, + error: 1, + warn: 2, + log: 3, + debug: 4, + } + + def initialize(level) + @level = LEVELS.fetch(level) + end + + def debug(*args) + $stdout.puts(*args) if should_output?(:debug) + end + + def log(*args) + $stdout.puts(*args) if should_output?(:log) + end + + def warn(*args) + $stderr.puts(*args) if should_output?(:warn) + end + + def error(*args) + $stderr.puts(*args) if should_output?(:error) + end + + private + + attr_reader :level + + def should_output?(level_to_check) + LEVELS.fetch(level_to_check) <= level + end + end +end diff --git a/spec/lib/rufo/logger_spec.rb b/spec/lib/rufo/logger_spec.rb new file mode 100644 index 00000000..a4eebbf4 --- /dev/null +++ b/spec/lib/rufo/logger_spec.rb @@ -0,0 +1,83 @@ +require "spec_helper" + +RSpec.describe Rufo::Logger do + subject { described_class.new(level) } + + let(:level) { :debug } + + it "logs all levels" do + expect { + subject.log("a") + subject.debug("b") + }.to output("a\nb\n").to_stdout + + expect { + subject.warn("c") + subject.error("d") + }.to output("c\nd\n").to_stderr + end + + context "when set to log" do + let(:level) { :log } + + it "logs all levels except debug" do + expect { + subject.log("a") + subject.debug("b") + }.to output("a\n").to_stdout + + expect { + subject.warn("c") + subject.error("d") + }.to output("c\nd\n").to_stderr + end + end + + context "when set to warn" do + let(:level) { :warn } + + it "logs only warn and error levels" do + expect { + subject.log("a") + subject.debug("b") + }.to output("").to_stdout + + expect { + subject.warn("c") + subject.error("d") + }.to output("c\nd\n").to_stderr + end + end + + context "when set to error" do + let(:level) { :error } + + it "logs only error levels" do + expect { + subject.log("a") + subject.debug("b") + }.to output("").to_stdout + + expect { + subject.warn("c") + subject.error("d") + }.to output("d\n").to_stderr + end + end + + context "when set to silent" do + let(:level) { :silent } + + it "does not log anything" do + expect { + subject.log("a") + subject.debug("b") + }.to output("").to_stdout + + expect { + subject.warn("c") + subject.error("d") + }.to output("").to_stderr + end + end +end