command_kit 0.1.0.pre1
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 +7 -0
- data/.document +3 -0
- data/.github/workflows/ruby.yml +29 -0
- data/.gitignore +7 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +29 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +20 -0
- data/README.md +283 -0
- data/Rakefile +23 -0
- data/command_kit.gemspec +60 -0
- data/gemspec.yml +14 -0
- data/lib/command_kit.rb +1 -0
- data/lib/command_kit/arguments.rb +161 -0
- data/lib/command_kit/arguments/argument.rb +111 -0
- data/lib/command_kit/arguments/argument_value.rb +81 -0
- data/lib/command_kit/arguments/usage.rb +6 -0
- data/lib/command_kit/colors.rb +355 -0
- data/lib/command_kit/command.rb +42 -0
- data/lib/command_kit/command_name.rb +95 -0
- data/lib/command_kit/commands.rb +299 -0
- data/lib/command_kit/commands/auto_load.rb +153 -0
- data/lib/command_kit/commands/auto_load/subcommand.rb +90 -0
- data/lib/command_kit/commands/auto_require.rb +138 -0
- data/lib/command_kit/commands/command.rb +12 -0
- data/lib/command_kit/commands/help.rb +43 -0
- data/lib/command_kit/commands/parent_command.rb +21 -0
- data/lib/command_kit/commands/subcommand.rb +51 -0
- data/lib/command_kit/console.rb +141 -0
- data/lib/command_kit/description.rb +89 -0
- data/lib/command_kit/env.rb +43 -0
- data/lib/command_kit/env/home.rb +71 -0
- data/lib/command_kit/env/path.rb +71 -0
- data/lib/command_kit/examples.rb +99 -0
- data/lib/command_kit/exception_handler.rb +55 -0
- data/lib/command_kit/help.rb +62 -0
- data/lib/command_kit/help/man.rb +125 -0
- data/lib/command_kit/inflector.rb +84 -0
- data/lib/command_kit/main.rb +103 -0
- data/lib/command_kit/options.rb +179 -0
- data/lib/command_kit/options/option.rb +171 -0
- data/lib/command_kit/options/option_value.rb +90 -0
- data/lib/command_kit/options/parser.rb +227 -0
- data/lib/command_kit/options/quiet.rb +53 -0
- data/lib/command_kit/options/usage.rb +6 -0
- data/lib/command_kit/options/verbose.rb +55 -0
- data/lib/command_kit/options/version.rb +62 -0
- data/lib/command_kit/os.rb +47 -0
- data/lib/command_kit/pager.rb +115 -0
- data/lib/command_kit/printing.rb +32 -0
- data/lib/command_kit/printing/indent.rb +78 -0
- data/lib/command_kit/program_name.rb +57 -0
- data/lib/command_kit/stdio.rb +138 -0
- data/lib/command_kit/usage.rb +102 -0
- data/lib/command_kit/version.rb +4 -0
- data/lib/command_kit/xdg.rb +138 -0
- data/spec/arguments/argument_spec.rb +169 -0
- data/spec/arguments/argument_value_spec.rb +126 -0
- data/spec/arguments_spec.rb +213 -0
- data/spec/colors_spec.rb +470 -0
- data/spec/command_kit_spec.rb +8 -0
- data/spec/command_name_spec.rb +130 -0
- data/spec/command_spec.rb +49 -0
- data/spec/commands/auto_load/subcommand_spec.rb +82 -0
- data/spec/commands/auto_load_spec.rb +128 -0
- data/spec/commands/auto_require_spec.rb +142 -0
- data/spec/commands/fixtures/test_auto_load/cli/commands/test1.rb +10 -0
- data/spec/commands/fixtures/test_auto_load/cli/commands/test2.rb +10 -0
- data/spec/commands/fixtures/test_auto_require/lib/test_auto_require/cli/commands/test1.rb +10 -0
- data/spec/commands/help_spec.rb +66 -0
- data/spec/commands/parent_command_spec.rb +40 -0
- data/spec/commands/subcommand_spec.rb +99 -0
- data/spec/commands_spec.rb +767 -0
- data/spec/console_spec.rb +201 -0
- data/spec/description_spec.rb +203 -0
- data/spec/env/home_spec.rb +46 -0
- data/spec/env/path_spec.rb +78 -0
- data/spec/env_spec.rb +123 -0
- data/spec/examples_spec.rb +235 -0
- data/spec/exception_handler_spec.rb +103 -0
- data/spec/help_spec.rb +119 -0
- data/spec/inflector_spec.rb +104 -0
- data/spec/main_spec.rb +179 -0
- data/spec/options/option_spec.rb +258 -0
- data/spec/options/option_value_spec.rb +67 -0
- data/spec/options/parser_spec.rb +265 -0
- data/spec/options_spec.rb +137 -0
- data/spec/os_spec.rb +46 -0
- data/spec/pager_spec.rb +154 -0
- data/spec/printing/indent_spec.rb +130 -0
- data/spec/printing_spec.rb +76 -0
- data/spec/program_name_spec.rb +62 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/stdio_spec.rb +264 -0
- data/spec/usage_spec.rb +237 -0
- data/spec/xdg_spec.rb +191 -0
- metadata +156 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'command_kit/main'
|
|
2
|
+
require 'command_kit/env'
|
|
3
|
+
require 'command_kit/stdio'
|
|
4
|
+
require 'command_kit/printing'
|
|
5
|
+
require 'command_kit/usage'
|
|
6
|
+
require 'command_kit/arguments'
|
|
7
|
+
require 'command_kit/options'
|
|
8
|
+
require 'command_kit/examples'
|
|
9
|
+
require 'command_kit/description'
|
|
10
|
+
require 'command_kit/exception_handler'
|
|
11
|
+
|
|
12
|
+
module CommandKit
|
|
13
|
+
#
|
|
14
|
+
# The command class base-class.
|
|
15
|
+
#
|
|
16
|
+
# ## Examples
|
|
17
|
+
#
|
|
18
|
+
# class MyCmd < CommandKit::Command
|
|
19
|
+
#
|
|
20
|
+
# # ...
|
|
21
|
+
#
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# @note Command classes are not required to inherit from {Command}. This class
|
|
25
|
+
# only exists as a convenience.
|
|
26
|
+
#
|
|
27
|
+
class Command
|
|
28
|
+
|
|
29
|
+
include Main
|
|
30
|
+
include Env
|
|
31
|
+
include Stdio
|
|
32
|
+
include Printing
|
|
33
|
+
include Help
|
|
34
|
+
include Usage
|
|
35
|
+
include Arguments
|
|
36
|
+
include Options
|
|
37
|
+
include Examples
|
|
38
|
+
include Description
|
|
39
|
+
include ExceptionHandler
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
require 'command_kit/inflector'
|
|
2
|
+
|
|
3
|
+
module CommandKit
|
|
4
|
+
#
|
|
5
|
+
# Defines or derives a command class'es command-name.
|
|
6
|
+
#
|
|
7
|
+
# ## Examples
|
|
8
|
+
#
|
|
9
|
+
# ### Implicit
|
|
10
|
+
#
|
|
11
|
+
# class MyCmd
|
|
12
|
+
#
|
|
13
|
+
# include CommandKit::CommandName
|
|
14
|
+
#
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# MyCmd.command_name
|
|
18
|
+
# # => "my_cmd"
|
|
19
|
+
#
|
|
20
|
+
# ### Explicit
|
|
21
|
+
#
|
|
22
|
+
# class MyCmd
|
|
23
|
+
#
|
|
24
|
+
# include CommandKit::CommandName
|
|
25
|
+
#
|
|
26
|
+
# commnad_name 'foo-cmd'
|
|
27
|
+
#
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# MyCmd.command_name
|
|
31
|
+
# # => "foo-cmd"
|
|
32
|
+
#
|
|
33
|
+
module CommandName
|
|
34
|
+
module ModuleMethods
|
|
35
|
+
#
|
|
36
|
+
# Extends {ClassMethods} or {ModuleMethods}, depending on whether
|
|
37
|
+
# {CommandName} is being included into a class or a module.
|
|
38
|
+
#
|
|
39
|
+
# @param [Class, Module] context
|
|
40
|
+
# The class or module which is including {CommandName}.
|
|
41
|
+
#
|
|
42
|
+
def included(context)
|
|
43
|
+
super(context)
|
|
44
|
+
|
|
45
|
+
if context.class == Module
|
|
46
|
+
context.extend ModuleMethods
|
|
47
|
+
else
|
|
48
|
+
context.extend ClassMethods
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
extend ModuleMethods
|
|
54
|
+
|
|
55
|
+
#
|
|
56
|
+
# Defines class-level methods.
|
|
57
|
+
#
|
|
58
|
+
module ClassMethods
|
|
59
|
+
#
|
|
60
|
+
# Derives the command name from the class name.
|
|
61
|
+
#
|
|
62
|
+
# @param [String, nil] new_command_name
|
|
63
|
+
# If given a command name argument, it will override the derived
|
|
64
|
+
# command name.
|
|
65
|
+
#
|
|
66
|
+
# @return [String]
|
|
67
|
+
#
|
|
68
|
+
def command_name(new_command_name=nil)
|
|
69
|
+
if new_command_name
|
|
70
|
+
@command_name = new_command_name.to_s
|
|
71
|
+
else
|
|
72
|
+
@command_name || Inflector.underscore(Inflector.demodularize(name))
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# The commands name.
|
|
78
|
+
#
|
|
79
|
+
# @return [String]
|
|
80
|
+
attr_reader :command_name
|
|
81
|
+
|
|
82
|
+
#
|
|
83
|
+
# Initializes command_name.
|
|
84
|
+
#
|
|
85
|
+
# @param [String] command_name
|
|
86
|
+
# Overrides the command name. Defaults to
|
|
87
|
+
# {ClassMethods#command_name self.class.command_name}.
|
|
88
|
+
#
|
|
89
|
+
def initialize(command_name: self.class.command_name, **kwargs)
|
|
90
|
+
@command_name = command_name
|
|
91
|
+
|
|
92
|
+
super(**kwargs)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'command_kit/commands/subcommand'
|
|
4
|
+
require 'command_kit/commands/parent_command'
|
|
5
|
+
require 'command_kit/commands/help'
|
|
6
|
+
require 'command_kit/command_name'
|
|
7
|
+
require 'command_kit/usage'
|
|
8
|
+
require 'command_kit/options'
|
|
9
|
+
require 'command_kit/stdio'
|
|
10
|
+
require 'command_kit/env'
|
|
11
|
+
|
|
12
|
+
module CommandKit
|
|
13
|
+
#
|
|
14
|
+
# Adds sub-commands to a command.
|
|
15
|
+
#
|
|
16
|
+
# ## Examples
|
|
17
|
+
#
|
|
18
|
+
# class CLI
|
|
19
|
+
#
|
|
20
|
+
# include CommandKit::Commands
|
|
21
|
+
#
|
|
22
|
+
# command_name :foo
|
|
23
|
+
#
|
|
24
|
+
# class Foo < CommandKit::Command
|
|
25
|
+
# # ...
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# class FooBar < CommandKit::Command
|
|
29
|
+
# # ...
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# command Foo
|
|
33
|
+
# command 'foo-bar', FooBar
|
|
34
|
+
#
|
|
35
|
+
# end
|
|
36
|
+
#
|
|
37
|
+
module Commands
|
|
38
|
+
include CommandName
|
|
39
|
+
include Usage
|
|
40
|
+
include Options
|
|
41
|
+
include Stdio
|
|
42
|
+
include Env
|
|
43
|
+
|
|
44
|
+
module ModuleMethods
|
|
45
|
+
#
|
|
46
|
+
# Extends {ClassMethods} or {ModuleMethods}, depending on whether
|
|
47
|
+
# {Commands} is being included into a class or a module.
|
|
48
|
+
#
|
|
49
|
+
# @param [Class, Module] context
|
|
50
|
+
# The class or module which is including {Commands}.
|
|
51
|
+
#
|
|
52
|
+
def included(context)
|
|
53
|
+
super(context)
|
|
54
|
+
|
|
55
|
+
if context.class == Module
|
|
56
|
+
context.extend ModuleMethods
|
|
57
|
+
else
|
|
58
|
+
context.usage "[options] [COMMAND [ARGS...]]"
|
|
59
|
+
context.extend ClassMethods
|
|
60
|
+
context.command Help
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
extend ModuleMethods
|
|
66
|
+
|
|
67
|
+
#
|
|
68
|
+
# Class-level methods.
|
|
69
|
+
#
|
|
70
|
+
module ClassMethods
|
|
71
|
+
#
|
|
72
|
+
# The registered sub-commands.
|
|
73
|
+
#
|
|
74
|
+
# @return [Hash{String => Subcommand}]
|
|
75
|
+
# The Hash of sub-command names and command classes.
|
|
76
|
+
#
|
|
77
|
+
def commands
|
|
78
|
+
@commands ||= if superclass.kind_of?(ClassMethods)
|
|
79
|
+
superclass.commands.dup
|
|
80
|
+
else
|
|
81
|
+
{}
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
#
|
|
86
|
+
# The registered command aliases.
|
|
87
|
+
#
|
|
88
|
+
# @return [Hash{String => String}]
|
|
89
|
+
# The Hash of command aliases to primary command names.
|
|
90
|
+
#
|
|
91
|
+
def command_aliases
|
|
92
|
+
@command_aliases ||= if superclass.kind_of?(ClassMethods)
|
|
93
|
+
superclass.command_aliases.dup
|
|
94
|
+
else
|
|
95
|
+
{}
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
#
|
|
100
|
+
# Mounts a command as a sub-command.
|
|
101
|
+
#
|
|
102
|
+
# @param [#to_s] name
|
|
103
|
+
# The optional name to mount the command as. Defaults to the command's
|
|
104
|
+
# {CommandName::ClassMethods#command_name command_name}.
|
|
105
|
+
#
|
|
106
|
+
# @param [Class#main] command_class
|
|
107
|
+
# The sub-command class.
|
|
108
|
+
#
|
|
109
|
+
# @param [Hash{Symbol => Object}] kwargs
|
|
110
|
+
# Keyword arguments.
|
|
111
|
+
#
|
|
112
|
+
# @option kwargs [String, nil] summary
|
|
113
|
+
# A short summary for the subcommand. Defaults to the first sentence
|
|
114
|
+
# of the command.
|
|
115
|
+
#
|
|
116
|
+
# @option kwags [Array<String>] aliases
|
|
117
|
+
# Optional alias names for the subcommand.
|
|
118
|
+
#
|
|
119
|
+
# @return [Subcommand]
|
|
120
|
+
# The registered sub-command class.
|
|
121
|
+
#
|
|
122
|
+
# @example
|
|
123
|
+
# command Foo
|
|
124
|
+
#
|
|
125
|
+
# @example
|
|
126
|
+
# command 'foo-bar', FooBar
|
|
127
|
+
#
|
|
128
|
+
def command(name=nil, command_class, **kwargs)
|
|
129
|
+
name = if name then name.to_s
|
|
130
|
+
else command_class.command_name
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
subcommand = Subcommand.new(command_class,**kwargs)
|
|
134
|
+
|
|
135
|
+
commands[name] = subcommand
|
|
136
|
+
|
|
137
|
+
subcommand.aliases.each do |command_alias|
|
|
138
|
+
command_aliases[command_alias] = name
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
return subcommand
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
#
|
|
145
|
+
# Gets the command.
|
|
146
|
+
#
|
|
147
|
+
# @param [String] name
|
|
148
|
+
#
|
|
149
|
+
# @return [Class#main, nil]
|
|
150
|
+
#
|
|
151
|
+
def get_command(name)
|
|
152
|
+
name = name.to_s
|
|
153
|
+
name = command_aliases.fetch(name,name)
|
|
154
|
+
|
|
155
|
+
if (subcommand = commands[name])
|
|
156
|
+
subcommand.command
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def initialize(**kwargs)
|
|
162
|
+
super(**kwargs)
|
|
163
|
+
|
|
164
|
+
@option_parser.on(/^[^-].*$/) do |command|
|
|
165
|
+
OptionParser.terminate(command)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
#
|
|
170
|
+
# Looks up the given command name and initializes a subcommand.
|
|
171
|
+
#
|
|
172
|
+
# @param [#to_s] name
|
|
173
|
+
# The given command name.
|
|
174
|
+
#
|
|
175
|
+
# @return [Object#main, nil]
|
|
176
|
+
# The initialized subcommand.
|
|
177
|
+
#
|
|
178
|
+
def command(name)
|
|
179
|
+
unless (command_class = self.class.get_command(name))
|
|
180
|
+
return
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
kwargs = {}
|
|
184
|
+
|
|
185
|
+
if command_class.include?(ParentCommand)
|
|
186
|
+
kwargs[:parent_command] = self
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
if command_class.include?(CommandName)
|
|
190
|
+
kwargs[:command_name] = "#{command_name} #{command_class.command_name}"
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
if command_class.include?(Stdio)
|
|
194
|
+
kwargs[:stdin] = stdin
|
|
195
|
+
kwargs[:stdout] = stdout
|
|
196
|
+
kwargs[:stderr] = stderr
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
if command_class.include?(Env)
|
|
200
|
+
kwargs[:env] = env.dup
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
if command_class.include?(Options)
|
|
204
|
+
kwargs[:options] = options.dup
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
return command_class.new(**kwargs)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
#
|
|
211
|
+
# Invokes the command with the given argv.
|
|
212
|
+
#
|
|
213
|
+
# @param [String] name
|
|
214
|
+
# The name of the command to invoke.
|
|
215
|
+
#
|
|
216
|
+
# @param [Array<String>] argv
|
|
217
|
+
# The additional arguments to pass to the command.
|
|
218
|
+
#
|
|
219
|
+
# @return [Integer]
|
|
220
|
+
# The exit status of the command.
|
|
221
|
+
#
|
|
222
|
+
def invoke(name,*argv)
|
|
223
|
+
if (command = command(name))
|
|
224
|
+
command.main(argv)
|
|
225
|
+
else
|
|
226
|
+
on_unknown_command(name,argv)
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
#
|
|
231
|
+
# Prints an error about an unknown command and exits with an error code.
|
|
232
|
+
#
|
|
233
|
+
# @param [String] name
|
|
234
|
+
#
|
|
235
|
+
def command_not_found(name)
|
|
236
|
+
print_error "'#{name}' is not a #{command_name} command. See `#{command_name} help`"
|
|
237
|
+
exit(1)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
#
|
|
241
|
+
# Place-holder method that is called when the subcommand is not known.
|
|
242
|
+
#
|
|
243
|
+
# @param [String] name
|
|
244
|
+
# The given sub-command name.
|
|
245
|
+
#
|
|
246
|
+
# @param [Array<String>] argv
|
|
247
|
+
# Additional argv.
|
|
248
|
+
#
|
|
249
|
+
# @abstract
|
|
250
|
+
#
|
|
251
|
+
# @see command_not_found
|
|
252
|
+
#
|
|
253
|
+
def on_unknown_command(name,argv=[])
|
|
254
|
+
command_not_found(name)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
#
|
|
258
|
+
# Runs the command or specified subcommand.
|
|
259
|
+
#
|
|
260
|
+
# @note If no subcommand is given, {#help} will be called.
|
|
261
|
+
#
|
|
262
|
+
def run(command=nil,*argv)
|
|
263
|
+
if command
|
|
264
|
+
exit invoke(command,*argv)
|
|
265
|
+
else
|
|
266
|
+
help
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
#
|
|
271
|
+
# Prints the available commands and their summaries.
|
|
272
|
+
#
|
|
273
|
+
def help_commands
|
|
274
|
+
unless self.class.commands.empty?
|
|
275
|
+
puts
|
|
276
|
+
puts "Commands:"
|
|
277
|
+
|
|
278
|
+
self.class.commands.sort.each do |name,subcommand|
|
|
279
|
+
names = [name, *subcommand.aliases].join(', ')
|
|
280
|
+
|
|
281
|
+
if subcommand.summary
|
|
282
|
+
puts " #{names}\t#{subcommand.summary}"
|
|
283
|
+
else
|
|
284
|
+
puts " #{names}"
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
#
|
|
291
|
+
# Prints help information and available commands.
|
|
292
|
+
#
|
|
293
|
+
def help
|
|
294
|
+
super
|
|
295
|
+
|
|
296
|
+
help_commands
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'command_kit/commands'
|
|
4
|
+
require 'command_kit/commands/auto_load/subcommand'
|
|
5
|
+
require 'command_kit/inflector'
|
|
6
|
+
|
|
7
|
+
module CommandKit
|
|
8
|
+
module Commands
|
|
9
|
+
#
|
|
10
|
+
# Provides lazy-loading access to a directory / module namespace of
|
|
11
|
+
# command classes.
|
|
12
|
+
#
|
|
13
|
+
# ## Examples
|
|
14
|
+
#
|
|
15
|
+
# class CLI
|
|
16
|
+
#
|
|
17
|
+
# include CommandKit::Commands::AutoLoad.new(
|
|
18
|
+
# dir: "#{__dir__}/cli/commands",
|
|
19
|
+
# namespace: 'CLI::Commands'
|
|
20
|
+
# )
|
|
21
|
+
#
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# ### Explicit Mapping
|
|
25
|
+
#
|
|
26
|
+
# class CLI
|
|
27
|
+
#
|
|
28
|
+
# include CommandKit::Commands::AutoLoad.new(
|
|
29
|
+
# dir: "#{__dir__}/cli/commands",
|
|
30
|
+
# namespace: 'CLI::Commands'
|
|
31
|
+
# ) { |autoload|
|
|
32
|
+
# autoload.command 'foo', 'Foo', 'foo.rb', summary: 'Foo command'
|
|
33
|
+
# autoload.command 'bar', 'Bar', 'bar.rb', summary: 'Bar command'
|
|
34
|
+
# }
|
|
35
|
+
#
|
|
36
|
+
# end
|
|
37
|
+
#
|
|
38
|
+
class AutoLoad < Module
|
|
39
|
+
|
|
40
|
+
# The auto-load subcommands.
|
|
41
|
+
#
|
|
42
|
+
# @return [Hash{String => Subcommand}]
|
|
43
|
+
attr_reader :commands
|
|
44
|
+
|
|
45
|
+
# The path to the directory containing the command files.
|
|
46
|
+
#
|
|
47
|
+
# @return [String]
|
|
48
|
+
attr_reader :dir
|
|
49
|
+
|
|
50
|
+
# The namespace that the will contain the command classes.
|
|
51
|
+
#
|
|
52
|
+
# @return [String]
|
|
53
|
+
attr_reader :namespace
|
|
54
|
+
|
|
55
|
+
#
|
|
56
|
+
# Initializes the namespace.
|
|
57
|
+
#
|
|
58
|
+
# @param [String] dir
|
|
59
|
+
# The path to the directory containing the command files.
|
|
60
|
+
#
|
|
61
|
+
# @param [Module, Class, String] namespace
|
|
62
|
+
# The namespace constant that contains the command classes.
|
|
63
|
+
#
|
|
64
|
+
# @yield [self]
|
|
65
|
+
# If a block is given, it will be used to explicitly map the files
|
|
66
|
+
# within {#dir} as commands.
|
|
67
|
+
#
|
|
68
|
+
def initialize(dir: , namespace: )
|
|
69
|
+
@commands = {}
|
|
70
|
+
|
|
71
|
+
@dir = dir
|
|
72
|
+
@namespace = namespace
|
|
73
|
+
|
|
74
|
+
if block_given?
|
|
75
|
+
yield self
|
|
76
|
+
else
|
|
77
|
+
files.each do |path|
|
|
78
|
+
base_name = File.basename(path)
|
|
79
|
+
file_name = base_name.chomp('.rb')
|
|
80
|
+
command_name = Inflector.dasherize(file_name)
|
|
81
|
+
class_name = Inflector.camelize(file_name)
|
|
82
|
+
|
|
83
|
+
command command_name, class_name, base_name
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
#
|
|
89
|
+
# Defines an auto-loaded command mapping.
|
|
90
|
+
#
|
|
91
|
+
# @param [#to_s] name
|
|
92
|
+
# The name of the command.
|
|
93
|
+
#
|
|
94
|
+
# @param [String] constant
|
|
95
|
+
# The constant name of the command class.
|
|
96
|
+
#
|
|
97
|
+
# @param [String] file
|
|
98
|
+
# The file name of the command class.
|
|
99
|
+
#
|
|
100
|
+
# @param [Hash{Symbol => Object}] kwargs
|
|
101
|
+
# Keyword arguments.
|
|
102
|
+
#
|
|
103
|
+
# @option kwargs [String, nil] summary
|
|
104
|
+
# An optional summary for the command.
|
|
105
|
+
#
|
|
106
|
+
# @option kwargs [Array<String>] aliases
|
|
107
|
+
# Optional alias names for the subcommand.
|
|
108
|
+
#
|
|
109
|
+
def command(name, constant, file, **kwargs)
|
|
110
|
+
@commands[name.to_s] = Subcommand.new(
|
|
111
|
+
"#{@namespace}::#{constant}",
|
|
112
|
+
join(file),
|
|
113
|
+
**kwargs
|
|
114
|
+
)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
#
|
|
118
|
+
# Joins a relative path with {#dir}.
|
|
119
|
+
#
|
|
120
|
+
# @param [String] path
|
|
121
|
+
# The relative path.
|
|
122
|
+
#
|
|
123
|
+
# @return [String]
|
|
124
|
+
# The joined absolute path.
|
|
125
|
+
#
|
|
126
|
+
def join(path)
|
|
127
|
+
File.join(@dir,path)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
#
|
|
131
|
+
# Returns the files within given directory.
|
|
132
|
+
#
|
|
133
|
+
# @return [Array<String>]
|
|
134
|
+
# The paths to the `.rb` files in the directory.
|
|
135
|
+
#
|
|
136
|
+
def files
|
|
137
|
+
Dir.glob(join('*.rb'))
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
#
|
|
141
|
+
# Includes {Commands} and registers all files within the namespace
|
|
142
|
+
# as lazy-loaded subcommands.
|
|
143
|
+
#
|
|
144
|
+
# @param [Class] command
|
|
145
|
+
# The command class including {AutoLoad}.
|
|
146
|
+
#
|
|
147
|
+
def included(command)
|
|
148
|
+
command.include Commands
|
|
149
|
+
command.commands.merge!(@commands)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|