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,66 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'command_kit/commands/help'
|
|
3
|
+
require 'command_kit/commands'
|
|
4
|
+
|
|
5
|
+
describe Commands::Help do
|
|
6
|
+
module TestHelpCommand
|
|
7
|
+
class CLI
|
|
8
|
+
include CommandKit::Commands
|
|
9
|
+
|
|
10
|
+
class Test < CommandKit::Command
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class TestWithoutHelp
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
command Test
|
|
17
|
+
command 'test-without-help', TestWithoutHelp
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
let(:parent_command_class) { TestHelpCommand::CLI }
|
|
22
|
+
let(:parent_command) { parent_command_class.new }
|
|
23
|
+
let(:command_class) { CommandKit::Commands::Help }
|
|
24
|
+
|
|
25
|
+
subject { command_class.new(parent_command: parent_command) }
|
|
26
|
+
|
|
27
|
+
describe ".run" do
|
|
28
|
+
context "when giving no arguments" do
|
|
29
|
+
it "must call the parent command's #help method" do
|
|
30
|
+
expect(parent_command).to receive(:help).with(no_args)
|
|
31
|
+
|
|
32
|
+
subject.run
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context "when given a command name" do
|
|
37
|
+
let(:command) { 'test' }
|
|
38
|
+
|
|
39
|
+
it "must lookup the command and call it's #help method" do
|
|
40
|
+
expect_any_instance_of(parent_command_class::Test).to receive(:help)
|
|
41
|
+
|
|
42
|
+
subject.run(command)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
context "but the command does not define a #help method" do
|
|
46
|
+
let(:command) { 'test-without-help' }
|
|
47
|
+
|
|
48
|
+
it do
|
|
49
|
+
expect { subject.run(command) }.to raise_error(TypeError)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
context "but the command name is invalid" do
|
|
54
|
+
let(:command) { 'xxx' }
|
|
55
|
+
|
|
56
|
+
it "must print an error message and exit with 1" do
|
|
57
|
+
expect(subject).to receive(:exit).with(1)
|
|
58
|
+
|
|
59
|
+
expect { subject.run(command) }.to output(
|
|
60
|
+
"#{subject.command_name}: unknown command: #{command}#{$/}"
|
|
61
|
+
).to_stderr
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'command_kit/commands'
|
|
3
|
+
require 'command_kit/commands/parent_command'
|
|
4
|
+
|
|
5
|
+
describe Commands::ParentCommand do
|
|
6
|
+
module TestParentCommand
|
|
7
|
+
class TestCommands
|
|
8
|
+
|
|
9
|
+
include CommandKit::Commands
|
|
10
|
+
|
|
11
|
+
class Test < CommandKit::Command
|
|
12
|
+
include CommandKit::Commands::ParentCommand
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
command Test
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
let(:parent_command_class) { TestParentCommand::TestCommands }
|
|
21
|
+
let(:command_class) { TestParentCommand::TestCommands::Test }
|
|
22
|
+
|
|
23
|
+
describe "#initialize" do
|
|
24
|
+
context "when given a parent_command: keyword argument" do
|
|
25
|
+
let(:parent_command) { parent_command_class.new }
|
|
26
|
+
|
|
27
|
+
subject { command_class.new(parent_command: parent_command) }
|
|
28
|
+
|
|
29
|
+
it "must initialize #parent_command" do
|
|
30
|
+
expect(subject.parent_command).to be(parent_command)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context "when the parent_command: keyword argument is not given" do
|
|
35
|
+
it do
|
|
36
|
+
expect { command_class.new }.to raise_error(ArgumentError)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'command_kit/commands/subcommand'
|
|
3
|
+
require 'command_kit/command'
|
|
4
|
+
|
|
5
|
+
describe Commands::Subcommand do
|
|
6
|
+
module TestSubcommands
|
|
7
|
+
class TestCommand < CommandKit::Command
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class TestCommandWithoutDescription
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class TestCommandWithASentenceFragmentDescription
|
|
14
|
+
include CommandKit::Description
|
|
15
|
+
|
|
16
|
+
description "The quick brown fox"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class TestCommandWithASingleSentenceDescription
|
|
20
|
+
include CommandKit::Description
|
|
21
|
+
|
|
22
|
+
description "The quick brown fox jumps over the lazy dog."
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class TestCommandWithAMultiSentenceDescription
|
|
26
|
+
include CommandKit::Description
|
|
27
|
+
|
|
28
|
+
description "The quick brown fox jumps over the lazy dog. Foo bar baz."
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe "#initialize" do
|
|
33
|
+
let(:command_class) { TestSubcommands::TestCommand }
|
|
34
|
+
|
|
35
|
+
subject { described_class.new(command_class) }
|
|
36
|
+
|
|
37
|
+
it "must initialize the subcommand with a given command class" do
|
|
38
|
+
expect(subject.command).to be(command_class)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
context "when the command class has no description" do
|
|
42
|
+
it "#summary must be nil" do
|
|
43
|
+
expect(subject.summary).to be(nil)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
context "when the command class have a description" do
|
|
48
|
+
let(:command_class) { TestSubcommands::TestCommandWithAMultiSentenceDescription }
|
|
49
|
+
|
|
50
|
+
it "must extract the summary using .summary" do
|
|
51
|
+
expect(subject.summary).to eq(described_class.summary(command_class))
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
context "when given aliases:" do
|
|
56
|
+
let(:aliases) { %w[test t] }
|
|
57
|
+
|
|
58
|
+
subject { described_class.new(command_class, aliases: aliases) }
|
|
59
|
+
|
|
60
|
+
it "must initialize #aliases" do
|
|
61
|
+
expect(subject.aliases).to eq(aliases)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
describe ".summary" do
|
|
67
|
+
subject { described_class.summary(command_class) }
|
|
68
|
+
|
|
69
|
+
context "when the command class does respond_to?(:description)" do
|
|
70
|
+
let(:command_class) { TestSubcommands::TestCommandWithoutDescription }
|
|
71
|
+
|
|
72
|
+
it { expect(subject).to be(nil) }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
context "when the command class has a sentence fragment description" do
|
|
76
|
+
let(:command_class) { TestSubcommands::TestCommandWithASentenceFragmentDescription }
|
|
77
|
+
|
|
78
|
+
it "must return the sentence fragment" do
|
|
79
|
+
expect(subject).to eq(command_class.description)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
context "when the command class has a single sentence description" do
|
|
84
|
+
let(:command_class) { TestSubcommands::TestCommandWithASingleSentenceDescription }
|
|
85
|
+
|
|
86
|
+
it "must return the single sentence without the terminating period" do
|
|
87
|
+
expect(subject).to eq(command_class.description.chomp('.'))
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
context "when the command class has a multi-sentence description" do
|
|
92
|
+
let(:command_class) { TestSubcommands::TestCommandWithAMultiSentenceDescription }
|
|
93
|
+
|
|
94
|
+
it "must extract the first sentence, without the terminating period" do
|
|
95
|
+
expect(subject).to eq(command_class.description.split(/\.\s*/).first)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'command_kit/commands'
|
|
3
|
+
|
|
4
|
+
describe 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
|
+
p method(:command).source_location
|
|
23
|
+
command Test1
|
|
24
|
+
command Test2
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class TestCommandsWithAliases
|
|
29
|
+
|
|
30
|
+
include CommandKit::Commands
|
|
31
|
+
|
|
32
|
+
class Test1 < CommandKit::Command
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class Test2 < CommandKit::Command
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
command Test1, aliases: %w[t1]
|
|
39
|
+
command Test2, aliases: %w[t2]
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class TestCommandsWithExplicitNames
|
|
44
|
+
|
|
45
|
+
include CommandKit::Commands
|
|
46
|
+
|
|
47
|
+
class Test1 < CommandKit::Command
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
class Test2 < CommandKit::Command
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
command 'command-name-1', Test1
|
|
54
|
+
command 'command-name-2', Test2
|
|
55
|
+
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class TestCommandsWithExplicitNamesAndAliases
|
|
59
|
+
|
|
60
|
+
include CommandKit::Commands
|
|
61
|
+
|
|
62
|
+
class Test1 < CommandKit::Command
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class Test2 < CommandKit::Command
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
command 'command-name-1', Test1, aliases: %w[t1]
|
|
69
|
+
command 'command-name-2', Test2, aliases: %w[t2]
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
class TestCommandsWithExplicitSummaries
|
|
74
|
+
|
|
75
|
+
include CommandKit::Commands
|
|
76
|
+
|
|
77
|
+
class Test1 < CommandKit::Command
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
class Test2 < CommandKit::Command
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
command Test1, summary: 'Explicit summary 1'
|
|
84
|
+
command Test2, summary: 'Explicit summary 2'
|
|
85
|
+
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
class TestCommandsWithCustomExitStatus
|
|
89
|
+
|
|
90
|
+
include CommandKit::Commands
|
|
91
|
+
|
|
92
|
+
class Test < CommandKit::Command
|
|
93
|
+
|
|
94
|
+
def run(*argv)
|
|
95
|
+
exit(2)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
command Test
|
|
101
|
+
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class TestCommandsWithGlobalOptions
|
|
105
|
+
|
|
106
|
+
include CommandKit::Commands
|
|
107
|
+
|
|
108
|
+
class Test1 < CommandKit::Command
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
class Test2 < CommandKit::Command
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
option :foo, short: '-f',
|
|
115
|
+
desc: "Global --foo option"
|
|
116
|
+
|
|
117
|
+
option :bar, short: '-b',
|
|
118
|
+
value: {
|
|
119
|
+
required: true,
|
|
120
|
+
type: String,
|
|
121
|
+
usage: 'BAR'
|
|
122
|
+
},
|
|
123
|
+
desc: "Global --bar option"
|
|
124
|
+
|
|
125
|
+
command Test1
|
|
126
|
+
command Test2
|
|
127
|
+
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
let(:command_class) { TestCommands::TestCommands }
|
|
132
|
+
|
|
133
|
+
describe ".commands" do
|
|
134
|
+
subject { command_class }
|
|
135
|
+
|
|
136
|
+
it "must return a Hash" do
|
|
137
|
+
expect(subject.commands).to be_kind_of(Hash)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it "must provide a default 'help' command" do
|
|
141
|
+
expect(subject.commands['help']).to_not be_nil
|
|
142
|
+
expect(subject.commands['help'].command).to eq(CommandKit::Commands::Help)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
context "when additional commands are defined in a superclass" do
|
|
146
|
+
module TestCommands
|
|
147
|
+
class TestInheritedCommands < TestCommands
|
|
148
|
+
|
|
149
|
+
class Test3 < CommandKit::Command
|
|
150
|
+
|
|
151
|
+
def run
|
|
152
|
+
puts 'test command three'
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
command :test3, Test3
|
|
158
|
+
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
let(:command_superclass) { TestCommands::TestCommands }
|
|
163
|
+
let(:command_class) { TestCommands::TestInheritedCommands }
|
|
164
|
+
|
|
165
|
+
it "must inherit the superclass'es commands" do
|
|
166
|
+
expect(subject.commands['test1']).to eq(command_superclass.commands['test1'])
|
|
167
|
+
expect(subject.commands['test2']).to eq(command_superclass.commands['test2'])
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it "must allow defining additional commands in the subclass" do
|
|
171
|
+
expect(subject.commands['test3']).to_not be_nil
|
|
172
|
+
expect(subject.commands['test3'].command).to eq(command_class::Test3)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
it "must not change the superclass'es commands" do
|
|
176
|
+
expect(command_superclass.commands['test3']).to be(nil)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
describe ".command_aliases" do
|
|
182
|
+
subject { command_class }
|
|
183
|
+
|
|
184
|
+
it "must return an empty Hash by default" do
|
|
185
|
+
expect(subject.command_aliases).to eq({})
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
context "when commands have aliases" do
|
|
189
|
+
let(:command_class) { TestCommands::TestCommandsWithAliases }
|
|
190
|
+
|
|
191
|
+
it "must contain the mapping of aliases to command names" do
|
|
192
|
+
expect(subject.command_aliases).to eq({
|
|
193
|
+
't1' => 'test1',
|
|
194
|
+
't2' => 'test2',
|
|
195
|
+
})
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
context "when additional command aliases are defined in a superclass" do
|
|
200
|
+
module TestCommands
|
|
201
|
+
class TestInheritedCommandsWithAliases < TestCommandsWithAliases
|
|
202
|
+
|
|
203
|
+
class Test3 < CommandKit::Command
|
|
204
|
+
|
|
205
|
+
def run
|
|
206
|
+
puts 'test command three'
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
command :test3, Test3, aliases: %w[t3]
|
|
212
|
+
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
let(:command_superclass) { TestCommands::TestCommandsWithAliases }
|
|
217
|
+
let(:command_class) { TestCommands::TestInheritedCommandsWithAliases }
|
|
218
|
+
|
|
219
|
+
it "must inherit the superclass'es command aliases" do
|
|
220
|
+
expect(subject.command_aliases['t1']).to eq(command_superclass.command_aliases['t1'])
|
|
221
|
+
expect(subject.command_aliases['t2']).to eq(command_superclass.command_aliases['t2'])
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
it "must allow defining additional command aliases in the subclass" do
|
|
225
|
+
expect(subject.command_aliases['t3']).to eq('test3')
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
it "must not change the superclass'es command aliases" do
|
|
229
|
+
expect(command_superclass.command_aliases['test3']).to be(nil)
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
describe ".command" do
|
|
235
|
+
subject { command_class }
|
|
236
|
+
|
|
237
|
+
context "when given only a command class" do
|
|
238
|
+
module TestCommands
|
|
239
|
+
class TestCommandWithOnlyACommandClass
|
|
240
|
+
include CommandKit::Commands
|
|
241
|
+
|
|
242
|
+
class Test < CommandKit::Command
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
command Test
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
let(:command_class) { TestCommands::TestCommandWithOnlyACommandClass }
|
|
250
|
+
|
|
251
|
+
it "must default the command name to the command class'es command_name" do
|
|
252
|
+
expect(subject.commands['test']).to_not be_nil
|
|
253
|
+
expect(subject.commands['test'].command).to eq(command_class::Test)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
context "and aliases:" do
|
|
257
|
+
let(:command_class) { TestCommands::TestCommandsWithAliases }
|
|
258
|
+
|
|
259
|
+
it "must populate aliases with the aliases and the command names" do
|
|
260
|
+
expect(subject.command_aliases['t1']).to eq(command_class::Test1.command_name)
|
|
261
|
+
expect(subject.command_aliases['t2']).to eq(command_class::Test2.command_name)
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
context "when given a command name and a command class" do
|
|
267
|
+
let(:command_class) { TestCommands::TestCommandsWithExplicitNames }
|
|
268
|
+
|
|
269
|
+
it "must not add an entry for the command class'es command_name" do
|
|
270
|
+
expect(subject.commands['test1']).to be_nil
|
|
271
|
+
expect(subject.commands['test2']).to be_nil
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
it "must add an entry for the command name" do
|
|
275
|
+
expect(subject.commands['command-name-1']).to_not be_nil
|
|
276
|
+
expect(subject.commands['command-name-1'].command).to eq(command_class::Test1)
|
|
277
|
+
|
|
278
|
+
expect(subject.commands['command-name-2']).to_not be_nil
|
|
279
|
+
expect(subject.commands['command-name-2'].command).to eq(command_class::Test2)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
context "and aliases:" do
|
|
283
|
+
let(:command_class) { TestCommands::TestCommandsWithExplicitNamesAndAliases }
|
|
284
|
+
|
|
285
|
+
it "must populate aliases with the aliases and the explicit command names" do
|
|
286
|
+
expect(subject.command_aliases['t1']).to eq('command-name-1')
|
|
287
|
+
expect(subject.command_aliases['t2']).to eq('command-name-2')
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
context "when the command name is a Symbol" do
|
|
292
|
+
module TestCommands
|
|
293
|
+
class TestCommandWithASymbolCommandName
|
|
294
|
+
include CommandKit::Commands
|
|
295
|
+
|
|
296
|
+
class Test < CommandKit::Command
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
command :test_sym, Test
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
let(:command_class) { TestCommands::TestCommandWithASymbolCommandName }
|
|
304
|
+
|
|
305
|
+
it "must not add an entry for the command class'es command_name" do
|
|
306
|
+
expect(subject.commands['test']).to be_nil
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
it "must not add entries with Symbol keys" do
|
|
310
|
+
expect(subject.commands.keys).to all(be_kind_of(String))
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
it "must convert the command name to a String" do
|
|
314
|
+
expect(subject.commands['test_sym']).to_not be_nil
|
|
315
|
+
expect(subject.commands['test_sym'].command).to eq(command_class::Test)
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
context "when given an explicit summary: keyword argument" do
|
|
321
|
+
let(:command_class) { TestCommands::TestCommandsWithExplicitSummaries }
|
|
322
|
+
|
|
323
|
+
it "must initialize the Subcommand#summary" do
|
|
324
|
+
expect(subject.commands['test1'].summary).to eq('Explicit summary 1')
|
|
325
|
+
expect(subject.commands['test2'].summary).to eq('Explicit summary 2')
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
describe ".get_command" do
|
|
331
|
+
subject { command_class }
|
|
332
|
+
|
|
333
|
+
context "when given a command name" do
|
|
334
|
+
let(:command_class) { TestCommands::TestCommands }
|
|
335
|
+
|
|
336
|
+
it "must return the command's class" do
|
|
337
|
+
expect(subject.get_command('test1')).to eq(command_class::Test1)
|
|
338
|
+
expect(subject.get_command('test2')).to eq(command_class::Test2)
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
context "when given a command's alias name" do
|
|
343
|
+
let(:command_class) { TestCommands::TestCommandsWithAliases }
|
|
344
|
+
|
|
345
|
+
it "must return the command's class associated with the alias" do
|
|
346
|
+
expect(subject.get_command('t1')).to eq(command_class::Test1)
|
|
347
|
+
expect(subject.get_command('t2')).to eq(command_class::Test2)
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
context "when given an unknown command name" do
|
|
352
|
+
it "must return nil" do
|
|
353
|
+
expect(subject.get_command('foo')).to be(nil)
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
subject { command_class.new }
|
|
359
|
+
|
|
360
|
+
describe "#command" do
|
|
361
|
+
module TestCommands
|
|
362
|
+
class TestSubCommandInitialization
|
|
363
|
+
include CommandKit::Commands
|
|
364
|
+
|
|
365
|
+
class Test < CommandKit::Command
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
command 'test', Test
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
let(:command_class) { TestCommands::TestSubCommandInitialization }
|
|
373
|
+
let(:subcommand_class) { command_class::Test }
|
|
374
|
+
|
|
375
|
+
context "when given a valid command name" do
|
|
376
|
+
it "must lookup the command and initialize it" do
|
|
377
|
+
expect(subject.command(subcommand_class.command_name)).to be_kind_of(subcommand_class)
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
context "when given a command alias" do
|
|
382
|
+
let(:command_class) { TestCommands::TestCommandsWithAliases }
|
|
383
|
+
|
|
384
|
+
it "must lookup the command and initialize it" do
|
|
385
|
+
expect(subject.command('t1')).to be_kind_of(command_class::Test1)
|
|
386
|
+
expect(subject.command('t2')).to be_kind_of(command_class::Test2)
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
context "when given an unknown command name" do
|
|
391
|
+
it "must return nil" do
|
|
392
|
+
expect(subject.command('foo')).to be_nil
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
context "when the command includes CommandKit::Commands::ParentCommand" do
|
|
397
|
+
module TestCommands
|
|
398
|
+
class TestSubCommandInitializationWithParentCommand
|
|
399
|
+
include CommandKit::Commands
|
|
400
|
+
|
|
401
|
+
class Test
|
|
402
|
+
include CommandKit::Commands::ParentCommand
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
command 'test', Test
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
let(:command_class) { TestCommands::TestSubCommandInitializationWithParentCommand }
|
|
410
|
+
let(:subcommand_class) { command_class::Test }
|
|
411
|
+
|
|
412
|
+
it "must initialize the sub-command with a parent_command value" do
|
|
413
|
+
subcommand = subject.command('test')
|
|
414
|
+
|
|
415
|
+
expect(subcommand.parent_command).to be(subject)
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
context "when the command includes CommandKit::CommandName" do
|
|
420
|
+
module TestCommands
|
|
421
|
+
class TestSubCommandInitializationWithCommandName
|
|
422
|
+
include CommandKit::Commands
|
|
423
|
+
|
|
424
|
+
class Test
|
|
425
|
+
include CommandKit::CommandName
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
command 'test', Test
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
let(:command_class) { TestCommands::TestSubCommandInitializationWithCommandName }
|
|
433
|
+
let(:subcommand_class) { command_class::Test }
|
|
434
|
+
let(:expected_subcommand_name) do
|
|
435
|
+
"#{command_class.command_name} #{subcommand_class.command_name}"
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
it "must initialize the sub-command with the command and subcommand name" do
|
|
439
|
+
subcommand = subject.command('test')
|
|
440
|
+
|
|
441
|
+
expect(subcommand.command_name).to eq(expected_subcommand_name)
|
|
442
|
+
end
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
context "when the command includes CommandKit::Stdio" do
|
|
446
|
+
module TestCommands
|
|
447
|
+
class TestSubCommandInitializationWithStdio
|
|
448
|
+
include CommandKit::Commands
|
|
449
|
+
|
|
450
|
+
class Test
|
|
451
|
+
include CommandKit::Stdio
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
command 'test', Test
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
let(:command_class) { TestCommands::TestSubCommandInitializationWithStdio }
|
|
459
|
+
let(:subcommand_class) { command_class::Test }
|
|
460
|
+
|
|
461
|
+
let(:stdin) { StringIO.new }
|
|
462
|
+
let(:stdout) { StringIO.new }
|
|
463
|
+
let(:stderr) { StringIO.new }
|
|
464
|
+
|
|
465
|
+
subject do
|
|
466
|
+
command_class.new(stdin: stdin, stdout: stdout, stderr: stderr)
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
it "must initialize the sub-command with the command's #stdin, #stdout, #stderr" do
|
|
470
|
+
subcommand = subject.command('test')
|
|
471
|
+
|
|
472
|
+
expect(subcommand.stdin).to be(stdin)
|
|
473
|
+
expect(subcommand.stdout).to be(stdout)
|
|
474
|
+
expect(subcommand.stderr).to be(stderr)
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
context "when the command includes CommandKit::Env" do
|
|
479
|
+
module TestCommands
|
|
480
|
+
class TestSubCommandInitializationWithEnv
|
|
481
|
+
include CommandKit::Commands
|
|
482
|
+
|
|
483
|
+
class Test
|
|
484
|
+
include CommandKit::Env
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
command 'test', Test
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
let(:command_class) { TestCommands::TestSubCommandInitializationWithEnv }
|
|
492
|
+
let(:subcommand_class) { command_class::Test }
|
|
493
|
+
|
|
494
|
+
let(:env) { {'FOO' => 'bar'} }
|
|
495
|
+
subject { command_class.new(env: env) }
|
|
496
|
+
|
|
497
|
+
it "must initialize the sub-command with a copy of the command's #env" do
|
|
498
|
+
subcommand = subject.command('test')
|
|
499
|
+
|
|
500
|
+
expect(subcommand.env).to eq(env)
|
|
501
|
+
expect(subcommand.env).to_not be(env)
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
context "when the command includes CommandKit::Options" do
|
|
506
|
+
module TestCommands
|
|
507
|
+
class TestSubCommandInitializationWithOptions
|
|
508
|
+
include CommandKit::Commands
|
|
509
|
+
|
|
510
|
+
class Test
|
|
511
|
+
include CommandKit::Options
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
command 'test', Test
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
let(:command_class) { TestCommands::TestSubCommandInitializationWithOptions }
|
|
519
|
+
let(:subcommand_class) { command_class::Test }
|
|
520
|
+
|
|
521
|
+
let(:options) { {foo: 'bar'} }
|
|
522
|
+
subject { command_class.new(options: options) }
|
|
523
|
+
|
|
524
|
+
it "must initialize the sub-command with a copy of the command's #options Hash" do
|
|
525
|
+
subcommand = subject.command('test')
|
|
526
|
+
|
|
527
|
+
expect(subcommand.options).to eq(options)
|
|
528
|
+
expect(subcommand.options).to_not be(options)
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
describe "#invoke" do
|
|
534
|
+
context "when given a valid command name" do
|
|
535
|
+
let(:command_name) { 'test2' }
|
|
536
|
+
let(:argv) { %w[--opt arg1 arg2] }
|
|
537
|
+
|
|
538
|
+
it "must call the command's #main method with the given argv" do
|
|
539
|
+
expect_any_instance_of(command_class::Test2).to receive(:main).with(argv)
|
|
540
|
+
|
|
541
|
+
subject.invoke(command_name,*argv)
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
context "when the command returns a custom exit code" do
|
|
545
|
+
let(:command_class) { TestCommands::TestCommandsWithCustomExitStatus }
|
|
546
|
+
|
|
547
|
+
it "must return the exit code" do
|
|
548
|
+
expect(subject.invoke('test')).to eq(2)
|
|
549
|
+
end
|
|
550
|
+
end
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
context "when given an unknown command name" do
|
|
554
|
+
let(:command_name) { 'xxx' }
|
|
555
|
+
let(:argv) { %w[--opt arg1 arg2] }
|
|
556
|
+
|
|
557
|
+
it "must call #on_unknown_command with the given name and argv" do
|
|
558
|
+
expect(subject).to receive(:on_unknown_command).with(command_name,argv)
|
|
559
|
+
|
|
560
|
+
subject.invoke(command_name,*argv)
|
|
561
|
+
end
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
let(:unknown_command) { 'xxx' }
|
|
566
|
+
|
|
567
|
+
describe "#command_not_found" do
|
|
568
|
+
it "must print an error message to stderr and exit with 1" do
|
|
569
|
+
expect(subject).to receive(:exit).with(1)
|
|
570
|
+
|
|
571
|
+
expect { subject.command_not_found(unknown_command) }.to output(
|
|
572
|
+
"'#{unknown_command}' is not a #{subject.command_name} command. See `#{subject.command_name} help`" + $/
|
|
573
|
+
).to_stderr
|
|
574
|
+
end
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
describe "#on_unknown_command" do
|
|
578
|
+
it "must call #command_not_found with the given command name by default" do
|
|
579
|
+
expect(subject).to receive(:command_not_found).with(unknown_command)
|
|
580
|
+
|
|
581
|
+
subject.on_unknown_command(unknown_command)
|
|
582
|
+
end
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
describe "#run" do
|
|
586
|
+
context "when a command name is the first argument" do
|
|
587
|
+
let(:command) { 'test1' }
|
|
588
|
+
let(:exit_status) { 2 }
|
|
589
|
+
|
|
590
|
+
it "must invoke the command and exit with it's status" do
|
|
591
|
+
expect(subject).to receive(:invoke).with(command).and_return(exit_status)
|
|
592
|
+
expect(subject).to receive(:exit).with(exit_status)
|
|
593
|
+
|
|
594
|
+
subject.run(command)
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
context "when additional argv is given after the command name" do
|
|
598
|
+
let(:argv) { %w[--opt arg1 arg2] }
|
|
599
|
+
|
|
600
|
+
it "must pass the additional argv to the command" do
|
|
601
|
+
expect(subject).to receive(:invoke).with(command,*argv).and_return(exit_status)
|
|
602
|
+
expect(subject).to receive(:exit).with(exit_status)
|
|
603
|
+
|
|
604
|
+
subject.run(command,*argv)
|
|
605
|
+
end
|
|
606
|
+
end
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
context "when given no arguments" do
|
|
610
|
+
it "must default to calling #help" do
|
|
611
|
+
expect(subject).to receive(:help)
|
|
612
|
+
|
|
613
|
+
subject.run()
|
|
614
|
+
end
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
describe "#option_parser" do
|
|
619
|
+
let(:command_name) { 'test1' }
|
|
620
|
+
let(:command_argv) { %w[foo bar baz] }
|
|
621
|
+
let(:argv) { [command_name, *command_argv] }
|
|
622
|
+
|
|
623
|
+
it "must stop before the first non-option argument" do
|
|
624
|
+
expect(subject.option_parser.parse(argv)).to eq(
|
|
625
|
+
[command_name, *command_argv]
|
|
626
|
+
)
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
context "when an unknown command name is given" do
|
|
630
|
+
let(:command_argv) { %w[bar baz] }
|
|
631
|
+
let(:argv) { ['foo', command_name, *command_argv] }
|
|
632
|
+
|
|
633
|
+
it "must stop before the first non-option argument" do
|
|
634
|
+
expect(subject.option_parser.parse(argv)).to eq(argv)
|
|
635
|
+
end
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
context "when additional global options are defined" do
|
|
639
|
+
let(:command_class) { TestCommands::TestCommandsWithGlobalOptions }
|
|
640
|
+
let(:bar) { '2' }
|
|
641
|
+
let(:argv) do
|
|
642
|
+
['--foo', '--bar', bar.to_s, command_name, *command_argv]
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
it "must parse the global options, but stop before the first non-option associated argument" do
|
|
646
|
+
expect(subject.option_parser.parse(argv)).to eq(
|
|
647
|
+
[command_name, *command_argv]
|
|
648
|
+
)
|
|
649
|
+
|
|
650
|
+
expect(subject.options[:foo]).to be(true)
|
|
651
|
+
expect(subject.options[:bar]).to eq(bar)
|
|
652
|
+
end
|
|
653
|
+
end
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
describe "#help_commands" do
|
|
657
|
+
it "must print usage and the list of available commands" do
|
|
658
|
+
expect { subject.help_commands }.to output(
|
|
659
|
+
[
|
|
660
|
+
"",
|
|
661
|
+
"Commands:",
|
|
662
|
+
" help",
|
|
663
|
+
" test1",
|
|
664
|
+
" test2",
|
|
665
|
+
""
|
|
666
|
+
].join($/)
|
|
667
|
+
).to_stdout
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
context "when the commands have custom names" do
|
|
671
|
+
let(:command_class) { TestCommands::TestCommandsWithExplicitNames }
|
|
672
|
+
|
|
673
|
+
it "must print the command names, not the command class'es #command_name" do
|
|
674
|
+
expect { subject.help_commands }.to output(
|
|
675
|
+
[
|
|
676
|
+
"",
|
|
677
|
+
"Commands:",
|
|
678
|
+
" command-name-1",
|
|
679
|
+
" command-name-2",
|
|
680
|
+
" help",
|
|
681
|
+
""
|
|
682
|
+
].join($/)
|
|
683
|
+
).to_stdout
|
|
684
|
+
end
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
context "when the commands have summaries" do
|
|
688
|
+
let(:command_class) { TestCommands::TestCommandsWithExplicitSummaries }
|
|
689
|
+
|
|
690
|
+
it "must print the command names and their summaries" do
|
|
691
|
+
expect { subject.help_commands }.to output(
|
|
692
|
+
[
|
|
693
|
+
"",
|
|
694
|
+
"Commands:",
|
|
695
|
+
" help",
|
|
696
|
+
" test1\t#{command_class.commands['test1'].summary}",
|
|
697
|
+
" test2\t#{command_class.commands['test2'].summary}",
|
|
698
|
+
""
|
|
699
|
+
].join($/)
|
|
700
|
+
).to_stdout
|
|
701
|
+
end
|
|
702
|
+
end
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
describe "#help" do
|
|
706
|
+
it "must print the list of available commands after other help output" do
|
|
707
|
+
expect { subject.help }.to output(
|
|
708
|
+
[
|
|
709
|
+
"Usage: #{subject.command_name} [options] [COMMAND [ARGS...]]",
|
|
710
|
+
"",
|
|
711
|
+
"Options:",
|
|
712
|
+
" -h, --help Print help information",
|
|
713
|
+
"",
|
|
714
|
+
"Commands:",
|
|
715
|
+
" help",
|
|
716
|
+
" test1",
|
|
717
|
+
" test2",
|
|
718
|
+
""
|
|
719
|
+
].join($/)
|
|
720
|
+
).to_stdout
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
context "when the command has command alises" do
|
|
724
|
+
let(:command_class) { TestCommands::TestCommandsWithAliases }
|
|
725
|
+
|
|
726
|
+
it "must print the command names, along with their command aliases" do
|
|
727
|
+
expect { subject.help }.to output(
|
|
728
|
+
[
|
|
729
|
+
"Usage: #{subject.command_name} [options] [COMMAND [ARGS...]]",
|
|
730
|
+
"",
|
|
731
|
+
"Options:",
|
|
732
|
+
" -h, --help Print help information",
|
|
733
|
+
"",
|
|
734
|
+
"Commands:",
|
|
735
|
+
" help",
|
|
736
|
+
" test1, t1",
|
|
737
|
+
" test2, t2",
|
|
738
|
+
""
|
|
739
|
+
].join($/)
|
|
740
|
+
).to_stdout
|
|
741
|
+
end
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
context "when the command defines additional global options" do
|
|
745
|
+
let(:command_class) { TestCommands::TestCommandsWithGlobalOptions }
|
|
746
|
+
|
|
747
|
+
it "must print any global options" do
|
|
748
|
+
expect { subject.help }.to output(
|
|
749
|
+
[
|
|
750
|
+
"Usage: #{subject.command_name} [options] [COMMAND [ARGS...]]",
|
|
751
|
+
"",
|
|
752
|
+
"Options:",
|
|
753
|
+
" -f, --foo Global --foo option",
|
|
754
|
+
" -b, --bar BAR Global --bar option",
|
|
755
|
+
" -h, --help Print help information",
|
|
756
|
+
"",
|
|
757
|
+
"Commands:",
|
|
758
|
+
" help",
|
|
759
|
+
" test1",
|
|
760
|
+
" test2",
|
|
761
|
+
""
|
|
762
|
+
].join($/)
|
|
763
|
+
).to_stdout
|
|
764
|
+
end
|
|
765
|
+
end
|
|
766
|
+
end
|
|
767
|
+
end
|