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,78 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/env/path'
3
+
4
+ describe Env::Path do
5
+ module TestEnvPath
6
+ class TestCommand
7
+
8
+ include CommandKit::Env::Path
9
+
10
+ end
11
+ end
12
+
13
+ let(:command_class) { TestEnvPath::TestCommand }
14
+ subject { command_class.new }
15
+
16
+ describe "#initialize" do
17
+ it "must split ENV['PATH'] into an Array of directories" do
18
+ expect(subject.path_dirs).to eq(ENV['PATH'].split(File::PATH_SEPARATOR))
19
+ end
20
+
21
+ context "when given a custom 'PATH variable" do
22
+ let(:path_dirs) { %w[/bin /usr/bin] }
23
+ let(:path) { path_dirs.join(File::PATH_SEPARATOR) }
24
+
25
+ subject { command_class.new(env: {'PATH' => path}) }
26
+
27
+ it "must split env['PATH'] into an Array of directories" do
28
+ expect(subject.path_dirs).to eq(path_dirs)
29
+ end
30
+ end
31
+
32
+ context "when given an empty 'PATH' env variable" do
33
+ subject { command_class.new(env: {'PATH' => ''}) }
34
+
35
+ it "must set #path_dirs to []" do
36
+ expect(subject.path_dirs).to eq([])
37
+ end
38
+ end
39
+ end
40
+
41
+ describe "#find_command" do
42
+ context "when given a valid command name" do
43
+ it "must search #path_dirs for the command" do
44
+ command = subject.find_command('ruby')
45
+
46
+ expect(subject.path_dirs).to include(File.dirname(command))
47
+ end
48
+
49
+ it "must return the path to the command" do
50
+ command = subject.find_command('ruby')
51
+
52
+ expect(command).to_not be(nil)
53
+ expect(File.file?(command)).to be(true)
54
+ expect(File.executable?(command)).to be(true)
55
+ end
56
+ end
57
+
58
+ context "when given an unknown command name" do
59
+ it "must return nil" do
60
+ expect(subject.find_command('does_not_exist')).to be(nil)
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "#command_installed?" do
66
+ context "when given a valid command name" do
67
+ it do
68
+ expect(subject.command_installed?('ruby')).to be(true)
69
+ end
70
+ end
71
+
72
+ context "when given an unknown command name" do
73
+ it "must return nil" do
74
+ expect(subject.command_installed?('does_not_exist')).to be(false)
75
+ end
76
+ end
77
+ end
78
+ end
data/spec/env_spec.rb ADDED
@@ -0,0 +1,123 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/env'
3
+
4
+ describe CommandKit::Env do
5
+ module TestEnv
6
+ class TestCommand
7
+ include CommandKit::Env
8
+ end
9
+ end
10
+
11
+ let(:command_class) { TestEnv::TestCommand }
12
+
13
+ describe "#initialize" do
14
+ module TestEnv
15
+ class TestCommandWithInitialize
16
+ include CommandKit::Env
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::Env
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 #env to ENV" do
44
+ expect(subject.env).to be(ENV)
45
+ end
46
+
47
+ context "and the including class defines it's own #initialize method" do
48
+ let(:command_class) { TestEnv::TestCommandWithInitialize }
49
+
50
+ it "must initialize #env to ENV" do
51
+ expect(subject.env).to be(ENV)
52
+ end
53
+
54
+ it "must also call the class'es #initialize method" do
55
+ expect(subject.var).to eq('foo')
56
+ end
57
+
58
+ context "and it accepts keyword arguments" do
59
+ let(:command_class) { TestEnv::TestCommandWithInitializeAndKeywordArgs }
60
+ let(:var) { 'custom value' }
61
+
62
+ subject { command_class.new(var: var) }
63
+
64
+ it "must initialize #env to ENV" do
65
+ expect(subject.env).to be(ENV)
66
+ end
67
+
68
+ it "must also call the class'es #initialize method with any additional keyword arguments" do
69
+ expect(subject.var).to eq(var)
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ context "when given a custom env: value" do
76
+ let(:custom_env) { {'FOO' => 'bar'} }
77
+
78
+ subject { command_class.new(env: custom_env) }
79
+
80
+ it "must initialize #env to the env: value" do
81
+ expect(subject.env).to eq(custom_env)
82
+ end
83
+
84
+ context "and the including class defines it's own #initialize method" do
85
+ let(:command_class) { TestEnv::TestCommandWithInitialize }
86
+
87
+ it "must initialize #env to the env: value" do
88
+ expect(subject.env).to eq(custom_env)
89
+ end
90
+
91
+ it "must also call the class'es #initialize method" do
92
+ expect(subject.var).to eq('foo')
93
+ end
94
+
95
+ context "and it accepts keyword arguments" do
96
+ let(:command_class) { TestEnv::TestCommandWithInitializeAndKeywordArgs }
97
+ let(:var) { 'custom value' }
98
+
99
+ subject { command_class.new(env: custom_env, var: var) }
100
+
101
+ it "must initialize #env to the env: value" do
102
+ expect(subject.env).to eq(custom_env)
103
+ end
104
+
105
+ it "must also call the class'es #initialize method with any additional keyword arguments" do
106
+ expect(subject.var).to eq(var)
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ describe "#env" do
114
+ let(:command_class) { TestEnv::TestCommand }
115
+ let(:env) { {"FOO" => "bar" } }
116
+
117
+ subject { command_class.new(env: env) }
118
+
119
+ it "must return the initialized env: value" do
120
+ expect(subject.env).to eq(env)
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,235 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/examples'
3
+
4
+ describe Examples do
5
+ module TestExamples
6
+ class ImplicitCmd
7
+ include CommandKit::Examples
8
+ end
9
+ end
10
+
11
+ let(:command_class) { TestExamples::ImplicitCmd }
12
+
13
+ describe ".examples" do
14
+ subject { TestExamples::ImplicitCmd }
15
+
16
+ context "when no examples have been set" do
17
+ it "should default to nil" do
18
+ expect(subject.examples).to be_nil
19
+ end
20
+ end
21
+
22
+ context "when a examples is explicitly set" do
23
+ module TestExamples
24
+ class ExplicitCmd
25
+ include CommandKit::Examples
26
+ examples [
27
+ '--example 1',
28
+ '--example 2'
29
+ ]
30
+ end
31
+ end
32
+
33
+ subject { TestExamples::ExplicitCmd }
34
+
35
+ it "must return the explicitly set examples" do
36
+ expect(subject.examples).to eq([
37
+ '--example 1',
38
+ '--example 2'
39
+ ])
40
+ end
41
+ end
42
+
43
+ context "when the command class inherites from another class" do
44
+ context "but no examples is set" do
45
+ module TestExamples
46
+ class BaseCmd
47
+ include CommandKit::Examples
48
+ end
49
+
50
+ class InheritedCmd < BaseCmd
51
+ end
52
+ end
53
+
54
+ subject { TestExamples::InheritedCmd }
55
+
56
+ it "must search each class then return nil "do
57
+ expect(subject.examples).to be_nil
58
+ end
59
+ end
60
+
61
+ module TestExamples
62
+ class ExplicitBaseCmd
63
+ include CommandKit::Examples
64
+ examples [
65
+ '--example 1',
66
+ '--example 2'
67
+ ]
68
+ end
69
+ end
70
+
71
+ context "when the superclass defines an explicit examples" do
72
+ module TestExamples
73
+ class ImplicitInheritedCmd < ExplicitBaseCmd
74
+ end
75
+ end
76
+
77
+ let(:super_subject) { TestExamples::ExplicitBaseCmd }
78
+ subject { TestExamples::ImplicitInheritedCmd }
79
+
80
+ it "must inherit the superclass'es examples" do
81
+ expect(subject.examples).to eq(super_subject.examples)
82
+ end
83
+
84
+ it "must not change the superclass'es examples" do
85
+ expect(super_subject.examples).to eq([
86
+ '--example 1',
87
+ '--example 2'
88
+ ])
89
+ end
90
+ end
91
+
92
+ context "when the subclass defines an explicit examples" do
93
+ module TestExamples
94
+ class ImplicitBaseCmd
95
+ include CommandKit::Examples
96
+ end
97
+
98
+ class ExplicitInheritedCmd < ImplicitBaseCmd
99
+ examples [
100
+ '--example 1',
101
+ '--example 2'
102
+ ]
103
+ end
104
+ end
105
+
106
+ let(:super_subject) { TestExamples::ImplicitBaseCmd }
107
+ subject { TestExamples::ExplicitInheritedCmd }
108
+
109
+ it "must return the subclass'es examples" do
110
+ expect(subject.examples).to eq([
111
+ '--example 1',
112
+ '--example 2'
113
+ ])
114
+ end
115
+
116
+ it "must not change the superclass'es examples" do
117
+ expect(super_subject.examples).to be_nil
118
+ end
119
+ end
120
+
121
+ context "when both the subclass overrides the superclass's exampless" do
122
+ module TestExamples
123
+ class ExplicitOverridingInheritedCmd < ExplicitBaseCmd
124
+ examples [
125
+ '--example override'
126
+ ]
127
+ end
128
+ end
129
+
130
+ let(:super_subject) { TestExamples::ExplicitBaseCmd }
131
+ subject { TestExamples::ExplicitOverridingInheritedCmd }
132
+
133
+ it "must return the subclass'es examples" do
134
+ expect(subject.examples).to eq([
135
+ '--example override'
136
+ ])
137
+ end
138
+
139
+ it "must not change the superclass'es examples" do
140
+ expect(super_subject.examples).to eq([
141
+ '--example 1',
142
+ '--example 2'
143
+ ])
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ subject { command_class.new }
150
+
151
+ describe "#examples" do
152
+ it "must be the same as .examples" do
153
+ expect(subject.examples).to eq(command_class.examples)
154
+ end
155
+ end
156
+
157
+ describe "#help_examples" do
158
+ context "when #examples returns nil" do
159
+ module TestExamples
160
+ class NoExamples
161
+ include CommandKit::Examples
162
+ end
163
+ end
164
+
165
+ let(:command_class) { TestExamples::NoExamples }
166
+ subject { command_class.new }
167
+
168
+ it "must print out the examples" do
169
+ expect { subject.help_examples }.to_not output.to_stdout
170
+ end
171
+ end
172
+
173
+ context "when #examples returns an Array" do
174
+ module TestExamples
175
+ class MultipleExamples
176
+ include CommandKit::Examples
177
+
178
+ examples [
179
+ '--example 1',
180
+ '--example 2',
181
+ '--example 3'
182
+ ]
183
+ end
184
+ end
185
+
186
+ let(:command_class) { TestExamples::MultipleExamples }
187
+ subject { command_class.new }
188
+
189
+ it "must print out the 'Examples:' section header and the examples" do
190
+ expect { subject.help_examples }.to output(
191
+ [
192
+ '',
193
+ "Examples:",
194
+ " #{subject.command_name} #{subject.examples[0]}",
195
+ " #{subject.command_name} #{subject.examples[1]}",
196
+ " #{subject.command_name} #{subject.examples[2]}",
197
+ ''
198
+ ].join($/)
199
+ ).to_stdout
200
+ end
201
+ end
202
+ end
203
+
204
+ describe "#help" do
205
+ it "must call #help_examples" do
206
+ expect(subject).to receive(:help_examples)
207
+
208
+ subject.help
209
+ end
210
+
211
+ context "when the superclass defines it's own #help method" do
212
+ module TestDescription
213
+ class SuperclassHelpMethod
214
+ def help
215
+ puts 'superclass'
216
+ end
217
+ end
218
+
219
+ class InheritedHelpMethod < SuperclassHelpMethod
220
+ include CommandKit::Examples
221
+ end
222
+ end
223
+
224
+ let(:super_command_class) { TestDescription::SuperclassHelpMethod }
225
+ let(:command_class) { TestDescription::InheritedHelpMethod }
226
+
227
+ it "must call the superclass'es #help method first" do
228
+ expect_any_instance_of(super_command_class).to receive(:help)
229
+ expect(subject).to receive(:help_examples)
230
+
231
+ subject.help
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/exception_handler'
3
+
4
+ describe CommandKit::ExceptionHandler do
5
+ module TestExceptionHandler
6
+ class TestIncludedCmd
7
+
8
+ include CommandKit::Main
9
+ include CommandKit::ExceptionHandler
10
+
11
+ end
12
+ end
13
+
14
+ let(:command_class) { TestExceptionHandler::TestIncludedCmd }
15
+ subject { command_class.new }
16
+
17
+ describe ".included" do
18
+ it "must include CommandKit::Printing" do
19
+ expect(command_class).to be_include(CommandKit::Printing)
20
+ end
21
+ end
22
+
23
+ describe "#main" do
24
+ context "when the #main method rescues an Interrupt" do
25
+ module TestExceptionHandler
26
+ class RaisesInterruptCmd
27
+
28
+ include CommandKit::Main
29
+ include CommandKit::ExceptionHandler
30
+
31
+ def run
32
+ raise(Interrupt.new)
33
+ end
34
+
35
+ end
36
+ end
37
+
38
+ let(:command_class) { TestExceptionHandler::RaisesInterruptCmd }
39
+ subject { command_class.new }
40
+
41
+ it "must re-raise the Interrupt exception" do
42
+ expect { subject.main }.to raise_error(Interrupt)
43
+ end
44
+ end
45
+
46
+ context "when the #main method raises an Errno::EPIPE" do
47
+ module TestExceptionHandler
48
+ class RaisesErrnoEPIPECmd
49
+
50
+ include CommandKit::Main
51
+ include CommandKit::ExceptionHandler
52
+
53
+ def run
54
+ raise(Errno::EPIPE.new)
55
+ end
56
+
57
+ end
58
+ end
59
+
60
+ let(:command_class) { TestExceptionHandler::RaisesErrnoEPIPECmd }
61
+ subject { command_class.new }
62
+
63
+ it "must re-raise the Errno::EPIPE exception" do
64
+ expect { subject.main }.to raise_error(Errno::EPIPE)
65
+ end
66
+ end
67
+
68
+ context "when the #main method raises another Exception" do
69
+ module TestExceptionHandler
70
+ class RaisesExceptionCmd
71
+
72
+ include CommandKit::Main
73
+ include CommandKit::ExceptionHandler
74
+
75
+ def run
76
+ raise(RuntimeError.new("error"))
77
+ end
78
+
79
+ end
80
+ end
81
+
82
+ let(:command_class) { TestExceptionHandler::RaisesExceptionCmd }
83
+ subject { command_class.new }
84
+
85
+ it "must rescue the exception and call #on_exception" do
86
+ expect(subject).to receive(:on_exception).with(RuntimeError)
87
+
88
+ subject.main
89
+ end
90
+ end
91
+ end
92
+
93
+ describe "#on_exception" do
94
+ let(:error) { RuntimeError.new('error') }
95
+
96
+ it "must call #print_exception and exit with 1" do
97
+ expect(subject).to receive(:print_exception).with(error)
98
+ expect(subject).to receive(:exit).with(1)
99
+
100
+ subject.on_exception(error)
101
+ end
102
+ end
103
+ end