command_kit 0.1.0.pre1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +15 -0
  3. data/.rubocop.yml +138 -0
  4. data/ChangeLog.md +34 -2
  5. data/Gemfile +3 -0
  6. data/README.md +135 -214
  7. data/Rakefile +3 -2
  8. data/command_kit.gemspec +4 -4
  9. data/examples/colors.rb +30 -0
  10. data/examples/command.rb +65 -0
  11. data/examples/pager.rb +30 -0
  12. data/gemspec.yml +10 -2
  13. data/lib/command_kit/arguments/argument.rb +16 -44
  14. data/lib/command_kit/arguments/argument_value.rb +3 -30
  15. data/lib/command_kit/arguments.rb +66 -20
  16. data/lib/command_kit/colors.rb +253 -45
  17. data/lib/command_kit/command.rb +50 -3
  18. data/lib/command_kit/command_name.rb +9 -0
  19. data/lib/command_kit/commands/auto_load/subcommand.rb +3 -0
  20. data/lib/command_kit/commands/auto_load.rb +16 -0
  21. data/lib/command_kit/commands/auto_require.rb +16 -0
  22. data/lib/command_kit/commands/command.rb +3 -0
  23. data/lib/command_kit/commands/help.rb +2 -0
  24. data/lib/command_kit/commands/parent_command.rb +7 -0
  25. data/lib/command_kit/commands/subcommand.rb +15 -0
  26. data/lib/command_kit/commands.rb +40 -4
  27. data/lib/command_kit/description.rb +15 -2
  28. data/lib/command_kit/env/home.rb +9 -0
  29. data/lib/command_kit/env/path.rb +15 -0
  30. data/lib/command_kit/env.rb +4 -0
  31. data/lib/command_kit/examples.rb +15 -2
  32. data/lib/command_kit/exception_handler.rb +4 -0
  33. data/lib/command_kit/help/man.rb +74 -47
  34. data/lib/command_kit/help.rb +10 -1
  35. data/lib/command_kit/inflector.rb +49 -17
  36. data/lib/command_kit/interactive.rb +239 -0
  37. data/lib/command_kit/main.rb +20 -9
  38. data/lib/command_kit/man.rb +44 -0
  39. data/lib/command_kit/open_app.rb +69 -0
  40. data/lib/command_kit/options/option.rb +36 -9
  41. data/lib/command_kit/options/option_value.rb +42 -3
  42. data/lib/command_kit/options/parser.rb +44 -17
  43. data/lib/command_kit/options/quiet.rb +3 -0
  44. data/lib/command_kit/options/verbose.rb +5 -0
  45. data/lib/command_kit/options/version.rb +6 -0
  46. data/lib/command_kit/options.rb +59 -10
  47. data/lib/command_kit/os/linux.rb +157 -0
  48. data/lib/command_kit/os.rb +165 -11
  49. data/lib/command_kit/package_manager.rb +200 -0
  50. data/lib/command_kit/pager.rb +84 -9
  51. data/lib/command_kit/printing/indent.rb +25 -2
  52. data/lib/command_kit/printing.rb +23 -0
  53. data/lib/command_kit/program_name.rb +7 -0
  54. data/lib/command_kit/stdio.rb +24 -0
  55. data/lib/command_kit/sudo.rb +40 -0
  56. data/lib/command_kit/terminal.rb +159 -0
  57. data/lib/command_kit/usage.rb +14 -0
  58. data/lib/command_kit/version.rb +1 -1
  59. data/lib/command_kit/xdg.rb +21 -1
  60. data/lib/command_kit.rb +1 -0
  61. data/spec/arguments/argument_spec.rb +5 -41
  62. data/spec/arguments/argument_value_spec.rb +1 -61
  63. data/spec/arguments_spec.rb +8 -25
  64. data/spec/colors_spec.rb +277 -13
  65. data/spec/command_name_spec.rb +1 -1
  66. data/spec/command_spec.rb +4 -1
  67. data/spec/commands/auto_load/subcommand_spec.rb +1 -1
  68. data/spec/commands/auto_load_spec.rb +1 -1
  69. data/spec/commands/auto_require_spec.rb +2 -2
  70. data/spec/commands/help_spec.rb +1 -1
  71. data/spec/commands/parent_command_spec.rb +1 -1
  72. data/spec/commands/subcommand_spec.rb +1 -1
  73. data/spec/commands_spec.rb +2 -2
  74. data/spec/description_spec.rb +1 -25
  75. data/spec/env/home_spec.rb +1 -1
  76. data/spec/env/path_spec.rb +1 -1
  77. data/spec/examples_spec.rb +1 -25
  78. data/spec/exception_handler_spec.rb +1 -1
  79. data/spec/help/man_spec.rb +316 -0
  80. data/spec/help_spec.rb +0 -25
  81. data/spec/inflector_spec.rb +71 -9
  82. data/spec/interactive_spec.rb +415 -0
  83. data/spec/main_spec.rb +7 -7
  84. data/spec/man_spec.rb +46 -0
  85. data/spec/open_app_spec.rb +85 -0
  86. data/spec/options/option_spec.rb +48 -9
  87. data/spec/options/option_value_spec.rb +53 -4
  88. data/spec/options_spec.rb +1 -1
  89. data/spec/os/linux_spec.rb +154 -0
  90. data/spec/os_spec.rb +201 -14
  91. data/spec/package_manager_spec.rb +806 -0
  92. data/spec/pager_spec.rb +78 -15
  93. data/spec/printing/indent_spec.rb +1 -1
  94. data/spec/printing_spec.rb +10 -2
  95. data/spec/program_name_spec.rb +1 -1
  96. data/spec/spec_helper.rb +0 -3
  97. data/spec/sudo_spec.rb +51 -0
  98. data/spec/{console_spec.rb → terminal_spec.rb} +65 -35
  99. data/spec/usage_spec.rb +2 -2
  100. data/spec/xdg_spec.rb +1 -1
  101. metadata +32 -13
  102. data/lib/command_kit/arguments/usage.rb +0 -6
  103. data/lib/command_kit/console.rb +0 -141
  104. data/lib/command_kit/options/usage.rb +0 -6
@@ -31,6 +31,9 @@ module CommandKit
31
31
  # # => "foo-cmd"
32
32
  #
33
33
  module CommandName
34
+ #
35
+ # @api private
36
+ #
34
37
  module ModuleMethods
35
38
  #
36
39
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether
@@ -65,6 +68,8 @@ module CommandKit
65
68
  #
66
69
  # @return [String]
67
70
  #
71
+ # @api public
72
+ #
68
73
  def command_name(new_command_name=nil)
69
74
  if new_command_name
70
75
  @command_name = new_command_name.to_s
@@ -77,6 +82,8 @@ module CommandKit
77
82
  # The commands name.
78
83
  #
79
84
  # @return [String]
85
+ #
86
+ # @api public
80
87
  attr_reader :command_name
81
88
 
82
89
  #
@@ -86,6 +93,8 @@ module CommandKit
86
93
  # Overrides the command name. Defaults to
87
94
  # {ClassMethods#command_name self.class.command_name}.
88
95
  #
96
+ # @api public
97
+ #
89
98
  def initialize(command_name: self.class.command_name, **kwargs)
90
99
  @command_name = command_name
91
100
 
@@ -3,6 +3,9 @@ require 'command_kit/commands/subcommand'
3
3
  module CommandKit
4
4
  module Commands
5
5
  class AutoLoad < Module
6
+ #
7
+ # Represents a registered subcommand that will be auto-loaded.
8
+ #
6
9
  class Subcommand < Commands::Subcommand
7
10
 
8
11
  # The fully qualified constant of the command class.
@@ -40,16 +40,22 @@ module CommandKit
40
40
  # The auto-load subcommands.
41
41
  #
42
42
  # @return [Hash{String => Subcommand}]
43
+ #
44
+ # @api private
43
45
  attr_reader :commands
44
46
 
45
47
  # The path to the directory containing the command files.
46
48
  #
47
49
  # @return [String]
50
+ #
51
+ # @api private
48
52
  attr_reader :dir
49
53
 
50
54
  # The namespace that the will contain the command classes.
51
55
  #
52
56
  # @return [String]
57
+ #
58
+ # @api private
53
59
  attr_reader :namespace
54
60
 
55
61
  #
@@ -65,6 +71,8 @@ module CommandKit
65
71
  # If a block is given, it will be used to explicitly map the files
66
72
  # within {#dir} as commands.
67
73
  #
74
+ # @api public
75
+ #
68
76
  def initialize(dir: , namespace: )
69
77
  @commands = {}
70
78
 
@@ -106,6 +114,8 @@ module CommandKit
106
114
  # @option kwargs [Array<String>] aliases
107
115
  # Optional alias names for the subcommand.
108
116
  #
117
+ # @api public
118
+ #
109
119
  def command(name, constant, file, **kwargs)
110
120
  @commands[name.to_s] = Subcommand.new(
111
121
  "#{@namespace}::#{constant}",
@@ -123,6 +133,8 @@ module CommandKit
123
133
  # @return [String]
124
134
  # The joined absolute path.
125
135
  #
136
+ # @api private
137
+ #
126
138
  def join(path)
127
139
  File.join(@dir,path)
128
140
  end
@@ -133,6 +145,8 @@ module CommandKit
133
145
  # @return [Array<String>]
134
146
  # The paths to the `.rb` files in the directory.
135
147
  #
148
+ # @api private
149
+ #
136
150
  def files
137
151
  Dir.glob(join('*.rb'))
138
152
  end
@@ -144,6 +158,8 @@ module CommandKit
144
158
  # @param [Class] command
145
159
  # The command class including {AutoLoad}.
146
160
  #
161
+ # @api private
162
+ #
147
163
  def included(command)
148
164
  command.include Commands
149
165
  command.commands.merge!(@commands)
@@ -27,11 +27,15 @@ module CommandKit
27
27
  # The directory to attempt to require command files within.
28
28
  #
29
29
  # @return [String]
30
+ #
31
+ # @api private
30
32
  attr_reader :dir
31
33
 
32
34
  # The namespace to lookup command classes within.
33
35
  #
34
36
  # @return [String]
37
+ #
38
+ # @api private
35
39
  attr_reader :namespace
36
40
 
37
41
  #
@@ -43,6 +47,8 @@ module CommandKit
43
47
  # @param [String] namespace
44
48
  # The namespace to search for command classes in.
45
49
  #
50
+ # @api public
51
+ #
46
52
  def initialize(dir: , namespace: )
47
53
  @dir = dir
48
54
  @namespace = namespace
@@ -57,6 +63,8 @@ module CommandKit
57
63
  # @return [String]
58
64
  # The path to the file that should contain the command.
59
65
  #
66
+ # @api private
67
+ #
60
68
  def join(name)
61
69
  File.join(@dir,name)
62
70
  end
@@ -70,6 +78,8 @@ module CommandKit
70
78
  #
71
79
  # @raise [LoadError]
72
80
  #
81
+ # @api private
82
+ #
73
83
  def require(file_name)
74
84
  super(join(file_name))
75
85
  end
@@ -86,6 +96,8 @@ module CommandKit
86
96
  # @raise [NameError]
87
97
  # The command class could not be found within the {#namespace}.
88
98
  #
99
+ # @api private
100
+ #
89
101
  def const_get(constant)
90
102
  Object.const_get("::#{@namespace}::#{constant}",false)
91
103
  end
@@ -100,6 +112,8 @@ module CommandKit
100
112
  # The command's class, or `nil` if the command cannot be loaded from
101
113
  # {#dir} or found within {#namespace}.
102
114
  #
115
+ # @api private
116
+ #
103
117
  def command(command_name)
104
118
  file_name = Inflector.underscore(command_name)
105
119
 
@@ -125,6 +139,8 @@ module CommandKit
125
139
  # @param [Class] command
126
140
  # The command class including {AutoRequire}.
127
141
  #
142
+ # @api private
143
+ #
128
144
  def included(command)
129
145
  command.include Commands
130
146
  command.commands.default_proc = ->(hash,key) {
@@ -3,6 +3,9 @@ require 'command_kit/commands/parent_command'
3
3
 
4
4
  module CommandKit
5
5
  module Commands
6
+ #
7
+ # @api public
8
+ #
6
9
  class Command < CommandKit::Command
7
10
 
8
11
  include ParentCommand
@@ -8,6 +8,8 @@ module CommandKit
8
8
  #
9
9
  # The default help command.
10
10
  #
11
+ # @api semipublic
12
+ #
11
13
  class Help < Command
12
14
 
13
15
  include ParentCommand
@@ -1,15 +1,22 @@
1
1
  module CommandKit
2
2
  module Commands
3
+ #
4
+ # Allows a command to be aware of it's parent command.
5
+ #
3
6
  module ParentCommand
4
7
 
5
8
  # The parent command instance.
6
9
  #
7
10
  # @return [Object<Commands>]
11
+ #
12
+ # @api semipublic
8
13
  attr_reader :parent_command
9
14
 
10
15
  #
11
16
  # Initializes the command and sets {#parent_command}.
12
17
  #
18
+ # @api public
19
+ #
13
20
  def initialize(parent_command: , **kwargs)
14
21
  @parent_command = parent_command
15
22
 
@@ -1,5 +1,10 @@
1
1
  module CommandKit
2
2
  module Commands
3
+ #
4
+ # Represents a registered subcommand.
5
+ #
6
+ # @api private
7
+ #
3
8
  class Subcommand
4
9
 
5
10
  # The command class.
@@ -37,6 +42,16 @@ module CommandKit
37
42
  @aliases = aliases.map(&:to_s)
38
43
  end
39
44
 
45
+ #
46
+ # Derives a summary from the command's description.
47
+ #
48
+ # @param [Class] command
49
+ # The command class.
50
+ #
51
+ # @return [String, nil]
52
+ # If the command responds to a `#description` method, the first sentence
53
+ # of the description will be returned. Otherwise `nil` is returned.
54
+ #
40
55
  def self.summary(command)
41
56
  if command.respond_to?(:description)
42
57
  if (desc = command.description)
@@ -41,6 +41,9 @@ module CommandKit
41
41
  include Stdio
42
42
  include Env
43
43
 
44
+ #
45
+ # @api private
46
+ #
44
47
  module ModuleMethods
45
48
  #
46
49
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether
@@ -73,13 +76,15 @@ module CommandKit
73
76
  #
74
77
  # @return [Hash{String => Subcommand}]
75
78
  # The Hash of sub-command names and command classes.
79
+ #
80
+ # @api semipublic
76
81
  #
77
82
  def commands
78
83
  @commands ||= if superclass.kind_of?(ClassMethods)
79
- superclass.commands.dup
80
- else
81
- {}
82
- end
84
+ superclass.commands.dup
85
+ else
86
+ {}
87
+ end
83
88
  end
84
89
 
85
90
  #
@@ -88,6 +93,8 @@ module CommandKit
88
93
  # @return [Hash{String => String}]
89
94
  # The Hash of command aliases to primary command names.
90
95
  #
96
+ # @api semipublic
97
+ #
91
98
  def command_aliases
92
99
  @command_aliases ||= if superclass.kind_of?(ClassMethods)
93
100
  superclass.command_aliases.dup
@@ -125,6 +132,8 @@ module CommandKit
125
132
  # @example
126
133
  # command 'foo-bar', FooBar
127
134
  #
135
+ # @api public
136
+ #
128
137
  def command(name=nil, command_class, **kwargs)
129
138
  name = if name then name.to_s
130
139
  else command_class.command_name
@@ -145,8 +154,12 @@ module CommandKit
145
154
  # Gets the command.
146
155
  #
147
156
  # @param [String] name
157
+ # The command name.
148
158
  #
149
159
  # @return [Class#main, nil]
160
+ # The command class or `nil` if no command could be found.
161
+ #
162
+ # @api private
150
163
  #
151
164
  def get_command(name)
152
165
  name = name.to_s
@@ -158,6 +171,14 @@ module CommandKit
158
171
  end
159
172
  end
160
173
 
174
+ #
175
+ # Initializes the command.
176
+ #
177
+ # @note Adds a special rule to the {Options#option_parser #option_parser} to
178
+ # stop parsing options when the first non-option is encountered.
179
+ #
180
+ # @api public
181
+ #
161
182
  def initialize(**kwargs)
162
183
  super(**kwargs)
163
184
 
@@ -175,6 +196,8 @@ module CommandKit
175
196
  # @return [Object#main, nil]
176
197
  # The initialized subcommand.
177
198
  #
199
+ # @api private
200
+ #
178
201
  def command(name)
179
202
  unless (command_class = self.class.get_command(name))
180
203
  return
@@ -219,6 +242,8 @@ module CommandKit
219
242
  # @return [Integer]
220
243
  # The exit status of the command.
221
244
  #
245
+ # @api semipublic
246
+ #
222
247
  def invoke(name,*argv)
223
248
  if (command = command(name))
224
249
  command.main(argv)
@@ -231,6 +256,9 @@ module CommandKit
231
256
  # Prints an error about an unknown command and exits with an error code.
232
257
  #
233
258
  # @param [String] name
259
+ # The command name.
260
+ #
261
+ # @api semipublic
234
262
  #
235
263
  def command_not_found(name)
236
264
  print_error "'#{name}' is not a #{command_name} command. See `#{command_name} help`"
@@ -250,6 +278,8 @@ module CommandKit
250
278
  #
251
279
  # @see command_not_found
252
280
  #
281
+ # @api semipublic
282
+ #
253
283
  def on_unknown_command(name,argv=[])
254
284
  command_not_found(name)
255
285
  end
@@ -259,6 +289,8 @@ module CommandKit
259
289
  #
260
290
  # @note If no subcommand is given, {#help} will be called.
261
291
  #
292
+ # @api public
293
+ #
262
294
  def run(command=nil,*argv)
263
295
  if command
264
296
  exit invoke(command,*argv)
@@ -270,6 +302,8 @@ module CommandKit
270
302
  #
271
303
  # Prints the available commands and their summaries.
272
304
  #
305
+ # @api semipublic
306
+ #
273
307
  def help_commands
274
308
  unless self.class.commands.empty?
275
309
  puts
@@ -290,6 +324,8 @@ module CommandKit
290
324
  #
291
325
  # Prints help information and available commands.
292
326
  #
327
+ # @api public
328
+ #
293
329
  def help
294
330
  super
295
331
 
@@ -13,6 +13,9 @@ module CommandKit
13
13
  module Description
14
14
  include Help
15
15
 
16
+ #
17
+ # @api private
18
+ #
16
19
  module ModuleMethods
17
20
  #
18
21
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether
@@ -50,11 +53,15 @@ module CommandKit
50
53
  # @example
51
54
  # description "Does things and stuff"
52
55
  #
56
+ # @api public
57
+ #
53
58
  def description(new_description=nil)
54
59
  if new_description
55
60
  @description = new_description
56
61
  else
57
- @description || (superclass.description if superclass.kind_of?(ClassMethods))
62
+ @description || if superclass.kind_of?(ClassMethods)
63
+ superclass.description
64
+ end
58
65
  end
59
66
  end
60
67
  end
@@ -62,6 +69,8 @@ module CommandKit
62
69
  #
63
70
  # @see ClassMethods#description
64
71
  #
72
+ # @api semipublic
73
+ #
65
74
  def description
66
75
  self.class.description
67
76
  end
@@ -69,6 +78,8 @@ module CommandKit
69
78
  #
70
79
  # Prints the {ClassMethods#description description}, if set.
71
80
  #
81
+ # @api semipublic
82
+ #
72
83
  def help_description
73
84
  if (description = self.description)
74
85
  puts
@@ -80,8 +91,10 @@ module CommandKit
80
91
  # Calls the superclass'es `#help` method, if it's defined, then calls
81
92
  # {#help_description}.
82
93
  #
94
+ # @api public
95
+ #
83
96
  def help
84
- super if defined?(super)
97
+ super
85
98
 
86
99
  help_description
87
100
  end
@@ -14,6 +14,9 @@ module CommandKit
14
14
  module Home
15
15
  include Env
16
16
 
17
+ #
18
+ # @api private
19
+ #
17
20
  module ModuleMethods
18
21
  #
19
22
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether
@@ -44,6 +47,8 @@ module CommandKit
44
47
  #
45
48
  # @return [String]
46
49
  #
50
+ # @api semipublic
51
+ #
47
52
  def home_dir
48
53
  Gem.user_home
49
54
  end
@@ -52,6 +57,8 @@ module CommandKit
52
57
  # The home directory.
53
58
  #
54
59
  # @return [String]
60
+ #
61
+ # @api public
55
62
  attr_reader :home_dir
56
63
 
57
64
  #
@@ -61,6 +68,8 @@ module CommandKit
61
68
  # @param [Hash{Symbol => Object}] kwargs
62
69
  # Additional keyword arguments.
63
70
  #
71
+ # @api public
72
+ #
64
73
  def initialize(**kwargs)
65
74
  super(**kwargs)
66
75