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,201 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/console'
3
+
4
+ require 'stringio'
5
+
6
+ describe Console do
7
+ module TestConsole
8
+ class TestCommand
9
+ include CommandKit::Console
10
+ end
11
+ end
12
+
13
+ let(:command_class) { TestConsole::TestCommand }
14
+ subject { command_class.new }
15
+
16
+ describe "#console?" do
17
+ context "when stdout is connected to a TTY" do
18
+ subject { command_class.new(stdout: STDOUT) }
19
+
20
+ it do
21
+ skip "STDOUT is not a TTY" unless STDOUT.tty?
22
+
23
+ expect(subject.console?).to be(true)
24
+ end
25
+ end
26
+
27
+ context "when stdout is not connected to a TTY" do
28
+ subject { command_class.new(stdout: StringIO.new) }
29
+
30
+ it do
31
+ expect(subject.console?).to be(false)
32
+ end
33
+ end
34
+
35
+ context "when IO.console is missing" do
36
+ before do
37
+ expect(IO).to receive(:respond_to?).with(:console).and_return(false)
38
+ end
39
+
40
+ it do
41
+ expect(subject.console?).to be(false)
42
+ end
43
+ end
44
+ end
45
+
46
+ describe "#console" do
47
+ context "when stdout is connected to a TTY" do
48
+ subject { command_class.new(stdout: STDOUT) }
49
+
50
+ it do
51
+ skip "STDOUT is not a TTY" unless STDOUT.tty?
52
+
53
+ expect(subject.console).to eq(IO.console)
54
+ end
55
+ end
56
+
57
+ context "when stdout is not connected to a TTY" do
58
+ subject { command_class.new(stdout: StringIO.new) }
59
+
60
+ it do
61
+ expect(subject.console).to eq(nil)
62
+ end
63
+ end
64
+
65
+ context "when IO.console is missing" do
66
+ before do
67
+ expect(IO).to receive(:respond_to?).with(:console).and_return(false)
68
+ end
69
+
70
+ it do
71
+ expect(subject.console).to be(nil)
72
+ end
73
+ end
74
+ end
75
+
76
+ describe "#console_height" do
77
+ context "when stdout is connected to a TTY" do
78
+ subject { command_class.new(stdout: STDOUT) }
79
+
80
+ it do
81
+ skip "STDOUT is not a TTY" unless STDOUT.tty?
82
+
83
+ expect(subject.console_height).to eq(STDOUT.winsize[0])
84
+ end
85
+ end
86
+
87
+ context "when stdout is not connected to a TTY" do
88
+ subject { command_class.new(stdout: StringIO.new) }
89
+
90
+ it "must fallback to DEFAULT_HEIGHT" do
91
+ expect(subject.console_height).to eq(described_class::DEFAULT_HEIGHT)
92
+ end
93
+
94
+ context "but the LINES env variable was set" do
95
+ let(:lines) { 10 }
96
+ let(:env) { {'LINES' => lines.to_s} }
97
+
98
+ subject { command_class.new(stdout: StringIO.new, env: env) }
99
+
100
+ it "must fallback to the LINES environment variable" do
101
+ expect(subject.console_height).to eq(lines)
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ describe "#console_width" do
108
+ context "when stdout is connected to a TTY" do
109
+ subject { command_class.new(stdout: STDOUT) }
110
+
111
+ it do
112
+ skip "STDOUT is not a TTY" unless STDOUT.tty?
113
+
114
+ expect(subject.console_width).to eq(STDOUT.winsize[1])
115
+ end
116
+ end
117
+
118
+ context "when stdout is not connected to a TTY" do
119
+ subject { command_class.new(stdout: StringIO.new) }
120
+
121
+ it "must fallback to DEFAULT_WIDTH" do
122
+ expect(subject.console_width).to eq(described_class::DEFAULT_WIDTH)
123
+ end
124
+
125
+ context "but the COLUMNS env variable was set" do
126
+ let(:columns) { 50 }
127
+ let(:env) { {'COLUMNS' => columns.to_s} }
128
+
129
+ subject { command_class.new(stdout: StringIO.new, env: env) }
130
+
131
+ it "must fallback to the COLUMNS environment variable" do
132
+ expect(subject.console_width).to eq(columns)
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ describe "#console_size" do
139
+ context "when stdout is connected to a TTY" do
140
+ subject { command_class.new(stdout: STDOUT) }
141
+
142
+ it do
143
+ skip "STDOUT is not a TTY" unless STDOUT.tty?
144
+
145
+ expect(subject.console_size).to eq(STDOUT.winsize)
146
+ end
147
+ end
148
+
149
+ context "when stdout is not connected to a TTY" do
150
+ subject { command_class.new(stdout: StringIO.new) }
151
+
152
+ it "must fallback to [DEFAULT_HEIGHT, DEFAULT_WIDTH]" do
153
+ expect(subject.console_size).to eq(
154
+ [described_class::DEFAULT_HEIGHT, described_class::DEFAULT_WIDTH]
155
+ )
156
+ end
157
+
158
+ context "but the LINES env variable was set" do
159
+ let(:lines) { 10 }
160
+ let(:env) { {'LINES' => lines.to_s} }
161
+
162
+ subject { command_class.new(stdout: StringIO.new, env: env) }
163
+
164
+ it "must fallback to the [$LINES, DEFAULT_WIDTH]" do
165
+ expect(subject.console_size).to eq(
166
+ [lines, described_class::DEFAULT_WIDTH]
167
+ )
168
+ end
169
+ end
170
+
171
+ context "but the COLUMNS env variable was set" do
172
+ let(:columns) { 50 }
173
+ let(:env) { {'COLUMNS' => columns.to_s} }
174
+
175
+ subject { command_class.new(stdout: StringIO.new, env: env) }
176
+
177
+ it "must fallback to the [DEFAULT_HEIGHT, COLUMNS]" do
178
+ expect(subject.console_size).to eq(
179
+ [described_class::DEFAULT_HEIGHT, columns]
180
+ )
181
+ end
182
+ end
183
+
184
+ context "but the LINES and COLUMNS env variable was set" do
185
+ let(:lines) { 10 }
186
+ let(:columns) { 50 }
187
+ let(:env) do
188
+ {'LINES' => lines.to_s, 'COLUMNS' => columns.to_s}
189
+ end
190
+
191
+ subject { command_class.new(stdout: StringIO.new, env: env) }
192
+
193
+ it "must fallback to the [LINES, COLUMNS]" do
194
+ expect(subject.console_size).to eq(
195
+ [lines, columns]
196
+ )
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,203 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/description'
3
+
4
+ describe Description do
5
+ module TestDescription
6
+ class ImplicitCmd
7
+ include CommandKit::Description
8
+ end
9
+ end
10
+
11
+ let(:command_class) { TestDescription::ImplicitCmd }
12
+
13
+ describe ".description" do
14
+ subject { TestDescription::ImplicitCmd }
15
+
16
+ context "when no description have been set" do
17
+ it "should default to nil" do
18
+ expect(subject.description).to be_nil
19
+ end
20
+ end
21
+
22
+ context "when a description is explicitly set" do
23
+ module TestDescription
24
+ class ExplicitCmd
25
+ include CommandKit::Description
26
+ description "Example command"
27
+ end
28
+ end
29
+
30
+ subject { TestDescription::ExplicitCmd }
31
+
32
+ it "must return the explicitly set description" do
33
+ expect(subject.description).to eq("Example command")
34
+ end
35
+ end
36
+
37
+ context "when the command class inherites from another class" do
38
+ context "but no description is set" do
39
+ module TestDescription
40
+ class BaseCmd
41
+ include CommandKit::Description
42
+ end
43
+
44
+ class InheritedCmd < BaseCmd
45
+ end
46
+ end
47
+
48
+ subject { TestDescription::InheritedCmd }
49
+
50
+ it "must search each class then return nil "do
51
+ expect(subject.description).to be_nil
52
+ end
53
+ end
54
+
55
+ module TestDescription
56
+ class ExplicitBaseCmd
57
+ include CommandKit::Description
58
+ description "Example base command"
59
+ end
60
+ end
61
+
62
+ context "when the superclass defines an explicit description" do
63
+ module TestDescription
64
+ class ImplicitInheritedCmd < ExplicitBaseCmd
65
+ end
66
+ end
67
+
68
+ let(:super_subject) { TestDescription::ExplicitBaseCmd }
69
+ subject { TestDescription::ImplicitInheritedCmd }
70
+
71
+ it "must inherit the superclass'es description" do
72
+ expect(subject.description).to eq(super_subject.description)
73
+ end
74
+
75
+ it "must not change the superclass'es description" do
76
+ expect(super_subject.description).to eq("Example base command")
77
+ end
78
+ end
79
+
80
+ context "when the subclass defines an explicit description" do
81
+ module TestDescription
82
+ class ImplicitBaseCmd
83
+ include CommandKit::Description
84
+ end
85
+
86
+ class ExplicitInheritedCmd < ImplicitBaseCmd
87
+ description "Example inherited command"
88
+ end
89
+ end
90
+
91
+ let(:super_subject) { TestDescription::ImplicitBaseCmd }
92
+ subject { TestDescription::ExplicitInheritedCmd }
93
+
94
+ it "must return the subclass'es description" do
95
+ expect(subject.description).to eq("Example inherited command")
96
+ end
97
+
98
+ it "must not change the superclass'es description" do
99
+ expect(super_subject.description).to be_nil
100
+ end
101
+ end
102
+
103
+ context "when both the subclass overrides the superclass's descriptions" do
104
+ module TestDescription
105
+ class ExplicitOverridingInheritedCmd < ExplicitBaseCmd
106
+ description "Example overrided description"
107
+ end
108
+ end
109
+
110
+ let(:super_subject) { TestDescription::ExplicitBaseCmd }
111
+ subject { TestDescription::ExplicitOverridingInheritedCmd }
112
+
113
+ it "must return the subclass'es description" do
114
+ expect(subject.description).to eq("Example overrided description")
115
+ end
116
+
117
+ it "must not change the superclass'es description" do
118
+ expect(super_subject.description).to eq("Example base command")
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ subject { command_class.new }
125
+
126
+ describe "#description" do
127
+ it "must be the same as .description" do
128
+ expect(subject.description).to eq(command_class.description)
129
+ end
130
+ end
131
+
132
+ describe "#help_description" do
133
+ context "when #description returns nil" do
134
+ module TestDescription
135
+ class NoDescription
136
+ include CommandKit::Description
137
+ end
138
+ end
139
+
140
+ let(:command_class) { TestDescription::NoDescription }
141
+ subject { command_class.new }
142
+
143
+ it "must print out the description" do
144
+ expect { subject.help_description }.to_not output.to_stdout
145
+ end
146
+ end
147
+
148
+ context "when #description returns a String" do
149
+ module TestDescription
150
+ class DefinesDescription
151
+ include CommandKit::Description
152
+
153
+ description "Example command"
154
+ end
155
+ end
156
+
157
+ let(:command_class) { TestDescription::DefinesDescription }
158
+ subject { command_class.new }
159
+
160
+ it "must print out the description" do
161
+ expect { subject.help_description }.to output(
162
+ [
163
+ '',
164
+ subject.description,
165
+ ''
166
+ ].join($/)
167
+ ).to_stdout
168
+ end
169
+ end
170
+ end
171
+
172
+ describe "#help" do
173
+ it "must call #help_description" do
174
+ expect(subject).to receive(:help_description)
175
+
176
+ subject.help
177
+ end
178
+
179
+ context "when the superclass defines it's own #help method" do
180
+ module TestDescription
181
+ class SuperclassHelpMethod
182
+ def help
183
+ puts 'superclass'
184
+ end
185
+ end
186
+
187
+ class InheritedHelpMethod < SuperclassHelpMethod
188
+ include CommandKit::Description
189
+ end
190
+ end
191
+
192
+ let(:super_command_class) { TestDescription::SuperclassHelpMethod }
193
+ let(:command_class) { TestDescription::InheritedHelpMethod }
194
+
195
+ it "must call the superclass'es #help method first" do
196
+ expect_any_instance_of(super_command_class).to receive(:help)
197
+ expect(subject).to receive(:help_description)
198
+
199
+ subject.help
200
+ end
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/env/home'
3
+
4
+ describe Env::Home do
5
+ module TestEnvHome
6
+ class TestCommand
7
+ include CommandKit::Env::Home
8
+ end
9
+ end
10
+
11
+ let(:command_class) { TestEnvHome::TestCommand }
12
+
13
+ describe ".included" do
14
+ it "must include CommandKit::Env" do
15
+ expect(command_class).to be_include(CommandKit::Env)
16
+ end
17
+ end
18
+
19
+ describe ".home_dir" do
20
+ subject { command_class }
21
+
22
+ it "must return Gem.user_home" do
23
+ expect(subject.home_dir).to eq(Gem.user_home)
24
+ end
25
+ end
26
+
27
+ describe "#home_dir" do
28
+ context "when env['HOME'] is set" do
29
+ let(:home_dir) { '/path/to/home_dir' }
30
+
31
+ subject { command_class.new(env: {'HOME' => home_dir}) }
32
+
33
+ it "must return the env['HOME'] value" do
34
+ expect(subject.home_dir).to eq(home_dir)
35
+ end
36
+ end
37
+
38
+ context "when env['HOME'] is not set" do
39
+ subject { command_class.new(env: {}) }
40
+
41
+ it "must return .home_dir" do
42
+ expect(subject.home_dir).to eq(subject.class.home_dir)
43
+ end
44
+ end
45
+ end
46
+ end