command_kit 0.4.1 → 0.5.1
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 +1 -1
- data/.rubocop.yml +7 -1
- data/ChangeLog.md +11 -0
- data/Gemfile +2 -0
- data/README.md +3 -0
- data/Rakefile +2 -0
- data/command_kit.gemspec +8 -6
- data/lib/command_kit/arguments/argument.rb +3 -1
- data/lib/command_kit/arguments/argument_value.rb +2 -0
- data/lib/command_kit/arguments.rb +5 -5
- data/lib/command_kit/bug_report.rb +4 -2
- data/lib/command_kit/colors.rb +2 -2
- data/lib/command_kit/command.rb +13 -11
- data/lib/command_kit/command_name.rb +3 -1
- data/lib/command_kit/commands/auto_load/subcommand.rb +3 -1
- data/lib/command_kit/commands/auto_load.rb +3 -3
- data/lib/command_kit/commands/auto_require.rb +5 -3
- data/lib/command_kit/commands/command.rb +4 -2
- data/lib/command_kit/commands/help.rb +2 -2
- data/lib/command_kit/commands/parent_command.rb +2 -0
- data/lib/command_kit/commands/subcommand.rb +2 -0
- data/lib/command_kit/commands.rb +8 -8
- data/lib/command_kit/completion/install.rb +277 -0
- data/lib/command_kit/description.rb +3 -1
- data/lib/command_kit/edit.rb +1 -1
- data/lib/command_kit/env/home.rb +1 -1
- data/lib/command_kit/env/path.rb +1 -1
- data/lib/command_kit/env/prefix.rb +41 -0
- data/lib/command_kit/env/shell.rb +58 -0
- data/lib/command_kit/env.rb +2 -0
- data/lib/command_kit/examples.rb +4 -2
- data/lib/command_kit/exception_handler.rb +4 -2
- data/lib/command_kit/file_utils.rb +2 -0
- data/lib/command_kit/help/man.rb +4 -4
- data/lib/command_kit/help.rb +2 -0
- data/lib/command_kit/interactive.rb +1 -1
- data/lib/command_kit/main.rb +2 -0
- data/lib/command_kit/open_app.rb +4 -2
- data/lib/command_kit/options/option.rb +3 -1
- data/lib/command_kit/options/option_value.rb +2 -2
- data/lib/command_kit/options/parser.rb +3 -3
- data/lib/command_kit/options/quiet.rb +3 -1
- data/lib/command_kit/options/verbose.rb +3 -1
- data/lib/command_kit/options/version.rb +3 -1
- data/lib/command_kit/options.rb +5 -3
- data/lib/command_kit/os/linux.rb +3 -1
- data/lib/command_kit/os.rb +2 -0
- data/lib/command_kit/package_manager.rb +6 -4
- data/lib/command_kit/pager.rb +4 -4
- data/lib/command_kit/printing/fields.rb +3 -1
- data/lib/command_kit/printing/indent.rb +2 -0
- data/lib/command_kit/printing/lists.rb +1 -1
- data/lib/command_kit/printing/tables/border_style.rb +2 -0
- data/lib/command_kit/printing/tables/row_builder.rb +3 -1
- data/lib/command_kit/printing/tables/style.rb +1 -1
- data/lib/command_kit/printing/tables/table_builder.rb +3 -1
- data/lib/command_kit/printing/tables.rb +4 -4
- data/lib/command_kit/printing.rb +1 -1
- data/lib/command_kit/program_name.rb +2 -0
- data/lib/command_kit/stdio.rb +2 -0
- data/lib/command_kit/sudo.rb +1 -1
- data/lib/command_kit/terminal.rb +4 -2
- data/lib/command_kit/usage.rb +4 -2
- data/lib/command_kit/version.rb +3 -1
- data/lib/command_kit/xdg.rb +4 -2
- data/lib/command_kit.rb +4 -2
- metadata +5 -65
- data/spec/arguments/argument_spec.rb +0 -133
- data/spec/arguments/argument_value_spec.rb +0 -66
- data/spec/arguments_spec.rb +0 -279
- data/spec/bug_report_spec.rb +0 -266
- data/spec/colors_spec.rb +0 -771
- data/spec/command_kit_spec.rb +0 -8
- data/spec/command_name_spec.rb +0 -130
- data/spec/command_spec.rb +0 -123
- data/spec/commands/auto_load/subcommand_spec.rb +0 -82
- data/spec/commands/auto_load_spec.rb +0 -159
- data/spec/commands/auto_require_spec.rb +0 -142
- data/spec/commands/fixtures/test_auto_load/cli/commands/test1.rb +0 -10
- data/spec/commands/fixtures/test_auto_load/cli/commands/test2.rb +0 -10
- data/spec/commands/fixtures/test_auto_require/lib/test_auto_require/cli/commands/test1.rb +0 -10
- data/spec/commands/help_spec.rb +0 -66
- data/spec/commands/parent_command_spec.rb +0 -40
- data/spec/commands/subcommand_spec.rb +0 -99
- data/spec/commands_spec.rb +0 -865
- data/spec/description_spec.rb +0 -179
- data/spec/edit_spec.rb +0 -72
- data/spec/env/home_spec.rb +0 -46
- data/spec/env/path_spec.rb +0 -84
- data/spec/env_spec.rb +0 -123
- data/spec/examples_spec.rb +0 -211
- data/spec/exception_handler_spec.rb +0 -103
- data/spec/file_utils_spec.rb +0 -59
- data/spec/fixtures/template.erb +0 -5
- data/spec/help/man_spec.rb +0 -345
- data/spec/help_spec.rb +0 -94
- data/spec/inflector_spec.rb +0 -166
- data/spec/interactive_spec.rb +0 -415
- data/spec/main_spec.rb +0 -179
- data/spec/man_spec.rb +0 -46
- data/spec/open_app_spec.rb +0 -85
- data/spec/options/option_spec.rb +0 -343
- data/spec/options/option_value_spec.rb +0 -171
- data/spec/options/parser_spec.rb +0 -274
- data/spec/options/quiet_spec.rb +0 -51
- data/spec/options/verbose_spec.rb +0 -51
- data/spec/options/version_spec.rb +0 -146
- data/spec/options_spec.rb +0 -465
- data/spec/os/linux_spec.rb +0 -164
- data/spec/os_spec.rb +0 -233
- data/spec/package_manager_spec.rb +0 -806
- data/spec/pager_spec.rb +0 -217
- data/spec/printing/fields_spec.rb +0 -167
- data/spec/printing/indent_spec.rb +0 -132
- data/spec/printing/lists_spec.rb +0 -99
- data/spec/printing/tables/border_style.rb +0 -43
- data/spec/printing/tables/cell_builer_spec.rb +0 -135
- data/spec/printing/tables/row_builder_spec.rb +0 -165
- data/spec/printing/tables/style_spec.rb +0 -377
- data/spec/printing/tables/table_builder_spec.rb +0 -252
- data/spec/printing/tables/table_formatter_spec.rb +0 -1190
- data/spec/printing/tables_spec.rb +0 -1069
- data/spec/printing_spec.rb +0 -106
- data/spec/program_name_spec.rb +0 -70
- data/spec/spec_helper.rb +0 -3
- data/spec/stdio_spec.rb +0 -264
- data/spec/sudo_spec.rb +0 -51
- data/spec/terminal_spec.rb +0 -231
- data/spec/usage_spec.rb +0 -237
- data/spec/xdg_spec.rb +0 -191
data/spec/commands_spec.rb
DELETED
@@ -1,865 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'command_kit/commands'
|
3
|
-
|
4
|
-
describe CommandKit::Commands do
|
5
|
-
module TestCommands
|
6
|
-
class TestEmptyCommands
|
7
|
-
|
8
|
-
include CommandKit::Commands
|
9
|
-
|
10
|
-
end
|
11
|
-
|
12
|
-
class TestCommands
|
13
|
-
|
14
|
-
include CommandKit::Commands
|
15
|
-
|
16
|
-
class Test1 < CommandKit::Command
|
17
|
-
end
|
18
|
-
|
19
|
-
class Test2 < CommandKit::Command
|
20
|
-
end
|
21
|
-
|
22
|
-
command Test1
|
23
|
-
command Test2
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
class TestCommandsWithAliases
|
28
|
-
|
29
|
-
include CommandKit::Commands
|
30
|
-
|
31
|
-
class Test1 < CommandKit::Command
|
32
|
-
end
|
33
|
-
|
34
|
-
class Test2 < CommandKit::Command
|
35
|
-
end
|
36
|
-
|
37
|
-
command Test1, aliases: %w[t1]
|
38
|
-
command Test2, aliases: %w[t2]
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
class TestCommandsWithExplicitNames
|
43
|
-
|
44
|
-
include CommandKit::Commands
|
45
|
-
|
46
|
-
class Test1 < CommandKit::Command
|
47
|
-
end
|
48
|
-
|
49
|
-
class Test2 < CommandKit::Command
|
50
|
-
end
|
51
|
-
|
52
|
-
command 'command-name-1', Test1
|
53
|
-
command 'command-name-2', Test2
|
54
|
-
|
55
|
-
end
|
56
|
-
|
57
|
-
class TestCommandsWithExplicitNamesAndAliases
|
58
|
-
|
59
|
-
include CommandKit::Commands
|
60
|
-
|
61
|
-
class Test1 < CommandKit::Command
|
62
|
-
end
|
63
|
-
|
64
|
-
class Test2 < CommandKit::Command
|
65
|
-
end
|
66
|
-
|
67
|
-
command 'command-name-1', Test1, aliases: %w[t1]
|
68
|
-
command 'command-name-2', Test2, aliases: %w[t2]
|
69
|
-
|
70
|
-
end
|
71
|
-
|
72
|
-
class TestCommandsWithExplicitSummaries
|
73
|
-
|
74
|
-
include CommandKit::Commands
|
75
|
-
|
76
|
-
class Test1 < CommandKit::Command
|
77
|
-
end
|
78
|
-
|
79
|
-
class Test2 < CommandKit::Command
|
80
|
-
end
|
81
|
-
|
82
|
-
command Test1, summary: 'Explicit summary 1'
|
83
|
-
command Test2, summary: 'Explicit summary 2'
|
84
|
-
|
85
|
-
end
|
86
|
-
|
87
|
-
class TestCommandsWithCustomExitStatus
|
88
|
-
|
89
|
-
include CommandKit::Commands
|
90
|
-
|
91
|
-
class Test < CommandKit::Command
|
92
|
-
|
93
|
-
def run(*argv)
|
94
|
-
exit(2)
|
95
|
-
end
|
96
|
-
|
97
|
-
end
|
98
|
-
|
99
|
-
command Test
|
100
|
-
|
101
|
-
end
|
102
|
-
|
103
|
-
class TestCommandsWithGlobalOptions
|
104
|
-
|
105
|
-
include CommandKit::Commands
|
106
|
-
|
107
|
-
class Test1 < CommandKit::Command
|
108
|
-
end
|
109
|
-
|
110
|
-
class Test2 < CommandKit::Command
|
111
|
-
end
|
112
|
-
|
113
|
-
option :foo, short: '-f',
|
114
|
-
desc: "Global --foo option"
|
115
|
-
|
116
|
-
option :bar, short: '-b',
|
117
|
-
value: {
|
118
|
-
required: true,
|
119
|
-
type: String,
|
120
|
-
usage: 'BAR'
|
121
|
-
},
|
122
|
-
desc: "Global --bar option"
|
123
|
-
|
124
|
-
command Test1
|
125
|
-
command Test2
|
126
|
-
|
127
|
-
end
|
128
|
-
|
129
|
-
class TestCommandsWithCommandArguments
|
130
|
-
|
131
|
-
include CommandKit::Commands
|
132
|
-
|
133
|
-
class Test1 < CommandKit::Command
|
134
|
-
argument :arg1, required: false,
|
135
|
-
desc: 'Argument 1'
|
136
|
-
|
137
|
-
argument :arg2, required: false,
|
138
|
-
desc: 'Argument 2'
|
139
|
-
end
|
140
|
-
|
141
|
-
class Test2 < CommandKit::Command
|
142
|
-
argument :arg1, required: false,
|
143
|
-
desc: 'Argument 1'
|
144
|
-
|
145
|
-
argument :arg2, required: false,
|
146
|
-
desc: 'Argument 2'
|
147
|
-
end
|
148
|
-
|
149
|
-
command Test1
|
150
|
-
command Test2
|
151
|
-
|
152
|
-
end
|
153
|
-
|
154
|
-
end
|
155
|
-
|
156
|
-
let(:command_class) { TestCommands::TestCommands }
|
157
|
-
|
158
|
-
describe ".included" do
|
159
|
-
subject { command_class }
|
160
|
-
|
161
|
-
it "must set .usage to '[options] [COMMAND [ARGS...]]'" do
|
162
|
-
expect(subject.usage).to eq('[options] [COMMAND [ARGS...]]')
|
163
|
-
end
|
164
|
-
|
165
|
-
it "must add a 'command' argument" do
|
166
|
-
expect(subject.arguments[:command]).to_not be_nil
|
167
|
-
expect(subject.arguments[:command].required?).to be(false)
|
168
|
-
expect(subject.arguments[:command].desc).to eq('The command name to run')
|
169
|
-
end
|
170
|
-
|
171
|
-
it "must add a 'args' argument" do
|
172
|
-
expect(subject.arguments[:args]).to_not be_nil
|
173
|
-
expect(subject.arguments[:args].required?).to be(false)
|
174
|
-
expect(subject.arguments[:args].repeats?).to be(true)
|
175
|
-
expect(subject.arguments[:args].desc).to eq('Additional arguments for the command')
|
176
|
-
end
|
177
|
-
|
178
|
-
it "must add a 'help' command" do
|
179
|
-
expect(subject.commands['help']).to_not be(nil)
|
180
|
-
expect(subject.commands['help'].command).to be(described_class::Help)
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
describe ".commands" do
|
185
|
-
subject { command_class }
|
186
|
-
|
187
|
-
it "must return a Hash" do
|
188
|
-
expect(subject.commands).to be_kind_of(Hash)
|
189
|
-
end
|
190
|
-
|
191
|
-
it "must provide a default 'help' command" do
|
192
|
-
expect(subject.commands['help']).to_not be_nil
|
193
|
-
expect(subject.commands['help'].command).to eq(CommandKit::Commands::Help)
|
194
|
-
end
|
195
|
-
|
196
|
-
context "when additional commands are defined in a superclass" do
|
197
|
-
module TestCommands
|
198
|
-
class TestInheritedCommands < TestCommands
|
199
|
-
|
200
|
-
class Test3 < CommandKit::Command
|
201
|
-
|
202
|
-
def run
|
203
|
-
puts 'test command three'
|
204
|
-
end
|
205
|
-
|
206
|
-
end
|
207
|
-
|
208
|
-
command :test3, Test3
|
209
|
-
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
let(:command_superclass) { TestCommands::TestCommands }
|
214
|
-
let(:command_class) { TestCommands::TestInheritedCommands }
|
215
|
-
|
216
|
-
it "must inherit the superclass'es commands" do
|
217
|
-
expect(subject.commands['test1']).to eq(command_superclass.commands['test1'])
|
218
|
-
expect(subject.commands['test2']).to eq(command_superclass.commands['test2'])
|
219
|
-
end
|
220
|
-
|
221
|
-
it "must allow defining additional commands in the subclass" do
|
222
|
-
expect(subject.commands['test3']).to_not be_nil
|
223
|
-
expect(subject.commands['test3'].command).to eq(command_class::Test3)
|
224
|
-
end
|
225
|
-
|
226
|
-
it "must not change the superclass'es commands" do
|
227
|
-
expect(command_superclass.commands['test3']).to be(nil)
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
describe ".command_aliases" do
|
233
|
-
subject { command_class }
|
234
|
-
|
235
|
-
it "must return an empty Hash by default" do
|
236
|
-
expect(subject.command_aliases).to eq({})
|
237
|
-
end
|
238
|
-
|
239
|
-
context "when commands have aliases" do
|
240
|
-
let(:command_class) { TestCommands::TestCommandsWithAliases }
|
241
|
-
|
242
|
-
it "must contain the mapping of aliases to command names" do
|
243
|
-
expect(subject.command_aliases).to eq({
|
244
|
-
't1' => 'test1',
|
245
|
-
't2' => 'test2'
|
246
|
-
})
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
context "when additional command aliases are defined in a superclass" do
|
251
|
-
module TestCommands
|
252
|
-
class TestInheritedCommandsWithAliases < TestCommandsWithAliases
|
253
|
-
|
254
|
-
class Test3 < CommandKit::Command
|
255
|
-
|
256
|
-
def run
|
257
|
-
puts 'test command three'
|
258
|
-
end
|
259
|
-
|
260
|
-
end
|
261
|
-
|
262
|
-
command :test3, Test3, aliases: %w[t3]
|
263
|
-
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
let(:command_superclass) { TestCommands::TestCommandsWithAliases }
|
268
|
-
let(:command_class) { TestCommands::TestInheritedCommandsWithAliases }
|
269
|
-
|
270
|
-
it "must inherit the superclass'es command aliases" do
|
271
|
-
expect(subject.command_aliases['t1']).to eq(command_superclass.command_aliases['t1'])
|
272
|
-
expect(subject.command_aliases['t2']).to eq(command_superclass.command_aliases['t2'])
|
273
|
-
end
|
274
|
-
|
275
|
-
it "must allow defining additional command aliases in the subclass" do
|
276
|
-
expect(subject.command_aliases['t3']).to eq('test3')
|
277
|
-
end
|
278
|
-
|
279
|
-
it "must not change the superclass'es command aliases" do
|
280
|
-
expect(command_superclass.command_aliases['test3']).to be(nil)
|
281
|
-
end
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
|
-
describe ".command" do
|
286
|
-
subject { command_class }
|
287
|
-
|
288
|
-
context "when given only a command class" do
|
289
|
-
module TestCommands
|
290
|
-
class TestCommandWithOnlyACommandClass
|
291
|
-
include CommandKit::Commands
|
292
|
-
|
293
|
-
class Test < CommandKit::Command
|
294
|
-
end
|
295
|
-
|
296
|
-
command Test
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
let(:command_class) { TestCommands::TestCommandWithOnlyACommandClass }
|
301
|
-
|
302
|
-
it "must default the command name to the command class'es command_name" do
|
303
|
-
expect(subject.commands['test']).to_not be_nil
|
304
|
-
expect(subject.commands['test'].command).to eq(command_class::Test)
|
305
|
-
end
|
306
|
-
|
307
|
-
context "and aliases:" do
|
308
|
-
let(:command_class) { TestCommands::TestCommandsWithAliases }
|
309
|
-
|
310
|
-
it "must populate aliases with the aliases and the command names" do
|
311
|
-
expect(subject.command_aliases['t1']).to eq(command_class::Test1.command_name)
|
312
|
-
expect(subject.command_aliases['t2']).to eq(command_class::Test2.command_name)
|
313
|
-
end
|
314
|
-
end
|
315
|
-
end
|
316
|
-
|
317
|
-
context "when given a command name and a command class" do
|
318
|
-
let(:command_class) { TestCommands::TestCommandsWithExplicitNames }
|
319
|
-
|
320
|
-
it "must not add an entry for the command class'es command_name" do
|
321
|
-
expect(subject.commands['test1']).to be_nil
|
322
|
-
expect(subject.commands['test2']).to be_nil
|
323
|
-
end
|
324
|
-
|
325
|
-
it "must add an entry for the command name" do
|
326
|
-
expect(subject.commands['command-name-1']).to_not be_nil
|
327
|
-
expect(subject.commands['command-name-1'].command).to eq(command_class::Test1)
|
328
|
-
|
329
|
-
expect(subject.commands['command-name-2']).to_not be_nil
|
330
|
-
expect(subject.commands['command-name-2'].command).to eq(command_class::Test2)
|
331
|
-
end
|
332
|
-
|
333
|
-
context "and aliases:" do
|
334
|
-
let(:command_class) { TestCommands::TestCommandsWithExplicitNamesAndAliases }
|
335
|
-
|
336
|
-
it "must populate aliases with the aliases and the explicit command names" do
|
337
|
-
expect(subject.command_aliases['t1']).to eq('command-name-1')
|
338
|
-
expect(subject.command_aliases['t2']).to eq('command-name-2')
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
342
|
-
context "when the command name is a Symbol" do
|
343
|
-
module TestCommands
|
344
|
-
class TestCommandWithASymbolCommandName
|
345
|
-
include CommandKit::Commands
|
346
|
-
|
347
|
-
class Test < CommandKit::Command
|
348
|
-
end
|
349
|
-
|
350
|
-
command :test_sym, Test
|
351
|
-
end
|
352
|
-
end
|
353
|
-
|
354
|
-
let(:command_class) { TestCommands::TestCommandWithASymbolCommandName }
|
355
|
-
|
356
|
-
it "must not add an entry for the command class'es command_name" do
|
357
|
-
expect(subject.commands['test']).to be_nil
|
358
|
-
end
|
359
|
-
|
360
|
-
it "must not add entries with Symbol keys" do
|
361
|
-
expect(subject.commands.keys).to all(be_kind_of(String))
|
362
|
-
end
|
363
|
-
|
364
|
-
it "must convert the command name to a String" do
|
365
|
-
expect(subject.commands['test_sym']).to_not be_nil
|
366
|
-
expect(subject.commands['test_sym'].command).to eq(command_class::Test)
|
367
|
-
end
|
368
|
-
end
|
369
|
-
end
|
370
|
-
|
371
|
-
context "when given an explicit summary: keyword argument" do
|
372
|
-
let(:command_class) { TestCommands::TestCommandsWithExplicitSummaries }
|
373
|
-
|
374
|
-
it "must initialize the Subcommand#summary" do
|
375
|
-
expect(subject.commands['test1'].summary).to eq('Explicit summary 1')
|
376
|
-
expect(subject.commands['test2'].summary).to eq('Explicit summary 2')
|
377
|
-
end
|
378
|
-
end
|
379
|
-
end
|
380
|
-
|
381
|
-
describe ".get_command" do
|
382
|
-
subject { command_class }
|
383
|
-
|
384
|
-
context "when given a command name" do
|
385
|
-
let(:command_class) { TestCommands::TestCommands }
|
386
|
-
|
387
|
-
it "must return the command's class" do
|
388
|
-
expect(subject.get_command('test1')).to eq(command_class::Test1)
|
389
|
-
expect(subject.get_command('test2')).to eq(command_class::Test2)
|
390
|
-
end
|
391
|
-
end
|
392
|
-
|
393
|
-
context "when given a command's alias name" do
|
394
|
-
let(:command_class) { TestCommands::TestCommandsWithAliases }
|
395
|
-
|
396
|
-
it "must return the command's class associated with the alias" do
|
397
|
-
expect(subject.get_command('t1')).to eq(command_class::Test1)
|
398
|
-
expect(subject.get_command('t2')).to eq(command_class::Test2)
|
399
|
-
end
|
400
|
-
end
|
401
|
-
|
402
|
-
context "when given an unknown command name" do
|
403
|
-
it "must return nil" do
|
404
|
-
expect(subject.get_command('foo')).to be(nil)
|
405
|
-
end
|
406
|
-
end
|
407
|
-
end
|
408
|
-
|
409
|
-
subject { command_class.new }
|
410
|
-
|
411
|
-
describe "#command" do
|
412
|
-
module TestCommands
|
413
|
-
class TestSubCommandInitialization
|
414
|
-
include CommandKit::Commands
|
415
|
-
|
416
|
-
class Test < CommandKit::Command
|
417
|
-
end
|
418
|
-
|
419
|
-
command 'test', Test
|
420
|
-
end
|
421
|
-
end
|
422
|
-
|
423
|
-
let(:command_class) { TestCommands::TestSubCommandInitialization }
|
424
|
-
let(:subcommand_class) { command_class::Test }
|
425
|
-
|
426
|
-
context "when given a valid command name" do
|
427
|
-
it "must lookup the command and initialize it" do
|
428
|
-
expect(subject.command(subcommand_class.command_name)).to be_kind_of(subcommand_class)
|
429
|
-
end
|
430
|
-
end
|
431
|
-
|
432
|
-
context "when given a command alias" do
|
433
|
-
let(:command_class) { TestCommands::TestCommandsWithAliases }
|
434
|
-
|
435
|
-
it "must lookup the command and initialize it" do
|
436
|
-
expect(subject.command('t1')).to be_kind_of(command_class::Test1)
|
437
|
-
expect(subject.command('t2')).to be_kind_of(command_class::Test2)
|
438
|
-
end
|
439
|
-
end
|
440
|
-
|
441
|
-
context "when given an unknown command name" do
|
442
|
-
it "must return nil" do
|
443
|
-
expect(subject.command('foo')).to be_nil
|
444
|
-
end
|
445
|
-
end
|
446
|
-
|
447
|
-
context "when the command includes CommandKit::Commands::ParentCommand" do
|
448
|
-
module TestCommands
|
449
|
-
class TestSubCommandInitializationWithParentCommand
|
450
|
-
include CommandKit::Commands
|
451
|
-
|
452
|
-
class Test
|
453
|
-
include CommandKit::Commands::ParentCommand
|
454
|
-
end
|
455
|
-
|
456
|
-
command 'test', Test
|
457
|
-
end
|
458
|
-
end
|
459
|
-
|
460
|
-
let(:command_class) { TestCommands::TestSubCommandInitializationWithParentCommand }
|
461
|
-
let(:subcommand_class) { command_class::Test }
|
462
|
-
|
463
|
-
it "must initialize the sub-command with a parent_command value" do
|
464
|
-
subcommand = subject.command('test')
|
465
|
-
|
466
|
-
expect(subcommand.parent_command).to be(subject)
|
467
|
-
end
|
468
|
-
end
|
469
|
-
|
470
|
-
context "when the command includes CommandKit::CommandName" do
|
471
|
-
module TestCommands
|
472
|
-
class TestSubCommandInitializationWithCommandName
|
473
|
-
include CommandKit::Commands
|
474
|
-
|
475
|
-
class Test
|
476
|
-
include CommandKit::CommandName
|
477
|
-
end
|
478
|
-
|
479
|
-
command 'test', Test
|
480
|
-
end
|
481
|
-
end
|
482
|
-
|
483
|
-
let(:command_class) { TestCommands::TestSubCommandInitializationWithCommandName }
|
484
|
-
let(:subcommand_class) { command_class::Test }
|
485
|
-
let(:expected_subcommand_name) do
|
486
|
-
"#{command_class.command_name} #{subcommand_class.command_name}"
|
487
|
-
end
|
488
|
-
|
489
|
-
it "must initialize the sub-command with the command and subcommand name" do
|
490
|
-
subcommand = subject.command('test')
|
491
|
-
|
492
|
-
expect(subcommand.command_name).to eq(expected_subcommand_name)
|
493
|
-
end
|
494
|
-
end
|
495
|
-
|
496
|
-
context "when the command includes CommandKit::Stdio" do
|
497
|
-
module TestCommands
|
498
|
-
class TestSubCommandInitializationWithStdio
|
499
|
-
include CommandKit::Commands
|
500
|
-
|
501
|
-
class Test
|
502
|
-
include CommandKit::Stdio
|
503
|
-
end
|
504
|
-
|
505
|
-
command 'test', Test
|
506
|
-
end
|
507
|
-
end
|
508
|
-
|
509
|
-
let(:command_class) { TestCommands::TestSubCommandInitializationWithStdio }
|
510
|
-
let(:subcommand_class) { command_class::Test }
|
511
|
-
|
512
|
-
let(:stdin) { StringIO.new }
|
513
|
-
let(:stdout) { StringIO.new }
|
514
|
-
let(:stderr) { StringIO.new }
|
515
|
-
|
516
|
-
subject do
|
517
|
-
command_class.new(stdin: stdin, stdout: stdout, stderr: stderr)
|
518
|
-
end
|
519
|
-
|
520
|
-
it "must initialize the sub-command with the command's #stdin, #stdout, #stderr" do
|
521
|
-
subcommand = subject.command('test')
|
522
|
-
|
523
|
-
expect(subcommand.stdin).to be(stdin)
|
524
|
-
expect(subcommand.stdout).to be(stdout)
|
525
|
-
expect(subcommand.stderr).to be(stderr)
|
526
|
-
end
|
527
|
-
end
|
528
|
-
|
529
|
-
context "when the command includes CommandKit::Env" do
|
530
|
-
module TestCommands
|
531
|
-
class TestSubCommandInitializationWithEnv
|
532
|
-
include CommandKit::Commands
|
533
|
-
|
534
|
-
class Test
|
535
|
-
include CommandKit::Env
|
536
|
-
end
|
537
|
-
|
538
|
-
command 'test', Test
|
539
|
-
end
|
540
|
-
end
|
541
|
-
|
542
|
-
let(:command_class) { TestCommands::TestSubCommandInitializationWithEnv }
|
543
|
-
let(:subcommand_class) { command_class::Test }
|
544
|
-
|
545
|
-
let(:env) { {'FOO' => 'bar'} }
|
546
|
-
subject { command_class.new(env: env) }
|
547
|
-
|
548
|
-
it "must initialize the sub-command with a copy of the command's #env" do
|
549
|
-
subcommand = subject.command('test')
|
550
|
-
|
551
|
-
expect(subcommand.env).to eq(env)
|
552
|
-
expect(subcommand.env).to_not be(env)
|
553
|
-
end
|
554
|
-
end
|
555
|
-
|
556
|
-
context "when the command includes CommandKit::Options" do
|
557
|
-
module TestCommands
|
558
|
-
class TestSubCommandInitializationWithOptions
|
559
|
-
include CommandKit::Commands
|
560
|
-
|
561
|
-
class Test
|
562
|
-
include CommandKit::Options
|
563
|
-
end
|
564
|
-
|
565
|
-
command 'test', Test
|
566
|
-
end
|
567
|
-
end
|
568
|
-
|
569
|
-
let(:command_class) { TestCommands::TestSubCommandInitializationWithOptions }
|
570
|
-
let(:subcommand_class) { command_class::Test }
|
571
|
-
|
572
|
-
let(:options) { {foo: 'bar'} }
|
573
|
-
subject { command_class.new(options: options) }
|
574
|
-
|
575
|
-
it "must initialize the sub-command with a copy of the command's #options Hash" do
|
576
|
-
subcommand = subject.command('test')
|
577
|
-
|
578
|
-
expect(subcommand.options).to eq(options)
|
579
|
-
expect(subcommand.options).to_not be(options)
|
580
|
-
end
|
581
|
-
end
|
582
|
-
end
|
583
|
-
|
584
|
-
describe "#invoke" do
|
585
|
-
context "when given a valid command name" do
|
586
|
-
let(:command_name) { 'test2' }
|
587
|
-
let(:argv) { %w[--opt arg1 arg2] }
|
588
|
-
|
589
|
-
it "must call the command's #main method with the given argv" do
|
590
|
-
expect_any_instance_of(command_class::Test2).to receive(:main).with(argv)
|
591
|
-
|
592
|
-
subject.invoke(command_name,*argv)
|
593
|
-
end
|
594
|
-
|
595
|
-
context "when the command returns a custom exit code" do
|
596
|
-
let(:command_class) { TestCommands::TestCommandsWithCustomExitStatus }
|
597
|
-
|
598
|
-
it "must return the exit code" do
|
599
|
-
expect(subject.invoke('test')).to eq(2)
|
600
|
-
end
|
601
|
-
end
|
602
|
-
end
|
603
|
-
|
604
|
-
context "when given an unknown command name" do
|
605
|
-
let(:command_name) { 'xxx' }
|
606
|
-
let(:argv) { %w[--opt arg1 arg2] }
|
607
|
-
|
608
|
-
it "must call #on_unknown_command with the given name and argv" do
|
609
|
-
expect(subject).to receive(:on_unknown_command).with(command_name,argv)
|
610
|
-
|
611
|
-
subject.invoke(command_name,*argv)
|
612
|
-
end
|
613
|
-
end
|
614
|
-
end
|
615
|
-
|
616
|
-
let(:unknown_command) { 'xxx' }
|
617
|
-
|
618
|
-
describe "#command_not_found" do
|
619
|
-
it "must print an error message to stderr and exit with 1" do
|
620
|
-
expect(subject).to receive(:exit).with(1)
|
621
|
-
|
622
|
-
expect { subject.command_not_found(unknown_command) }.to output(
|
623
|
-
"#{subject.command_name}: '#{unknown_command}' is not a #{subject.command_name} command. See `#{subject.command_name} help`" + $/
|
624
|
-
).to_stderr
|
625
|
-
end
|
626
|
-
end
|
627
|
-
|
628
|
-
describe "#on_unknown_command" do
|
629
|
-
it "must call #command_not_found with the given command name by default" do
|
630
|
-
expect(subject).to receive(:command_not_found).with(unknown_command)
|
631
|
-
|
632
|
-
subject.on_unknown_command(unknown_command)
|
633
|
-
end
|
634
|
-
end
|
635
|
-
|
636
|
-
describe "#option_parser" do
|
637
|
-
let(:command_name) { 'test1' }
|
638
|
-
let(:command_argv) { %w[foo bar baz] }
|
639
|
-
let(:argv) { [command_name, *command_argv] }
|
640
|
-
|
641
|
-
it "must stop before the first non-option argument" do
|
642
|
-
expect(subject.option_parser.parse(argv)).to eq(
|
643
|
-
[command_name, *command_argv]
|
644
|
-
)
|
645
|
-
end
|
646
|
-
|
647
|
-
context "when an unknown command name is given" do
|
648
|
-
let(:command_argv) { %w[bar baz] }
|
649
|
-
let(:argv) { ['foo', command_name, *command_argv] }
|
650
|
-
|
651
|
-
it "must stop before the first non-option argument" do
|
652
|
-
expect(subject.option_parser.parse(argv)).to eq(argv)
|
653
|
-
end
|
654
|
-
end
|
655
|
-
|
656
|
-
context "when additional global options are defined" do
|
657
|
-
let(:command_class) { TestCommands::TestCommandsWithGlobalOptions }
|
658
|
-
let(:bar) { '2' }
|
659
|
-
let(:argv) do
|
660
|
-
['--foo', '--bar', bar.to_s, command_name, *command_argv]
|
661
|
-
end
|
662
|
-
|
663
|
-
it "must parse the global options, but stop before the first non-option associated argument" do
|
664
|
-
expect(subject.option_parser.parse(argv)).to eq(
|
665
|
-
[command_name, *command_argv]
|
666
|
-
)
|
667
|
-
|
668
|
-
expect(subject.options[:foo]).to be(true)
|
669
|
-
expect(subject.options[:bar]).to eq(bar)
|
670
|
-
end
|
671
|
-
end
|
672
|
-
end
|
673
|
-
|
674
|
-
describe "#run" do
|
675
|
-
context "when a command name is the first argument" do
|
676
|
-
let(:command) { 'test1' }
|
677
|
-
let(:exit_status) { 2 }
|
678
|
-
|
679
|
-
it "must invoke the command and exit with it's status" do
|
680
|
-
expect(subject).to receive(:invoke).with(command).and_return(exit_status)
|
681
|
-
expect(subject).to receive(:exit).with(exit_status)
|
682
|
-
|
683
|
-
subject.run(command)
|
684
|
-
end
|
685
|
-
|
686
|
-
context "when additional argv is given after the command name" do
|
687
|
-
let(:argv) { %w[--opt arg1 arg2] }
|
688
|
-
|
689
|
-
it "must pass the additional argv to the command" do
|
690
|
-
expect(subject).to receive(:invoke).with(command,*argv).and_return(exit_status)
|
691
|
-
expect(subject).to receive(:exit).with(exit_status)
|
692
|
-
|
693
|
-
subject.run(command,*argv)
|
694
|
-
end
|
695
|
-
end
|
696
|
-
end
|
697
|
-
|
698
|
-
context "when given no arguments" do
|
699
|
-
let(:exit_status) { 1 }
|
700
|
-
|
701
|
-
it "must default to calling #help and exit with 1" do
|
702
|
-
expect(subject).to receive(:help)
|
703
|
-
expect(subject).to receive(:exit).with(exit_status)
|
704
|
-
|
705
|
-
subject.run()
|
706
|
-
end
|
707
|
-
end
|
708
|
-
end
|
709
|
-
|
710
|
-
describe "#main" do
|
711
|
-
let(:command_class) { TestCommands::TestCommandsWithCommandArguments }
|
712
|
-
|
713
|
-
context "when a command name is the first argument" do
|
714
|
-
let(:command) { 'test1' }
|
715
|
-
let(:argv) { [command] }
|
716
|
-
|
717
|
-
it "must return 0" do
|
718
|
-
expect(subject.main(argv)).to eq(0)
|
719
|
-
end
|
720
|
-
end
|
721
|
-
|
722
|
-
context "when given additional command arguments" do
|
723
|
-
let(:argv) { %w[test1 arg1 arg2] }
|
724
|
-
|
725
|
-
it "must pass them to the command" do
|
726
|
-
expect(subject.main(argv)).to eq(0)
|
727
|
-
end
|
728
|
-
end
|
729
|
-
|
730
|
-
context "when given no arguments" do
|
731
|
-
let(:exit_status) { 1 }
|
732
|
-
let(:argv) { %w[] }
|
733
|
-
|
734
|
-
it "must call #help and return 1" do
|
735
|
-
expect(subject).to receive(:help)
|
736
|
-
|
737
|
-
expect(subject.main(argv)).to eq(exit_status)
|
738
|
-
end
|
739
|
-
end
|
740
|
-
end
|
741
|
-
|
742
|
-
describe "#help_commands" do
|
743
|
-
it "must print usage and the list of available commands" do
|
744
|
-
expect { subject.help_commands }.to output(
|
745
|
-
[
|
746
|
-
"",
|
747
|
-
"Commands:",
|
748
|
-
" help",
|
749
|
-
" test1",
|
750
|
-
" test2",
|
751
|
-
""
|
752
|
-
].join($/)
|
753
|
-
).to_stdout
|
754
|
-
end
|
755
|
-
|
756
|
-
context "when the commands have custom names" do
|
757
|
-
let(:command_class) { TestCommands::TestCommandsWithExplicitNames }
|
758
|
-
|
759
|
-
it "must print the command names, not the command class'es #command_name" do
|
760
|
-
expect { subject.help_commands }.to output(
|
761
|
-
[
|
762
|
-
"",
|
763
|
-
"Commands:",
|
764
|
-
" command-name-1",
|
765
|
-
" command-name-2",
|
766
|
-
" help",
|
767
|
-
""
|
768
|
-
].join($/)
|
769
|
-
).to_stdout
|
770
|
-
end
|
771
|
-
end
|
772
|
-
|
773
|
-
context "when the commands have summaries" do
|
774
|
-
let(:command_class) { TestCommands::TestCommandsWithExplicitSummaries }
|
775
|
-
|
776
|
-
it "must print the command names and their summaries" do
|
777
|
-
expect { subject.help_commands }.to output(
|
778
|
-
[
|
779
|
-
"",
|
780
|
-
"Commands:",
|
781
|
-
" help",
|
782
|
-
" test1\t#{command_class.commands['test1'].summary}",
|
783
|
-
" test2\t#{command_class.commands['test2'].summary}",
|
784
|
-
""
|
785
|
-
].join($/)
|
786
|
-
).to_stdout
|
787
|
-
end
|
788
|
-
end
|
789
|
-
end
|
790
|
-
|
791
|
-
describe "#help" do
|
792
|
-
it "must print the list of available commands after other help output" do
|
793
|
-
expect { subject.help }.to output(
|
794
|
-
[
|
795
|
-
"Usage: #{subject.command_name} [options] [COMMAND [ARGS...]]",
|
796
|
-
"",
|
797
|
-
"Options:",
|
798
|
-
" -h, --help Print help information",
|
799
|
-
"",
|
800
|
-
"Arguments:",
|
801
|
-
" [COMMAND] The command name to run",
|
802
|
-
" [ARGS ...] Additional arguments for the command",
|
803
|
-
"",
|
804
|
-
"Commands:",
|
805
|
-
" help",
|
806
|
-
" test1",
|
807
|
-
" test2",
|
808
|
-
""
|
809
|
-
].join($/)
|
810
|
-
).to_stdout
|
811
|
-
end
|
812
|
-
|
813
|
-
context "when the command has command alises" do
|
814
|
-
let(:command_class) { TestCommands::TestCommandsWithAliases }
|
815
|
-
|
816
|
-
it "must print the command names, along with their command aliases" do
|
817
|
-
expect { subject.help }.to output(
|
818
|
-
[
|
819
|
-
"Usage: #{subject.command_name} [options] [COMMAND [ARGS...]]",
|
820
|
-
"",
|
821
|
-
"Options:",
|
822
|
-
" -h, --help Print help information",
|
823
|
-
"",
|
824
|
-
"Arguments:",
|
825
|
-
" [COMMAND] The command name to run",
|
826
|
-
" [ARGS ...] Additional arguments for the command",
|
827
|
-
"",
|
828
|
-
"Commands:",
|
829
|
-
" help",
|
830
|
-
" test1, t1",
|
831
|
-
" test2, t2",
|
832
|
-
""
|
833
|
-
].join($/)
|
834
|
-
).to_stdout
|
835
|
-
end
|
836
|
-
end
|
837
|
-
|
838
|
-
context "when the command defines additional global options" do
|
839
|
-
let(:command_class) { TestCommands::TestCommandsWithGlobalOptions }
|
840
|
-
|
841
|
-
it "must print any global options" do
|
842
|
-
expect { subject.help }.to output(
|
843
|
-
[
|
844
|
-
"Usage: #{subject.command_name} [options] [COMMAND [ARGS...]]",
|
845
|
-
"",
|
846
|
-
"Options:",
|
847
|
-
" -f, --foo Global --foo option",
|
848
|
-
" -b, --bar BAR Global --bar option",
|
849
|
-
" -h, --help Print help information",
|
850
|
-
"",
|
851
|
-
"Arguments:",
|
852
|
-
" [COMMAND] The command name to run",
|
853
|
-
" [ARGS ...] Additional arguments for the command",
|
854
|
-
"",
|
855
|
-
"Commands:",
|
856
|
-
" help",
|
857
|
-
" test1",
|
858
|
-
" test2",
|
859
|
-
""
|
860
|
-
].join($/)
|
861
|
-
).to_stdout
|
862
|
-
end
|
863
|
-
end
|
864
|
-
end
|
865
|
-
end
|