#!/usr/bin/env ruby require 'pathname' require 'shellwords' require 'stringio' require 'ipaddr' require_relative '../lib/dencli' cli = DenCli.new :example, "This is an example for generate a DenCli-API" class Capture def initialize cli, verbose: @cli, @counter, @verbose = cli, 0, verbose end def capture @args = NilClass @counter += 1 stdout, stderr = $stdout, $stderr $stdout = $stderr = StringIO.new begin yield stdout, stderr ensure $stderr, $stdout = stderr, stdout end end def args= args @args = args end def logstart command STDERR.printf "[% 4d] \e[1;35m? \e[0m %s tests %s\r", @counter, $0.shellescape, command.shelljoin end def logok info, command STDERR.printf "[% 4d] \e[1;32mok\e[0m %s | %s tests %s\e[J\n", @counter, info, $0.shellescape, command.shelljoin end def logfail command STDERR.printf "[% 4d] \e[1;31mer\e[0m %s tests %s\e[J\n", @counter, $0.shellescape, command.shelljoin end def logexception prefix, exception loginfo "#{prefix} (#{exception.class.name}) #{exception}" exception.backtrace[0...-Kernel.caller.length].each {|l| loginfo " #{l}" } end def loginfo text STDERR.printf " %s\n", text end def should_ok expect, *command logstart command $capture.capture { @cli.call 'tests', *command } if expect === @args logok @args, command else logfail command loginfo "expected args: #{expect.inspect}" loginfo "given args: #{@args.inspect}" STDERR.puts end rescue SystemExit if 0 == $!.status logok @args, command else logfail command end rescue Object logfail command logexception "unexpected raise:", $! STDERR.puts end def should_fail exception, message, *command logstart command $capture.capture { @cli.call 'tests', *command } logfail command rescue exception if message === $!.message logok exception, command if @verbose logexception "raised:", $! STDERR.puts end else logexception "unexpected message:", $! STDERR.puts end rescue Object logfail command logexception "unexpected raised:", $! STDERR.puts end end cli.cmd( :args, "Expects and prints given arguments", &lambda {|a, b, c:, d:, e:, f:, g:| p a: a, b: b, c: c, d: d, e: e }). opt( :c, '-c=ForC', "Option c"). opt( :d, '-dForD', "Option d", default: "something"). opt( :e, '-e', "Toggle e", default: false). opt( :f, '--[no-]f', "Toggle f", default: false). opt( :g, '--long-option=sth', "Long option, no short option", default: "nothing"). opt( :h, '-hsth', "No long option, only short option", default: "nothing") cli.cmd( :example, "I have an example command") { $stderr.puts "This is an example" } cli.cmd( :help, "An example for help", aliases: [nil, '-h', '--help'], &lambda {|*commands, full:| if full cli.help_full *commands, output: $stderr else cli.help *commands, output: $stderr end }). opt( :full, '-f', '--[no-]full', "Print all commands and sub-commands.", default: false) cli.sub( :more, "Sub-Commands are also possible with a new cli") do |sub| sub.cmd( :help, "", aliases: [nil, '-h', '--help']) {|*args| $stderr.puts sub.help(*args) } sub.cmd( :example, "Here is an example, too") { $stderr.puts "This is an other example" } sub.cmd( :foo, "BAR") { $stderr.puts "FOO bar"} sub.cmd( :args, "Expects and prints given arguments", &lambda {|a, b=1, c:, d: 5, e:| p a: a, b: b, c: c, d: d, e: e }). opt( :c, '-c=ForC', "Option c"). opt( :d, '-d=ForD', "Option d (implicit default)"). opt( :e, '-e', "Toggle e") sub.sub( :deeper, "You want to have Sub-Sub-Commands?") do |sub2| sub2.cmd( :help, "", aliases: [nil, '-h', '--help'], &lambda {|*commands| sub2.help( *commands, output: $stderr) }) sub2.cmd( :last, "The last example", &lambda { $stderr.puts "The last example" }) sub2.sub( :'sub-commands', "Endless Sub-Sub- ... with a special alias") do |sub3| # h -> help # he -> hehe # hel -> help # help -> help # heh -> hehe # hehe -> hehe sub3.cmd( :help, "", min: 3, aliases: [nil, :h]) {|*args| $stderr.puts sub3.help( *args) } sub3.cmd( :hehe, "The real last example", min: 2) { $stderr.puts "Trust me!" } end end end cli.cmd( :cli, "Interactive shell", min: 3, &lambda {|| cli.interactive( File.basename($0, '.rb')).run }) cli.sub :tests, "Some tests", noshortaliases: true do |tcli| tcli.cmd( :help, "", min: 4) {|*args| $stderr.puts tcli.help( *args) } OptionParser.accept IPAddr do |arg| begin IPAddr.new arg rescue IPAddr::InvalidAddressError raise OptionParser::InvalidArgument, "#{$!.message}: #{arg}" end end tcli.cmd( :'-', "No arguments no options expected", &lambda {|| $capture.args = [] }) tcli.cmd( :'arg', "", &lambda {|one| $capture.args = [one] }) tcli.cmd( :'oar', "", &lambda {|one=nil| $capture.args = [one] }) tcli.cmd( :'arg-arg', "", &lambda {|one, two| $capture.args = [one, two] }) tcli.cmd( :'oar-oar', "", &lambda {|one=nil, two=nil| $capture.args = [one, two] }) tcli.cmd( :'arg-oar', "expected", &lambda {|one, two=nil| $capture.args = [one, two] }) tcli.cmd( :'oar-arg', "expected", &lambda {|one=nil, two| $capture.args = [one, two] }) tcli.cmd( :'bool', '', &lambda {|a:| $capture.args = [a] }).opt(:a, '-a', '') tcli.cmd( :'optbool', '', &lambda {|a: nil| $capture.args = [a] }).opt(:a, '-a', '') tcli.cmd( :'defbool', '', &lambda {|a:| $capture.args = [a] }).opt(:a, '-a', '', default: 'default') tcli.cmd( :'str', '', &lambda {|a:| $capture.args = [a] }).opt(:a, '-a=STR', '') tcli.cmd( :'lstr', '', &lambda {|a:| $capture.args = [a] }).opt(:a, '--astr=STR', '') tcli.cmd( :'bstr', '', &lambda {|a:| $capture.args = [a] }).opt(:a, '-a', '--astr=STR', '') tcli.cmd( :'ipaddr', '', &lambda {|a:| $capture&.args = [a] }).opt(:a, '-a', '--addr=ADDR', IPAddr, '') tcli.cmd( :run, "Run all tests") do |verbose:| $capture = Capture.new cli, verbose: verbose $capture.should_fail DenCli::UnknownCommand, //, 'unknown-command' $capture.should_ok [], '-' $capture.should_fail DenCli::UsageError, //, '-', 'unexpected' $capture.should_ok %w[first], 'arg', 'first' $capture.should_fail DenCli::UsageError, //, 'arg' $capture.should_fail DenCli::UsageError, //, 'arg', 'first', 'unexpected' $capture.should_ok %w[first], 'oar', 'first' $capture.should_ok [nil], 'oar' $capture.should_fail DenCli::UsageError, //, 'oar', 'first', 'unexpected' $capture.should_ok %w[first two], 'oar-oar', 'first', 'two' $capture.should_ok ['first', nil], 'oar-oar', 'first' $capture.should_ok [nil,nil], 'oar-oar' $capture.should_fail DenCli::UsageError, //, 'oar-oar', 'first', 'two', 'unexpected' $capture.should_ok %w[first two], 'arg-oar', 'first', 'two' $capture.should_ok ['first', nil], 'arg-oar', 'first' $capture.should_fail DenCli::UsageError, //, 'arg-oar' $capture.should_ok [nil, 'first'], 'oar-arg', 'first' $capture.should_ok ['first', 'second'], 'oar-arg', 'first', 'second' $capture.should_fail DenCli::UsageError, //, 'oar-arg' $capture.should_fail DenCli::UsageError, //, 'oar-arg', 'first', 'two', 'unexpected' $capture.should_ok [true], 'bool', '-a' $capture.should_fail DenCli::UsageError, //, 'bool' $capture.should_fail DenCli::UsageError, //, 'bool', '-a', 'unexpected' $capture.should_fail OptionParser::InvalidOption, //, 'bool', '-b' $capture.should_fail OptionParser::InvalidOption, //, 'bool', '--unexpected' $capture.should_fail DenCli::UsageError, //, 'bool', 'unexpected' $capture.should_ok [true], 'optbool', '-a' $capture.should_ok [nil], 'optbool' $capture.should_fail DenCli::UsageError, //, 'optbool', '-a', 'unexpected' $capture.should_fail OptionParser::InvalidOption, //, 'optbool', '-b' $capture.should_fail OptionParser::InvalidOption, //, 'optbool', '--unexpected' $capture.should_fail DenCli::UsageError, //, 'optbool', 'unexpected' $capture.should_ok [true], 'defbool', '-a' $capture.should_ok ['default'], 'defbool' $capture.should_fail DenCli::UsageError, //, 'defbool', '-a', 'unexpected' $capture.should_fail OptionParser::InvalidOption, //, 'defbool', '-b' $capture.should_fail OptionParser::InvalidOption, //, 'defbool', '--unexpected' $capture.should_fail DenCli::UsageError, //, 'defbool', 'unexpected' $capture.should_ok %w[first], 'str', '-a', 'first' $capture.should_ok %w[first], 'str', '-afirst' $capture.should_fail OptionParser::MissingArgument, //, 'str', '-a' $capture.should_fail DenCli::UsageError, //, 'str' $capture.should_fail OptionParser::InvalidOption, //, 'str', '-b' $capture.should_fail OptionParser::InvalidOption, //, 'str', '--unexpected' $capture.should_fail DenCli::UsageError, //, 'str', 'unexpected' $capture.should_ok %w[first], 'lstr', '--astr', 'first' $capture.should_ok %w[first], 'lstr', '--astr=first' $capture.should_fail OptionParser::MissingArgument, //, 'lstr', '--astr' $capture.should_fail DenCli::UsageError, //, 'lstr' $capture.should_fail OptionParser::InvalidOption, //, 'lstr', '-b' $capture.should_fail OptionParser::InvalidOption, //, 'lstr', '--unexpected' $capture.should_fail DenCli::UsageError, //, 'lstr', 'unexpected' $capture.should_ok %w[first], 'bstr', '-a', 'first' $capture.should_ok %w[first], 'bstr', '-afirst' $capture.should_ok %w[first], 'bstr', '--astr', 'first' $capture.should_ok %w[first], 'bstr', '--astr=first' $capture.should_fail OptionParser::MissingArgument, //, 'bstr', '--astr' $capture.should_fail OptionParser::MissingArgument, //, 'bstr', '-a' $capture.should_fail DenCli::UsageError, //, 'bstr' $capture.should_fail OptionParser::InvalidOption, //, 'bstr', '-b' $capture.should_fail OptionParser::InvalidOption, //, 'bstr', '--unexpected' $capture.should_fail DenCli::UsageError, //, 'bstr', 'unexpected' $capture.should_ok [IPAddr.new('1.2.3.4')], 'ipaddr', '-a', '1.2.3.4' $capture.should_fail OptionParser::InvalidArgument, /invalid address/, 'ipaddr', '-a', '1.2.3.400' end.opt( :verbose, '-v', 'Prints additional information per test', default: false) end begin cli.call *ARGV rescue DenCli::UsageError $stderr.puts $! exit 1 end