command_kit 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +18 -3
  3. data/.rubocop.yml +141 -0
  4. data/ChangeLog.md +165 -0
  5. data/Gemfile +3 -0
  6. data/README.md +186 -118
  7. data/Rakefile +3 -2
  8. data/command_kit.gemspec +4 -4
  9. data/examples/command.rb +1 -1
  10. data/gemspec.yml +7 -0
  11. data/lib/command_kit/arguments/argument.rb +2 -2
  12. data/lib/command_kit/arguments.rb +36 -7
  13. data/lib/command_kit/colors.rb +702 -53
  14. data/lib/command_kit/command.rb +2 -3
  15. data/lib/command_kit/commands/auto_load.rb +8 -1
  16. data/lib/command_kit/commands/help.rb +3 -2
  17. data/lib/command_kit/commands/subcommand.rb +1 -1
  18. data/lib/command_kit/commands.rb +24 -9
  19. data/lib/command_kit/env/path.rb +1 -1
  20. data/lib/command_kit/file_utils.rb +46 -0
  21. data/lib/command_kit/help/man.rb +17 -33
  22. data/lib/command_kit/inflector.rb +47 -17
  23. data/lib/command_kit/interactive.rb +9 -0
  24. data/lib/command_kit/main.rb +7 -9
  25. data/lib/command_kit/man.rb +44 -0
  26. data/lib/command_kit/open_app.rb +69 -0
  27. data/lib/command_kit/options/option.rb +41 -27
  28. data/lib/command_kit/options/option_value.rb +3 -2
  29. data/lib/command_kit/options/parser.rb +17 -22
  30. data/lib/command_kit/options.rb +102 -14
  31. data/lib/command_kit/os/linux.rb +157 -0
  32. data/lib/command_kit/os.rb +159 -11
  33. data/lib/command_kit/package_manager.rb +200 -0
  34. data/lib/command_kit/pager.rb +46 -4
  35. data/lib/command_kit/printing/indent.rb +4 -4
  36. data/lib/command_kit/printing.rb +14 -3
  37. data/lib/command_kit/program_name.rb +9 -0
  38. data/lib/command_kit/sudo.rb +40 -0
  39. data/lib/command_kit/terminal.rb +5 -0
  40. data/lib/command_kit/version.rb +1 -1
  41. data/spec/arguments/argument_spec.rb +1 -1
  42. data/spec/arguments_spec.rb +84 -1
  43. data/spec/colors_spec.rb +357 -70
  44. data/spec/command_spec.rb +77 -6
  45. data/spec/commands/auto_load_spec.rb +33 -2
  46. data/spec/commands_spec.rb +101 -29
  47. data/spec/env/path_spec.rb +6 -0
  48. data/spec/exception_handler_spec.rb +1 -1
  49. data/spec/file_utils_spec.rb +59 -0
  50. data/spec/fixtures/template.erb +5 -0
  51. data/spec/help/man_spec.rb +54 -57
  52. data/spec/inflector_spec.rb +70 -8
  53. data/spec/man_spec.rb +46 -0
  54. data/spec/open_app_spec.rb +85 -0
  55. data/spec/options/option_spec.rb +38 -2
  56. data/spec/options/option_value_spec.rb +55 -0
  57. data/spec/options/parser_spec.rb +0 -10
  58. data/spec/options_spec.rb +328 -0
  59. data/spec/os/linux_spec.rb +164 -0
  60. data/spec/os_spec.rb +200 -13
  61. data/spec/package_manager_spec.rb +806 -0
  62. data/spec/pager_spec.rb +71 -6
  63. data/spec/printing/indent_spec.rb +7 -5
  64. data/spec/printing_spec.rb +23 -1
  65. data/spec/program_name_spec.rb +8 -0
  66. data/spec/sudo_spec.rb +51 -0
  67. data/spec/terminal_spec.rb +30 -0
  68. data/spec/usage_spec.rb +1 -1
  69. metadata +23 -4
@@ -8,8 +8,7 @@ require 'command_kit/options'
8
8
  require 'command_kit/examples'
9
9
  require 'command_kit/description'
10
10
  require 'command_kit/exception_handler'
11
-
12
- require 'fileutils'
11
+ require 'command_kit/file_utils'
13
12
 
14
13
  module CommandKit
15
14
  #
@@ -56,7 +55,7 @@ module CommandKit
56
55
  # end
57
56
  # end
58
57
  #
59
- # ### initialize and using ivars
58
+ # ### initialize and using instance variables
60
59
  #
61
60
  # option :verbose, short: '-v', desc: "Increase verbose level" do
62
61
  # @verbose += 1
@@ -162,7 +162,14 @@ module CommandKit
162
162
  #
163
163
  def included(command)
164
164
  command.include Commands
165
- command.commands.merge!(@commands)
165
+
166
+ @commands.each do |name,subcommand|
167
+ command.commands[name] = subcommand
168
+
169
+ subcommand.aliases.each do |alias_name|
170
+ command.command_aliases[alias_name] = name
171
+ end
172
+ end
166
173
  end
167
174
  end
168
175
  end
@@ -14,7 +14,8 @@ module CommandKit
14
14
 
15
15
  include ParentCommand
16
16
 
17
- argument :command, desc: 'Command name to lookup'
17
+ argument :command, required: false,
18
+ desc: 'Command name to lookup'
18
19
 
19
20
  #
20
21
  # Prints the given commands `--help` output or lists registered commands.
@@ -34,7 +35,7 @@ module CommandKit
34
35
 
35
36
  subcommand.help
36
37
  else
37
- print_error "#{command_name}: unknown command: #{command}"
38
+ print_error "unknown command: #{command}"
38
39
  exit(1)
39
40
  end
40
41
  end
@@ -36,7 +36,7 @@ module CommandKit
36
36
  # Optional alias names for the subcommand.
37
37
  #
38
38
  def initialize(command, summary: self.class.summary(command),
39
- aliases: [])
39
+ aliases: [])
40
40
  @command = command
41
41
  @summary = summary
42
42
  @aliases = aliases.map(&:to_s)
@@ -59,6 +59,13 @@ module CommandKit
59
59
  context.extend ModuleMethods
60
60
  else
61
61
  context.usage "[options] [COMMAND [ARGS...]]"
62
+ context.argument :command, required: false,
63
+ desc: 'The command name to run'
64
+
65
+ context.argument :args, required: false,
66
+ repeats: true,
67
+ desc: 'Additional arguments for the command'
68
+
62
69
  context.extend ClassMethods
63
70
  context.command Help
64
71
  end
@@ -81,10 +88,10 @@ module CommandKit
81
88
  #
82
89
  def commands
83
90
  @commands ||= if superclass.kind_of?(ClassMethods)
84
- superclass.commands.dup
85
- else
86
- {}
87
- end
91
+ superclass.commands.dup
92
+ else
93
+ {}
94
+ end
88
95
  end
89
96
 
90
97
  #
@@ -140,11 +147,10 @@ module CommandKit
140
147
  end
141
148
 
142
149
  subcommand = Subcommand.new(command_class,**kwargs)
143
-
144
150
  commands[name] = subcommand
145
151
 
146
- subcommand.aliases.each do |command_alias|
147
- command_aliases[command_alias] = name
152
+ subcommand.aliases.each do |alias_name|
153
+ command_aliases[alias_name] = name
148
154
  end
149
155
 
150
156
  return subcommand
@@ -220,7 +226,9 @@ module CommandKit
220
226
  end
221
227
 
222
228
  if command_class.include?(Env)
223
- kwargs[:env] = env.dup
229
+ kwargs[:env] = if env.eql?(ENV) then env.to_h
230
+ else env.dup
231
+ end
224
232
  end
225
233
 
226
234
  if command_class.include?(Options)
@@ -296,6 +304,7 @@ module CommandKit
296
304
  exit invoke(command,*argv)
297
305
  else
298
306
  help
307
+ exit(1)
299
308
  end
300
309
  end
301
310
 
@@ -309,8 +318,14 @@ module CommandKit
309
318
  puts
310
319
  puts "Commands:"
311
320
 
321
+ command_aliases = Hash.new { |hash,key| hash[key] = [] }
322
+
323
+ self.class.command_aliases.each do |alias_name,name|
324
+ command_aliases[name] << alias_name
325
+ end
326
+
312
327
  self.class.commands.sort.each do |name,subcommand|
313
- names = [name, *subcommand.aliases].join(', ')
328
+ names = [name, *command_aliases[name]].join(', ')
314
329
 
315
330
  if subcommand.summary
316
331
  puts " #{names}\t#{subcommand.summary}"
@@ -16,7 +16,7 @@ module CommandKit
16
16
 
17
17
  # The home directory.
18
18
  #
19
- # @return [String]
19
+ # @return [Array<String>]
20
20
  #
21
21
  # @api semipublic
22
22
  attr_reader :path_dirs
@@ -0,0 +1,46 @@
1
+ require 'fileutils'
2
+ require 'erb'
3
+
4
+ module CommandKit
5
+ #
6
+ # File manipulation related methods.
7
+ #
8
+ # @since 0.3.0
9
+ #
10
+ # @api public
11
+ #
12
+ module FileUtils
13
+ include ::FileUtils
14
+
15
+ #
16
+ # Renders an erb file and optionally writes it out to a destination file.
17
+ #
18
+ # @param [String] source
19
+ # The path to the erb template file.
20
+ #
21
+ # @param [String, nil] dest
22
+ # The path to the destination file.
23
+ #
24
+ # @return [String, nil]
25
+ # If no destination path argument is given, the rendered erb template
26
+ # String will be returned.
27
+ #
28
+ # @example Rendering a ERB template and saving it's output:
29
+ # erb File.join(template_dir,'README.md.erb'), 'README.md'
30
+ #
31
+ # @example Rendering a ERB template and capturing it's output:
32
+ # output = erb(File.join(template_dir,'_partial.erb'))
33
+ #
34
+ def erb(source,dest=nil)
35
+ erb = ERB.new(File.read(source), trim_mode: '-')
36
+ result = erb.result(binding)
37
+
38
+ if dest
39
+ File.write(dest,result)
40
+ return
41
+ else
42
+ return result
43
+ end
44
+ end
45
+ end
46
+ end
@@ -3,6 +3,7 @@
3
3
  require 'command_kit/command_name'
4
4
  require 'command_kit/help'
5
5
  require 'command_kit/stdio'
6
+ require 'command_kit/man'
6
7
 
7
8
  module CommandKit
8
9
  module Help
@@ -23,6 +24,7 @@ module CommandKit
23
24
  include CommandName
24
25
  include Help
25
26
  include Stdio
27
+ include CommandKit::Man
26
28
 
27
29
  #
28
30
  # @api private
@@ -68,7 +70,7 @@ module CommandKit
68
70
  #
69
71
  def man_dir(new_man_dir=nil)
70
72
  if new_man_dir
71
- @man_dir = new_man_dir
73
+ @man_dir = File.expand_path(new_man_dir)
72
74
  else
73
75
  @man_dir || if superclass.kind_of?(ClassMethods)
74
76
  superclass.man_dir
@@ -96,29 +98,6 @@ module CommandKit
96
98
  end
97
99
  end
98
100
 
99
- #
100
- # Displays the given man page.
101
- #
102
- # @param [String] page
103
- # The man page file name.
104
- #
105
- # @param [Integer, String, nil] section
106
- # The optional section number to specify.
107
- #
108
- # @return [Boolean, nil]
109
- # Specifies whether the `man` command was successful or not.
110
- # Returns `nil` when the `man` command is not installed.
111
- #
112
- # @api public
113
- #
114
- def man(page, section: nil)
115
- if section
116
- system('man',section.to_s,page.to_s)
117
- else
118
- system('man',page.to_s)
119
- end
120
- end
121
-
122
101
  #
123
102
  # Provides help information by showing one of the man pages within
124
103
  # {ClassMethods#man_dir .man_dir}.
@@ -131,13 +110,13 @@ module CommandKit
131
110
  # Returns `nil` when the `man` command is not installed.
132
111
  #
133
112
  # @raise [NotImplementedError]
134
- # {ClassMethods#man_dir .man_dir} does not have a value.
113
+ # {ClassMethods#man_dir .man_dir} was not set in the class.
135
114
  #
136
115
  # @api semipublic
137
116
  #
138
117
  def help_man(man_page=self.class.man_page)
139
118
  unless self.class.man_dir
140
- raise(NotImplementedError,"#{self.class}.man_dir not set")
119
+ raise(NotImplementedError,"man_dir was not set in #{self.class}")
141
120
  end
142
121
 
143
122
  man_path = File.join(self.class.man_dir,man_page)
@@ -149,22 +128,27 @@ module CommandKit
149
128
  # Displays the {ClassMethods#man_page .man_page} in
150
129
  # {ClassMethods#man_dir .man_dir} instead of the usual `--help` output.
151
130
  #
152
- # @raise [NotImplementedError]
153
- # {ClassMethods#man_dir .man_dir} does not have a value.
154
- #
155
131
  # @note
156
- # if `TERM` is `dumb` or `$stdout` is not a TTY, fallsback to printing
157
- # the usual `--help` output.
132
+ # if `TERM` is `dumb` or `$stdout` is not a TTY, will fall back to
133
+ # printing the usual `--help` output.
158
134
  #
159
135
  # @api public
160
136
  #
161
137
  def help
162
138
  if stdout.tty?
163
- if help_man.nil?
164
- # the `man` command is not installed
139
+ if self.class.man_dir
140
+ status = help_man
141
+
142
+ if status.nil?
143
+ # the `man` command is not installed
144
+ super
145
+ end
146
+ else
147
+ # man_dir was not set
165
148
  super
166
149
  end
167
150
  else
151
+ # stdout is not a TTY
168
152
  super
169
153
  end
170
154
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'strscan'
4
+
3
5
  module CommandKit
4
6
  #
5
7
  # A very simple inflector.
@@ -33,16 +35,37 @@ module CommandKit
33
35
  # @return [String]
34
36
  # The resulting under_scored name.
35
37
  #
38
+ # @raise [ArgumentError]
39
+ # The given string contained non-alpha-numeric characters.
40
+ #
36
41
  def self.underscore(name)
37
- # sourced from: https://github.com/dry-rb/dry-inflector/blob/c918f967ff82611da374eb0847a77b7e012d3fa8/lib/dry/inflector.rb#L286-L287
38
- name = name.to_s.dup
42
+ scanner = StringScanner.new(name.to_s)
43
+ new_string = String.new
44
+
45
+ until scanner.eos?
46
+ if (separator = scanner.scan(/[_-]+/))
47
+ new_string << ('_' * separator.length)
48
+ else
49
+ if (capitalized = scanner.scan(/[A-Z][a-z\d]+/))
50
+ new_string << capitalized
51
+ elsif (uppercase = scanner.scan(/[A-Z][A-Z\d]*(?=[A-Z_-]|$)/))
52
+ new_string << uppercase
53
+ elsif (lowercase = scanner.scan(/[a-z][a-z\d]*/))
54
+ new_string << lowercase
55
+ else
56
+ raise(ArgumentError,"cannot convert string to underscored: #{scanner.string.inspect}")
57
+ end
39
58
 
40
- name.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
41
- name.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
42
- name.tr!('-','_')
43
- name.downcase!
59
+ if (separator = scanner.scan(/[_-]+/))
60
+ new_string << ('_' * separator.length)
61
+ elsif !scanner.eos?
62
+ new_string << '_'
63
+ end
64
+ end
65
+ end
44
66
 
45
- name
67
+ new_string.downcase!
68
+ new_string
46
69
  end
47
70
 
48
71
  #
@@ -67,20 +90,27 @@ module CommandKit
67
90
  # @return [String]
68
91
  # The CamelCased name.
69
92
  #
93
+ # @raise [ArgumentError]
94
+ # The given under_scored string contained non-alpha-numeric characters.
95
+ #
70
96
  def self.camelize(name)
71
- name = name.to_s.dup
72
-
73
- # sourced from: https://github.com/dry-rb/dry-inflector/blob/c918f967ff82611da374eb0847a77b7e012d3fa8/lib/dry/inflector.rb#L329-L334
74
- name.sub!(/^[a-z\d]*/,&:capitalize)
75
- name.gsub!(%r{(?:[_-]|(/))([a-z\d]*)}i) do |match|
76
- slash = Regexp.last_match(1)
77
- word = Regexp.last_match(2)
97
+ scanner = StringScanner.new(name.to_s)
98
+ new_string = String.new
78
99
 
79
- "#{slash}#{word.capitalize}"
100
+ until scanner.eos?
101
+ if (word = scanner.scan(/[A-Za-z\d]+/))
102
+ word.capitalize!
103
+ new_string << word
104
+ elsif scanner.scan(/[_-]+/)
105
+ # skip
106
+ elsif scanner.scan(/\//)
107
+ new_string << '::'
108
+ else
109
+ raise(ArgumentError,"cannot convert string to CamelCase: #{scanner.string.inspect}")
110
+ end
80
111
  end
81
112
 
82
- name.gsub!('/','::')
83
- name
113
+ new_string
84
114
  end
85
115
  end
86
116
  end
@@ -218,6 +218,15 @@ module CommandKit
218
218
  #
219
219
  # Asks the user for secret input.
220
220
  #
221
+ # @param [String] prompt
222
+ # The prompt that will be printed before reading input.
223
+ #
224
+ # @param [Boolean] required
225
+ # Requires non-empty input.
226
+ #
227
+ # @return [String]
228
+ # The user input.
229
+ #
221
230
  # @example
222
231
  # ask_secret("Password")
223
232
  # # Password:
@@ -49,15 +49,13 @@ module CommandKit
49
49
  # @api public
50
50
  #
51
51
  def start(argv=ARGV, **kwargs)
52
- begin
53
- exit main(argv, **kwargs)
54
- rescue Interrupt
55
- # https://tldp.org/LDP/abs/html/exitcodes.html
56
- exit 130
57
- rescue Errno::EPIPE
58
- # STDOUT pipe broken
59
- exit 0
60
- end
52
+ exit main(argv, **kwargs)
53
+ rescue Interrupt
54
+ # https://tldp.org/LDP/abs/html/exitcodes.html
55
+ exit 130
56
+ rescue Errno::EPIPE
57
+ # STDOUT pipe broken
58
+ exit 0
61
59
  end
62
60
 
63
61
  #
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandKit
4
+ #
5
+ # Allows displaying man pages.
6
+ #
7
+ # ## Examples
8
+ #
9
+ # man "passwd"
10
+ # man "passwd", section: 5
11
+ #
12
+ # @since 0.2.0
13
+ #
14
+ module Man
15
+ #
16
+ # Displays the given man page.
17
+ #
18
+ # @param [String] page
19
+ # The man page file name.
20
+ #
21
+ # @param [Integer, String, nil] section
22
+ # The optional section number to specify.
23
+ #
24
+ # @return [Boolean, nil]
25
+ # Specifies whether the `man` command was successful or not.
26
+ # Returns `nil` when the `man` command is not installed.
27
+ #
28
+ # @example
29
+ # man "passwd"
30
+ #
31
+ # @example Display a man-page from a specific section:
32
+ # man "passwd", section: 5
33
+ #
34
+ # @api public
35
+ #
36
+ def man(page, section: nil)
37
+ if section
38
+ system('man',section.to_s,page.to_s)
39
+ else
40
+ system('man',page.to_s)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,69 @@
1
+ require 'command_kit/os'
2
+ require 'command_kit/env/path'
3
+
4
+ module CommandKit
5
+ #
6
+ # Allows opening a file or a URI with the system's preferred application for
7
+ # that file type or URI scheme.
8
+ #
9
+ # ## Examples
10
+ #
11
+ # open_app_for "movie.avi"
12
+ # open_app_for "https://github.com/postmodern/command_kit.rb#readme"
13
+ #
14
+ # @since 0.2.0
15
+ #
16
+ module OpenApp
17
+ include OS
18
+ include Env::Path
19
+
20
+ #
21
+ # Initializes the command and determines which open command to use.
22
+ #
23
+ # @param [Hash{Symbol => Object}] kwargs
24
+ # Additional keyword arguments.
25
+ #
26
+ # @api public
27
+ #
28
+ def initialize(**kwargs)
29
+ super(**kwargs)
30
+
31
+ @open_command = if macos?
32
+ 'open'
33
+ elsif linux? || bsd?
34
+ if command_installed?('xdg-open')
35
+ 'xdg-open'
36
+ end
37
+ elsif windows?
38
+ if command_installed?('invoke-item')
39
+ 'invoke-item'
40
+ else
41
+ 'start'
42
+ end
43
+ end
44
+ end
45
+
46
+ #
47
+ # Opens a file or URI using the system's preferred application for that
48
+ # file type or URI scheme.
49
+ #
50
+ # @param [String, URI] file_or_uri
51
+ # The file path or URI to open.
52
+ #
53
+ # @return [Boolean, nil]
54
+ # Specifies whether the file or URI was successfully opened or not.
55
+ # If the open command could not be determined, `nil` is returned.
56
+ #
57
+ # @example Open a file:
58
+ # open_app_for "movie.avi"
59
+ #
60
+ # @example Open a URI:
61
+ # open_app_for "https://github.com/postmodern/command_kit.rb"
62
+ #
63
+ def open_app_for(file_or_uri)
64
+ if @open_command
65
+ system(@open_command,file_or_uri.to_s)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -35,16 +35,18 @@ module CommandKit
35
35
  # @return [OptionValue, nil]
36
36
  attr_reader :value
37
37
 
38
- # The option's description.
39
- #
40
- # @return [String]
41
- attr_reader :desc
42
-
43
38
  # The optional block that will receive the parsed option value.
44
39
  #
45
40
  # @return [Proc, nil]
46
41
  attr_reader :block
47
42
 
43
+ # The optional category to group the option under.
44
+ #
45
+ # @return [String, nil]
46
+ #
47
+ # @since 0.3.0
48
+ attr_reader :category
49
+
48
50
  #
49
51
  # Initializes the option.
50
52
  #
@@ -70,9 +72,12 @@ module CommandKit
70
72
  # @option value [String, nil] usage
71
73
  # The usage string for the option's value.
72
74
  #
73
- # @param [String] desc
75
+ # @param [String, Array<String>] desc
74
76
  # The description for the option.
75
77
  #
78
+ # @param [String, nil] category
79
+ # The optional category to group the option under.
80
+ #
76
81
  # @yield [(value)]
77
82
  # If a block is given, it will be called when the option is parsed.
78
83
  #
@@ -83,25 +88,27 @@ module CommandKit
83
88
  # The `value` keyword argument was not a `Hash`, `true`, `false`, or
84
89
  # `nil`.
85
90
  #
86
- def initialize(name, short: nil,
87
- long: self.class.default_long_opt(name),
88
- equals: false,
89
- value: nil,
90
- desc: ,
91
+ def initialize(name, short: nil,
92
+ long: self.class.default_long_opt(name),
93
+ equals: false,
94
+ value: nil,
95
+ desc: ,
96
+ category: nil,
91
97
  &block)
92
- @name = name
93
- @short = short
94
- @long = long
95
- @equals = equals
96
- @value = case value
97
- when Hash then OptionValue.new(**value)
98
- when true then OptionValue.new()
99
- when false, nil then nil
100
- else
101
- raise(TypeError,"value: keyword must be Hash, true, false, or nil")
102
- end
103
- @desc = desc
104
- @block = block
98
+ @name = name
99
+ @short = short
100
+ @long = long
101
+ @equals = equals
102
+ @value = case value
103
+ when Hash then OptionValue.new(**value)
104
+ when true then OptionValue.new()
105
+ when false, nil then nil
106
+ else
107
+ raise(TypeError,"value: keyword must be Hash, true, false, or nil")
108
+ end
109
+ @desc = desc
110
+ @category = category
111
+ @block = block
105
112
  end
106
113
 
107
114
  #
@@ -128,7 +135,7 @@ module CommandKit
128
135
  end
129
136
 
130
137
  #
131
- # The separator characer between the option and option value.
138
+ # The separator character between the option and option value.
132
139
  #
133
140
  # @return ['=', ' ', nil]
134
141
  #
@@ -184,7 +191,7 @@ module CommandKit
184
191
  #
185
192
  # The option description.
186
193
  #
187
- # @return [String]
194
+ # @return [String, Array<String>]
188
195
  #
189
196
  # @note
190
197
  # If {#default_value} returns a value, the description will contain the
@@ -192,7 +199,14 @@ module CommandKit
192
199
  #
193
200
  def desc
194
201
  if (value = default_value)
195
- "#{@desc} (Default: #{value})"
202
+ default_text = "(Default: #{value})"
203
+
204
+ case @desc
205
+ when Array
206
+ @desc + [default_text]
207
+ else
208
+ "#{@desc} #{default_text}"
209
+ end
196
210
  else
197
211
  @desc
198
212
  end
@@ -41,7 +41,7 @@ module CommandKit
41
41
 
42
42
  # The desired type of the argument value.
43
43
  #
44
- # @return [Class, Hash, Array, Regexp, nil]
44
+ # @return [Class, Hash, Array, Regexp]
45
45
  attr_reader :type
46
46
 
47
47
  # The default parsed value for the argument value.
@@ -92,10 +92,11 @@ module CommandKit
92
92
  def self.default_usage(type)
93
93
  USAGES.fetch(type) do
94
94
  case type
95
- when Class then Inflector.underscore(type.name).upcase
96
95
  when Hash then type.keys.join('|')
97
96
  when Array then type.join('|')
98
97
  when Regexp then type.source
98
+ when Class
99
+ Inflector.underscore(Inflector.demodularize(type.name)).upcase
99
100
  else
100
101
  raise(TypeError,"unsupported option type: #{type.inspect}")
101
102
  end