simple_scripting 0.11.1 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/simple_scripting/argv.rb +38 -11
- data/lib/simple_scripting/version.rb +1 -1
- data/simple_scripting.gemspec +1 -1
- data/spec/simple_scripting/argv_spec.rb +371 -356
- data/spec/simple_scripting/configuration_spec.rb +4 -8
- data/spec/simple_scripting/tab_completion_spec.rb +0 -12
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0a31e06185c711924565be44455735e02e55b31a98fc6782a101214cf9d37266
|
4
|
+
data.tar.gz: 2fa2139a689a85e1703604a3da2be186ac8596b9b17c0bc2af4c8c8244c8ad4d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bbf4a7477108d85b62922592eedbc550f0896ca75265385c219c052636d0c45628cd93ca59603649f9ee9227cf00fdf2526732918efb0ab7d55fe62d4b158196
|
7
|
+
data.tar.gz: b754caa9b812a0d7a5882d5bc2c67b0e72a276da9385a2be70d423838f00da711a182fb1f9b67c4072176c73a9d4020acb3b2070fba54f83615edd830cc5aa5c
|
@@ -7,9 +7,19 @@ module SimpleScripting
|
|
7
7
|
# The fact that the following errors don't descend from OptionParser::InvalidOption is somewhat
|
8
8
|
# annoying, however, there should be no practical problem.
|
9
9
|
#
|
10
|
-
class InvalidCommand < StandardError; end
|
11
10
|
class ArgumentError < StandardError; end
|
12
11
|
|
12
|
+
class InvalidCommand < StandardError
|
13
|
+
|
14
|
+
attr_reader :valid_commands
|
15
|
+
|
16
|
+
def initialize(message, valid_commands)
|
17
|
+
super(message)
|
18
|
+
@valid_commands = valid_commands
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
13
23
|
class ExitWithCommandsHelpPrinting < Struct.new(:commands_definition)
|
14
24
|
# Note that :long_help is not used.
|
15
25
|
def print_help(output, long_help)
|
@@ -61,12 +71,18 @@ module SimpleScripting
|
|
61
71
|
exit_data.print_help(output, @long_help)
|
62
72
|
|
63
73
|
nil # to be used with the 'decode(...) || exit' pattern
|
64
|
-
rescue SimpleScripting::Argv::ArgumentError,
|
65
|
-
if raise_errors
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
74
|
+
rescue SimpleScripting::Argv::ArgumentError, OptionParser::InvalidOption => error
|
75
|
+
raise if raise_errors
|
76
|
+
|
77
|
+
output.puts "Command error!: #{error.message}"
|
78
|
+
rescue SimpleScripting::Argv::InvalidCommand => error
|
79
|
+
raise if raise_errors
|
80
|
+
|
81
|
+
output.puts <<~MESSAGE
|
82
|
+
Command error!: #{error.message}"
|
83
|
+
|
84
|
+
Valid commands: #{error.valid_commands.join(", ")}
|
85
|
+
MESSAGE
|
70
86
|
ensure
|
71
87
|
@long_help = nil
|
72
88
|
end
|
@@ -129,13 +145,13 @@ module SimpleScripting
|
|
129
145
|
|
130
146
|
command = command_for_check
|
131
147
|
|
132
|
-
raise InvalidCommand.new("Missing command") if command.nil?
|
148
|
+
raise InvalidCommand.new("Missing command!", commands_definition.keys) if command.nil?
|
133
149
|
|
134
150
|
command_params_definition = commands_definition[command]
|
135
151
|
|
136
152
|
case command_params_definition
|
137
153
|
when nil
|
138
|
-
raise InvalidCommand.new("Invalid command: #{command}")
|
154
|
+
raise InvalidCommand.new("Invalid command: #{command}", commands_definition.keys)
|
139
155
|
when Hash
|
140
156
|
commands_stack << command
|
141
157
|
|
@@ -210,14 +226,25 @@ module SimpleScripting
|
|
210
226
|
# DEFINITIONS PROCESSING ###############################
|
211
227
|
|
212
228
|
def process_option_definition!(param_definition, parser_opts, result)
|
229
|
+
# Work on a copy; in at least one case (data type definition), we perform destructive
|
230
|
+
# operations.
|
231
|
+
#
|
232
|
+
param_definition = param_definition.dup
|
233
|
+
|
213
234
|
if param_definition[1] && param_definition[1].start_with?('--')
|
214
|
-
|
235
|
+
raw_key, key_argument = param_definition[1].split(' ')
|
236
|
+
key = raw_key[2 .. -1].tr('-', '_').to_sym
|
237
|
+
|
238
|
+
if key_argument&.include?(',')
|
239
|
+
param_definition.insert(2, Array)
|
240
|
+
end
|
215
241
|
else
|
216
242
|
key = param_definition[0][1 .. -1].to_sym
|
217
243
|
end
|
218
244
|
|
219
245
|
parser_opts.on(*param_definition) do |value|
|
220
|
-
|
246
|
+
raise "Unexpected (nil; likely programmatic error) value for param definition #{param_definition}" if value.nil?
|
247
|
+
result[key] = value
|
221
248
|
end
|
222
249
|
end
|
223
250
|
|
data/simple_scripting.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.platform = Gem::Platform::RUBY
|
11
11
|
s.required_ruby_version = '>= 2.3.0'
|
12
12
|
s.authors = ["Saverio Miroddi"]
|
13
|
-
s.date = "
|
13
|
+
s.date = "2022-07-21"
|
14
14
|
s.email = ["saverio.pub2@gmail.com"]
|
15
15
|
s.homepage = "https://github.com/saveriomiroddi/simple_scripting"
|
16
16
|
s.summary = "Library for simplifying some typical scripting functionalities."
|
@@ -2,497 +2,512 @@ require_relative '../../lib/simple_scripting/argv.rb'
|
|
2
2
|
|
3
3
|
require 'stringio'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
describe 'Basic functionality' do
|
12
|
-
|
13
|
-
let(:decoder_params) {[
|
14
|
-
['-a' ],
|
15
|
-
['-b', '"-b" description'],
|
16
|
-
['-c', '--c-switch' ],
|
17
|
-
['-d', '--d-switch', '"-d" description'],
|
18
|
-
['-e', '--e-switch VALUE' ],
|
19
|
-
['-f', '--f-switch VALUE', '"-f" description'],
|
20
|
-
'mandatory',
|
21
|
-
'[optional]',
|
22
|
-
long_help: 'This is the long help!',
|
23
|
-
output: output_buffer,
|
24
|
-
]}
|
25
|
-
|
26
|
-
context 'help' do
|
27
|
-
|
28
|
-
it 'should print help automatically by default' do
|
29
|
-
decoder_params.last[:arguments] = ['-h']
|
30
|
-
|
31
|
-
return_value = described_class.decode(*decoder_params)
|
32
|
-
|
33
|
-
expected_output = <<~OUTPUT
|
34
|
-
Usage: rspec [options] <mandatory> [<optional>]
|
35
|
-
-a
|
36
|
-
-b "-b" description
|
37
|
-
-c, --c-switch
|
38
|
-
-d, --d-switch "-d" description
|
39
|
-
-e, --e-switch VALUE
|
40
|
-
-f, --f-switch VALUE "-f" description
|
41
|
-
-h, --help Help
|
42
|
-
|
43
|
-
This is the long help!
|
44
|
-
OUTPUT
|
45
|
-
|
46
|
-
expect(output_buffer.string).to eql(expected_output)
|
47
|
-
expect(return_value).to be(nil)
|
48
|
-
end
|
49
|
-
|
50
|
-
it 'should not interpret the --help argument, and not print the help, on auto_help: false' do
|
51
|
-
decoder_params.last.merge!(
|
52
|
-
arguments: ['--help', 'm_arg'],
|
53
|
-
auto_help: false
|
54
|
-
)
|
55
|
-
|
56
|
-
actual_result = described_class.decode(*decoder_params)
|
57
|
-
|
58
|
-
expected_result = {
|
59
|
-
help: true,
|
60
|
-
mandatory: 'm_arg',
|
61
|
-
}
|
62
|
-
|
63
|
-
expect(output_buffer.string).to eql('')
|
64
|
-
expect(actual_result).to eql(expected_result)
|
65
|
-
end
|
66
|
-
|
67
|
-
it "should check all the options/arguments when --help is passed, raising an error when they're not correct" do
|
68
|
-
decoder_params.last.merge!(
|
69
|
-
arguments: ['--help'],
|
70
|
-
auto_help: false,
|
71
|
-
raise_errors: true,
|
72
|
-
)
|
73
|
-
|
74
|
-
decoding = -> { described_class.decode(*decoder_params) }
|
5
|
+
module SimpleScripting
|
6
|
+
describe Argv do
|
7
|
+
let(:output_buffer) do
|
8
|
+
StringIO.new
|
9
|
+
end
|
75
10
|
|
76
|
-
|
77
|
-
|
11
|
+
describe 'Basic functionality' do
|
12
|
+
let(:decoder_params) {[
|
13
|
+
['-a' ],
|
14
|
+
['-b', '"-b" description'],
|
15
|
+
['-c', '--c-switch VAL1,VAL2' ],
|
16
|
+
['-d', '--d-switch', '"-d" description'],
|
17
|
+
['-e', '--e-switch VALUE' ],
|
18
|
+
['-f', '--f-switch VALUE', '"-f" description'],
|
19
|
+
'mandatory',
|
20
|
+
'[optional]',
|
21
|
+
long_help: 'This is the long help!',
|
22
|
+
output: output_buffer,
|
23
|
+
]}
|
78
24
|
|
79
|
-
|
25
|
+
context 'help' do
|
26
|
+
it 'should print help automatically by default' do
|
27
|
+
decoder_params.last[:arguments] = ['-h']
|
80
28
|
|
81
|
-
|
82
|
-
decoder_params.last[:arguments] = ['-a', '-b', '-c', '-d', '-ev_swt', '-fv_swt', 'm_arg', 'o_arg']
|
29
|
+
return_value = described_class.decode(*decoder_params)
|
83
30
|
|
84
|
-
|
31
|
+
expected_output = <<~OUTPUT
|
32
|
+
Usage: rspec [options] <mandatory> [<optional>]
|
33
|
+
-a
|
34
|
+
-b "-b" description
|
35
|
+
-c, --c-switch VAL1,VAL2
|
36
|
+
-d, --d-switch "-d" description
|
37
|
+
-e, --e-switch VALUE
|
38
|
+
-f, --f-switch VALUE "-f" description
|
39
|
+
-h, --help Help
|
85
40
|
|
86
|
-
|
87
|
-
|
88
|
-
b: true,
|
89
|
-
c_switch: true,
|
90
|
-
d_switch: true,
|
91
|
-
e_switch: 'v_swt',
|
92
|
-
f_switch: 'v_swt',
|
93
|
-
mandatory: 'm_arg',
|
94
|
-
optional: 'o_arg',
|
95
|
-
}
|
41
|
+
This is the long help!
|
42
|
+
OUTPUT
|
96
43
|
|
97
|
-
|
98
|
-
|
44
|
+
expect(output_buffer.string).to eql(expected_output)
|
45
|
+
expect(return_value).to be(nil)
|
46
|
+
end
|
99
47
|
|
100
|
-
|
101
|
-
|
48
|
+
it 'should not interpret the --help argument, and not print the help, on auto_help: false' do
|
49
|
+
decoder_params.last.merge!(
|
50
|
+
arguments: ['--help', 'm_arg'],
|
51
|
+
auto_help: false
|
52
|
+
)
|
102
53
|
|
103
|
-
|
54
|
+
actual_result = described_class.decode(*decoder_params)
|
104
55
|
|
105
|
-
|
106
|
-
|
107
|
-
|
56
|
+
expected_result = {
|
57
|
+
help: true,
|
58
|
+
mandatory: 'm_arg',
|
59
|
+
}
|
108
60
|
|
109
|
-
|
110
|
-
|
61
|
+
expect(output_buffer.string).to eql('')
|
62
|
+
expect(actual_result).to eql(expected_result)
|
63
|
+
end
|
111
64
|
|
112
|
-
|
65
|
+
it "should check all the options/arguments when --help is passed, raising an error when they're not correct" do
|
66
|
+
decoder_params.last.merge!(
|
67
|
+
arguments: ['--help'],
|
68
|
+
auto_help: false,
|
69
|
+
raise_errors: true,
|
70
|
+
)
|
113
71
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
72
|
+
expect {
|
73
|
+
described_class.decode(*decoder_params)
|
74
|
+
}.to raise_error(Argv::ArgumentError, "Missing mandatory argument(s)")
|
75
|
+
end
|
76
|
+
end # context 'help'
|
119
77
|
|
120
|
-
it "should
|
121
|
-
decoder_params.last[:arguments] = ['
|
78
|
+
it "should implement basic switches, with conversion, and arguments (all set)" do
|
79
|
+
decoder_params.last[:arguments] = ['-a', '-b', '-c', 'a,b,c', '-d', '-ev_swt', '-fv_swt', 'm_arg', 'o_arg']
|
122
80
|
|
123
81
|
actual_result = described_class.decode(*decoder_params)
|
124
82
|
|
125
83
|
expected_result = {
|
126
|
-
|
84
|
+
a: true,
|
85
|
+
b: true,
|
86
|
+
c_switch: %w(a b c),
|
87
|
+
d_switch: true,
|
88
|
+
e_switch: 'v_swt',
|
89
|
+
f_switch: 'v_swt',
|
90
|
+
mandatory: 'm_arg',
|
91
|
+
optional: 'o_arg',
|
127
92
|
}
|
128
93
|
|
129
94
|
expect(actual_result).to eql(expected_result)
|
130
95
|
end
|
131
96
|
|
132
|
-
it "should
|
133
|
-
decoder_params.last[:arguments] = ['
|
97
|
+
it "should implement basic switches and arguments (no optional argument)" do
|
98
|
+
decoder_params.last[:arguments] = ['m_arg']
|
134
99
|
|
135
100
|
actual_result = described_class.decode(*decoder_params)
|
136
101
|
|
137
102
|
expected_result = {
|
138
|
-
|
139
|
-
optional2: 'o_arg2',
|
103
|
+
mandatory: 'm_arg',
|
140
104
|
}
|
141
105
|
|
142
106
|
expect(actual_result).to eql(expected_result)
|
143
107
|
end
|
144
108
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
it "should print the error, with a previx, by default, instead of raising an error" do
|
151
|
-
decoder_params.last[:arguments] = []
|
152
|
-
|
153
|
-
actual_result = described_class.decode(*decoder_params)
|
154
|
-
|
155
|
-
# Returning nil is an important specification, as it's part of the Argv protocol of doing
|
156
|
-
# so in case of problem/exit.
|
157
|
-
expect(actual_result).to be(nil)
|
158
|
-
|
159
|
-
expect(output_buffer.string).to eql("Command error!: Missing mandatory argument(s)\n")
|
160
|
-
end
|
161
|
-
|
162
|
-
it "should raise an error when mandatory arguments are missing" do
|
163
|
-
decoder_params.last.merge!(
|
164
|
-
arguments: [],
|
165
|
-
raise_errors: true,
|
166
|
-
)
|
109
|
+
context "booleans" do
|
110
|
+
VALID_BOOLS = {
|
111
|
+
'false' => false,
|
112
|
+
'true' => true,
|
113
|
+
}
|
167
114
|
|
168
|
-
|
115
|
+
INVALID_BOOLS = %w[falx FALSE TRUE]
|
169
116
|
|
170
|
-
|
171
|
-
|
117
|
+
VALID_BOOLS.each do |user_value, decoded_value|
|
118
|
+
it "should decode a #{decoded_value} value" do
|
119
|
+
decoder_params = [
|
120
|
+
["-b", "--mybool VAL", TrueClass],
|
121
|
+
output: output_buffer,
|
122
|
+
arguments: ['--mybool', 'false']
|
123
|
+
]
|
172
124
|
|
173
|
-
|
174
|
-
decoder_params.last.merge!(
|
175
|
-
arguments: ['arg1', 'arg2', 'excessive_arg'],
|
176
|
-
raise_errors: true,
|
177
|
-
)
|
125
|
+
actual_result = described_class.decode(*decoder_params)
|
178
126
|
|
179
|
-
|
127
|
+
expected_result = {
|
128
|
+
mybool: false
|
129
|
+
}
|
180
130
|
|
181
|
-
|
131
|
+
expect(actual_result).to eql(expected_result)
|
132
|
+
end
|
133
|
+
end # context "booleans"
|
134
|
+
|
135
|
+
INVALID_BOOLS.each do |value|
|
136
|
+
it "should raise an error on invalid bool #{value.inspect}" do
|
137
|
+
decoder_params = [
|
138
|
+
["-b", "--mybool VAL", TrueClass],
|
139
|
+
output: output_buffer,
|
140
|
+
arguments: ['--mybool', value],
|
141
|
+
raise_errors: true,
|
142
|
+
]
|
143
|
+
|
144
|
+
expect {
|
145
|
+
described_class.decode(*decoder_params)
|
146
|
+
}.to raise_error(OptionParser::InvalidArgument)
|
147
|
+
end
|
148
|
+
end
|
182
149
|
end
|
183
150
|
|
184
|
-
|
185
|
-
|
186
|
-
end # describe 'Basic functionality'
|
187
|
-
|
188
|
-
describe 'Varargs' do
|
189
|
-
|
190
|
-
describe '(mandatory)' do
|
191
|
-
|
192
|
-
context 'as only parameter' do
|
193
|
-
|
151
|
+
context "multiple optional arguments" do
|
194
152
|
let(:decoder_params) {[
|
195
|
-
'
|
153
|
+
'[optional1]',
|
154
|
+
'[optional2]',
|
196
155
|
output: output_buffer,
|
197
|
-
arguments: ['varval1', 'varval2'],
|
198
156
|
]}
|
199
157
|
|
200
|
-
it "should
|
158
|
+
it "should correctly decode a single argument passed" do
|
159
|
+
decoder_params.last[:arguments] = ['o_arg1']
|
160
|
+
|
201
161
|
actual_result = described_class.decode(*decoder_params)
|
202
162
|
|
203
163
|
expected_result = {
|
204
|
-
|
164
|
+
optional1: 'o_arg1',
|
205
165
|
}
|
206
166
|
|
207
167
|
expect(actual_result).to eql(expected_result)
|
208
168
|
end
|
209
169
|
|
210
|
-
|
211
|
-
|
212
|
-
context 'followed by varargs' do
|
170
|
+
it "should correctly decode all arguments passed" do
|
171
|
+
decoder_params.last[:arguments] = ['o_arg1', 'o_arg2']
|
213
172
|
|
214
|
-
let(:decoder_params) {[
|
215
|
-
'mandatory',
|
216
|
-
'*varargs',
|
217
|
-
output: output_buffer,
|
218
|
-
arguments: ['mandval', 'varval1', 'varval2']
|
219
|
-
]}
|
220
|
-
|
221
|
-
it "should be decoded" do
|
222
173
|
actual_result = described_class.decode(*decoder_params)
|
223
174
|
|
224
175
|
expected_result = {
|
225
|
-
|
226
|
-
|
176
|
+
optional1: 'o_arg1',
|
177
|
+
optional2: 'o_arg2',
|
227
178
|
}
|
228
179
|
|
229
180
|
expect(actual_result).to eql(expected_result)
|
230
181
|
end
|
231
|
-
|
232
182
|
end
|
233
183
|
|
234
184
|
context "error handling" do
|
185
|
+
# All the other UTs use error raising, for convenience.
|
186
|
+
it "should print the error, with a previx, by default, instead of raising an error" do
|
187
|
+
decoder_params.last[:arguments] = []
|
235
188
|
|
236
|
-
|
237
|
-
'*varargs',
|
238
|
-
output: output_buffer,
|
239
|
-
arguments: [],
|
240
|
-
]}
|
241
|
-
|
242
|
-
it "should raise an error when they are not specified" do
|
243
|
-
decoder_params.last[:raise_errors] = true
|
189
|
+
actual_result = described_class.decode(*decoder_params)
|
244
190
|
|
245
|
-
|
191
|
+
# Returning nil is an important specification, as it's part of the Argv protocol of doing
|
192
|
+
# so in case of problem/exit.
|
193
|
+
expect(actual_result).to be(nil)
|
246
194
|
|
247
|
-
expect(
|
195
|
+
expect(output_buffer.string).to eql("Command error!: Missing mandatory argument(s)\n")
|
248
196
|
end
|
249
197
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
let(:decoder_params) {[
|
257
|
-
'[*varargs]',
|
258
|
-
output: output_buffer,
|
259
|
-
]}
|
260
|
-
|
261
|
-
it "should be decoded" do
|
262
|
-
decoder_params.last[:arguments] = ['varval1', 'varval2']
|
263
|
-
|
264
|
-
actual_result = described_class.decode(*decoder_params)
|
265
|
-
|
266
|
-
expected_result = {
|
267
|
-
varargs: ['varval1', 'varval2'],
|
268
|
-
}
|
269
|
-
|
270
|
-
expect(actual_result).to eql(expected_result)
|
271
|
-
end
|
272
|
-
|
273
|
-
it "should be allowed not to be specified" do
|
274
|
-
decoder_params.last[:arguments] = []
|
198
|
+
it "should raise an error when mandatory arguments are missing" do
|
199
|
+
decoder_params.last.merge!(
|
200
|
+
arguments: [],
|
201
|
+
raise_errors: true,
|
202
|
+
)
|
275
203
|
|
276
|
-
|
204
|
+
expect {
|
205
|
+
described_class.decode(*decoder_params)
|
206
|
+
}.to raise_error(Argv::ArgumentError, "Missing mandatory argument(s)")
|
207
|
+
end
|
277
208
|
|
278
|
-
|
279
|
-
|
280
|
-
|
209
|
+
it "should raise an error when there are too many arguments" do
|
210
|
+
decoder_params.last.merge!(
|
211
|
+
arguments: ['arg1', 'arg2', 'excessive_arg'],
|
212
|
+
raise_errors: true,
|
213
|
+
)
|
281
214
|
|
282
|
-
|
283
|
-
|
215
|
+
expect {
|
216
|
+
described_class.decode(*decoder_params)
|
217
|
+
}.to raise_error(Argv::ArgumentError, "Too many arguments")
|
218
|
+
end
|
219
|
+
end # context "error handling"
|
220
|
+
end # describe 'Basic functionality'
|
284
221
|
|
285
|
-
|
222
|
+
describe 'Varargs' do
|
223
|
+
describe '(mandatory)' do
|
224
|
+
context 'as only parameter' do
|
225
|
+
let(:decoder_params) {[
|
226
|
+
'*varargs',
|
227
|
+
output: output_buffer,
|
228
|
+
arguments: ['varval1', 'varval2'],
|
229
|
+
]}
|
286
230
|
|
287
|
-
|
231
|
+
it "should be decoded" do
|
232
|
+
actual_result = described_class.decode(*decoder_params)
|
288
233
|
|
289
|
-
|
234
|
+
expected_result = {
|
235
|
+
varargs: ['varval1', 'varval2'],
|
236
|
+
}
|
290
237
|
|
291
|
-
|
238
|
+
expect(actual_result).to eql(expected_result)
|
239
|
+
end
|
240
|
+
end
|
292
241
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
],
|
301
|
-
output: output_buffer,
|
302
|
-
}}
|
242
|
+
context 'followed by varargs' do
|
243
|
+
let(:decoder_params) {[
|
244
|
+
'mandatory',
|
245
|
+
'*varargs',
|
246
|
+
output: output_buffer,
|
247
|
+
arguments: ['mandval', 'varval1', 'varval2']
|
248
|
+
]}
|
303
249
|
|
304
|
-
|
305
|
-
|
250
|
+
it "should be decoded" do
|
251
|
+
actual_result = described_class.decode(*decoder_params)
|
306
252
|
|
307
|
-
|
253
|
+
expected_result = {
|
254
|
+
mandatory: 'mandval',
|
255
|
+
varargs: ['varval1', 'varval2'],
|
256
|
+
}
|
308
257
|
|
309
|
-
|
258
|
+
expect(actual_result).to eql(expected_result)
|
259
|
+
end
|
260
|
+
end
|
310
261
|
|
311
|
-
|
312
|
-
|
262
|
+
context "error handling" do
|
263
|
+
let(:decoder_params) {[
|
264
|
+
'*varargs',
|
265
|
+
output: output_buffer,
|
266
|
+
arguments: [],
|
267
|
+
]}
|
313
268
|
|
314
|
-
|
269
|
+
it "should raise an error when they are not specified" do
|
270
|
+
decoder_params.last[:raise_errors] = true
|
315
271
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
272
|
+
expect {
|
273
|
+
described_class.decode(*decoder_params)
|
274
|
+
}.to raise_error(Argv::ArgumentError, "Missing mandatory argument(s)")
|
275
|
+
end
|
276
|
+
end # context "error handling"
|
277
|
+
end # describe '(mandatory)'
|
321
278
|
|
322
|
-
|
279
|
+
describe '(optional)' do
|
280
|
+
let(:decoder_params) {[
|
281
|
+
'[*varargs]',
|
282
|
+
output: output_buffer,
|
283
|
+
]}
|
323
284
|
|
324
|
-
|
325
|
-
|
285
|
+
it "should be decoded" do
|
286
|
+
decoder_params.last[:arguments] = ['varval1', 'varval2']
|
326
287
|
|
327
|
-
|
328
|
-
decoder_params.merge!(
|
329
|
-
arguments: [],
|
330
|
-
raise_errors: true,
|
331
|
-
)
|
288
|
+
actual_result = described_class.decode(*decoder_params)
|
332
289
|
|
333
|
-
|
290
|
+
expected_result = {
|
291
|
+
varargs: ['varval1', 'varval2'],
|
292
|
+
}
|
334
293
|
|
335
|
-
expect(
|
294
|
+
expect(actual_result).to eql(expected_result)
|
336
295
|
end
|
337
296
|
|
338
|
-
|
339
|
-
|
340
|
-
context "help" do
|
341
|
-
|
342
|
-
it 'should implement the commands help' do
|
343
|
-
decoder_params[:arguments] = ['-h']
|
297
|
+
it "should be allowed not to be specified" do
|
298
|
+
decoder_params.last[:arguments] = []
|
344
299
|
|
345
|
-
described_class.decode(decoder_params)
|
346
|
-
|
347
|
-
expected_output = <<~OUTPUT
|
348
|
-
Valid commands:
|
300
|
+
actual_result = described_class.decode(*decoder_params)
|
349
301
|
|
350
|
-
|
351
|
-
|
302
|
+
expected_result = {
|
303
|
+
varargs: [],
|
304
|
+
}
|
352
305
|
|
353
|
-
expect(
|
306
|
+
expect(actual_result).to eql(expected_result)
|
354
307
|
end
|
308
|
+
end # describe '(optional)'
|
309
|
+
end # describe 'Varargs'
|
355
310
|
|
356
|
-
|
357
|
-
|
311
|
+
describe 'Commands' do
|
312
|
+
describe 'regular case' do
|
313
|
+
let(:decoder_params) {{
|
314
|
+
'command1' => [
|
315
|
+
'arg1',
|
316
|
+
long_help: 'This is the long help.'
|
317
|
+
],
|
318
|
+
'command2' => [
|
319
|
+
'arg2'
|
320
|
+
],
|
321
|
+
output: output_buffer,
|
322
|
+
}}
|
358
323
|
|
359
|
-
|
324
|
+
it 'should be decoded' do
|
325
|
+
decoder_params[:arguments] = ['command1', 'value1']
|
360
326
|
|
361
|
-
|
362
|
-
Usage: rspec command1 [options] <arg1>
|
363
|
-
-h, --help Help
|
327
|
+
actual_result = described_class.decode(decoder_params)
|
364
328
|
|
365
|
-
|
366
|
-
OUTPUT
|
329
|
+
expected_result = ['command1', arg1: 'value1']
|
367
330
|
|
368
|
-
expect(
|
331
|
+
expect(actual_result).to eql(expected_result)
|
369
332
|
end
|
370
333
|
|
371
|
-
context
|
372
|
-
|
373
|
-
it 'should not interpret the --help argument, and not print the help' do
|
334
|
+
context "error handling" do
|
335
|
+
it "should raise an error on invalid command" do
|
374
336
|
decoder_params.merge!(
|
375
|
-
arguments: ['
|
376
|
-
|
337
|
+
arguments: ['pizza'],
|
338
|
+
raise_errors: true,
|
377
339
|
)
|
378
340
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
expect(actual_result).to eql(expected_result)
|
341
|
+
expect {
|
342
|
+
described_class.decode(decoder_params)
|
343
|
+
}.to raise_error(an_instance_of(Argv::InvalidCommand).and having_attributes(
|
344
|
+
message: "Invalid command: pizza",
|
345
|
+
valid_commands: ["command1", "command2"],
|
346
|
+
))
|
386
347
|
end
|
387
348
|
|
388
|
-
it
|
349
|
+
it "should raise a specific error message on missing command" do
|
389
350
|
decoder_params.merge!(
|
390
|
-
arguments: [
|
391
|
-
|
351
|
+
arguments: [],
|
352
|
+
raise_errors: true,
|
392
353
|
)
|
393
354
|
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
expect(actual_result).to eql(expected_result)
|
355
|
+
expect {
|
356
|
+
described_class.decode(decoder_params)
|
357
|
+
}.to raise_error(an_instance_of(Argv::InvalidCommand).and having_attributes(
|
358
|
+
message: "Missing command!",
|
359
|
+
valid_commands: ["command1", "command2"],
|
360
|
+
))
|
401
361
|
end
|
362
|
+
end # context "error handling"
|
402
363
|
|
403
|
-
|
364
|
+
context "help" do
|
365
|
+
it 'should implement the commands help' do
|
366
|
+
decoder_params[:arguments] = ['-h']
|
404
367
|
|
405
|
-
|
368
|
+
described_class.decode(decoder_params)
|
406
369
|
|
407
|
-
|
370
|
+
expected_output = <<~OUTPUT
|
371
|
+
Valid commands:
|
408
372
|
|
409
|
-
|
373
|
+
command1, command2
|
374
|
+
OUTPUT
|
410
375
|
|
411
|
-
|
412
|
-
|
413
|
-
'nested1a' => [
|
414
|
-
'arg1',
|
415
|
-
long_help: 'nested1a long help.'
|
416
|
-
],
|
417
|
-
'nested1b' => [
|
418
|
-
'arg1b'
|
419
|
-
],
|
420
|
-
},
|
421
|
-
'command2' => [
|
422
|
-
'arg2'
|
423
|
-
],
|
424
|
-
output: output_buffer
|
425
|
-
}}
|
376
|
+
expect(output_buffer.string).to eql(expected_output)
|
377
|
+
end
|
426
378
|
|
427
|
-
|
428
|
-
|
379
|
+
it "should display the command given command's help" do
|
380
|
+
decoder_params[:arguments] = ['command1', '-h']
|
429
381
|
|
430
|
-
|
382
|
+
described_class.decode(decoder_params)
|
431
383
|
|
432
|
-
|
384
|
+
expected_output = <<~OUTPUT
|
385
|
+
Usage: rspec command1 [options] <arg1>
|
386
|
+
-h, --help Help
|
433
387
|
|
434
|
-
|
435
|
-
|
388
|
+
This is the long help.
|
389
|
+
OUTPUT
|
436
390
|
|
437
|
-
|
438
|
-
|
391
|
+
expect(output_buffer.string).to eql(expected_output)
|
392
|
+
end
|
439
393
|
|
440
|
-
|
394
|
+
context 'auto_help: false' do
|
395
|
+
it 'should not interpret the --help argument, and not print the help' do
|
396
|
+
decoder_params.merge!(
|
397
|
+
arguments: ['-h'],
|
398
|
+
auto_help: false,
|
399
|
+
)
|
400
|
+
|
401
|
+
actual_result = described_class.decode(decoder_params)
|
402
|
+
|
403
|
+
expected_result = {
|
404
|
+
help: true,
|
405
|
+
}
|
406
|
+
|
407
|
+
expect(actual_result).to eql(expected_result)
|
408
|
+
end
|
409
|
+
|
410
|
+
it 'should ignore and not return all the other arguments' do
|
411
|
+
decoder_params.merge!(
|
412
|
+
arguments: ['-h', 'pizza'],
|
413
|
+
auto_help: false,
|
414
|
+
)
|
415
|
+
|
416
|
+
actual_result = described_class.decode(decoder_params)
|
417
|
+
|
418
|
+
expected_result = {
|
419
|
+
help: true,
|
420
|
+
}
|
421
|
+
|
422
|
+
expect(actual_result).to eql(expected_result)
|
423
|
+
end
|
424
|
+
end # context 'auto_help: false'
|
425
|
+
end # context 'help'
|
426
|
+
end # describe 'regular case'
|
427
|
+
|
428
|
+
describe 'Nested commands' do
|
429
|
+
let(:decoder_params) {{
|
430
|
+
'command1' => {
|
431
|
+
'nested1a' => [
|
432
|
+
'arg1',
|
433
|
+
long_help: 'nested1a long help.'
|
434
|
+
],
|
435
|
+
'nested1b' => [
|
436
|
+
'arg1b'
|
437
|
+
],
|
438
|
+
},
|
439
|
+
'command2' => [
|
440
|
+
'arg2'
|
441
|
+
],
|
442
|
+
output: output_buffer
|
443
|
+
}}
|
441
444
|
|
442
|
-
|
445
|
+
it 'should be decoded (two levels)' do
|
446
|
+
decoder_params[:arguments] = ['command1', 'nested1a', 'value1']
|
443
447
|
|
444
|
-
|
445
|
-
end
|
448
|
+
actual_result = described_class.decode(decoder_params)
|
446
449
|
|
447
|
-
|
448
|
-
decoder_params[:arguments] = ['command1', '-h']
|
450
|
+
expected_result = ['command1.nested1a', arg1: 'value1']
|
449
451
|
|
450
|
-
|
452
|
+
expect(actual_result).to eql(expected_result)
|
453
|
+
end
|
451
454
|
|
452
|
-
|
453
|
-
|
455
|
+
it 'should be decoded (one level)' do
|
456
|
+
decoder_params[:arguments] = ['command2', 'value2']
|
454
457
|
|
455
|
-
|
456
|
-
OUTPUT
|
458
|
+
actual_result = described_class.decode(decoder_params)
|
457
459
|
|
458
|
-
|
459
|
-
end
|
460
|
+
expected_result = ['command2', arg2: 'value2']
|
460
461
|
|
461
|
-
|
462
|
-
|
462
|
+
expect(actual_result).to eql(expected_result)
|
463
|
+
end
|
463
464
|
|
464
|
-
|
465
|
+
it 'should print the command1 help' do
|
466
|
+
decoder_params[:arguments] = ['command1', '-h']
|
467
|
+
|
468
|
+
actual_result = described_class.decode(decoder_params)
|
465
469
|
|
466
|
-
|
467
|
-
|
468
|
-
-h, --help Help
|
470
|
+
expected_output = <<~OUTPUT
|
471
|
+
Valid commands:
|
469
472
|
|
470
|
-
|
471
|
-
|
473
|
+
nested1a, nested1b
|
474
|
+
OUTPUT
|
472
475
|
|
473
|
-
|
474
|
-
|
475
|
-
end # describe 'Nested commands'
|
476
|
+
expect(output_buffer.string).to eql(expected_output)
|
477
|
+
end
|
476
478
|
|
477
|
-
|
479
|
+
it 'should print the nested1a help, and long help' do
|
480
|
+
decoder_params[:arguments] = ['command1', 'nested1a', '-h']
|
478
481
|
|
479
|
-
|
480
|
-
#
|
481
|
-
describe 'No definitions given' do
|
482
|
+
actual_result = described_class.decode(decoder_params)
|
482
483
|
|
483
|
-
|
484
|
-
|
485
|
-
|
484
|
+
expected_output = <<~OUTPUT
|
485
|
+
Usage: rspec command1 nested1a [options] <arg1>
|
486
|
+
-h, --help Help
|
486
487
|
|
487
|
-
|
488
|
-
|
489
|
-
decoder_params[:raise_errors] = true
|
488
|
+
nested1a long help.
|
489
|
+
OUTPUT
|
490
490
|
|
491
|
-
|
491
|
+
expect(output_buffer.string).to eql(expected_output)
|
492
|
+
end
|
493
|
+
end # describe 'Nested commands'
|
494
|
+
end # describe 'Commands'
|
492
495
|
|
493
|
-
|
494
|
-
|
496
|
+
# Special case.
|
497
|
+
#
|
498
|
+
describe 'No definitions given' do
|
499
|
+
let(:decoder_params) {{
|
500
|
+
output: output_buffer,
|
501
|
+
}}
|
495
502
|
|
496
|
-
|
503
|
+
it 'should avoid options being interpreted as definitions' do
|
504
|
+
decoder_params[:arguments] = ['pizza']
|
505
|
+
decoder_params[:raise_errors] = true
|
497
506
|
|
498
|
-
|
507
|
+
expect {
|
508
|
+
described_class.decode(decoder_params)
|
509
|
+
}.to raise_error(Argv::ArgumentError, "Too many arguments")
|
510
|
+
end
|
511
|
+
end # describe 'No definitions given'
|
512
|
+
end # describe Argv
|
513
|
+
end # module SimpleScripting
|
@@ -4,7 +4,6 @@ require 'tempfile'
|
|
4
4
|
require 'tmpdir'
|
5
5
|
|
6
6
|
module SimpleScripting::ConfigurationSpecHelper
|
7
|
-
|
8
7
|
def with_tempfile(config_content)
|
9
8
|
tempfile = Tempfile.new('ss_config_test')
|
10
9
|
tempfile.write(config_content)
|
@@ -14,11 +13,9 @@ module SimpleScripting::ConfigurationSpecHelper
|
|
14
13
|
ensure
|
15
14
|
tempfile.unlink
|
16
15
|
end
|
17
|
-
|
18
16
|
end
|
19
17
|
|
20
18
|
describe SimpleScripting::Configuration do
|
21
|
-
|
22
19
|
include SimpleScripting::ConfigurationSpecHelper
|
23
20
|
|
24
21
|
let(:configuration_text) {"
|
@@ -56,9 +53,9 @@ g2_key=bang
|
|
56
53
|
|
57
54
|
it "should raise an error when required keys are missing" do
|
58
55
|
with_tempfile(configuration_text) do |config_file|
|
59
|
-
|
60
|
-
|
61
|
-
|
56
|
+
expect {
|
57
|
+
described_class.load(config_file: config_file, required: %w(abspath_key missing_key group1))
|
58
|
+
}.to raise_error(RuntimeError, "Missing required configuration key(s): missing_key, group1")
|
62
59
|
end
|
63
60
|
end
|
64
61
|
|
@@ -73,5 +70,4 @@ g2_key=bang
|
|
73
70
|
File.delete(temp_config_file)
|
74
71
|
end
|
75
72
|
end
|
76
|
-
|
77
|
-
end
|
73
|
+
end # describe SimpleScripting::Configuration
|
@@ -3,7 +3,6 @@
|
|
3
3
|
require_relative '../../lib/simple_scripting/tab_completion.rb'
|
4
4
|
|
5
5
|
describe SimpleScripting::TabCompletion do
|
6
|
-
|
7
6
|
include TabCompletionCustomRSpecMatchers
|
8
7
|
|
9
8
|
let(:output_buffer) {
|
@@ -45,9 +44,7 @@ describe SimpleScripting::TabCompletion do
|
|
45
44
|
subject { described_class.new(switches_definition, output: output_buffer) }
|
46
45
|
|
47
46
|
context "with a correct configuration" do
|
48
|
-
|
49
47
|
context "standard cases" do
|
50
|
-
|
51
48
|
# Note that the conversion of mandatory to optional argument is defined by most of the cases.
|
52
49
|
#
|
53
50
|
STANDARD_CASES = {
|
@@ -79,11 +76,9 @@ describe SimpleScripting::TabCompletion do
|
|
79
76
|
expect(symbolic_commandline_options).to complete_with(expected_entries)
|
80
77
|
end
|
81
78
|
end
|
82
|
-
|
83
79
|
end # context "standard cases"
|
84
80
|
|
85
81
|
context "suffix management" do
|
86
|
-
|
87
82
|
SUFFIX_CASES = {
|
88
83
|
"arg1<tab>v" => %w(arg1v1 arg1v2), # the execution target of the test suite doesn't
|
89
84
|
"arg1<tab>x" => %w(), # ignore the suffix; programmer-defined
|
@@ -97,11 +92,9 @@ describe SimpleScripting::TabCompletion do
|
|
97
92
|
expect(symbolic_commandline_options).to complete_with(expected_entries)
|
98
93
|
end
|
99
94
|
end
|
100
|
-
|
101
95
|
end # context "suffix management"
|
102
96
|
|
103
97
|
context "escaped cases" do
|
104
|
-
|
105
98
|
ESCAPED_CASES = {
|
106
99
|
"\ <tab>" => [" _argv1spc"],
|
107
100
|
'\-<tab>' => %w(), # this is the result of typing `command "\-<tab>`
|
@@ -111,7 +104,6 @@ describe SimpleScripting::TabCompletion do
|
|
111
104
|
ESCAPED_CASES.each do |symbolic_commandline_options, _|
|
112
105
|
it "should output the entries for #{symbolic_commandline_options.inspect}"
|
113
106
|
end
|
114
|
-
|
115
107
|
end # context "escaped cases"
|
116
108
|
|
117
109
|
it "should support multiple values for an option"
|
@@ -119,11 +111,9 @@ describe SimpleScripting::TabCompletion do
|
|
119
111
|
it "should keep parsing also when --help is passed" do
|
120
112
|
expect("--help a<tab>").to complete_with(%w(arg1v1 arg1v2))
|
121
113
|
end
|
122
|
-
|
123
114
|
end # context "with a correct configuration"
|
124
115
|
|
125
116
|
context "with an incorrect configuration" do
|
126
|
-
|
127
117
|
INCORRECT_CASES = [
|
128
118
|
"a b <tab>", # too many args
|
129
119
|
"-O<tab>", # no values for this option
|
@@ -134,7 +124,5 @@ describe SimpleScripting::TabCompletion do
|
|
134
124
|
expect(symbolic_commandline_options).to not_complete
|
135
125
|
end
|
136
126
|
end
|
137
|
-
|
138
127
|
end # context "with an incorrect configuration"
|
139
|
-
|
140
128
|
end # describe SimpleScripting::TabCompletion
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_scripting
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Saverio Miroddi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-07-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parseconfig
|
@@ -99,8 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
99
99
|
- !ruby/object:Gem::Version
|
100
100
|
version: '0'
|
101
101
|
requirements: []
|
102
|
-
|
103
|
-
rubygems_version: 2.7.8
|
102
|
+
rubygems_version: 3.1.6
|
104
103
|
signing_key:
|
105
104
|
specification_version: 4
|
106
105
|
summary: Library for simplifying some typical scripting functionalities.
|