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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +15 -0
- data/.rubocop.yml +138 -0
- data/ChangeLog.md +34 -2
- data/Gemfile +3 -0
- data/README.md +135 -214
- data/Rakefile +3 -2
- data/command_kit.gemspec +4 -4
- data/examples/colors.rb +30 -0
- data/examples/command.rb +65 -0
- data/examples/pager.rb +30 -0
- data/gemspec.yml +10 -2
- data/lib/command_kit/arguments/argument.rb +16 -44
- data/lib/command_kit/arguments/argument_value.rb +3 -30
- data/lib/command_kit/arguments.rb +66 -20
- data/lib/command_kit/colors.rb +253 -45
- data/lib/command_kit/command.rb +50 -3
- data/lib/command_kit/command_name.rb +9 -0
- data/lib/command_kit/commands/auto_load/subcommand.rb +3 -0
- data/lib/command_kit/commands/auto_load.rb +16 -0
- data/lib/command_kit/commands/auto_require.rb +16 -0
- data/lib/command_kit/commands/command.rb +3 -0
- data/lib/command_kit/commands/help.rb +2 -0
- data/lib/command_kit/commands/parent_command.rb +7 -0
- data/lib/command_kit/commands/subcommand.rb +15 -0
- data/lib/command_kit/commands.rb +40 -4
- data/lib/command_kit/description.rb +15 -2
- data/lib/command_kit/env/home.rb +9 -0
- data/lib/command_kit/env/path.rb +15 -0
- data/lib/command_kit/env.rb +4 -0
- data/lib/command_kit/examples.rb +15 -2
- data/lib/command_kit/exception_handler.rb +4 -0
- data/lib/command_kit/help/man.rb +74 -47
- data/lib/command_kit/help.rb +10 -1
- data/lib/command_kit/inflector.rb +49 -17
- data/lib/command_kit/interactive.rb +239 -0
- data/lib/command_kit/main.rb +20 -9
- data/lib/command_kit/man.rb +44 -0
- data/lib/command_kit/open_app.rb +69 -0
- data/lib/command_kit/options/option.rb +36 -9
- data/lib/command_kit/options/option_value.rb +42 -3
- data/lib/command_kit/options/parser.rb +44 -17
- data/lib/command_kit/options/quiet.rb +3 -0
- data/lib/command_kit/options/verbose.rb +5 -0
- data/lib/command_kit/options/version.rb +6 -0
- data/lib/command_kit/options.rb +59 -10
- data/lib/command_kit/os/linux.rb +157 -0
- data/lib/command_kit/os.rb +165 -11
- data/lib/command_kit/package_manager.rb +200 -0
- data/lib/command_kit/pager.rb +84 -9
- data/lib/command_kit/printing/indent.rb +25 -2
- data/lib/command_kit/printing.rb +23 -0
- data/lib/command_kit/program_name.rb +7 -0
- data/lib/command_kit/stdio.rb +24 -0
- data/lib/command_kit/sudo.rb +40 -0
- data/lib/command_kit/terminal.rb +159 -0
- data/lib/command_kit/usage.rb +14 -0
- data/lib/command_kit/version.rb +1 -1
- data/lib/command_kit/xdg.rb +21 -1
- data/lib/command_kit.rb +1 -0
- data/spec/arguments/argument_spec.rb +5 -41
- data/spec/arguments/argument_value_spec.rb +1 -61
- data/spec/arguments_spec.rb +8 -25
- data/spec/colors_spec.rb +277 -13
- data/spec/command_name_spec.rb +1 -1
- data/spec/command_spec.rb +4 -1
- data/spec/commands/auto_load/subcommand_spec.rb +1 -1
- data/spec/commands/auto_load_spec.rb +1 -1
- data/spec/commands/auto_require_spec.rb +2 -2
- data/spec/commands/help_spec.rb +1 -1
- data/spec/commands/parent_command_spec.rb +1 -1
- data/spec/commands/subcommand_spec.rb +1 -1
- data/spec/commands_spec.rb +2 -2
- data/spec/description_spec.rb +1 -25
- data/spec/env/home_spec.rb +1 -1
- data/spec/env/path_spec.rb +1 -1
- data/spec/examples_spec.rb +1 -25
- data/spec/exception_handler_spec.rb +1 -1
- data/spec/help/man_spec.rb +316 -0
- data/spec/help_spec.rb +0 -25
- data/spec/inflector_spec.rb +71 -9
- data/spec/interactive_spec.rb +415 -0
- data/spec/main_spec.rb +7 -7
- data/spec/man_spec.rb +46 -0
- data/spec/open_app_spec.rb +85 -0
- data/spec/options/option_spec.rb +48 -9
- data/spec/options/option_value_spec.rb +53 -4
- data/spec/options_spec.rb +1 -1
- data/spec/os/linux_spec.rb +154 -0
- data/spec/os_spec.rb +201 -14
- data/spec/package_manager_spec.rb +806 -0
- data/spec/pager_spec.rb +78 -15
- data/spec/printing/indent_spec.rb +1 -1
- data/spec/printing_spec.rb +10 -2
- data/spec/program_name_spec.rb +1 -1
- data/spec/spec_helper.rb +0 -3
- data/spec/sudo_spec.rb +51 -0
- data/spec/{console_spec.rb → terminal_spec.rb} +65 -35
- data/spec/usage_spec.rb +2 -2
- data/spec/xdg_spec.rb +1 -1
- metadata +32 -13
- data/lib/command_kit/arguments/usage.rb +0 -6
- data/lib/command_kit/console.rb +0 -141
- 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) {
|
@@ -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)
|
data/lib/command_kit/commands.rb
CHANGED
@@ -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
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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 ||
|
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
|
97
|
+
super
|
85
98
|
|
86
99
|
help_description
|
87
100
|
end
|
data/lib/command_kit/env/home.rb
CHANGED
@@ -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
|
|