Skip to content

Commit

Permalink
Add logging shortcuts methods 'info', 'debug', ... to Cmd
Browse files Browse the repository at this point in the history
  • Loading branch information
maiha committed Jan 24, 2019
1 parent df44a1a commit b2c2ef0
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 55 deletions.
20 changes: 12 additions & 8 deletions spec/examples/basic_spec.cr
Expand Up @@ -46,22 +46,26 @@ describe "(basic usage)" do
it "fails with usages when the task name is wrong" do
shell = run("basic json xxx")
shell.success?.should be_false
remove_ansi_color(shell.stderr).should eq <<-EOF
Error: unknown task: 'xxx'
Possible tasks are: ["fails", "inspect", "pretty"]
remove_ansi_color(shell.stderr).chomp.should eq <<-EOF
fatal: 'xxx' is not a valid task name for the command 'json'.
Usage: ./tmp/basic json <<TASK>>
fails, inspect, pretty
Examples:
./tmp/basic json pretty file.json
./tmp/basic json fails
EOF
end

it "fails when the command name is wrong" do
shell = run("basic foo")
shell.success?.should be_false
remove_ansi_color(shell.stderr).should eq <<-EOF
Error: unknown command: 'foo'
Possible commands are: ["json"]
remove_ansi_color(shell.stderr).chomp.chomp.should eq <<-EOF
fatal: 'foo' is not a valid command name.
Usage: ./tmp/basic <<COMMAND>>
json
EOF
end
end
3 changes: 3 additions & 0 deletions src/cmds.cr
@@ -1,5 +1,6 @@
# lib
require "levenshtein"
require "logger"

# shards
require "pretty"
Expand All @@ -17,6 +18,8 @@ module Cmds
NAME = {{name_s}}
::Cmds.register({{name_s}}, self)

include ::Cmds::Cmd::DefaultActions

{{ yield }}
end
end
Expand Down
3 changes: 2 additions & 1 deletion src/cmds/cli.cr
Expand Up @@ -8,8 +8,9 @@ abstract class Cmds::Cli
class Default < Cmds::Cli
def run(args)
Cmds[args.shift?].run(args)
rescue Cmds::Finished
rescue err : Cmds::Abort | Cmds::CommandNotFound | Cmds::TaskNotFound
STDERR.puts err.to_s.colorize(:red)
STDERR.puts err.to_s.chomp.colorize(:red)
exit 1
end
end
Expand Down
95 changes: 54 additions & 41 deletions src/cmds/cmd.cr
Expand Up @@ -2,24 +2,56 @@ abstract class Cmds::Cmd
var args : ::Array(String)
var task_state : Cmds::State = Cmds::State::BEFORE
var error : Exception
var logger = Logger.new(STDOUT)

@task_name : ::String?
var task_name : ::String

NAME = "(name not found)"

def before
def self.cmd_name
NAME
end

def after
def task_name
@task_name || raise TaskNotFound.new("", self)
end

def finished!(msg : String = "")
self.task_state = Cmds::State::FINISHED
raise Cmds::Finished.new(msg)
end
module DefaultActions
def before
end

def after
end

def run
invoke_task(task_name)
end

def abort(msg)
raise Abort.new(msg)
def run(args : Array(String))
self.args = args
self.task_state = Cmds::State::BEFORE
self.task_name = args.shift?
before
self.task_state = Cmds::State::RUNNING
run
self.task_state = Cmds::State::FINISHED
rescue Cmds::Finished
# successfully done
rescue err
self.error = err
raise err
ensure
after
end

def finished!(msg : String = "")
self.task_state = Cmds::State::FINISHED
raise Cmds::Finished.new(msg)
end

def abort(msg)
raise Abort.new(msg)
end
end

# should be overriden in inherited macro
Expand Down Expand Up @@ -69,40 +101,21 @@ abstract class Cmds::Cmd
return array
end

def run
invoke_task(task_name)
end

protected def build_task_name
raise TaskNotFound.new("", self)
end

protected def task_name : String
@task_name || build_task_name
end

protected def task_name? : String?
@task_name
end

def run(args : Array(String))
self.args = args
self.task_state = Cmds::State::BEFORE
@task_name = args.shift?
before
self.task_state = Cmds::State::RUNNING
run
self.task_state = Cmds::State::FINISHED
rescue Cmds::Finished
# successfully done
rescue err
self.error = err
raise err
ensure
after
end
{% for name in Logger::Severity.constants %}
private def {{name.id.downcase}}(message)
name = String.build do |s|
s << self.class.cmd_name
s << " " << task_name if task_name?
end
logger.{{name.id.downcase}}(message, name)
end
{% end %}

macro inherited
def self.cmd_name
NAME
end

def self.pretty_usage(prefix : String = "", delimiter : String = " ")
array = usages.map{|i| [prefix + PROGRAM_NAME, NAME, i]}
Pretty.lines(array, delimiter: delimiter)
Expand Down
34 changes: 29 additions & 5 deletions src/cmds/errors.cr
Expand Up @@ -5,16 +5,40 @@ module Cmds

klass CommandNotFound < Exception, name : String, possible : Array(String) do
def to_s(io : IO)
io << "Error: unknown command: '#{name}'\n"
io << "Possible commands are: #{possible}"
io <<
if name.empty?
"fatal: You must specify a command name.\n"
else
"fatal: '%s' is not a valid command name.\n" % [name]
end
io << "\n"
io << "Usage: %s <<COMMAND>>\n" % [PROGRAM_NAME]
if possible.any?
io << " %s\n" % possible.join(", ")
end
end
end

klass TaskNotFound < Exception, name : String, cmd : Cmd do
delegate cmd_name, task_names, pretty_usage, to: cmd.class

def to_s(io : IO)
io << "Error: unknown task: '#{name}'\n"
io << "Possible tasks are: #{cmd.class.task_names}\n"
io << cmd.class.pretty_usage(prefix: " ")
io <<
if name.empty?
"fatal: You must specify a task name for the command '%s'.\n" % cmd_name
else
"fatal: '%s' is not a valid task name for the command '%s'.\n" % [name, cmd_name]
end
io << "\n"
io << "Usage: %s %s <<TASK>>\n" % [PROGRAM_NAME, cmd_name]
if task_names.any?
io << " %s\n" % task_names.join(", ")
end
if cmd.class.usages.any?
io << "\n"
io << "Examples:\n"
io << pretty_usage(prefix: " ")
end
end
end
end

0 comments on commit b2c2ef0

Please sign in to comment.