aliases are strings only or nil. more examples.

master
Denis Knauf 2022-01-30 14:18:20 +01:00
parent 09b467f7bd
commit c946a093f7
4 changed files with 121 additions and 65 deletions

View File

@ -95,7 +95,62 @@ class Capture
end end
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| 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| OptionParser.accept IPAddr do |arg|
begin begin
IPAddr.new arg IPAddr.new arg
@ -202,50 +257,6 @@ cli.sub :tests, "Some tests", noshortaliases: true do |tcli|
end.opt( :verbose, '-v', 'Prints additional information per test', default: false) end.opt( :verbose, '-v', 'Prints additional information per test', default: false)
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- ...") do |sub3|
sub3.cmd( :help, "") {|*args| $stderr.puts sub3.help( sub3, *args) }
sub3.cmd( :hehe, "The real last example", min: 2) { $stderr.puts "Trust me!" }
end
end
end
begin begin
cli.call *ARGV cli.call *ARGV
rescue DenCli::UsageError rescue DenCli::UsageError

View File

@ -80,6 +80,9 @@ class DenCli
post post
end end
def []( k) @subs[k] end
def has?( k) @subs.has? k end
def sub *a, **o, &exe def sub *a, **o, &exe
@subs.sub *a, **o, &exe @subs.sub *a, **o, &exe
end end
@ -110,10 +113,6 @@ class DenCli
Sub._help_commands output, subs.to_enum( :commands) Sub._help_commands output, subs.to_enum( :commands)
end end
def [] k
@subs[k]
end
def interactive *args, **opts def interactive *args, **opts
Interactive.new self, *args, **opts Interactive.new self, *args, **opts
end end

View File

@ -17,12 +17,15 @@ class DenCli::Interactive
end end
read_history if @histfile read_history if @histfile
Readline.vi_editing_mode rescue NotImplementedError begin
Readline.vi_editing_mode
rescue NotImplementedError
end
Readline.completion_append_character = " " Readline.completion_append_character = " "
Readline.completion_proc = method :complete Readline.completion_proc = method :complete
prepare_sub cl.subs prepare_sub cl.subs
cl.cmd :exit, "exit", min: 2 do cl.cmd :exit, "exit", min: cl.has?(:ex) ? cl.has?(:exi) ? 4 : 3 : 2 do
exit 0 exit 0
end end
cl.subs.aliases['?'] = cl.subs.subs['help'] cl.subs.aliases['?'] = cl.subs.subs['help']
@ -90,14 +93,16 @@ class DenCli::Interactive
c.subs.values.each do |n| c.subs.values.each do |n|
case n case n
when DenCli::Sub when DenCli::Sub
n.cmd :exit, "<- #{n.parent.full_cmd.join ' '} - #{n.parent.description[3..-1]}", min: 2 do n.cmd :exit,
@cur = n.parent "<- #{n.parent.full_cmd.join ' '} - #{n.parent.description[3..-1]}",
end min: n.has?(:ex) ? n.has?( :exi) ? 4 : 3 : 2,
&lambda {|| @cur = n.parent }
n.aliases.delete nil
n.subs.delete nil
n.cmd '', "", min: 2, aliases: [nil] do n.cmd '', "", min: 2, aliases: [nil] do
@cur = n @cur = n
end end
n.subs.delete '' n.aliases['?'] = n[:help] if n.has? :help and not n.has? '?'
n.aliases['?'] = n.subs['help']
prepare_sub n prepare_sub n
when DenCli::CMD when DenCli::CMD
else raise "Unsupported sub-type: #{x}" else raise "Unsupported sub-type: #{x}"

View File

@ -13,7 +13,9 @@ class DenCli::Sub
def _full_cmd( post) parent._full_cmd [@name]+post end def _full_cmd( post) parent._full_cmd [@name]+post end
def full_cmd() _full_cmd [] end def full_cmd() _full_cmd [] end
def []( k) @aliases[k] end def []( name) @aliases[name&.to_s] end
def has?( name) @aliases.has_key? name&.to_s end
def usage output: nil def usage output: nil
output ||= '' output ||= ''
@ -40,8 +42,8 @@ class DenCli::Sub
if n.nil? if n.nil?
output << "#{full_cmd.join ' '}: #{description}\n\n" output << "#{full_cmd.join ' '}: #{description}\n\n"
self.class._help_commands output, @subs self.class._help_commands output, @subs
elsif @aliases.has_key? n elsif has? n
@aliases[n]._help output, *a self[n]._help output, *a
else else
raise DenCli::UnknownCommand, "unknown command: #{_full_cmd( [n])[1..-1].join ' '}, available for #{full_cmd[1..-1].join' '}: #{@subs.keys.join ' '}" raise DenCli::UnknownCommand, "unknown command: #{_full_cmd( [n])[1..-1].join ' '}, available for #{full_cmd[1..-1].join' '}: #{@subs.keys.join ' '}"
end end
@ -96,8 +98,8 @@ class DenCli::Sub
def goto *a def goto *a
return self if a.empty? return self if a.empty?
n, *a = *a n, *a = *a
if @aliases.has_key? n if has? n
@aliases[n].goto *a self[n].goto *a
else else
raise DenCli::UnknownCommand, "unknown command: #{_full_cmd( [n])[1..-1].join ' '}, available for #{full_cmd[1..-1].join' '}: #{@subs.keys.join ' '}" raise DenCli::UnknownCommand, "unknown command: #{_full_cmd( [n])[1..-1].join ' '}, available for #{full_cmd[1..-1].join' '}: #{@subs.keys.join ' '}"
end end
@ -105,19 +107,19 @@ class DenCli::Sub
def call *a def call *a
n, *a = *a n, *a = *a
if @aliases.has_key? n if has? n
@aliases[n].call *a self[n].call *a
else else
raise DenCli::UnknownCommand, "unknown command: #{_full_cmd( [n])[1..-1].join ' '}, available for #{full_cmd[1..-1].join' '}: #{@subs.keys.join ' '}" raise DenCli::UnknownCommand, "unknown command: #{_full_cmd( [n])[1..-1].join ' '}, available for #{full_cmd[1..-1].join' '}: #{@subs.keys.join ' '}"
end end
end end
def _add name, min, obj, aliases def _add name, min, obj, aliases
#DenCli::assert_type self, __method__, :name, name, Symbol, NilClass #DenCli::assert_type self, __method__, :name, name, Symbol, String
#DenCli::assert_type self, __method__, :min, min, Integer, NilClass #DenCli::assert_type self, __method__, :min, min, Integer, NilClass
#DenCli::assert_type self, __method__, :obj, obj, DenCli::Sub, DenCli::CMD #DenCli::assert_type self, __method__, :obj, obj, DenCli::Sub, DenCli::CMD
#DenCli::assert_type self, __method__, :aliases, aliases, Array, NilClass #DenCli::assert_type self, __method__, :aliases, aliases, Array, NilClass
name = name.to_s unless name.nil? name = name.to_s
@subs[name] = obj @subs[name] = obj
if @noshortaliases if @noshortaliases
warn "Command/Alias for #{obj.full_cmd} defined in #{obj.defined_in} already exists: #{full_cmd.join ' '} #{name}. Used by #{@aliases[name].full_cmd} defined in #{@aliases[name].defined_in}" if @aliases.has_key? name warn "Command/Alias for #{obj.full_cmd} defined in #{obj.defined_in} already exists: #{full_cmd.join ' '} #{name}. Used by #{@aliases[name].full_cmd} defined in #{@aliases[name].defined_in}" if @aliases.has_key? name
@ -130,6 +132,7 @@ class DenCli::Sub
end end
if aliases if aliases
[*aliases].each do |a| [*aliases].each do |a|
a = a&.to_s
raise ArgumentError, "Alias for #{obj.full_cmd} defined in #{obj.defined_in} already exists: #{full_cmd.join ' '} #{a}. Used by #{@aliases[a].full_cmd} defined in #{@aliases[a].defined_in}" if @aliases.has_key? a raise ArgumentError, "Alias for #{obj.full_cmd} defined in #{obj.defined_in} already exists: #{full_cmd.join ' '} #{a}. Used by #{@aliases[a].full_cmd} defined in #{@aliases[a].defined_in}" if @aliases.has_key? a
@aliases[a] = obj @aliases[a] = obj
end end
@ -138,11 +141,49 @@ class DenCli::Sub
end end
private :_add private :_add
# Define a new sub-menu:
#
# DenCli.new {|c|
# c.sub( 'sub-command') {|s|
# s.cmd( :hello, 'Greetings', &lambda {|| puts 'hello world' })
# }
# }
#
# # ./prog sub-command hello
#
# name should be a string/symbol. It will be converted to string.
# If provided, aliases must be a list of different aliases. It will be converted to string.
def sub name, description, min: nil, aliases: nil, noshortaliases: nil, defined_in: nil, &exe def sub name, description, min: nil, aliases: nil, noshortaliases: nil, defined_in: nil, &exe
r = _add name, min, DenCli::Sub.new( self, name, description, noshortaliases: noshortaliases, defined_in: defined_in || Kernel.caller.first), aliases r = _add name.to_s, min, DenCli::Sub.new( self, name, description, noshortaliases: noshortaliases, defined_in: defined_in || Kernel.caller.first), aliases
block_given? ? yield( r) : r block_given? ? yield( r) : r
end end
# Define a new command:
#
# DenCli.new {|c|
# c.cmd( :hello, 'Greetings', &lambda {|| puts 'hello world' })
# }
#
# # ./prog hello
# hello world
#
# name should be a string/symbol. It will be converted to string.
# If provided, aliases must be a list of different aliases. Except of nil, any alias will be converted to string.
# nil is an alias for a default command for sub-commands, but interactive shells.
#
# DenCli.new {|c|
# c.sub( :greetings, 'Hello, Welcome, ...') do |s|
# s.cmd( :hello, 'A simple Hello', aliases: %w[hello-world hello_world], &lambda {|| puts 'Hello World' })
# s.cmd( :welcome, 'More gracefull', aliases: [nil, 'welcome-world', :hello_world], &lambda {|| puts 'Welcome World' })
# }
# }
#
# # ./prog greetings
# Welcome World
# # ./prog greetings welcome
# Welcome World
# # ./prog greetings hello
# Hello World
def cmd name, description, min: nil, aliases: nil, defined_in: nil, &exe def cmd name, description, min: nil, aliases: nil, defined_in: nil, &exe
_add name, min, DenCli::CMD.new( self, name, description, exe, defined_in || Kernel.caller.first), aliases _add name, min, DenCli::CMD.new( self, name, description, exe, defined_in || Kernel.caller.first), aliases
end end