command_kit 0.1.0.pre1 → 0.1.0.pre2
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/README.md +37 -140
- data/examples/colors.rb +30 -0
- data/examples/command.rb +65 -0
- data/examples/pager.rb +30 -0
- data/lib/command_kit/arguments.rb +49 -18
- data/lib/command_kit/arguments/argument.rb +14 -44
- data/lib/command_kit/arguments/argument_value.rb +1 -30
- data/lib/command_kit/command.rb +45 -3
- data/lib/command_kit/commands.rb +6 -0
- data/lib/command_kit/commands/auto_load/subcommand.rb +3 -0
- data/lib/command_kit/commands/subcommand.rb +3 -0
- data/lib/command_kit/description.rb +3 -1
- data/lib/command_kit/examples.rb +3 -1
- data/lib/command_kit/help.rb +3 -0
- data/lib/command_kit/help/man.rb +74 -39
- data/lib/command_kit/main.rb +2 -0
- data/lib/command_kit/options.rb +46 -9
- data/lib/command_kit/options/option.rb +35 -5
- data/lib/command_kit/options/option_value.rb +40 -3
- data/lib/command_kit/pager.rb +6 -0
- data/lib/command_kit/version.rb +1 -1
- data/lib/command_kit/xdg.rb +8 -1
- data/spec/arguments/argument_spec.rb +3 -39
- data/spec/arguments/argument_value_spec.rb +1 -61
- data/spec/arguments_spec.rb +7 -0
- data/spec/help/man_spec.rb +348 -0
- data/spec/options/option_spec.rb +45 -6
- data/spec/options/option_value_spec.rb +52 -3
- data/spec/pager_spec.rb +2 -4
- metadata +10 -8
- data/lib/command_kit/arguments/usage.rb +0 -6
- data/lib/command_kit/options/usage.rb +0 -6
data/spec/arguments_spec.rb
CHANGED
@@ -10,6 +10,13 @@ describe Arguments do
|
|
10
10
|
|
11
11
|
let(:command_class) { TestArguments::ImplicitCmd }
|
12
12
|
|
13
|
+
describe ".included" do
|
14
|
+
subject { command_class }
|
15
|
+
|
16
|
+
it { expect(subject).to include(Main) }
|
17
|
+
it { expect(subject).to include(Help) }
|
18
|
+
end
|
19
|
+
|
13
20
|
describe ".arguments" do
|
14
21
|
subject { TestArguments::ImplicitCmd }
|
15
22
|
|
@@ -0,0 +1,348 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'command_kit/help/man'
|
3
|
+
|
4
|
+
describe Help::Man do
|
5
|
+
module TestHelpMan
|
6
|
+
class TestCommand
|
7
|
+
include CommandKit::Help::Man
|
8
|
+
|
9
|
+
man_dir "#{__dir__}/fixtures/man"
|
10
|
+
end
|
11
|
+
|
12
|
+
class TestCommandWithManPage
|
13
|
+
include CommandKit::Help::Man
|
14
|
+
|
15
|
+
man_dir "#{__dir__}/fixtures/man"
|
16
|
+
man_page 'foo.1'
|
17
|
+
end
|
18
|
+
|
19
|
+
class EmptyCommand
|
20
|
+
include CommandKit::Help::Man
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:command_class) { TestHelpMan::TestCommand }
|
25
|
+
|
26
|
+
describe ".included" do
|
27
|
+
subject { command_class }
|
28
|
+
|
29
|
+
it "must include CommandName" do
|
30
|
+
expect(subject).to include(CommandKit::CommandName)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "must include Help" do
|
34
|
+
expect(subject).to include(CommandKit::Help)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "must include Stdio" do
|
38
|
+
expect(subject).to include(CommandKit::Stdio)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe ".man_dir" do
|
43
|
+
context "when no man_dir have been set" do
|
44
|
+
subject { TestHelpMan::EmptyCommand }
|
45
|
+
|
46
|
+
it "should default to nil" do
|
47
|
+
expect(subject.man_dir).to be_nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when a man_dir is explicitly set" do
|
52
|
+
subject { TestHelpMan::TestCommand }
|
53
|
+
|
54
|
+
it "must return the explicitly set man_dir" do
|
55
|
+
expect(subject.man_dir).to eq(File.expand_path('../fixtures/man',__FILE__))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "when the command class inherites from another class" do
|
60
|
+
context "but no man_dir is set" do
|
61
|
+
module TestHelpMan
|
62
|
+
class BaseCmd
|
63
|
+
include CommandKit::Help::Man
|
64
|
+
end
|
65
|
+
|
66
|
+
class InheritedCmd < BaseCmd
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
subject { TestHelpMan::InheritedCmd }
|
71
|
+
|
72
|
+
it "must search each class then return nil "do
|
73
|
+
expect(subject.man_dir).to be_nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
module TestHelpMan
|
78
|
+
class ExplicitBaseCmd
|
79
|
+
include CommandKit::Help::Man
|
80
|
+
|
81
|
+
man_dir 'set/in/baseclass'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "when the superclass defines an explicit man_dir" do
|
86
|
+
module TestHelpMan
|
87
|
+
class ImplicitInheritedCmd < ExplicitBaseCmd
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
let(:super_subject) { TestHelpMan::ExplicitBaseCmd }
|
92
|
+
subject { TestHelpMan::ImplicitInheritedCmd }
|
93
|
+
|
94
|
+
it "must inherit the superclass'es man_dir" do
|
95
|
+
expect(subject.man_dir).to eq(super_subject.man_dir)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "must not change the superclass'es man_dir" do
|
99
|
+
expect(super_subject.man_dir).to eq('set/in/baseclass')
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "when the subclass defines an explicit man_dir" do
|
104
|
+
module TestHelpMan
|
105
|
+
class ImplicitBaseCmd
|
106
|
+
include CommandKit::Help::Man
|
107
|
+
end
|
108
|
+
|
109
|
+
class ExplicitInheritedCmd < ImplicitBaseCmd
|
110
|
+
man_dir 'set/in/subclass'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
let(:super_subject) { TestHelpMan::ImplicitBaseCmd }
|
115
|
+
subject { TestHelpMan::ExplicitInheritedCmd }
|
116
|
+
|
117
|
+
it "must return the subclass'es man_dir" do
|
118
|
+
expect(subject.man_dir).to eq('set/in/subclass')
|
119
|
+
end
|
120
|
+
|
121
|
+
it "must not change the superclass'es man_dir" do
|
122
|
+
expect(super_subject.man_dir).to be_nil
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "when both the subclass overrides the superclass's man_dirs" do
|
127
|
+
module TestHelpMan
|
128
|
+
class ExplicitOverridingInheritedCmd < ExplicitBaseCmd
|
129
|
+
man_dir 'set/in/subclass'
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
let(:super_subject) { TestHelpMan::ExplicitBaseCmd }
|
134
|
+
subject { TestHelpMan::ExplicitOverridingInheritedCmd }
|
135
|
+
|
136
|
+
it "must return the subclass'es man_dir" do
|
137
|
+
expect(subject.man_dir).to eq('set/in/subclass')
|
138
|
+
end
|
139
|
+
|
140
|
+
it "must not change the superclass'es man_dir" do
|
141
|
+
expect(super_subject.man_dir).to eq('set/in/baseclass')
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe ".man_page" do
|
148
|
+
context "when no man_page has been set" do
|
149
|
+
subject { TestHelpMan::TestCommand }
|
150
|
+
|
151
|
+
it "should default to \"\#{command_name}.1\"" do
|
152
|
+
expect(subject.man_page).to eq("#{subject.command_name}.1")
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context "when a man_page is explicitly set" do
|
157
|
+
module TestHelpMan
|
158
|
+
class ExplicitCmd
|
159
|
+
include CommandKit::Help::Man
|
160
|
+
man_page 'explicit.1'
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
subject { TestHelpMan::ExplicitCmd }
|
165
|
+
|
166
|
+
it "must return the explicitly set man_page" do
|
167
|
+
expect(subject.man_page).to eq('explicit.1')
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context "when the command class inherites from another class" do
|
172
|
+
module TestHelpMan
|
173
|
+
class BaseCmd
|
174
|
+
include CommandKit::Help::Man
|
175
|
+
end
|
176
|
+
|
177
|
+
class InheritedCmd < BaseCmd
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
subject { TestHelpMan::InheritedCmd }
|
182
|
+
|
183
|
+
it "should underscore the class'es name" do
|
184
|
+
expect(subject.man_page).to eq('inherited_cmd.1')
|
185
|
+
end
|
186
|
+
|
187
|
+
context "when the superclass defines an explicit man_page" do
|
188
|
+
module TestHelpMan
|
189
|
+
class ExplicitBaseCmd
|
190
|
+
include CommandKit::Help::Man
|
191
|
+
man_page 'explicit.1'
|
192
|
+
end
|
193
|
+
|
194
|
+
class ImplicitInheritedCmd < ExplicitBaseCmd
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
let(:super_subject) { TestHelpMan::ExplicitBaseCmd }
|
199
|
+
subject { TestHelpMan::ImplicitInheritedCmd }
|
200
|
+
|
201
|
+
it "must return the subclass'es man_page, not the superclass'es" do
|
202
|
+
expect(subject.man_page).to eq('implicit_inherited_cmd.1')
|
203
|
+
end
|
204
|
+
|
205
|
+
it "must not change the superclass'es man_page" do
|
206
|
+
expect(super_subject.man_page).to eq('explicit.1')
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
context "when the subclass defines an explicit man_page" do
|
211
|
+
module TestHelpMan
|
212
|
+
class ImplicitBaseCmd
|
213
|
+
include CommandKit::Help::Man
|
214
|
+
end
|
215
|
+
|
216
|
+
class ExplicitInheritedCmd < ImplicitBaseCmd
|
217
|
+
man_page 'explicit.1'
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
let(:super_subject) { TestHelpMan::ImplicitBaseCmd }
|
222
|
+
subject { TestHelpMan::ExplicitInheritedCmd }
|
223
|
+
|
224
|
+
it "must return the subclass'es man_page, not the superclass'es" do
|
225
|
+
expect(subject.man_page).to eq('explicit.1')
|
226
|
+
end
|
227
|
+
|
228
|
+
it "must not change the superclass'es man_page" do
|
229
|
+
expect(super_subject.man_page).to eq('implicit_base_cmd.1')
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
subject { command_class.new }
|
236
|
+
|
237
|
+
describe "#man" do
|
238
|
+
let(:man_page) { 'foo' }
|
239
|
+
|
240
|
+
it "must call system() with the given man page" do
|
241
|
+
expect(subject).to receive(:system).with('man',man_page)
|
242
|
+
|
243
|
+
subject.man(man_page)
|
244
|
+
end
|
245
|
+
|
246
|
+
context "when given a non-String man-page argument" do
|
247
|
+
let(:man_page_arg) { double(:non_string_arg) }
|
248
|
+
|
249
|
+
it "must call #to_s on the man-page argument" do
|
250
|
+
expect(man_page_arg).to receive(:to_s).and_return(man_page)
|
251
|
+
|
252
|
+
expect(subject).to receive(:system).with('man',man_page)
|
253
|
+
|
254
|
+
subject.man(man_page_arg)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
context "when given the section: keyword argument" do
|
259
|
+
let(:section) { 7 }
|
260
|
+
|
261
|
+
it "must call system() with the given section number and man page" do
|
262
|
+
expect(subject).to receive(:system).with('man',section.to_s,man_page)
|
263
|
+
|
264
|
+
subject.man(man_page, section: section)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
describe "#help_man" do
|
270
|
+
context "when .man_dir is not set" do
|
271
|
+
let(:command_class) { TestHelpMan::EmptyCommand }
|
272
|
+
subject { command_class.new }
|
273
|
+
|
274
|
+
it do
|
275
|
+
expect { subject.help_man }.to raise_error(NotImplementedError)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
let(:man_page_path) do
|
280
|
+
File.join(subject.class.man_dir,subject.class.man_page)
|
281
|
+
end
|
282
|
+
|
283
|
+
it "must open the .man_page within the .man_dir" do
|
284
|
+
expect(subject).to receive(:system).with('man',man_page_path)
|
285
|
+
|
286
|
+
subject.help_man
|
287
|
+
end
|
288
|
+
|
289
|
+
context "when given a custom man page" do
|
290
|
+
let(:man_page) { 'bar.1' }
|
291
|
+
let(:man_page_path) { File.join(subject.class.man_dir,man_page) }
|
292
|
+
|
293
|
+
it "must open the custom man-page within the .man_dir" do
|
294
|
+
expect(subject).to receive(:system).with('man',man_page_path)
|
295
|
+
|
296
|
+
subject.help_man(man_page)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
describe "#help" do
|
302
|
+
let(:normal_help_output) do
|
303
|
+
stdout = StringIO.new
|
304
|
+
|
305
|
+
command_class.new(stdout: stdout).tap do |command|
|
306
|
+
command.method(:help).super_method.call
|
307
|
+
end
|
308
|
+
|
309
|
+
stdout.string
|
310
|
+
end
|
311
|
+
|
312
|
+
let(:stdout) { StringIO.new }
|
313
|
+
|
314
|
+
subject { command_class.new(stdout: stdout) }
|
315
|
+
|
316
|
+
context "when stdout is a TTY" do
|
317
|
+
before do
|
318
|
+
expect(subject.stdout).to receive(:tty?).and_return(true)
|
319
|
+
end
|
320
|
+
|
321
|
+
it "must open the command's man-page" do
|
322
|
+
expect(subject).to receive(:help_man).and_return(true)
|
323
|
+
|
324
|
+
subject.help
|
325
|
+
end
|
326
|
+
|
327
|
+
context "but when the man command is not installed" do
|
328
|
+
before do
|
329
|
+
expect(subject).to receive(:help_man).and_return(nil)
|
330
|
+
end
|
331
|
+
|
332
|
+
it "must call the super help() method" do
|
333
|
+
subject.help
|
334
|
+
|
335
|
+
expect(subject.stdout.string).to eq(normal_help_output)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
context "when stdout is not a TTY" do
|
341
|
+
it "must call the super help() method" do
|
342
|
+
subject.help
|
343
|
+
|
344
|
+
expect(stdout.string).to eq(normal_help_output)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
data/spec/options/option_spec.rb
CHANGED
@@ -65,22 +65,61 @@ describe Options::Option do
|
|
65
65
|
subject { described_class.new(name, equals: equals, desc: desc) }
|
66
66
|
|
67
67
|
it "must set #equals" do
|
68
|
-
expect(subject.equals).to eq(equals)
|
68
|
+
expect(subject.equals?).to eq(equals)
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
72
|
context "when the equals: keyword is not given" do
|
73
73
|
subject { described_class.new(name, desc: desc) }
|
74
74
|
|
75
|
-
it { expect(subject.equals).to be(false) }
|
75
|
+
it { expect(subject.equals?).to be(false) }
|
76
76
|
end
|
77
77
|
|
78
78
|
context "when the values: keyword is given" do
|
79
|
-
|
80
|
-
|
79
|
+
context "and it is a Hash" do
|
80
|
+
let(:type) { Integer }
|
81
|
+
|
82
|
+
subject { described_class.new(name, value: {type: type}, desc: desc) }
|
83
|
+
|
84
|
+
it "must initialize #value" do
|
85
|
+
expect(subject.value).to be_kind_of(Options::OptionValue)
|
86
|
+
expect(subject.value.type).to eq(type)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "and it is true" do
|
91
|
+
subject { described_class.new(name, value: true, desc: desc) }
|
92
|
+
|
93
|
+
it "must initialize #value with defaults" do
|
94
|
+
expect(subject.value).to be_kind_of(Options::OptionValue)
|
95
|
+
expect(subject.value.type).to eq(String)
|
96
|
+
expect(subject.value.required?).to be(true)
|
97
|
+
expect(subject.value.usage).to eq('STR')
|
98
|
+
end
|
99
|
+
end
|
81
100
|
|
82
|
-
|
83
|
-
|
101
|
+
context "and it is false" do
|
102
|
+
subject { described_class.new(name, value: false, desc: desc) }
|
103
|
+
|
104
|
+
it "must not initialize #value" do
|
105
|
+
expect(subject.value).to be(nil)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "and it is nil" do
|
110
|
+
subject { described_class.new(name, value: nil, desc: desc) }
|
111
|
+
|
112
|
+
it "must not initialize #value" do
|
113
|
+
expect(subject.value).to be(nil)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "when it is another type" do
|
118
|
+
it do
|
119
|
+
expect {
|
120
|
+
described_class.new(name, value: 'foo', desc: desc)
|
121
|
+
}.to raise_error(TypeError)
|
122
|
+
end
|
84
123
|
end
|
85
124
|
end
|
86
125
|
|
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
require 'command_kit/options/option_value'
|
3
3
|
|
4
4
|
describe Options::OptionValue do
|
5
|
-
let(:type)
|
5
|
+
let(:type) { Integer }
|
6
6
|
let(:usage) { 'COUNT' }
|
7
7
|
let(:required) { true }
|
8
8
|
let(:default) { 1 }
|
@@ -26,6 +26,22 @@ describe Options::OptionValue do
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
+
context "when the default: keyword is given" do
|
30
|
+
subject { described_class.new(default: default) }
|
31
|
+
|
32
|
+
it "must set #default" do
|
33
|
+
expect(subject.default).to eq(default)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when the default: keyword is not given" do
|
38
|
+
subject { described_class.new() }
|
39
|
+
|
40
|
+
it "default #default to String" do
|
41
|
+
expect(subject.default).to eq(nil)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
29
45
|
context "when the usage: keyword is given" do
|
30
46
|
let(:usage) { 'FOO' }
|
31
47
|
|
@@ -45,11 +61,20 @@ describe Options::OptionValue do
|
|
45
61
|
end
|
46
62
|
end
|
47
63
|
|
64
|
+
subject do
|
65
|
+
described_class.new(
|
66
|
+
type: type,
|
67
|
+
usage: usage,
|
68
|
+
required: required,
|
69
|
+
default: default
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
48
73
|
describe "#usage" do
|
49
74
|
let(:usage) { 'FOO' }
|
50
75
|
|
51
76
|
context "when #optional? is true" do
|
52
|
-
|
77
|
+
let(:required) { false }
|
53
78
|
|
54
79
|
it "must wrap the usage within [ ] brackets" do
|
55
80
|
expect(subject.usage).to eq("[#{usage}]")
|
@@ -57,11 +82,35 @@ describe Options::OptionValue do
|
|
57
82
|
end
|
58
83
|
|
59
84
|
context "when #optional? is false" do
|
60
|
-
|
85
|
+
let(:required) { true }
|
61
86
|
|
62
87
|
it "should return the usage string unchanged" do
|
63
88
|
expect(subject.usage).to eq(usage)
|
64
89
|
end
|
65
90
|
end
|
66
91
|
end
|
92
|
+
|
93
|
+
describe "#default_value" do
|
94
|
+
context "when initialized with a default: that responded to #call" do
|
95
|
+
let(:default) do
|
96
|
+
->{ [] }
|
97
|
+
end
|
98
|
+
|
99
|
+
it "must call the default #call method" do
|
100
|
+
expect(subject.default_value).to eq(default.call)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "when initialized with a default: that it's an Object" do
|
105
|
+
let(:default) { "" }
|
106
|
+
|
107
|
+
it "must return the default: object" do
|
108
|
+
expect(subject.default_value).to eq(default)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "must duplicate the default: object each time" do
|
112
|
+
expect(subject.default_value).to_not be(subject.default_value)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
67
116
|
end
|