command_kit 0.1.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
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