require 'optparse' class DenCli def self.assert_type klass, method, argument_name, object, *types unless types.any? {|type| object.is_a? type } raise ArgumentError, "#{klass.name}.#{method} expects #{types.map( &:to_s).join '|' } for #{argument_name}, not: #{object.inspect}" end end class UsageError < ::RuntimeError end class UnknownCommand < UsageError end require_relative 'dencli/cmd' require_relative 'dencli/sub' require_relative 'dencli/interactive' require_relative 'dencli/version' class < `/abcd(?:|e|ef)/` def n s, min = nil min ||= 1 /#{s.length <= min ? Regexp.quote(s) : "#{Regexp.quote s[0...min]}#{ s[min...-1]. reverse. each_char. reduce( "#{Regexp.quote s[-1]}?") {|f,n| "(?:#{Regexp.quote n}#{f})?" } }" }/ end # Wraps `n(s,min=)` in a full matching RegExp with ending `\0`: # `r("abc")` would produce: `/\A(?:a|ab|abc)\0\z/` # You can define a minimum length to match: # `r("abcdef",4)` => `/\aabcd(?:|e|ef)\0\z/` def r s, min = nil /\A#{n s, min}\0\z/ end # Generates a list of aliases for given `cmd`: # `g(:abc)` => `["a", "ab", "abc"]` # `g(:abcdef, 4)` => `["abcd", "abcde", "abcdef"]` def gen_aliases cmd, min = nil case min when false then min = cmd.length when nil then min = 1 end r = ([min, 1].max - 1).upto cmd.length-2 if block_given? r.each {|i| yield cmd[0..i] } yield cmd else r.map {|i| cmd[0..i] } + [cmd] end end alias g gen_aliases end attr_reader :subs def initialize progname, description @subs = Sub.new self, progname, description end def full_cmd [] end def _full_cmd post post end def sub *a, **o, &exe @subs.sub *a, **o, &exe end def cmd *a, **o, &exe @subs.cmd *a, **o, &exe end def call *a @subs.call *a end def usage *args, **opts @subs.usage *args, **opts end def help *args, **opts @subs.help *args, **opts end def help_full *args, output: nil output ||= $stdout x = @subs.goto *args _help_full output, x end def _help_full output, subs Sub._help_commands output, subs.to_enum( :commands) end def [] k @subs[k] end def interactive *args, **opts Interactive.new self, *args, **opts end end