command_kit 0.1.0.rc1 → 0.2.2

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.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +17 -3
  3. data/.rubocop.yml +141 -0
  4. data/ChangeLog.md +102 -1
  5. data/Gemfile +3 -0
  6. data/README.md +187 -116
  7. data/Rakefile +3 -2
  8. data/command_kit.gemspec +4 -4
  9. data/examples/command.rb +1 -1
  10. data/gemspec.yml +8 -1
  11. data/lib/command_kit/arguments/argument.rb +2 -0
  12. data/lib/command_kit/arguments/argument_value.rb +2 -0
  13. data/lib/command_kit/arguments.rb +25 -6
  14. data/lib/command_kit/colors.rb +253 -45
  15. data/lib/command_kit/command.rb +6 -1
  16. data/lib/command_kit/command_name.rb +9 -0
  17. data/lib/command_kit/commands/auto_load.rb +24 -1
  18. data/lib/command_kit/commands/auto_require.rb +16 -0
  19. data/lib/command_kit/commands/command.rb +3 -0
  20. data/lib/command_kit/commands/help.rb +5 -2
  21. data/lib/command_kit/commands/parent_command.rb +7 -0
  22. data/lib/command_kit/commands/subcommand.rb +13 -1
  23. data/lib/command_kit/commands.rb +54 -9
  24. data/lib/command_kit/description.rb +12 -1
  25. data/lib/command_kit/env/home.rb +9 -0
  26. data/lib/command_kit/env/path.rb +16 -1
  27. data/lib/command_kit/env.rb +4 -0
  28. data/lib/command_kit/examples.rb +12 -1
  29. data/lib/command_kit/exception_handler.rb +4 -0
  30. data/lib/command_kit/help/man.rb +28 -31
  31. data/lib/command_kit/help.rb +7 -1
  32. data/lib/command_kit/inflector.rb +49 -17
  33. data/lib/command_kit/interactive.rb +71 -1
  34. data/lib/command_kit/main.rb +18 -9
  35. data/lib/command_kit/man.rb +44 -0
  36. data/lib/command_kit/open_app.rb +69 -0
  37. data/lib/command_kit/options/option.rb +3 -6
  38. data/lib/command_kit/options/option_value.rb +5 -2
  39. data/lib/command_kit/options/parser.rb +46 -19
  40. data/lib/command_kit/options/quiet.rb +3 -0
  41. data/lib/command_kit/options/verbose.rb +5 -0
  42. data/lib/command_kit/options/version.rb +6 -0
  43. data/lib/command_kit/options.rb +32 -7
  44. data/lib/command_kit/os/linux.rb +157 -0
  45. data/lib/command_kit/os.rb +165 -11
  46. data/lib/command_kit/package_manager.rb +200 -0
  47. data/lib/command_kit/pager.rb +73 -4
  48. data/lib/command_kit/printing/indent.rb +27 -4
  49. data/lib/command_kit/printing.rb +35 -1
  50. data/lib/command_kit/program_name.rb +7 -0
  51. data/lib/command_kit/stdio.rb +24 -0
  52. data/lib/command_kit/sudo.rb +40 -0
  53. data/lib/command_kit/terminal.rb +17 -0
  54. data/lib/command_kit/usage.rb +14 -0
  55. data/lib/command_kit/version.rb +1 -1
  56. data/lib/command_kit/xdg.rb +13 -0
  57. data/lib/command_kit.rb +1 -0
  58. data/spec/arguments/argument_spec.rb +2 -2
  59. data/spec/arguments_spec.rb +54 -28
  60. data/spec/colors_spec.rb +277 -13
  61. data/spec/command_name_spec.rb +1 -1
  62. data/spec/command_spec.rb +79 -5
  63. data/spec/commands/auto_load/subcommand_spec.rb +1 -1
  64. data/spec/commands/auto_load_spec.rb +34 -3
  65. data/spec/commands/auto_require_spec.rb +2 -2
  66. data/spec/commands/help_spec.rb +1 -1
  67. data/spec/commands/parent_command_spec.rb +1 -1
  68. data/spec/commands/subcommand_spec.rb +1 -1
  69. data/spec/commands_spec.rb +102 -30
  70. data/spec/description_spec.rb +1 -25
  71. data/spec/env/home_spec.rb +1 -1
  72. data/spec/env/path_spec.rb +7 -1
  73. data/spec/examples_spec.rb +1 -25
  74. data/spec/exception_handler_spec.rb +1 -1
  75. data/spec/help/man_spec.rb +55 -58
  76. data/spec/help_spec.rb +0 -25
  77. data/spec/inflector_spec.rb +71 -9
  78. data/spec/main_spec.rb +7 -7
  79. data/spec/man_spec.rb +46 -0
  80. data/spec/open_app_spec.rb +85 -0
  81. data/spec/options/option_spec.rb +5 -5
  82. data/spec/options/option_value_spec.rb +56 -1
  83. data/spec/options_spec.rb +283 -1
  84. data/spec/os/linux_spec.rb +164 -0
  85. data/spec/os_spec.rb +201 -14
  86. data/spec/package_manager_spec.rb +806 -0
  87. data/spec/pager_spec.rb +72 -7
  88. data/spec/printing/indent_spec.rb +8 -6
  89. data/spec/printing_spec.rb +33 -3
  90. data/spec/program_name_spec.rb +1 -1
  91. data/spec/spec_helper.rb +0 -3
  92. data/spec/sudo_spec.rb +51 -0
  93. data/spec/terminal_spec.rb +31 -1
  94. data/spec/usage_spec.rb +2 -2
  95. data/spec/xdg_spec.rb +1 -1
  96. metadata +21 -5
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'command_kit/inflector'
3
3
 
4
- describe Inflector do
4
+ describe CommandKit::Inflector do
5
5
  describe ".demodularize" do
6
6
  context "when given a single class or module name" do
7
7
  let(:name) { 'Foo' }
@@ -28,6 +28,10 @@ describe Inflector do
28
28
  end
29
29
  end
30
30
 
31
+ ascii = (0..255).map(&:chr)
32
+ alpha = ('a'..'z').to_a + ('A'..'Z').to_a
33
+ numeric = ('0'..'9').to_a
34
+
31
35
  describe ".underscore" do
32
36
  it "must convert CamelCase to camel_case" do
33
37
  expect(subject.underscore('CamelCase')).to eq('camel_case')
@@ -37,18 +41,48 @@ describe Inflector do
37
41
  expect(subject.underscore('camelCase')).to eq('camel_case')
38
42
  end
39
43
 
40
- it "must convert Camel to camel" do
41
- expect(subject.underscore('Camel')).to eq('camel')
44
+ it "must convert Camelcase to camelcase" do
45
+ expect(subject.underscore('Camelcase')).to eq('camelcase')
42
46
  end
43
47
 
44
- it "must convert CAMEL to camel" do
45
- expect(subject.underscore('CAMEL')).to eq('camel')
48
+ it "must convert CAMELCASE to camelcase" do
49
+ expect(subject.underscore('CAMELCASE')).to eq('camelcase')
46
50
  end
47
51
 
48
- context "when given a String containing '/' characters" do
49
- it "must replace dashes with underscores" do
50
- expect(subject.underscore('foo-bar')).to eq('foo_bar')
51
- end
52
+ it "must convert CAMELCase to camel_case" do
53
+ expect(subject.underscore('CAMELCase')).to eq('camel_case')
54
+ end
55
+
56
+ it "must convert CamelCASE to camel_case" do
57
+ expect(subject.underscore('CAMELCase')).to eq('camel_case')
58
+ end
59
+
60
+ it "must convert FooBARBaz to foo_bar_baz" do
61
+ expect(subject.underscore('FooBARBaz')).to eq('foo_bar_baz')
62
+ end
63
+
64
+ it "must convert foo_bar_baz to foo_bar_baz" do
65
+ expect(subject.underscore('foo_bar_baz')).to eq('foo_bar_baz')
66
+ end
67
+
68
+ it "must convert foo___bar___baz to foo___bar___baz" do
69
+ expect(subject.underscore('foo___bar___baz')).to eq('foo___bar___baz')
70
+ end
71
+
72
+ it "must convert foo-bar-baz to foo_bar_baz" do
73
+ expect(subject.underscore('foo-bar-baz')).to eq('foo_bar_baz')
74
+ end
75
+
76
+ it "must convert foo---bar---baz to foo___bar___baz" do
77
+ expect(subject.underscore('foo---bar---baz')).to eq('foo___bar___baz')
78
+ end
79
+
80
+ it "must convert foo_BAR_baz to foo_bar_baz" do
81
+ expect(subject.underscore('foo_BAR_baz')).to eq('foo_bar_baz')
82
+ end
83
+
84
+ it "must convert foo-BAR-baz to foo_bar_baz" do
85
+ expect(subject.underscore('foo-BAR-baz')).to eq('foo_bar_baz')
52
86
  end
53
87
 
54
88
  context "when given a non-String" do
@@ -56,6 +90,20 @@ describe Inflector do
56
90
  expect(subject.underscore(:CamelCase)).to eq('camel_case')
57
91
  end
58
92
  end
93
+
94
+ separators = %w[_ -]
95
+
96
+ (ascii - alpha - numeric - separators).each do |char|
97
+ context "when the given String contains a #{char.inspect} character" do
98
+ let(:string) { "Foo#{char}Bar" }
99
+
100
+ it do
101
+ expect {
102
+ subject.underscore(string)
103
+ }.to raise_error(ArgumentError,"cannot convert string to underscored: #{string.inspect}")
104
+ end
105
+ end
106
+ end
59
107
  end
60
108
 
61
109
  describe ".dasherize" do
@@ -100,5 +148,19 @@ describe Inflector do
100
148
  expect(subject.camelize(:foo_bar)).to eq('FooBar')
101
149
  end
102
150
  end
151
+
152
+ separators = %w[_ - /]
153
+
154
+ (ascii - alpha - numeric - separators).each do |char|
155
+ context "when the given String contains a #{char.inspect} character" do
156
+ let(:string) { "foo#{char}bar" }
157
+
158
+ it do
159
+ expect {
160
+ subject.camelize(string)
161
+ }.to raise_error(ArgumentError,"cannot convert string to CamelCase: #{string.inspect}")
162
+ end
163
+ end
164
+ end
103
165
  end
104
166
  end
data/spec/main_spec.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'command_kit/main'
3
3
 
4
- describe Main do
4
+ describe CommandKit::Main do
5
5
  module TestMain
6
6
  class TestCommand
7
7
  include CommandKit::Main
@@ -156,16 +156,16 @@ describe Main do
156
156
  describe "#main" do
157
157
  subject { command_class.new }
158
158
 
159
- it "must provide a default #main" do
160
- expect(subject).to respond_to(:main)
161
- end
162
-
163
159
  it "must return 0 by default" do
164
160
  expect(subject.main).to eq(0)
165
161
  end
166
162
 
167
- it "must accept arbitrary argv" do
168
- expect { subject.main([1,2,3]) }.to_not raise_error
163
+ it "must call #run with the given argv" do
164
+ argv = ["foo", "bar", "baz"]
165
+
166
+ expect(subject).to receive(:run).with(*argv)
167
+
168
+ subject.main(argv)
169
169
  end
170
170
 
171
171
  context "when #run raises SystemExit" do
data/spec/man_spec.rb ADDED
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/man'
3
+
4
+ describe CommandKit::Man do
5
+ module TestMan
6
+ class TestCommand
7
+ include CommandKit::Man
8
+ end
9
+ end
10
+
11
+ let(:command_class) { TestMan::TestCommand }
12
+
13
+ subject { command_class.new }
14
+
15
+ describe "#man" do
16
+ let(:man_page) { 'foo' }
17
+
18
+ it "must call system() with the given man page" do
19
+ expect(subject).to receive(:system).with('man',man_page)
20
+
21
+ subject.man(man_page)
22
+ end
23
+
24
+ context "when given a non-String man-page argument" do
25
+ let(:man_page_arg) { double(:non_string_arg) }
26
+
27
+ it "must call #to_s on the man-page argument" do
28
+ expect(man_page_arg).to receive(:to_s).and_return(man_page)
29
+
30
+ expect(subject).to receive(:system).with('man',man_page)
31
+
32
+ subject.man(man_page_arg)
33
+ end
34
+ end
35
+
36
+ context "when given the section: keyword argument" do
37
+ let(:section) { 7 }
38
+
39
+ it "must call system() with the given section number and man page" do
40
+ expect(subject).to receive(:system).with('man',section.to_s,man_page)
41
+
42
+ subject.man(man_page, section: section)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/open_app'
3
+
4
+ describe CommandKit::OpenApp do
5
+ module TestOpenApp
6
+ class TestCommand
7
+ include CommandKit::OpenApp
8
+ end
9
+ end
10
+
11
+ let(:command_class) { TestOpenApp::TestCommand }
12
+
13
+ subject { command_class.new }
14
+
15
+ describe "#initialize" do
16
+ context "when the OS is macOS" do
17
+ subject { command_class.new(os: :macos) }
18
+
19
+ it "must set @open_command to \"open\"" do
20
+ expect(subject.instance_variable_get("@open_command")).to eq("open")
21
+ end
22
+ end
23
+
24
+ context "when the OS is Linux" do
25
+ subject { command_class.new(os: :linux) }
26
+
27
+ it "must set @open_command to \"xdg-open\"" do
28
+ expect(subject.instance_variable_get("@open_command")).to eq("xdg-open")
29
+ end
30
+ end
31
+
32
+ context "when the OS is FreeBSD" do
33
+ subject { command_class.new(os: :freebsd) }
34
+
35
+ it "must set @open_command to \"xdg-open\"" do
36
+ expect(subject.instance_variable_get("@open_command")).to eq("xdg-open")
37
+ end
38
+ end
39
+
40
+ context "when the OS is OpenBSD" do
41
+ subject { command_class.new(os: :openbsd) }
42
+
43
+ it "must set @open_command to \"xdg-open\"" do
44
+ expect(subject.instance_variable_get("@open_command")).to eq("xdg-open")
45
+ end
46
+ end
47
+
48
+ context "when the OS is NetBSD" do
49
+ subject { command_class.new(os: :openbsd) }
50
+
51
+ it "must set @open_command to \"xdg-open\"" do
52
+ expect(subject.instance_variable_get("@open_command")).to eq("xdg-open")
53
+ end
54
+ end
55
+
56
+ context "when the OS is Windows" do
57
+ subject { command_class.new(os: :windows) }
58
+
59
+ it "must set @open_command to \"start\"" do
60
+ expect(subject.instance_variable_get("@open_command")).to eq("start")
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "#open_app_for" do
66
+ context "when @open_command is set" do
67
+ let(:file_or_uri) { "foo" }
68
+ let(:status) { true }
69
+
70
+ it "must execute the @open_command with the given URI or file" do
71
+ expect(subject).to receive(:system).with(subject.instance_variable_get("@open_command"),file_or_uri).and_return(status)
72
+
73
+ expect(subject.open_app_for(file_or_uri)).to be(status)
74
+ end
75
+ end
76
+
77
+ context "when @open_command is not set" do
78
+ before do
79
+ subject.instance_variable_set("@open_command",nil)
80
+ end
81
+
82
+ it { expect(subject.open_app_for("foo")).to be(nil) }
83
+ end
84
+ end
85
+ end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'command_kit/options/option'
3
3
 
4
- describe Options::Option do
4
+ describe CommandKit::Options::Option do
5
5
  let(:name) { :foo }
6
6
  let(:short) { nil }
7
7
  let(:long) { '--foo' }
@@ -18,12 +18,12 @@ describe Options::Option do
18
18
  end
19
19
 
20
20
  subject do
21
- described_class.new name, short: short,
21
+ described_class.new(name, short: short,
22
22
  long: long,
23
23
  equals: equals,
24
24
  desc: desc,
25
25
  value: value,
26
- &block
26
+ &block)
27
27
  end
28
28
 
29
29
  describe "#initialize" do
@@ -82,7 +82,7 @@ describe Options::Option do
82
82
  subject { described_class.new(name, value: {type: type}, desc: desc) }
83
83
 
84
84
  it "must initialize #value" do
85
- expect(subject.value).to be_kind_of(Options::OptionValue)
85
+ expect(subject.value).to be_kind_of(CommandKit::Options::OptionValue)
86
86
  expect(subject.value.type).to eq(type)
87
87
  end
88
88
  end
@@ -91,7 +91,7 @@ describe Options::Option do
91
91
  subject { described_class.new(name, value: true, desc: desc) }
92
92
 
93
93
  it "must initialize #value with defaults" do
94
- expect(subject.value).to be_kind_of(Options::OptionValue)
94
+ expect(subject.value).to be_kind_of(CommandKit::Options::OptionValue)
95
95
  expect(subject.value.type).to eq(String)
96
96
  expect(subject.value.required?).to be(true)
97
97
  expect(subject.value.usage).to eq('STR')
@@ -1,12 +1,67 @@
1
1
  require 'spec_helper'
2
2
  require 'command_kit/options/option_value'
3
3
 
4
- describe Options::OptionValue do
4
+ describe CommandKit::Options::OptionValue do
5
5
  let(:type) { Integer }
6
6
  let(:usage) { 'COUNT' }
7
7
  let(:required) { true }
8
8
  let(:default) { 1 }
9
9
 
10
+ describe ".default_usage" do
11
+ subject { described_class }
12
+
13
+ context "when given a Hash" do
14
+ let(:type) do
15
+ {'foo' => :foo, "bar" => :bar}
16
+ end
17
+
18
+ it "must join the Hash's keys with a '|' character" do
19
+ expect(subject.default_usage(type)).to eq(type.keys.join('|'))
20
+ end
21
+ end
22
+
23
+ context "when given an Array" do
24
+ let(:type) do
25
+ ['foo', 'bar', 'baz']
26
+ end
27
+
28
+ it "must join the Array's elements with a '|' character" do
29
+ expect(subject.default_usage(type)).to eq(type.join('|'))
30
+ end
31
+ end
32
+
33
+ context "when given a Regexp" do
34
+ let(:type) { /[0-9a-f]+/ }
35
+
36
+ it "must return the Regexp's source" do
37
+ expect(subject.default_usage(type)).to eq(type.source)
38
+ end
39
+ end
40
+
41
+ context "when given a Class" do
42
+ module TestOptionValue
43
+ class FooBarBaz
44
+ end
45
+ end
46
+
47
+ let(:type) { TestOptionValue::FooBarBaz }
48
+
49
+ it "must return the uppercase and underscored version of it's name" do
50
+ expect(subject.default_usage(type)).to eq("FOO_BAR_BAZ")
51
+ end
52
+ end
53
+
54
+ context "when given another kind of Object" do
55
+ let(:type) { Object.new }
56
+
57
+ it do
58
+ expect {
59
+ subject.default_usage(type)
60
+ }.to raise_error(TypeError,"unsupported option type: #{type.inspect}")
61
+ end
62
+ end
63
+ end
64
+
10
65
  describe "#initialize" do
11
66
  context "when the type: keyword is given" do
12
67
  let(:type) { Integer }
data/spec/options_spec.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'command_kit/options'
3
3
 
4
- describe Options do
4
+ describe CommandKit::Options do
5
5
  module TestOptions
6
6
  class ImplicitCmd
7
7
  include CommandKit::Options
@@ -133,5 +133,287 @@ describe Options do
133
133
  it "must initialize #options" do
134
134
  expect(subject.options).to eq({})
135
135
  end
136
+
137
+ context "when options have default values" do
138
+ module TestOptions
139
+ class TestCommandWithDefaultValues
140
+
141
+ include CommandKit::Options
142
+
143
+ option :option1, value: {
144
+ required: true,
145
+ type: String
146
+ },
147
+ desc: 'Option 1'
148
+
149
+ option :option2, value: {
150
+ required: false,
151
+ type: String,
152
+ default: "foo"
153
+ },
154
+ desc: 'Option 2'
155
+ end
156
+ end
157
+
158
+ let(:command_class) { TestOptions::TestCommandWithDefaultValues }
159
+
160
+ it "must pre-populate #options with the default values" do
161
+ expect(subject.options).to_not have_key(:option1)
162
+ expect(subject.options).to have_key(:option2)
163
+ expect(subject.options[:option2]).to eq("foo")
164
+ end
165
+ end
166
+ end
167
+
168
+ module TestOptions
169
+ class TestCommandWithOptionsAndArguments
170
+
171
+ include CommandKit::Options
172
+
173
+ usage '[OPTIONS] ARG1 [ARG2]'
174
+
175
+ option :option1, short: '-a',
176
+ value: {
177
+ type: Integer,
178
+ default: 1
179
+ },
180
+ desc: "Option 1"
181
+
182
+ option :option2, short: '-b',
183
+ value: {
184
+ type: String,
185
+ usage: 'FILE'
186
+ },
187
+ desc: "Option 2"
188
+
189
+ argument :argument1, required: true,
190
+ usage: 'ARG1',
191
+ desc: "Argument 1"
192
+
193
+ argument :argument2, required: false,
194
+ usage: 'ARG2',
195
+ desc: "Argument 2"
196
+
197
+ end
198
+ end
199
+
200
+ describe "#option_parser" do
201
+ context "when an option does not accept a value" do
202
+ module TestOptions
203
+ class TestCommandWithOptionWithoutValue
204
+
205
+ include CommandKit::Options
206
+
207
+ option :opt, desc: "Option without a value"
208
+
209
+ end
210
+ end
211
+
212
+ let(:command_class) { TestOptions::TestCommandWithOptionWithoutValue }
213
+
214
+ context "but the option flag was not given" do
215
+ let(:argv) { [] }
216
+
217
+ before { subject.option_parser.parse(argv) }
218
+
219
+ it "must not populate #options with a value" do
220
+ expect(subject.options).to be_empty
221
+ end
222
+ end
223
+
224
+ context "and the option flag was given" do
225
+ let(:argv) { %w[--opt] }
226
+
227
+ before { subject.option_parser.parse(argv) }
228
+
229
+ it "must set a key in #options to true" do
230
+ expect(subject.options[:opt]).to be(true)
231
+ end
232
+ end
233
+ end
234
+
235
+ context "when an option requires a value" do
236
+ module TestOptions
237
+ class TestCommandWithOptionWithRequiredValue
238
+
239
+ include CommandKit::Options
240
+
241
+ option :opt, value: {
242
+ required: true,
243
+ type: String
244
+ },
245
+ desc: "Option without a value"
246
+
247
+ end
248
+ end
249
+
250
+ let(:command_class) do
251
+ TestOptions::TestCommandWithOptionWithRequiredValue
252
+ end
253
+
254
+ context "but the option flag was not given" do
255
+ let(:argv) { [] }
256
+
257
+ before { subject.option_parser.parse(argv) }
258
+
259
+ it "must not populate #options with a value" do
260
+ expect(subject.options).to be_empty
261
+ end
262
+ end
263
+
264
+ context "and the option flag and value were given" do
265
+ let(:value) { 'foo' }
266
+ let(:argv) { ['--opt', value] }
267
+
268
+ before { subject.option_parser.parse(argv) }
269
+
270
+ it "must set a key in #options to the value" do
271
+ expect(subject.options[:opt]).to eq(value)
272
+ end
273
+ end
274
+ end
275
+
276
+ context "when an option does not require a value" do
277
+ module TestOptions
278
+ class TestCommandWithOptionWithOptionalValue
279
+
280
+ include CommandKit::Options
281
+
282
+ option :opt, value: {
283
+ required: false,
284
+ type: String
285
+ },
286
+ desc: "Option without a value"
287
+
288
+ end
289
+ end
290
+
291
+ let(:command_class) do
292
+ TestOptions::TestCommandWithOptionWithOptionalValue
293
+ end
294
+
295
+ context "but the option flag was not given" do
296
+ let(:argv) { [] }
297
+
298
+ before { subject.option_parser.parse(argv) }
299
+
300
+ it "must not populate #options with a value" do
301
+ expect(subject.options).to be_empty
302
+ end
303
+ end
304
+
305
+ context "and the option flag and value were given" do
306
+ let(:value) { 'foo' }
307
+ let(:argv) { ['--opt', value] }
308
+
309
+ before { subject.option_parser.parse(argv) }
310
+
311
+ it "must set a key in #options to the value" do
312
+ expect(subject.options[:opt]).to eq(value)
313
+ end
314
+ end
315
+
316
+ context "and the option has a default value" do
317
+ module TestOptions
318
+ class TestCommandWithOptionWithOptionalValueAndDefaultValue
319
+
320
+ include CommandKit::Options
321
+
322
+ option :opt, value: {
323
+ required: false,
324
+ type: String,
325
+ default: "bar"
326
+ },
327
+ desc: "Option without a value"
328
+
329
+ end
330
+ end
331
+
332
+ let(:command_class) do
333
+ TestOptions::TestCommandWithOptionWithOptionalValueAndDefaultValue
334
+ end
335
+
336
+ context "but the option flag was not given" do
337
+ let(:argv) { [] }
338
+
339
+ before { subject.option_parser.parse(argv) }
340
+
341
+ it "must set a key in #options to the default value" do
342
+ expect(subject.options[:opt]).to eq("bar")
343
+ end
344
+ end
345
+
346
+ context "and the option flag and value were given" do
347
+ let(:value) { 'foo' }
348
+ let(:argv) { ['--opt', value] }
349
+
350
+ before { subject.option_parser.parse(argv) }
351
+
352
+ it "must set a key in #options to the value" do
353
+ expect(subject.options[:opt]).to eq(value)
354
+ end
355
+ end
356
+
357
+ context "and the option flag but not the value are given" do
358
+ let(:argv) { ['--opt'] }
359
+
360
+ before { subject.option_parser.parse(argv) }
361
+
362
+ it "must set a key in #options to nil" do
363
+ expect(subject.options).to have_key(:opt)
364
+ expect(subject.options[:opt]).to be(nil)
365
+ end
366
+ end
367
+ end
368
+ end
369
+ end
370
+
371
+ describe "#main" do
372
+ let(:command_class) { TestOptions::TestCommandWithOptionsAndArguments }
373
+
374
+ let(:argv) { %w[-a 42 -b foo.txt arg1 arg2] }
375
+
376
+ it "must parse options before validating the number of arguments" do
377
+ expect {
378
+ expect(subject.main(argv)).to eq(0)
379
+ }.to_not output.to_stderr
380
+ end
381
+
382
+ context "but the wrong number of arguments are given" do
383
+ let(:argv) { %w[-a 42 -b foo.txt] }
384
+
385
+ it "must still validate the number of arguments" do
386
+ expect {
387
+ expect(subject.main(argv)).to eq(1)
388
+ }.to output("#{subject.command_name}: insufficient number of arguments.#{$/}").to_stderr
389
+ end
390
+ end
391
+ end
392
+
393
+ describe "#help" do
394
+ let(:command_class) { TestOptions::TestCommandWithOptionsAndArguments }
395
+
396
+ let(:option1) { command_class.options[:option1] }
397
+ let(:option2) { command_class.options[:option2] }
398
+ let(:argument1) { command_class.arguments[:argument1] }
399
+ let(:argument2) { command_class.arguments[:argument2] }
400
+
401
+ it "must print the usage, options and arguments" do
402
+ expect { subject.help }.to output(
403
+ [
404
+ "Usage: #{subject.usage}",
405
+ '',
406
+ 'Options:',
407
+ " #{option1.usage.join(', ').ljust(33 - 1)} #{option1.desc}",
408
+ " #{option2.usage.join(', ').ljust(33 - 1)} #{option2.desc}",
409
+ ' -h, --help Print help information',
410
+ '',
411
+ "Arguments:",
412
+ " #{argument1.usage.ljust(33)}#{argument1.desc}",
413
+ " #{argument2.usage.ljust(33)}#{argument2.desc}",
414
+ ''
415
+ ].join($/)
416
+ ).to_stdout
417
+ end
136
418
  end
137
419
  end