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,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