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,6 @@
1
+ require 'rspec'
2
+ require 'simplecov'
3
+ SimpleCov.start
4
+
5
+ require 'command_kit/version'
6
+ include CommandKit
@@ -0,0 +1,264 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/stdio'
3
+
4
+ describe CommandKit::Stdio do
5
+ module TestStdio
6
+ class TestCommand
7
+ include CommandKit::Stdio
8
+ end
9
+ end
10
+
11
+ let(:command_class) { TestStdio::TestCommand }
12
+
13
+ describe "#initialize" do
14
+ module TestStdio
15
+ class TestCommandWithInitialize
16
+ include CommandKit::Stdio
17
+
18
+ attr_reader :var
19
+
20
+ def initialize(**kwargs)
21
+ super(**kwargs)
22
+
23
+ @var = 'foo'
24
+ end
25
+ end
26
+
27
+ class TestCommandWithInitializeAndKeywordArgs
28
+ include CommandKit::Stdio
29
+
30
+ attr_reader :var
31
+
32
+ def initialize(var: "foo", **kwargs)
33
+ super(**kwargs)
34
+
35
+ @var = var
36
+ end
37
+ end
38
+ end
39
+
40
+ context "when given no arguments" do
41
+ subject { command_class.new() }
42
+
43
+ it "must initialize #stdin to $stdin" do
44
+ expect(subject.stdin).to be($stdin)
45
+ end
46
+
47
+ it "must initialize #stdout to $stdout" do
48
+ expect(subject.stdout).to be($stdout)
49
+ end
50
+
51
+ it "must initialize #stderr to $stderr" do
52
+ expect(subject.stderr).to be($stderr)
53
+ end
54
+
55
+ context "and the including class defines it's own #initialize method" do
56
+ let(:command_class) { TestStdio::TestCommandWithInitialize }
57
+
58
+ it "must initialize #stdin to $stdin" do
59
+ expect(subject.stdin).to be($stdin)
60
+ end
61
+
62
+ it "must initialize #stdout to $stdout" do
63
+ expect(subject.stdout).to be($stdout)
64
+ end
65
+
66
+ it "must initialize #stderr to $stderr" do
67
+ expect(subject.stderr).to be($stderr)
68
+ end
69
+
70
+ it "must also call the class'es #initialize method" do
71
+ expect(subject.var).to eq('foo')
72
+ end
73
+
74
+ context "and it accepts keyword arguments" do
75
+ let(:command_class) { TestStdio::TestCommandWithInitializeAndKeywordArgs }
76
+ let(:var) { 'custom value' }
77
+
78
+ subject { command_class.new(var: var) }
79
+
80
+ it "must initialize #stdin to $stdin" do
81
+ expect(subject.stdin).to be($stdin)
82
+ end
83
+
84
+ it "must initialize #stdout to $stdout" do
85
+ expect(subject.stdout).to be($stdout)
86
+ end
87
+
88
+ it "must initialize #stderr to $stderr" do
89
+ expect(subject.stderr).to be($stderr)
90
+ end
91
+
92
+ it "must also call the class'es #initialize method with any additional keyword arguments" do
93
+ expect(subject.var).to eq(var)
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ context "when given a custom env: value" do
100
+ let(:custom_stdin) { StringIO.new }
101
+ let(:custom_stdout) { StringIO.new }
102
+ let(:custom_stderr) { StringIO.new }
103
+
104
+ subject do
105
+ command_class.new(
106
+ stdin: custom_stdin,
107
+ stdout: custom_stdout,
108
+ stderr: custom_stderr
109
+ )
110
+ end
111
+
112
+ it "must initialize #stdin to the stdin: value" do
113
+ expect(subject.stdin).to eq(custom_stdin)
114
+ end
115
+
116
+ it "must initialize #stdout to the stdout: value" do
117
+ expect(subject.stdout).to eq(custom_stdout)
118
+ end
119
+
120
+ it "must initialize #stderr to the stderr: value" do
121
+ expect(subject.stderr).to eq(custom_stderr)
122
+ end
123
+
124
+ context "and the including class defines it's own #initialize method" do
125
+ let(:command_class) { TestStdio::TestCommandWithInitialize }
126
+
127
+ it "must initialize #stdin to the stdin: value" do
128
+ expect(subject.stdin).to eq(custom_stdin)
129
+ end
130
+
131
+ it "must initialize #stdout to the stdout: value" do
132
+ expect(subject.stdout).to eq(custom_stdout)
133
+ end
134
+
135
+ it "must initialize #stderr to the stderr: value" do
136
+ expect(subject.stderr).to eq(custom_stderr)
137
+ end
138
+
139
+ it "must also call the class'es #initialize method" do
140
+ expect(subject.var).to eq('foo')
141
+ end
142
+
143
+ context "and it accepts keyword arguments" do
144
+ let(:command_class) { TestStdio::TestCommandWithInitializeAndKeywordArgs }
145
+ let(:var) { 'custom value' }
146
+
147
+ subject do
148
+ command_class.new(
149
+ stdin: custom_stdin,
150
+ stdout: custom_stdout,
151
+ stderr: custom_stderr,
152
+ var: var
153
+ )
154
+ end
155
+
156
+ it "must initialize #stdin to the stdin: value" do
157
+ expect(subject.stdin).to eq(custom_stdin)
158
+ end
159
+
160
+ it "must initialize #stdout to the stdout: value" do
161
+ expect(subject.stdout).to eq(custom_stdout)
162
+ end
163
+
164
+ it "must initialize #stderr to the stderr: value" do
165
+ expect(subject.stderr).to eq(custom_stderr)
166
+ end
167
+
168
+ it "must also call the class'es #initialize method with any additional keyword arguments" do
169
+ expect(subject.var).to eq(var)
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
175
+
176
+ describe "#stdin" do
177
+ let(:command_class) { TestStdio::TestCommand }
178
+ let(:stdin) { StringIO.new }
179
+
180
+ subject { command_class.new(stdin: stdin) }
181
+
182
+ it "must return the initialized stdin: value" do
183
+ expect(subject.stdin).to eq(stdin)
184
+ end
185
+ end
186
+
187
+ describe "#stdout" do
188
+ let(:command_class) { TestStdio::TestCommand }
189
+ let(:stdout) { StringIO.new }
190
+
191
+ subject { command_class.new(stdout: stdout) }
192
+
193
+ it "must return the initialized stdout: value" do
194
+ expect(subject.stdout).to eq(stdout)
195
+ end
196
+ end
197
+
198
+ describe "#stderr" do
199
+ let(:command_class) { TestStdio::TestCommand }
200
+ let(:stderr) { StringIO.new }
201
+
202
+ subject { command_class.new(stderr: stderr) }
203
+
204
+ it "must return the initialized stderr: value" do
205
+ expect(subject.stderr).to eq(stderr)
206
+ end
207
+ end
208
+
209
+ [:gets, :readline, :readlines].each do |method|
210
+ describe "##{method}" do
211
+ let(:stdin) { StringIO.new }
212
+ let(:args) { [double(:arg1), double(:arg2)] }
213
+ let(:ret) { double(:return_value) }
214
+
215
+ subject { command_class.new(stdin: stdin) }
216
+
217
+ it "must pass all arguments ot stdin.#{method}" do
218
+ expect(stdin).to receive(method).with(*args).and_return(ret)
219
+
220
+ expect(subject.send(method,*args)).to be(ret)
221
+ end
222
+ end
223
+ end
224
+
225
+ [:putc, :puts, :print, :printf].each do |method|
226
+ describe "##{method}" do
227
+ let(:stdout) { StringIO.new }
228
+ let(:args) { [double(:arg1), double(:arg2)] }
229
+ let(:ret) { double(:return_value) }
230
+
231
+ subject { command_class.new(stdout: stdout) }
232
+
233
+ it "must pass all arguments ot stdout.#{method}" do
234
+ expect(stdout).to receive(method).with(*args).and_return(ret)
235
+
236
+ expect(subject.send(method,*args)).to be(ret)
237
+ end
238
+ end
239
+ end
240
+
241
+ describe "#abort" do
242
+ let(:stderr) { StringIO.new }
243
+ subject { command_class.new(stderr: stderr) }
244
+
245
+ context "when given an abort message" do
246
+ let(:message) { 'fail' }
247
+
248
+ it "must print the message to stderr and exit with 1" do
249
+ expect(subject.stderr).to receive(:puts).with(message)
250
+ expect(subject).to receive(:exit).with(1)
251
+
252
+ subject.abort(message)
253
+ end
254
+ end
255
+
256
+ context "when called without any arguments" do
257
+ it "must exit with 1" do
258
+ expect(subject).to receive(:exit).with(1)
259
+
260
+ subject.abort
261
+ end
262
+ end
263
+ end
264
+ end
@@ -0,0 +1,237 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/usage'
3
+
4
+ describe Usage do
5
+ module TestUsage
6
+ class ImplicitCmd
7
+ include CommandKit::Usage
8
+ end
9
+ end
10
+
11
+ let(:command_class) { TestUsage::ImplicitCmd }
12
+
13
+ describe ".included" do
14
+ it { expect(command_class).to include(CommandKit::CommandName) }
15
+ it { expect(command_class).to include(CommandKit::Help) }
16
+ end
17
+
18
+ describe ".usage" do
19
+ subject { TestUsage::ImplicitCmd }
20
+
21
+ context "when no usage has been set" do
22
+ it "should default to nil" do
23
+ expect(subject.usage).to be_nil
24
+ end
25
+ end
26
+
27
+ context "when a usage is explicitly set" do
28
+ module TestUsage
29
+ class ExplicitCmd
30
+ include CommandKit::Usage
31
+ usage 'EXPLICIT'
32
+ end
33
+ end
34
+
35
+ subject { TestUsage::ExplicitCmd }
36
+
37
+ it "must return the explicitly set usage" do
38
+ expect(subject.usage).to eq("EXPLICIT")
39
+ end
40
+ end
41
+
42
+ context "when the command class inherites from another class" do
43
+ context "but no usage is set" do
44
+ module TestUsage
45
+ class BaseCmd
46
+ include CommandKit::Usage
47
+ end
48
+
49
+ class InheritedCmd < BaseCmd
50
+ end
51
+ end
52
+
53
+ subject { TestUsage::InheritedCmd }
54
+
55
+ it "must search each class then return nil "do
56
+ expect(subject.usage).to be_nil
57
+ end
58
+ end
59
+
60
+ module TestUsage
61
+ class ExplicitBaseCmd
62
+ include CommandKit::Usage
63
+ usage 'EXPLICIT'
64
+ end
65
+ end
66
+
67
+ context "when the superclass defines an explicit usage" do
68
+ module TestUsage
69
+ class ImplicitInheritedCmd < ExplicitBaseCmd
70
+ end
71
+ end
72
+
73
+ let(:super_subject) { TestUsage::ExplicitBaseCmd }
74
+ subject { TestUsage::ImplicitInheritedCmd }
75
+
76
+ it "must inherit the superclass'es usage" do
77
+ expect(subject.usage).to eq(super_subject.usage)
78
+ end
79
+
80
+ it "must not change the superclass'es usage" do
81
+ expect(super_subject.usage).to eq("EXPLICIT")
82
+ end
83
+ end
84
+
85
+ context "when the subclass defines an explicit usage" do
86
+ module TestUsage
87
+ class ImplicitBaseCmd
88
+ include CommandKit::Usage
89
+ end
90
+
91
+ class ExplicitInheritedCmd < ImplicitBaseCmd
92
+ usage 'EXPLICIT'
93
+ end
94
+ end
95
+
96
+ let(:super_subject) { TestUsage::ImplicitBaseCmd }
97
+ subject { TestUsage::ExplicitInheritedCmd }
98
+
99
+ it "must return the subclass'es usage" do
100
+ expect(subject.usage).to eq("EXPLICIT")
101
+ end
102
+
103
+ it "must not change the superclass'es usage" do
104
+ expect(super_subject.usage).to be_nil
105
+ end
106
+ end
107
+
108
+ context "when both the subclass overrides the superclass's usages" do
109
+ module TestUsage
110
+ class ExplicitOverridingInheritedCmd < ExplicitBaseCmd
111
+ usage 'EXPLICIT_OVERRIDE'
112
+ end
113
+ end
114
+
115
+ let(:super_subject) { TestUsage::ExplicitBaseCmd }
116
+ subject { TestUsage::ExplicitOverridingInheritedCmd }
117
+
118
+ it "must return the subclass'es usage" do
119
+ expect(subject.usage).to eq("EXPLICIT_OVERRIDE")
120
+ end
121
+
122
+ it "must not change the superclass'es usage" do
123
+ expect(super_subject.usage).to eq("EXPLICIT")
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ module TestUsage
130
+ class NoUsage
131
+ include CommandKit::Usage
132
+ end
133
+ end
134
+
135
+ module TestUsage
136
+ class SingleUsage
137
+ include CommandKit::Usage
138
+
139
+ usage 'ONE USAGE'
140
+ end
141
+ end
142
+
143
+ module TestUsage
144
+ class MultipleUsage
145
+ include CommandKit::Usage
146
+
147
+ usage [
148
+ 'ONE USAGE',
149
+ 'TWO USAGE',
150
+ 'THREE USAGE'
151
+ ]
152
+ end
153
+ end
154
+
155
+ subject { command_class.new }
156
+
157
+ describe "#usage" do
158
+ context "when .usage is nil" do
159
+ let(:command_class) { TestUsage::NoUsage }
160
+ subject { command_class.new }
161
+
162
+ it "must not print anything" do
163
+ expect(subject.usage).to be(nil)
164
+ end
165
+ end
166
+
167
+ context "when .usage is a String" do
168
+ let(:command_class) { TestUsage::SingleUsage }
169
+ subject { command_class.new }
170
+
171
+ it "must return .usage, but with #command_name prepended" do
172
+ expect(subject.usage).to eq("#{subject.command_name} #{command_class.usage}")
173
+ end
174
+ end
175
+
176
+ context "when #usage is an Array of Strings" do
177
+ let(:command_class) { TestUsage::MultipleUsage }
178
+ subject { command_class.new }
179
+
180
+ it "must return .usage, but with #command_name prepended to each element" do
181
+ expect(subject.usage).to eq(
182
+ [
183
+ "#{subject.command_name} #{command_class.usage[0]}",
184
+ "#{subject.command_name} #{command_class.usage[1]}",
185
+ "#{subject.command_name} #{command_class.usage[2]}",
186
+ ]
187
+ )
188
+ end
189
+ end
190
+ end
191
+
192
+ describe "#help_usage" do
193
+ context "when #usage is nil" do
194
+ let(:command_class) { TestUsage::NoUsage }
195
+ subject { command_class.new }
196
+
197
+ it "must not print anything" do
198
+ expect { subject.help_usage }.to_not output.to_stdout
199
+ end
200
+ end
201
+
202
+ context "when #usage is a String" do
203
+ let(:command_class) { TestUsage::SingleUsage }
204
+ subject { command_class.new }
205
+
206
+ it "must print out 'usage:' and only one usage" do
207
+ expect { subject.help_usage }.to output(
208
+ "usage: #{subject.usage}#{$/}"
209
+ ).to_stdout
210
+ end
211
+ end
212
+
213
+ context "when #usage is an Array of Strings" do
214
+ let(:command_class) { TestUsage::MultipleUsage }
215
+ subject { command_class.new }
216
+
217
+ it "must print out the 'usage:' and all usage Strings" do
218
+ expect { subject.help_usage }.to output(
219
+ [
220
+ "usage: #{subject.usage[0]}",
221
+ " #{subject.usage[1]}",
222
+ " #{subject.usage[2]}",
223
+ ''
224
+ ].join($/)
225
+ ).to_stdout
226
+ end
227
+ end
228
+ end
229
+
230
+ describe "#help" do
231
+ it "must call #help_usage" do
232
+ expect(subject).to receive(:help_usage)
233
+
234
+ subject.help
235
+ end
236
+ end
237
+ end