command_kit 0.1.0.pre2 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +15 -0
- data/.rubocop.yml +141 -0
- data/ChangeLog.md +98 -2
- data/Gemfile +3 -0
- data/README.md +189 -117
- data/Rakefile +3 -2
- data/command_kit.gemspec +4 -4
- data/examples/command.rb +1 -1
- data/gemspec.yml +10 -2
- data/lib/command_kit/arguments/argument.rb +2 -0
- data/lib/command_kit/arguments/argument_value.rb +2 -0
- data/lib/command_kit/arguments.rb +23 -4
- data/lib/command_kit/colors.rb +253 -45
- data/lib/command_kit/command.rb +6 -1
- data/lib/command_kit/command_name.rb +9 -0
- data/lib/command_kit/commands/auto_load.rb +24 -1
- data/lib/command_kit/commands/auto_require.rb +16 -0
- data/lib/command_kit/commands/command.rb +3 -0
- data/lib/command_kit/commands/help.rb +5 -2
- data/lib/command_kit/commands/parent_command.rb +7 -0
- data/lib/command_kit/commands/subcommand.rb +13 -1
- data/lib/command_kit/commands.rb +54 -9
- data/lib/command_kit/description.rb +12 -1
- data/lib/command_kit/env/home.rb +9 -0
- data/lib/command_kit/env/path.rb +16 -1
- data/lib/command_kit/env.rb +4 -0
- data/lib/command_kit/examples.rb +12 -1
- data/lib/command_kit/exception_handler.rb +4 -0
- data/lib/command_kit/help/man.rb +26 -30
- data/lib/command_kit/help.rb +7 -1
- data/lib/command_kit/inflector.rb +49 -17
- data/lib/command_kit/interactive.rb +248 -0
- data/lib/command_kit/main.rb +18 -9
- data/lib/command_kit/man.rb +44 -0
- data/lib/command_kit/open_app.rb +69 -0
- data/lib/command_kit/options/option.rb +3 -6
- data/lib/command_kit/options/option_value.rb +5 -2
- data/lib/command_kit/options/parser.rb +46 -19
- data/lib/command_kit/options/quiet.rb +3 -0
- data/lib/command_kit/options/verbose.rb +5 -0
- data/lib/command_kit/options/version.rb +6 -0
- data/lib/command_kit/options.rb +32 -7
- data/lib/command_kit/os/linux.rb +157 -0
- data/lib/command_kit/os.rb +165 -11
- data/lib/command_kit/package_manager.rb +200 -0
- data/lib/command_kit/pager.rb +80 -11
- data/lib/command_kit/printing/indent.rb +27 -4
- data/lib/command_kit/printing.rb +35 -1
- data/lib/command_kit/program_name.rb +7 -0
- data/lib/command_kit/stdio.rb +24 -0
- data/lib/command_kit/sudo.rb +40 -0
- data/lib/command_kit/terminal.rb +159 -0
- data/lib/command_kit/usage.rb +14 -0
- data/lib/command_kit/version.rb +1 -1
- data/lib/command_kit/xdg.rb +13 -0
- data/lib/command_kit.rb +1 -0
- data/spec/arguments/argument_spec.rb +2 -2
- data/spec/arguments_spec.rb +53 -27
- data/spec/colors_spec.rb +277 -13
- data/spec/command_name_spec.rb +1 -1
- data/spec/command_spec.rb +79 -5
- data/spec/commands/auto_load/subcommand_spec.rb +1 -1
- data/spec/commands/auto_load_spec.rb +34 -3
- data/spec/commands/auto_require_spec.rb +2 -2
- data/spec/commands/help_spec.rb +1 -1
- data/spec/commands/parent_command_spec.rb +1 -1
- data/spec/commands/subcommand_spec.rb +1 -1
- data/spec/commands_spec.rb +103 -29
- data/spec/description_spec.rb +1 -25
- data/spec/env/home_spec.rb +1 -1
- data/spec/env/path_spec.rb +7 -1
- data/spec/examples_spec.rb +1 -25
- data/spec/exception_handler_spec.rb +1 -1
- data/spec/help/man_spec.rb +45 -58
- data/spec/help_spec.rb +0 -25
- data/spec/inflector_spec.rb +71 -9
- data/spec/interactive_spec.rb +415 -0
- data/spec/main_spec.rb +7 -7
- data/spec/man_spec.rb +46 -0
- data/spec/open_app_spec.rb +85 -0
- data/spec/options/option_spec.rb +5 -5
- data/spec/options/option_value_spec.rb +56 -1
- data/spec/options_spec.rb +283 -1
- data/spec/os/linux_spec.rb +164 -0
- data/spec/os_spec.rb +201 -14
- data/spec/package_manager_spec.rb +806 -0
- data/spec/pager_spec.rb +76 -11
- data/spec/printing/indent_spec.rb +8 -6
- data/spec/printing_spec.rb +33 -3
- data/spec/program_name_spec.rb +1 -1
- data/spec/spec_helper.rb +0 -3
- data/spec/sudo_spec.rb +51 -0
- data/spec/{console_spec.rb → terminal_spec.rb} +65 -35
- data/spec/usage_spec.rb +2 -2
- data/spec/xdg_spec.rb +1 -1
- metadata +26 -8
- data/lib/command_kit/console.rb +0 -141
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'command_kit/stdio'
|
2
|
+
require 'command_kit/env'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'io/console'
|
6
|
+
rescue LoadError
|
7
|
+
end
|
8
|
+
|
9
|
+
module CommandKit
|
10
|
+
#
|
11
|
+
# Provides direct access to the terminal.
|
12
|
+
#
|
13
|
+
# ## Environment Variables
|
14
|
+
#
|
15
|
+
# * `LINES` - The explicit number of lines or rows the console should have.
|
16
|
+
# * `COLUMNS` - The explicit number of columns the console should have.
|
17
|
+
#
|
18
|
+
# @see https://rubydoc.info/gems/io-console/IO
|
19
|
+
#
|
20
|
+
module Terminal
|
21
|
+
include Stdio
|
22
|
+
include Env
|
23
|
+
|
24
|
+
# The default terminal height to fallback to.
|
25
|
+
DEFAULT_TERMINAL_HEIGHT = 25
|
26
|
+
|
27
|
+
# The default terminal width to fallback to.
|
28
|
+
DEFAULT_TERMINAL_WIDTH = 80
|
29
|
+
|
30
|
+
#
|
31
|
+
# Initializes any terminal settings.
|
32
|
+
#
|
33
|
+
# @param [Hash{Symbol => Object}] kwargs
|
34
|
+
# Additional keyword arguments.
|
35
|
+
#
|
36
|
+
# @note
|
37
|
+
# If the `$LINES` env variable is set, and is non-zero, it will be
|
38
|
+
# returned by {#terminal_height}.
|
39
|
+
#
|
40
|
+
# @note
|
41
|
+
# If the `$COLUMNS` env variable is set, and is non-zero, it will be
|
42
|
+
# returned by {#terminal_width}.
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
#
|
46
|
+
def initialize(**kwargs)
|
47
|
+
super(**kwargs)
|
48
|
+
|
49
|
+
@terminal_height = if (lines = env['LINES'])
|
50
|
+
lines.to_i
|
51
|
+
else
|
52
|
+
DEFAULT_TERMINAL_HEIGHT
|
53
|
+
end
|
54
|
+
|
55
|
+
@terminal_width = if (columns = env['COLUMNS'])
|
56
|
+
columns.to_i
|
57
|
+
else
|
58
|
+
DEFAULT_TERMINAL_WIDTH
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Determines if program is running in a terminal.
|
64
|
+
#
|
65
|
+
# @return [Boolean]
|
66
|
+
# Specifies whether {Stdio#stdout stdout} is connected to a terminal.
|
67
|
+
#
|
68
|
+
# @api public
|
69
|
+
#
|
70
|
+
def terminal?
|
71
|
+
IO.respond_to?(:console) && stdout.tty?
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# @since 0.2.0
|
76
|
+
#
|
77
|
+
alias tty? terminal?
|
78
|
+
|
79
|
+
#
|
80
|
+
# Returns the terminal object, if {Stdio#stdout stdout} is connected to a
|
81
|
+
# terminal.
|
82
|
+
#
|
83
|
+
# @return [IO, nil]
|
84
|
+
# The IO objects or `nil` if {Stdio#stdout stdout} is not connected to a
|
85
|
+
# terminal.
|
86
|
+
#
|
87
|
+
# @example
|
88
|
+
# terminal
|
89
|
+
# # => #<File:/dev/tty>
|
90
|
+
#
|
91
|
+
# @see https://rubydoc.info/gems/io-console/IO
|
92
|
+
#
|
93
|
+
# @api semipublic
|
94
|
+
#
|
95
|
+
def terminal
|
96
|
+
IO.console if terminal?
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Returns the terminal's height in number of lines.
|
101
|
+
#
|
102
|
+
# @return [Integer]
|
103
|
+
# The terminal's height in number of lines.
|
104
|
+
#
|
105
|
+
# @example
|
106
|
+
# terminal_height
|
107
|
+
# # => 22
|
108
|
+
#
|
109
|
+
# @api public
|
110
|
+
#
|
111
|
+
def terminal_height
|
112
|
+
if (terminal = self.terminal)
|
113
|
+
terminal.winsize[0]
|
114
|
+
else
|
115
|
+
@terminal_height
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# Returns the terminal's width in number of lines.
|
121
|
+
#
|
122
|
+
# @return [Integer]
|
123
|
+
# The terminal's width in number of columns.
|
124
|
+
#
|
125
|
+
# @example
|
126
|
+
# terminal_width
|
127
|
+
# # => 91
|
128
|
+
#
|
129
|
+
# @api public
|
130
|
+
#
|
131
|
+
def terminal_width
|
132
|
+
if (terminal = self.terminal)
|
133
|
+
terminal.winsize[1]
|
134
|
+
else
|
135
|
+
@terminal_width
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
#
|
140
|
+
# The terminal height (lines) and width (columns).
|
141
|
+
#
|
142
|
+
# @return [(Integer, Integer)]
|
143
|
+
# Returns the height and width of the terminal.
|
144
|
+
#
|
145
|
+
# @example
|
146
|
+
# terminal_size
|
147
|
+
# # => [23, 91]
|
148
|
+
#
|
149
|
+
# @api public
|
150
|
+
#
|
151
|
+
def terminal_size
|
152
|
+
if (terminal = self.terminal)
|
153
|
+
terminal.winsize
|
154
|
+
else
|
155
|
+
[@terminal_height, @terminal_width]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
data/lib/command_kit/usage.rb
CHANGED
@@ -15,6 +15,9 @@ module CommandKit
|
|
15
15
|
include CommandName
|
16
16
|
include Help
|
17
17
|
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
#
|
18
21
|
module ModuleMethods
|
19
22
|
#
|
20
23
|
# Extends {ClassMethods} or {ModuleMethods}, depending on whether {Usage}
|
@@ -50,6 +53,11 @@ module CommandKit
|
|
50
53
|
# @return [String, Array<String>]
|
51
54
|
# The class'es or superclass'es usage string(s).
|
52
55
|
#
|
56
|
+
# @example
|
57
|
+
# usage "[options] ARG1 ARG2 [ARG3 ...]"
|
58
|
+
#
|
59
|
+
# @api public
|
60
|
+
#
|
53
61
|
def usage(new_usage=nil)
|
54
62
|
if new_usage
|
55
63
|
@usage = new_usage
|
@@ -65,6 +73,8 @@ module CommandKit
|
|
65
73
|
#
|
66
74
|
# @return [Array<String>, String, nil]
|
67
75
|
#
|
76
|
+
# @api public
|
77
|
+
#
|
68
78
|
def usage
|
69
79
|
case (usage = self.class.usage)
|
70
80
|
when Array
|
@@ -77,6 +87,8 @@ module CommandKit
|
|
77
87
|
#
|
78
88
|
# Prints the `usage: ...` output.
|
79
89
|
#
|
90
|
+
# @api semipublic
|
91
|
+
#
|
80
92
|
def help_usage
|
81
93
|
case (usage = self.usage)
|
82
94
|
when Array
|
@@ -95,6 +107,8 @@ module CommandKit
|
|
95
107
|
#
|
96
108
|
# @see #help_usage
|
97
109
|
#
|
110
|
+
# @api public
|
111
|
+
#
|
98
112
|
def help
|
99
113
|
help_usage
|
100
114
|
end
|
data/lib/command_kit/version.rb
CHANGED
data/lib/command_kit/xdg.rb
CHANGED
@@ -24,6 +24,9 @@ module CommandKit
|
|
24
24
|
include CommandName
|
25
25
|
include Env::Home
|
26
26
|
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
#
|
27
30
|
module ModuleMethods
|
28
31
|
#
|
29
32
|
# Extends {ClassMethods} or {ModuleMethods}, depending on whether {XDG} is
|
@@ -61,6 +64,8 @@ module CommandKit
|
|
61
64
|
# {CommandName::ClassMethods#command_name} if no {#xdg_namespace} has
|
62
65
|
# been defined.
|
63
66
|
#
|
67
|
+
# @api public
|
68
|
+
#
|
64
69
|
def xdg_namespace(new_namespace=nil)
|
65
70
|
if new_namespace
|
66
71
|
@xdg_namespace = new_namespace.to_s
|
@@ -77,16 +82,22 @@ module CommandKit
|
|
77
82
|
# The `~/.config/<xdg_namespace>` directory.
|
78
83
|
#
|
79
84
|
# @return [String]
|
85
|
+
#
|
86
|
+
# @api public
|
80
87
|
attr_reader :config_dir
|
81
88
|
|
82
89
|
# The `~/.local/share/<xdg_namespace>` directory.
|
83
90
|
#
|
84
91
|
# @return [String]
|
92
|
+
#
|
93
|
+
# @api public
|
85
94
|
attr_reader :local_share_dir
|
86
95
|
|
87
96
|
# The `~/.cache/<xdg_namespace>` directory.
|
88
97
|
#
|
89
98
|
# @return [String]
|
99
|
+
#
|
100
|
+
# @api public
|
90
101
|
attr_reader :cache_dir
|
91
102
|
|
92
103
|
#
|
@@ -138,6 +149,8 @@ module CommandKit
|
|
138
149
|
#
|
139
150
|
# @see ClassMethods#xdg_namespace
|
140
151
|
#
|
152
|
+
# @api semipublic
|
153
|
+
#
|
141
154
|
def xdg_namespace
|
142
155
|
self.class.xdg_namespace
|
143
156
|
end
|
data/lib/command_kit.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'command_kit/arguments/argument'
|
3
3
|
|
4
|
-
describe Arguments::Argument do
|
4
|
+
describe CommandKit::Arguments::Argument do
|
5
5
|
let(:name) { :foo }
|
6
6
|
let(:usage) { 'FOO' }
|
7
7
|
let(:required) { true }
|
@@ -126,7 +126,7 @@ describe Arguments::Argument do
|
|
126
126
|
let(:repeats) { false }
|
127
127
|
|
128
128
|
it "must return the usage name unchanged" do
|
129
|
-
expect(subject.usage).to eq(
|
129
|
+
expect(subject.usage).to eq(usage)
|
130
130
|
end
|
131
131
|
end
|
132
132
|
end
|
data/spec/arguments_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'command_kit/arguments'
|
3
3
|
|
4
|
-
describe Arguments do
|
4
|
+
describe CommandKit::Arguments do
|
5
5
|
module TestArguments
|
6
6
|
class ImplicitCmd
|
7
7
|
include CommandKit::Arguments
|
@@ -13,8 +13,8 @@ describe Arguments do
|
|
13
13
|
describe ".included" do
|
14
14
|
subject { command_class }
|
15
15
|
|
16
|
-
it { expect(subject).to include(Main) }
|
17
|
-
it { expect(subject).to include(Help) }
|
16
|
+
it { expect(subject).to include(CommandKit::Main) }
|
17
|
+
it { expect(subject).to include(CommandKit::Help) }
|
18
18
|
end
|
19
19
|
|
20
20
|
describe ".arguments" do
|
@@ -137,6 +137,56 @@ describe Arguments do
|
|
137
137
|
|
138
138
|
subject { command_class.new }
|
139
139
|
|
140
|
+
describe "#main" do
|
141
|
+
module TestArguments
|
142
|
+
class TestCommand
|
143
|
+
|
144
|
+
include CommandKit::Arguments
|
145
|
+
|
146
|
+
argument :argument1, required: true,
|
147
|
+
usage: 'ARG1',
|
148
|
+
desc: "Argument 1"
|
149
|
+
|
150
|
+
argument :argument2, required: false,
|
151
|
+
usage: 'ARG2',
|
152
|
+
desc: "Argument 2"
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
let(:command_class) { TestArguments::TestCommand }
|
158
|
+
|
159
|
+
context "when given the correct number of arguments" do
|
160
|
+
let(:argv) { %w[arg1 arg2] }
|
161
|
+
|
162
|
+
it "must parse options before validating the number of arguments" do
|
163
|
+
expect {
|
164
|
+
expect(subject.main(argv)).to eq(0)
|
165
|
+
}.to_not output.to_stderr
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context "when given fewer than the required number of arguments" do
|
170
|
+
let(:argv) { %w[] }
|
171
|
+
|
172
|
+
it "must print an error message and return 1" do
|
173
|
+
expect {
|
174
|
+
expect(subject.main(argv)).to eq(1)
|
175
|
+
}.to output("#{subject.command_name}: insufficient number of arguments.#{$/}").to_stderr
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context "when given more than the total number of arguments" do
|
180
|
+
let(:argv) { %w[foo bar baz] }
|
181
|
+
|
182
|
+
it "must print an error message and return 1" do
|
183
|
+
expect {
|
184
|
+
expect(subject.main(argv)).to eq(1)
|
185
|
+
}.to output("#{subject.command_name}: too many arguments given.#{$/}").to_stderr
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
140
190
|
describe "#help_arguments" do
|
141
191
|
context "when #arguments returns {}" do
|
142
192
|
module TestArguments
|
@@ -192,29 +242,5 @@ describe Arguments do
|
|
192
242
|
|
193
243
|
subject.help
|
194
244
|
end
|
195
|
-
|
196
|
-
context "when the superclass defines it's own #help method" do
|
197
|
-
module TestDescription
|
198
|
-
class SuperclassHelpMethod
|
199
|
-
def help
|
200
|
-
puts 'superclass'
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
class InheritedHelpMethod < SuperclassHelpMethod
|
205
|
-
include CommandKit::Arguments
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
let(:super_command_class) { TestDescription::SuperclassHelpMethod }
|
210
|
-
let(:command_class) { TestDescription::InheritedHelpMethod }
|
211
|
-
|
212
|
-
it "must call the superclass'es #help method first" do
|
213
|
-
expect_any_instance_of(super_command_class).to receive(:help)
|
214
|
-
expect(subject).to receive(:help_arguments)
|
215
|
-
|
216
|
-
subject.help
|
217
|
-
end
|
218
|
-
end
|
219
245
|
end
|
220
246
|
end
|