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.
Files changed (98) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.github/workflows/ruby.yml +29 -0
  4. data/.gitignore +7 -0
  5. data/.rspec +1 -0
  6. data/.yardopts +1 -0
  7. data/ChangeLog.md +29 -0
  8. data/Gemfile +14 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +283 -0
  11. data/Rakefile +23 -0
  12. data/command_kit.gemspec +60 -0
  13. data/gemspec.yml +14 -0
  14. data/lib/command_kit.rb +1 -0
  15. data/lib/command_kit/arguments.rb +161 -0
  16. data/lib/command_kit/arguments/argument.rb +111 -0
  17. data/lib/command_kit/arguments/argument_value.rb +81 -0
  18. data/lib/command_kit/arguments/usage.rb +6 -0
  19. data/lib/command_kit/colors.rb +355 -0
  20. data/lib/command_kit/command.rb +42 -0
  21. data/lib/command_kit/command_name.rb +95 -0
  22. data/lib/command_kit/commands.rb +299 -0
  23. data/lib/command_kit/commands/auto_load.rb +153 -0
  24. data/lib/command_kit/commands/auto_load/subcommand.rb +90 -0
  25. data/lib/command_kit/commands/auto_require.rb +138 -0
  26. data/lib/command_kit/commands/command.rb +12 -0
  27. data/lib/command_kit/commands/help.rb +43 -0
  28. data/lib/command_kit/commands/parent_command.rb +21 -0
  29. data/lib/command_kit/commands/subcommand.rb +51 -0
  30. data/lib/command_kit/console.rb +141 -0
  31. data/lib/command_kit/description.rb +89 -0
  32. data/lib/command_kit/env.rb +43 -0
  33. data/lib/command_kit/env/home.rb +71 -0
  34. data/lib/command_kit/env/path.rb +71 -0
  35. data/lib/command_kit/examples.rb +99 -0
  36. data/lib/command_kit/exception_handler.rb +55 -0
  37. data/lib/command_kit/help.rb +62 -0
  38. data/lib/command_kit/help/man.rb +125 -0
  39. data/lib/command_kit/inflector.rb +84 -0
  40. data/lib/command_kit/main.rb +103 -0
  41. data/lib/command_kit/options.rb +179 -0
  42. data/lib/command_kit/options/option.rb +171 -0
  43. data/lib/command_kit/options/option_value.rb +90 -0
  44. data/lib/command_kit/options/parser.rb +227 -0
  45. data/lib/command_kit/options/quiet.rb +53 -0
  46. data/lib/command_kit/options/usage.rb +6 -0
  47. data/lib/command_kit/options/verbose.rb +55 -0
  48. data/lib/command_kit/options/version.rb +62 -0
  49. data/lib/command_kit/os.rb +47 -0
  50. data/lib/command_kit/pager.rb +115 -0
  51. data/lib/command_kit/printing.rb +32 -0
  52. data/lib/command_kit/printing/indent.rb +78 -0
  53. data/lib/command_kit/program_name.rb +57 -0
  54. data/lib/command_kit/stdio.rb +138 -0
  55. data/lib/command_kit/usage.rb +102 -0
  56. data/lib/command_kit/version.rb +4 -0
  57. data/lib/command_kit/xdg.rb +138 -0
  58. data/spec/arguments/argument_spec.rb +169 -0
  59. data/spec/arguments/argument_value_spec.rb +126 -0
  60. data/spec/arguments_spec.rb +213 -0
  61. data/spec/colors_spec.rb +470 -0
  62. data/spec/command_kit_spec.rb +8 -0
  63. data/spec/command_name_spec.rb +130 -0
  64. data/spec/command_spec.rb +49 -0
  65. data/spec/commands/auto_load/subcommand_spec.rb +82 -0
  66. data/spec/commands/auto_load_spec.rb +128 -0
  67. data/spec/commands/auto_require_spec.rb +142 -0
  68. data/spec/commands/fixtures/test_auto_load/cli/commands/test1.rb +10 -0
  69. data/spec/commands/fixtures/test_auto_load/cli/commands/test2.rb +10 -0
  70. data/spec/commands/fixtures/test_auto_require/lib/test_auto_require/cli/commands/test1.rb +10 -0
  71. data/spec/commands/help_spec.rb +66 -0
  72. data/spec/commands/parent_command_spec.rb +40 -0
  73. data/spec/commands/subcommand_spec.rb +99 -0
  74. data/spec/commands_spec.rb +767 -0
  75. data/spec/console_spec.rb +201 -0
  76. data/spec/description_spec.rb +203 -0
  77. data/spec/env/home_spec.rb +46 -0
  78. data/spec/env/path_spec.rb +78 -0
  79. data/spec/env_spec.rb +123 -0
  80. data/spec/examples_spec.rb +235 -0
  81. data/spec/exception_handler_spec.rb +103 -0
  82. data/spec/help_spec.rb +119 -0
  83. data/spec/inflector_spec.rb +104 -0
  84. data/spec/main_spec.rb +179 -0
  85. data/spec/options/option_spec.rb +258 -0
  86. data/spec/options/option_value_spec.rb +67 -0
  87. data/spec/options/parser_spec.rb +265 -0
  88. data/spec/options_spec.rb +137 -0
  89. data/spec/os_spec.rb +46 -0
  90. data/spec/pager_spec.rb +154 -0
  91. data/spec/printing/indent_spec.rb +130 -0
  92. data/spec/printing_spec.rb +76 -0
  93. data/spec/program_name_spec.rb +62 -0
  94. data/spec/spec_helper.rb +6 -0
  95. data/spec/stdio_spec.rb +264 -0
  96. data/spec/usage_spec.rb +237 -0
  97. data/spec/xdg_spec.rb +191 -0
  98. metadata +156 -0
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/options/option_value'
3
+
4
+ describe Options::OptionValue do
5
+ let(:type) { Integer }
6
+ let(:usage) { 'COUNT' }
7
+ let(:required) { true }
8
+ let(:default) { 1 }
9
+
10
+ describe "#initialize" do
11
+ context "when the type: keyword is given" do
12
+ let(:type) { Integer }
13
+
14
+ subject { described_class.new(type: type) }
15
+
16
+ it "must default #type to String" do
17
+ expect(subject.type).to eq(type)
18
+ end
19
+ end
20
+
21
+ context "when the type: keyword is not given" do
22
+ subject { described_class.new }
23
+
24
+ it "must default #type to String" do
25
+ expect(subject.type).to eq(String)
26
+ end
27
+ end
28
+
29
+ context "when the usage: keyword is given" do
30
+ let(:usage) { 'FOO' }
31
+
32
+ subject { described_class.new(usage: usage) }
33
+
34
+ it "must default #usage to String" do
35
+ expect(subject.usage).to eq(usage)
36
+ end
37
+ end
38
+
39
+ context "when the usage: keyword is not given" do
40
+ subject { described_class.new }
41
+
42
+ it "must default #usage based on #type" do
43
+ expect(subject.usage).to eq(described_class.default_usage(subject.type))
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "#usage" do
49
+ let(:usage) { 'FOO' }
50
+
51
+ context "when #optional? is true" do
52
+ subject { described_class.new(usage: usage, required: false) }
53
+
54
+ it "must wrap the usage within [ ] brackets" do
55
+ expect(subject.usage).to eq("[#{usage}]")
56
+ end
57
+ end
58
+
59
+ context "when #optional? is false" do
60
+ subject { described_class.new(usage: usage, required: true) }
61
+
62
+ it "should return the usage string unchanged" do
63
+ expect(subject.usage).to eq(usage)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,265 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/options/parser'
3
+
4
+ describe CommandKit::Options::Parser do
5
+ module TestOptionsParser
6
+ class TestCommand
7
+ include CommandKit::Options::Parser
8
+
9
+ command_name 'cmd'
10
+
11
+ end
12
+ end
13
+
14
+ let(:command_class) { TestOptionsParser::TestCommand }
15
+
16
+ describe ".included" do
17
+ subject { command_class }
18
+
19
+ it { expect(subject).to include(CommandKit::Main) }
20
+ it { expect(subject).to include(CommandKit::Usage) }
21
+ it { expect(subject.usage).to eq('[options]') }
22
+ end
23
+
24
+ subject { command_class.new }
25
+
26
+ describe "#initialize" do
27
+ it "must initialize #option_parser" do
28
+ expect(subject.option_parser).to be_kind_of(OptionParser)
29
+ end
30
+ end
31
+
32
+ describe "#option_parser" do
33
+ it "must have a default #banner" do
34
+ expect(subject.option_parser.banner).to eq("Usage: #{subject.usage}")
35
+ end
36
+
37
+ it "must include a 'Options:' separator" do
38
+ expect(subject.option_parser.to_s).to include(
39
+ [
40
+ '',
41
+ 'Options:',
42
+ ''
43
+ ].join($/)
44
+ )
45
+ end
46
+
47
+ it "must define a default --help option" do
48
+ expect(subject.option_parser.to_s).to include(
49
+ [
50
+ '',
51
+ ' -h, --help Print help information',
52
+ ''
53
+ ].join($/)
54
+ )
55
+ end
56
+ end
57
+
58
+ module TestOptionsParser
59
+ class CommandWithOptions
60
+
61
+ include CommandKit::Options::Parser
62
+
63
+ command_name 'cmd'
64
+
65
+ attr_reader :foo
66
+
67
+ attr_reader :bar
68
+
69
+ attr_reader :argv
70
+
71
+ def initialize(**kwargs)
72
+ super(**kwargs)
73
+
74
+ @option_parser.on('-f','--foo','Foo option') do
75
+ @foo = true
76
+ end
77
+
78
+ @option_parser.on('-b','--bar BAR',Integer,'Bar option') do |bar|
79
+ @bar = bar
80
+ end
81
+ end
82
+
83
+ def run(*argv)
84
+ @argv = argv
85
+ end
86
+
87
+ end
88
+ end
89
+
90
+ let(:command_class) { TestOptionsParser::CommandWithOptions }
91
+ subject { command_class.new }
92
+
93
+ describe "#option_parser" do
94
+ it { expect(subject.option_parser).to be_kind_of(OptionParser) }
95
+ end
96
+
97
+ describe "#main" do
98
+ context "when --help is given as an argument" do
99
+ it "must call #help" do
100
+ expect(subject).to receive(:help)
101
+
102
+ subject.main(['--help'])
103
+ end
104
+
105
+ it "must return 0" do
106
+ allow(subject).to receive(:help)
107
+
108
+ expect(subject.main(['--help'])).to eq(0)
109
+ end
110
+
111
+ it "must stop parsing options" do
112
+ allow(subject).to receive(:help)
113
+ subject.main(['--help', '--foo', '1'])
114
+
115
+ expect(subject.foo).to be(nil)
116
+ expect(subject.argv).to be(nil)
117
+ end
118
+ end
119
+ end
120
+
121
+ describe "#parse_options" do
122
+ it "must parse options and return any additional arguments" do
123
+ additional_args = %w[arg1 arg2]
124
+ bar = 2
125
+ argv = ['--foo', '--bar', bar.to_s, *additional_args]
126
+
127
+ expect(subject.parse_options(argv)).to eq(additional_args)
128
+ expect(subject.foo).to eq(true)
129
+ expect(subject.bar).to eq(bar)
130
+ end
131
+
132
+ context "when -h,--help is passed" do
133
+ it "must call #help and exit with 0" do
134
+ expect(subject).to receive(:help)
135
+ expect(subject).to receive(:exit).with(0)
136
+
137
+ subject.parse_options(['--help'])
138
+ end
139
+ end
140
+
141
+ context "when an invalid option is given" do
142
+ it "must call #on_invalid_option" do
143
+ expect(subject).to receive(:on_invalid_option).with(OptionParser::InvalidOption)
144
+
145
+ subject.parse_options(['--xxx'])
146
+ end
147
+ end
148
+
149
+ context "when an invalid argument is given" do
150
+ it "must call #on_invalid_argument" do
151
+ expect(subject).to receive(:on_invalid_argument).with(OptionParser::InvalidArgument)
152
+
153
+ subject.parse_options(['--bar', 'xxx'])
154
+ end
155
+ end
156
+
157
+ context "when a required argument is missing" do
158
+ it "must call #on_missing_argument" do
159
+ expect(subject).to receive(:on_missing_argument).with(OptionParser::MissingArgument)
160
+
161
+ subject.parse_options(['--bar'])
162
+ end
163
+ end
164
+
165
+ context "when a needless argument is given" do
166
+ it "must call #on_needless_argument" do
167
+ expect(subject).to receive(:on_needless_argument).with(OptionParser::NeedlessArgument)
168
+
169
+ subject.parse_options(['--foo=xxx'])
170
+ end
171
+ end
172
+ end
173
+
174
+ describe "#on_parse_error" do
175
+ let(:error) { OptionParser::InvalidOption.new("--xxx") }
176
+
177
+ it "must print an error message and exit with 1" do
178
+ expect(subject).to receive(:exit).with(1)
179
+
180
+ expect { subject.on_parse_error(error) }.to output(
181
+ [
182
+ "#{subject.command_name}: #{error.message}",
183
+ "Try '#{subject.command_name} --help' for more information.",
184
+ ''
185
+ ].join($/)
186
+ ).to_stderr
187
+ end
188
+ end
189
+
190
+ describe "#on_invalid_option" do
191
+ let(:error) { OptionParser::InvalidOption.new('--xxx') }
192
+
193
+ it "must call #on_parse_error by default" do
194
+ expect(subject).to receive(:on_parse_error).with(error)
195
+
196
+ subject.on_invalid_option(error)
197
+ end
198
+ end
199
+
200
+ describe "#on_ambiguous_option" do
201
+ let(:error) { OptionParser::AmbiguousOption.new('--xxx') }
202
+
203
+ it "must call #on_parse_error by default" do
204
+ expect(subject).to receive(:on_parse_error).with(error)
205
+
206
+ subject.on_ambiguous_option(error)
207
+ end
208
+ end
209
+
210
+ describe "#on_invalid_argument" do
211
+ let(:error) { OptionParser::InvalidArgument.new('--xxx') }
212
+
213
+ it "must call #on_parse_error by default" do
214
+ expect(subject).to receive(:on_parse_error).with(error)
215
+
216
+ subject.on_invalid_argument(error)
217
+ end
218
+ end
219
+
220
+ describe "#on_missing_argument" do
221
+ let(:error) { OptionParser::MissingArgument.new('--xxx') }
222
+
223
+ it "must call #on_parse_error by default" do
224
+ expect(subject).to receive(:on_parse_error).with(error)
225
+
226
+ subject.on_missing_argument(error)
227
+ end
228
+ end
229
+
230
+ describe "#on_needless_argument" do
231
+ let(:error) { OptionParser::NeedlessArgument.new('--xxx') }
232
+
233
+ it "must call #on_parse_error by default" do
234
+ expect(subject).to receive(:on_parse_error).with(error)
235
+
236
+ subject.on_needless_argument(error)
237
+ end
238
+ end
239
+
240
+ describe "#on_ambiguous_argument" do
241
+ let(:error) { OptionParser::AmbiguousArgument.new('--xxx') }
242
+
243
+ it "must call #on_parse_error by default" do
244
+ expect(subject).to receive(:on_parse_error).with(error)
245
+
246
+ subject.on_ambiguous_argument(error)
247
+ end
248
+ end
249
+
250
+ describe "#help_options" do
251
+ it "must print the #option_parser" do
252
+ expect { subject.help_options }.to output(
253
+ subject.option_parser.to_s
254
+ ).to_stdout
255
+ end
256
+ end
257
+
258
+ describe "#help" do
259
+ it "must call #help_options" do
260
+ expect(subject).to receive(:help_options)
261
+
262
+ subject.help
263
+ end
264
+ end
265
+ end
@@ -0,0 +1,137 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/options'
3
+
4
+ describe Options do
5
+ module TestOptions
6
+ class ImplicitCmd
7
+ include CommandKit::Options
8
+ end
9
+ end
10
+
11
+ let(:command_class) { TestOptions::ImplicitCmd }
12
+ subject { command_class.new }
13
+
14
+ describe ".options" do
15
+ subject { TestOptions::ImplicitCmd }
16
+
17
+ context "when no options have been defined" do
18
+ it "should default to {}" do
19
+ expect(subject.options).to eq({})
20
+ end
21
+ end
22
+
23
+ context "when a options is explicitly set" do
24
+ module TestOptions
25
+ class ExplicitCmd
26
+ include CommandKit::Options
27
+ option :foo, desc: 'Foo option'
28
+ option :bar, desc: 'Bar option'
29
+ end
30
+ end
31
+
32
+ subject { TestOptions::ExplicitCmd }
33
+
34
+ it "must return the explicitly set options" do
35
+ expect(subject.options.keys).to eq([:foo, :bar])
36
+ end
37
+ end
38
+
39
+ context "when the command class inherites from another class" do
40
+ context "but no options are defined" do
41
+ module TestOptions
42
+ class BaseCmd
43
+ include CommandKit::Options
44
+ end
45
+
46
+ class InheritedCmd < BaseCmd
47
+ end
48
+ end
49
+
50
+ subject { TestOptions::InheritedCmd }
51
+
52
+ it "must search each class then return {}"do
53
+ expect(subject.options).to eq({})
54
+ end
55
+ end
56
+
57
+ module TestOptions
58
+ class ExplicitBaseCmd
59
+ include CommandKit::Options
60
+ option :foo, desc: 'Foo option'
61
+ option :bar, desc: 'Bar option'
62
+ end
63
+ end
64
+
65
+ context "when the superclass defines options" do
66
+ module TestOptions
67
+ class ImplicitInheritedCmd < ExplicitBaseCmd
68
+ end
69
+ end
70
+
71
+ let(:super_subject) { TestOptions::ExplicitBaseCmd }
72
+ subject { TestOptions::ImplicitInheritedCmd }
73
+
74
+ it "must inherit the superclass'es options" do
75
+ expect(subject.options).to eq(super_subject.options)
76
+ end
77
+
78
+ it "must not change the superclass'es options" do
79
+ expect(super_subject.options.keys).to eq([:foo, :bar])
80
+ end
81
+ end
82
+
83
+ context "when the subclass defines options" do
84
+ module TestOptions
85
+ class ImplicitBaseCmd
86
+ include CommandKit::Options
87
+ end
88
+
89
+ class ExplicitInheritedCmd < ImplicitBaseCmd
90
+ option :baz, desc: 'Baz option'
91
+ option :qux, desc: 'Qux option'
92
+ end
93
+ end
94
+
95
+ let(:super_subject) { TestOptions::ImplicitBaseCmd }
96
+ subject { TestOptions::ExplicitInheritedCmd }
97
+
98
+ it "must return the subclass'es options" do
99
+ expect(subject.options.keys).to eq([:baz, :qux])
100
+ end
101
+
102
+ it "must not change the superclass'es options" do
103
+ expect(super_subject.options).to eq({})
104
+ end
105
+ end
106
+
107
+ context "when subclass overrides the superclass's optionss" do
108
+ module TestOptions
109
+ class ExplicitOverridingInheritedCmd < ExplicitBaseCmd
110
+ option :foo, desc: "Overriden foo option"
111
+ end
112
+ end
113
+
114
+ let(:super_subject) { TestOptions::ExplicitBaseCmd }
115
+ subject { TestOptions::ExplicitOverridingInheritedCmd }
116
+
117
+ it "must combine the superclass'es options with the subclass'es" do
118
+ expect(subject.options.keys).to eq([:foo, :bar])
119
+ expect(subject.options[:foo].desc).to eq("Overriden foo option")
120
+ expect(subject.options[:bar].desc).to eq("Bar option")
121
+ end
122
+
123
+ it "must not change the superclass'es options" do
124
+ expect(super_subject.options.keys).to eq([:foo, :bar])
125
+ expect(super_subject.options[:foo].desc).to eq("Foo option")
126
+ expect(super_subject.options[:bar].desc).to eq("Bar option")
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ describe "#initialize" do
133
+ it "must initialize #options" do
134
+ expect(subject.options).to eq({})
135
+ end
136
+ end
137
+ end