simple_scripting 0.14.1 → 0.16.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +15 -8
- data/.simplecov +4 -5
- data/Gemfile +2 -2
- data/README.md +9 -9
- data/lib/simple_scripting/argv.rb +17 -24
- data/lib/simple_scripting/configuration.rb +13 -3
- data/lib/simple_scripting/tab_completion/commandline_processor.rb +20 -4
- data/lib/simple_scripting/tab_completion.rb +2 -2
- data/lib/simple_scripting/version.rb +1 -1
- data/simple_scripting.gemspec +4 -3
- data/spec/simple_scripting/argv_spec.rb +46 -0
- data/spec/simple_scripting/configuration_spec.rb +54 -0
- data/spec/simple_scripting/tab_completion_spec.rb +30 -15
- data/spec/spec_helper.rb +1 -2
- metadata +19 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 19d1802651a6d6c7ba48675475a2031da57ccde2ca508977f35c4865897734b1
|
|
4
|
+
data.tar.gz: 81108ceef64d225fd28c2752992aba5f46053ece551e3179364426cd708c9788
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 64373b69f2c72da9d253d8ad0bfa4660ca8e39de83ba3d3c7140454c71e76a82a80b33f1e5a1e885216c6ac8d573b3e0c373de1316db459e44b0a43f532f9c83
|
|
7
|
+
data.tar.gz: 6dd6a9ef7ceec62cf139f006f66a364ae881325bdfa5e2af61baad56377ce02ad8536eb440a9fc445cc110c977c7d1856655067f75707d946f61a15d901dcff5
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -4,21 +4,28 @@ on: [pull_request]
|
|
|
4
4
|
|
|
5
5
|
jobs:
|
|
6
6
|
test:
|
|
7
|
+
name: Test Suite
|
|
7
8
|
runs-on: ubuntu-latest
|
|
8
9
|
strategy:
|
|
9
10
|
matrix:
|
|
10
|
-
ruby-version: [head,
|
|
11
|
+
ruby-version: [head, 4.0, 3.4, 3.3, 3.2]
|
|
11
12
|
fail-fast: false
|
|
12
|
-
continue-on-error: ${{ endsWith(matrix
|
|
13
|
+
continue-on-error: ${{ endsWith(matrix['ruby-version'], 'head') }}
|
|
13
14
|
steps:
|
|
14
|
-
- uses: actions/checkout@
|
|
15
|
-
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
|
|
15
|
+
- uses: actions/checkout@v7
|
|
16
16
|
- name: Set up Ruby ${{ matrix.ruby-version }}
|
|
17
17
|
uses: ruby/setup-ruby@v1
|
|
18
18
|
with:
|
|
19
19
|
ruby-version: ${{ matrix.ruby-version }}
|
|
20
20
|
bundler-cache: true
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
- run: bundle exec rspec
|
|
22
|
+
# Summary job for branch-protection rules; head failures are ignored via continue-on-error above.
|
|
23
|
+
all-tests:
|
|
24
|
+
name: All (non-head) tests passed
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
needs: [test]
|
|
27
|
+
if: always()
|
|
28
|
+
steps:
|
|
29
|
+
- name: Check test suite result
|
|
30
|
+
run: |
|
|
31
|
+
[[ "${{ needs.test.result }}" == "success" ]] || exit 1
|
data/.simplecov
CHANGED
data/Gemfile
CHANGED
|
@@ -3,10 +3,10 @@ source 'https://rubygems.org'
|
|
|
3
3
|
gemspec
|
|
4
4
|
|
|
5
5
|
group :test do
|
|
6
|
-
gem '
|
|
6
|
+
gem 'simplecov', '~> 0.22', require: false
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
group :tools do
|
|
10
|
-
gem 'byebug'
|
|
10
|
+
gem 'byebug'
|
|
11
11
|
gem 'rubocop', '= 0.58.1'
|
|
12
12
|
end
|
data/README.md
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
[![Gem Version][GV img]](https://rubygems.org/gems/simple_scripting)
|
|
2
|
-
[![
|
|
3
|
-
[![Code Climate][CC img]](https://codeclimate.com/github/saveriomiroddi/simple_scripting)
|
|
4
|
-
[![Coverage Status][CS img]](https://coveralls.io/r/saveriomiroddi/simple_scripting)
|
|
2
|
+
[![CI][CI img]](https://github.com/64kramsystem/simple_scripting/actions/workflows/ci.yml)
|
|
5
3
|
|
|
6
4
|
# SimpleScripting
|
|
7
5
|
|
|
@@ -90,7 +88,7 @@ Note that a **recent version of Zsh is required**; the stock Ubuntu 16.04 versio
|
|
|
90
88
|
|
|
91
89
|
### More complex use cases
|
|
92
90
|
|
|
93
|
-
For a description of the more complex use cases, including edge cases and error handling, see the [wiki](https://github.com/
|
|
91
|
+
For a description of the more complex use cases, including edge cases and error handling, see the [wiki](https://github.com/64kramsystem/simple_scripting/wiki/SimpleScripting::TabCompletion-Guide).
|
|
94
92
|
|
|
95
93
|
## SimpleScripting::Argv
|
|
96
94
|
|
|
@@ -151,7 +149,7 @@ Commands are also supported (with unlimited depth), by using a hash:
|
|
|
151
149
|
}
|
|
152
150
|
) || exit
|
|
153
151
|
|
|
154
|
-
For the guide, see the [wiki page](https://github.com/
|
|
152
|
+
For the guide, see the [wiki page](https://github.com/64kramsystem/simple_scripting/wiki/SimpleScripting::Argv-Guide).
|
|
155
153
|
|
|
156
154
|
## SimpleScripting::Configuration
|
|
157
155
|
|
|
@@ -184,15 +182,17 @@ This is the workflow and functionality offered by `Configuration`:
|
|
|
184
182
|
|
|
185
183
|
configuration.a_group.group_key # 'baz'; also supports #full_path and #decrypted
|
|
186
184
|
|
|
185
|
+
### Local configuration
|
|
186
|
+
|
|
187
|
+
If a file with the same name plus the `.local` suffix exists (e.g. `$HOME/.foo_my_bar.local`), its keys are merged over the main configuration's (per-group). This is useful for keeping the main file shared across machines, with machine-specific overrides in the local one.
|
|
188
|
+
|
|
187
189
|
### Encryption note
|
|
188
190
|
|
|
189
191
|
The purpose of encryption in this library is just to avoid displaying passwords in plaintext; it's not considered safe against attacks.
|
|
190
192
|
|
|
191
193
|
## Help
|
|
192
194
|
|
|
193
|
-
See the [wiki](https://github.com/
|
|
195
|
+
See the [wiki](https://github.com/64kramsystem/simple_scripting/wiki) for additional help.
|
|
194
196
|
|
|
195
197
|
[GV img]: https://badge.fury.io/rb/simple_scripting.png
|
|
196
|
-
[
|
|
197
|
-
[CC img]: https://codeclimate.com/github/saveriomiroddi/simple_scripting.png
|
|
198
|
-
[CS img]: https://coveralls.io/repos/saveriomiroddi/simple_scripting/badge.png?branch=master
|
|
198
|
+
[CI img]: https://github.com/64kramsystem/simple_scripting/actions/workflows/ci.yml/badge.svg
|
|
@@ -21,14 +21,13 @@ module SimpleScripting
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
class ExitWithCommandsHelpPrinting < Struct.new(:commands_definition)
|
|
24
|
-
|
|
25
|
-
def print_help(output, long_help)
|
|
24
|
+
def print_help(output)
|
|
26
25
|
output.puts "Valid commands:", "", " " + commands_definition.keys.join(', ')
|
|
27
26
|
end
|
|
28
27
|
end
|
|
29
28
|
|
|
30
|
-
class ExitWithArgumentsHelpPrinting < Struct.new(:commands_stack, :args, :parser_opts_copy)
|
|
31
|
-
def print_help(output
|
|
29
|
+
class ExitWithArgumentsHelpPrinting < Struct.new(:commands_stack, :args, :parser_opts_copy, :long_help)
|
|
30
|
+
def print_help(output)
|
|
32
31
|
parser_opts_help = parser_opts_copy.to_s
|
|
33
32
|
|
|
34
33
|
if commands_stack.size > 0
|
|
@@ -56,19 +55,15 @@ module SimpleScripting
|
|
|
56
55
|
output = options.fetch(:output, $stdout)
|
|
57
56
|
raise_errors = options.fetch(:raise_errors, false)
|
|
58
57
|
|
|
59
|
-
# WATCH OUT! @long_help can also be set in :decode_command!. See issue #17.
|
|
60
|
-
#
|
|
61
|
-
@long_help = long_help
|
|
62
|
-
|
|
63
58
|
exit_data = catch(:exit) do
|
|
64
59
|
if params_definition.first.is_a?(Hash)
|
|
65
|
-
return decode_command!(params_definition, arguments, auto_help)
|
|
60
|
+
return decode_command!(params_definition, arguments, auto_help, long_help)
|
|
66
61
|
else
|
|
67
|
-
return decode_arguments!(params_definition, arguments, auto_help)
|
|
62
|
+
return decode_arguments!(params_definition, arguments, auto_help, long_help)
|
|
68
63
|
end
|
|
69
64
|
end
|
|
70
65
|
|
|
71
|
-
exit_data.print_help(output
|
|
66
|
+
exit_data.print_help(output)
|
|
72
67
|
|
|
73
68
|
nil # to be used with the 'decode(...) || exit' pattern
|
|
74
69
|
rescue SimpleScripting::Argv::ArgumentError, OptionParser::InvalidOption => error
|
|
@@ -79,12 +74,10 @@ module SimpleScripting
|
|
|
79
74
|
raise if raise_errors
|
|
80
75
|
|
|
81
76
|
output.puts <<~MESSAGE
|
|
82
|
-
Command error!: #{error.message}
|
|
77
|
+
Command error!: #{error.message}
|
|
83
78
|
|
|
84
79
|
Valid commands: #{error.valid_commands.join(", ")}
|
|
85
80
|
MESSAGE
|
|
86
|
-
ensure
|
|
87
|
-
@long_help = nil
|
|
88
81
|
end
|
|
89
82
|
|
|
90
83
|
private
|
|
@@ -120,7 +113,7 @@ module SimpleScripting
|
|
|
120
113
|
#
|
|
121
114
|
# [{"command1"=>["arg1", {:long_help=>"This is the long help."}], "command2"=>["arg2"]}]
|
|
122
115
|
#
|
|
123
|
-
def decode_command!(params_definition, arguments, auto_help, commands_stack=[])
|
|
116
|
+
def decode_command!(params_definition, arguments, auto_help, long_help, commands_stack=[])
|
|
124
117
|
commands_definition = params_definition.first
|
|
125
118
|
|
|
126
119
|
# Set the `command` variable only after; in the case where we print the help, this variable
|
|
@@ -157,23 +150,23 @@ module SimpleScripting
|
|
|
157
150
|
|
|
158
151
|
# Nested case! Decode recursively
|
|
159
152
|
#
|
|
160
|
-
decode_command!([command_params_definition], arguments, auto_help, commands_stack)
|
|
153
|
+
decode_command!([command_params_definition], arguments, auto_help, long_help, commands_stack)
|
|
161
154
|
else
|
|
162
155
|
commands_stack << command
|
|
163
156
|
|
|
164
157
|
if command_params_definition.last.is_a?(Hash)
|
|
165
158
|
internal_params = command_params_definition.pop # only long_help is here, if present
|
|
166
|
-
|
|
159
|
+
long_help = internal_params.delete(:long_help)
|
|
167
160
|
end
|
|
168
161
|
|
|
169
162
|
[
|
|
170
163
|
compose_returned_commands(commands_stack),
|
|
171
|
-
decode_arguments!(command_params_definition, arguments, auto_help, commands_stack),
|
|
164
|
+
decode_arguments!(command_params_definition, arguments, auto_help, long_help, commands_stack),
|
|
172
165
|
]
|
|
173
166
|
end
|
|
174
167
|
end
|
|
175
168
|
|
|
176
|
-
def decode_arguments!(params_definition, arg_values, auto_help, commands_stack=[])
|
|
169
|
+
def decode_arguments!(params_definition, arg_values, auto_help, long_help, commands_stack=[])
|
|
177
170
|
result = {}
|
|
178
171
|
parser_opts_copy = nil # not available outside the block
|
|
179
172
|
arg_definitions = {} # { 'name' => mandatory? }
|
|
@@ -197,7 +190,7 @@ module SimpleScripting
|
|
|
197
190
|
#
|
|
198
191
|
parser_opts.on('-h', '--help', 'Help') do
|
|
199
192
|
if auto_help
|
|
200
|
-
throw :exit, ExitWithArgumentsHelpPrinting.new(commands_stack, arg_definitions, parser_opts_copy)
|
|
193
|
+
throw :exit, ExitWithArgumentsHelpPrinting.new(commands_stack, arg_definitions, parser_opts_copy, long_help)
|
|
201
194
|
else
|
|
202
195
|
# Needs to be better handled. When help is required, generally, it trumps the
|
|
203
196
|
# correctness of the rest of the options/arguments.
|
|
@@ -212,9 +205,9 @@ module SimpleScripting
|
|
|
212
205
|
arg_definitions.each do |arg_name, arg_is_mandatory|
|
|
213
206
|
if arg_name.to_s.start_with?('*')
|
|
214
207
|
arg_name = arg_name.to_s[1..-1].to_sym
|
|
215
|
-
process_varargs!(arg_values, result,
|
|
208
|
+
process_varargs!(arg_values, result, arg_name, arg_is_mandatory)
|
|
216
209
|
else
|
|
217
|
-
process_regular_argument!(arg_values, result,
|
|
210
|
+
process_regular_argument!(arg_values, result, arg_name, arg_is_mandatory)
|
|
218
211
|
end
|
|
219
212
|
end
|
|
220
213
|
|
|
@@ -260,14 +253,14 @@ module SimpleScripting
|
|
|
260
253
|
end
|
|
261
254
|
end
|
|
262
255
|
|
|
263
|
-
def process_varargs!(arg_values, result,
|
|
256
|
+
def process_varargs!(arg_values, result, arg_name, arg_is_mandatory)
|
|
264
257
|
raise ArgumentError.new("Missing mandatory argument(s)") if arg_is_mandatory && arg_values.empty?
|
|
265
258
|
|
|
266
259
|
result[arg_name] = arg_values.dup
|
|
267
260
|
arg_values.clear
|
|
268
261
|
end
|
|
269
262
|
|
|
270
|
-
def process_regular_argument!(arg_values, result,
|
|
263
|
+
def process_regular_argument!(arg_values, result, arg_name, arg_is_mandatory)
|
|
271
264
|
if arg_values.empty?
|
|
272
265
|
if arg_is_mandatory
|
|
273
266
|
raise ArgumentError.new("Missing mandatory argument(s)")
|
|
@@ -16,11 +16,21 @@ module SimpleScripting
|
|
|
16
16
|
def load(config_file: default_config_file, passwords_key: nil, required: [])
|
|
17
17
|
create_empty_file(config_file) if !File.exist?(config_file)
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
params = ParseConfig.new(config_file).params
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
local_config_file = "#{config_file}.local"
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
if File.exist?(local_config_file)
|
|
24
|
+
local_params = ParseConfig.new(local_config_file).params
|
|
25
|
+
|
|
26
|
+
params = params.merge(local_params) do |_, value, local_value|
|
|
27
|
+
value.is_a?(Hash) && local_value.is_a?(Hash) ? value.merge(local_value) : local_value
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
enforce_required_keys(params, required)
|
|
32
|
+
|
|
33
|
+
convert_to_cool_format(OpenStruct.new, params, passwords_key)
|
|
24
34
|
end
|
|
25
35
|
|
|
26
36
|
private
|
|
@@ -8,7 +8,7 @@ module SimpleScripting
|
|
|
8
8
|
|
|
9
9
|
class TabCompletion
|
|
10
10
|
|
|
11
|
-
class CommandlineProcessor < Struct.new(:processed_argv, :cursor_marker, :switches_definition)
|
|
11
|
+
class CommandlineProcessor < Struct.new(:processed_argv, :cursor_marker, :switches_definition, :escaped_dash)
|
|
12
12
|
|
|
13
13
|
# Arbitrary; can be anything (except an empty string).
|
|
14
14
|
BASE_CURSOR_MARKER = "<tab>"
|
|
@@ -27,7 +27,12 @@ module SimpleScripting
|
|
|
27
27
|
# Remove the executable.
|
|
28
28
|
processed_argv = Shellwords.split(commandline_with_marker)[1..-1]
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
# Shellwords strips the backslash, losing the information that the word is a value, not
|
|
31
|
+
# an option.
|
|
32
|
+
#
|
|
33
|
+
escaped_dash = commandline_with_marker.match?(/(?:\A|\s)\\-\S*#{Regexp.escape(cursor_marker)}/)
|
|
34
|
+
|
|
35
|
+
return new(processed_argv, cursor_marker, switches_definition, escaped_dash)
|
|
31
36
|
end
|
|
32
37
|
end
|
|
33
38
|
end
|
|
@@ -56,15 +61,23 @@ module SimpleScripting
|
|
|
56
61
|
parsed_pairs = parse_argv || raise("Parsing error")
|
|
57
62
|
|
|
58
63
|
key, value = parsed_pairs.detect do |_, value|
|
|
59
|
-
|
|
64
|
+
if value.is_a?(Array)
|
|
65
|
+
value.any? { |entry| entry.include?(cursor_marker) }
|
|
66
|
+
else
|
|
67
|
+
!boolean?(value) && value.include?(cursor_marker)
|
|
68
|
+
end
|
|
60
69
|
end
|
|
61
70
|
|
|
62
71
|
# Impossible case, unless there is a programmatic error.
|
|
63
72
|
#
|
|
64
73
|
key || raise("Guru meditation! (#{self.class}##{__method__}:#{__LINE__})")
|
|
65
74
|
|
|
75
|
+
value = value.detect { |entry| entry.include?(cursor_marker) } if value.is_a?(Array)
|
|
76
|
+
|
|
66
77
|
value_prefix, value_suffix = value.split(cursor_marker)
|
|
67
78
|
|
|
79
|
+
value_prefix = "-#{value_prefix}" if escaped_dash
|
|
80
|
+
|
|
68
81
|
parsed_pairs.delete(key)
|
|
69
82
|
|
|
70
83
|
[key, value_prefix || "", value_suffix || "", parsed_pairs]
|
|
@@ -98,7 +111,10 @@ module SimpleScripting
|
|
|
98
111
|
adapted_switches_definition[i] = "[#{definition}]" if definition.is_a?(String) && !definition.start_with?('[')
|
|
99
112
|
end
|
|
100
113
|
|
|
101
|
-
|
|
114
|
+
adapted_argv = processed_argv.dup
|
|
115
|
+
adapted_argv[marked_word_position] = adapted_argv[marked_word_position].delete_prefix("-") if escaped_dash
|
|
116
|
+
|
|
117
|
+
SimpleScripting::Argv.decode(*adapted_switches_definition, arguments: adapted_argv, auto_help: false)
|
|
102
118
|
rescue Argv::InvalidCommand, Argv::ArgumentError, OptionParser::InvalidOption
|
|
103
119
|
# OptionParser::InvalidOption: see case "-O<tab>" in test suite.
|
|
104
120
|
|
|
@@ -29,7 +29,7 @@ module SimpleScripting
|
|
|
29
29
|
commandline_processor = CommandlineProcessor.process_commandline(source_commandline, cursor_position, @switches_definition)
|
|
30
30
|
|
|
31
31
|
if commandline_processor.completing_an_option?
|
|
32
|
-
complete_option(commandline_processor
|
|
32
|
+
complete_option(commandline_processor)
|
|
33
33
|
elsif commandline_processor.parsing_error?
|
|
34
34
|
return
|
|
35
35
|
else # completing_a_value?
|
|
@@ -43,7 +43,7 @@ module SimpleScripting
|
|
|
43
43
|
# Completion!
|
|
44
44
|
#############################################
|
|
45
45
|
|
|
46
|
-
def complete_option(commandline_processor
|
|
46
|
+
def complete_option(commandline_processor)
|
|
47
47
|
all_switches = @switches_definition.select { |definition| definition.is_a?(Array) }.map { |definition| definition[1][/^--\S+/] }
|
|
48
48
|
|
|
49
49
|
matching_switches = all_switches.select { |switch| switch.start_with?(commandline_processor.completing_word_prefix) }
|
data/simple_scripting.gemspec
CHANGED
|
@@ -8,17 +8,18 @@ Gem::Specification.new do |s|
|
|
|
8
8
|
s.name = "simple_scripting"
|
|
9
9
|
s.version = SimpleScripting::VERSION
|
|
10
10
|
s.platform = Gem::Platform::RUBY
|
|
11
|
-
s.required_ruby_version = '>= 2.
|
|
11
|
+
s.required_ruby_version = '>= 3.2.0'
|
|
12
12
|
s.authors = ["Saverio Miroddi"]
|
|
13
|
-
s.date = "
|
|
13
|
+
s.date = "2026-07-07"
|
|
14
14
|
s.email = ["saverio.pub2@gmail.com"]
|
|
15
|
-
s.homepage = "https://github.com/
|
|
15
|
+
s.homepage = "https://github.com/64kramsystem/simple_scripting"
|
|
16
16
|
s.summary = "Library for simplifying some typical scripting functionalities."
|
|
17
17
|
s.description = "Simplifies options parsing and configuration loading."
|
|
18
18
|
s.license = "GPL-3.0"
|
|
19
19
|
|
|
20
20
|
s.add_runtime_dependency "parseconfig", "~> 1.0"
|
|
21
21
|
s.add_runtime_dependency "ostruct", "~> 0.6.1"
|
|
22
|
+
s.add_runtime_dependency "base64", "~> 0.3"
|
|
22
23
|
|
|
23
24
|
s.add_development_dependency "rake", "~> 12.0"
|
|
24
25
|
s.add_development_dependency "rspec", "~> 3.7"
|
|
@@ -45,6 +45,18 @@ module SimpleScripting
|
|
|
45
45
|
expect(return_value).to be(nil)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
it 'should print help without arguments usage and long help, when they are not defined' do
|
|
49
|
+
described_class.decode(['-a'], output: output_buffer, arguments: ['-h'])
|
|
50
|
+
|
|
51
|
+
expected_output = <<~OUTPUT
|
|
52
|
+
Usage: rspec [options]
|
|
53
|
+
-a
|
|
54
|
+
-h, --help Help
|
|
55
|
+
OUTPUT
|
|
56
|
+
|
|
57
|
+
expect(output_buffer.string).to eql(expected_output)
|
|
58
|
+
end
|
|
59
|
+
|
|
48
60
|
it 'should not interpret the --help argument, and not print the help, on auto_help: false' do
|
|
49
61
|
decoder_params.last.merge!(
|
|
50
62
|
arguments: ['--help', 'm_arg'],
|
|
@@ -359,6 +371,19 @@ module SimpleScripting
|
|
|
359
371
|
valid_commands: ["command1", "command2"],
|
|
360
372
|
))
|
|
361
373
|
end
|
|
374
|
+
|
|
375
|
+
it "should print the invalid-command error and valid commands by default, instead of raising" do
|
|
376
|
+
decoder_params[:arguments] = ['pizza']
|
|
377
|
+
|
|
378
|
+
actual_result = described_class.decode(decoder_params)
|
|
379
|
+
|
|
380
|
+
expect(actual_result).to be(nil)
|
|
381
|
+
expect(output_buffer.string).to eql(<<~OUTPUT)
|
|
382
|
+
Command error!: Invalid command: pizza
|
|
383
|
+
|
|
384
|
+
Valid commands: command1, command2
|
|
385
|
+
OUTPUT
|
|
386
|
+
end
|
|
362
387
|
end # context "error handling"
|
|
363
388
|
|
|
364
389
|
context "help" do
|
|
@@ -509,5 +534,26 @@ module SimpleScripting
|
|
|
509
534
|
}.to raise_error(Argv::ArgumentError, "Too many arguments")
|
|
510
535
|
end
|
|
511
536
|
end # describe 'No definitions given'
|
|
537
|
+
|
|
538
|
+
describe 'Definition/options handling' do
|
|
539
|
+
it "should work when no options hash is passed, reading ARGV" do
|
|
540
|
+
original_argv = ARGV.dup
|
|
541
|
+
ARGV.replace(['m_arg'])
|
|
542
|
+
|
|
543
|
+
begin
|
|
544
|
+
actual_result = described_class.decode('mandatory')
|
|
545
|
+
|
|
546
|
+
expect(actual_result).to eql(mandatory: 'm_arg')
|
|
547
|
+
ensure
|
|
548
|
+
ARGV.replace(original_argv)
|
|
549
|
+
end
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
it "should raise on an unrecognized param definition" do
|
|
553
|
+
expect {
|
|
554
|
+
described_class.decode(123, arguments: [], output: output_buffer)
|
|
555
|
+
}.to raise_error(RuntimeError, "Unrecognized value: 123")
|
|
556
|
+
end
|
|
557
|
+
end # describe 'Definition/options handling'
|
|
512
558
|
end # describe Argv
|
|
513
559
|
end # module SimpleScripting
|
|
@@ -51,6 +51,42 @@ g2_key=bang
|
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
it "should merge the .local configuration file, when present, over the main one" do
|
|
55
|
+
with_tempfile(configuration_text) do |config_file|
|
|
56
|
+
local_configuration_text = "
|
|
57
|
+
relpath_key=local_foo
|
|
58
|
+
|
|
59
|
+
[group1]
|
|
60
|
+
g_key=local_baz
|
|
61
|
+
"
|
|
62
|
+
|
|
63
|
+
File.write("#{config_file}.local", local_configuration_text)
|
|
64
|
+
|
|
65
|
+
begin
|
|
66
|
+
configuration = described_class.load(config_file: config_file)
|
|
67
|
+
|
|
68
|
+
expect(configuration.relpath_key).to eql('local_foo')
|
|
69
|
+
expect(configuration.group1.g_key).to eql('local_baz')
|
|
70
|
+
|
|
71
|
+
# Keys not overridden are preserved.
|
|
72
|
+
expect(configuration.abspath_key).to eql('/tmp/bar')
|
|
73
|
+
expect(configuration.group2.g2_key).to eql('bang')
|
|
74
|
+
ensure
|
|
75
|
+
File.delete("#{config_file}.local")
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "should raise an error when decrypting a value without the passwords key" do
|
|
81
|
+
with_tempfile(configuration_text) do |config_file|
|
|
82
|
+
configuration = described_class.load(config_file: config_file)
|
|
83
|
+
|
|
84
|
+
expect {
|
|
85
|
+
configuration.encr_key.decrypted
|
|
86
|
+
}.to raise_error("Encryption key not provided!")
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
54
90
|
it "should raise an error when required keys are missing" do
|
|
55
91
|
with_tempfile(configuration_text) do |config_file|
|
|
56
92
|
expect {
|
|
@@ -70,4 +106,22 @@ g2_key=bang
|
|
|
70
106
|
File.delete(temp_config_file)
|
|
71
107
|
end
|
|
72
108
|
end
|
|
109
|
+
|
|
110
|
+
it "should derive the default config file from the program name, when none is given" do
|
|
111
|
+
Dir.mktmpdir do |home|
|
|
112
|
+
original_home = ENV['HOME']
|
|
113
|
+
original_prog = $PROGRAM_NAME
|
|
114
|
+
ENV['HOME'] = home
|
|
115
|
+
$PROGRAM_NAME = 'my_test_prog.rb'
|
|
116
|
+
|
|
117
|
+
begin
|
|
118
|
+
described_class.load
|
|
119
|
+
|
|
120
|
+
expect(File).to exist(File.join(home, '.my_test_prog'))
|
|
121
|
+
ensure
|
|
122
|
+
ENV['HOME'] = original_home
|
|
123
|
+
$PROGRAM_NAME = original_prog
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
73
127
|
end # describe SimpleScripting::Configuration
|
|
@@ -13,6 +13,7 @@ describe SimpleScripting::TabCompletion do
|
|
|
13
13
|
[
|
|
14
14
|
["-o", "--opt1 ARG"],
|
|
15
15
|
["-O", "--opt2"],
|
|
16
|
+
["-m", "--multi V1,V2"],
|
|
16
17
|
"arg1", # this and the following are internally converted to optional, as
|
|
17
18
|
"arg2", # according to the Argv spec, without brackets they are mandatory.
|
|
18
19
|
]
|
|
@@ -33,6 +34,10 @@ describe SimpleScripting::TabCompletion do
|
|
|
33
34
|
['arg1v1', 'arg1v2', '_arg1v3', ' _argv1spc'].select { |entry| entry =~ /^#{prefix}#{suffix}/ }
|
|
34
35
|
end
|
|
35
36
|
|
|
37
|
+
def multi(prefix, suffix, context)
|
|
38
|
+
%w(multiv1 _multiv2).select { |entry| entry =~ /^#{prefix}#{suffix}/ }
|
|
39
|
+
end
|
|
40
|
+
|
|
36
41
|
def arg2(prefix, suffix, context)
|
|
37
42
|
# A value starting with minus is valid.
|
|
38
43
|
#
|
|
@@ -57,11 +62,11 @@ describe SimpleScripting::TabCompletion do
|
|
|
57
62
|
"a -- --<tab>" => %w(--arg2v3),
|
|
58
63
|
"-- --aaa <tab>" => %w(arg2v1 arg2v2 --arg2v3),
|
|
59
64
|
|
|
60
|
-
"--<tab>" => %w(--opt1 --opt2),
|
|
61
|
-
"--<tab> a" => %w(--opt1 --opt2),
|
|
62
|
-
"--<tab> -- a" => %w(--opt1 --opt2),
|
|
63
|
-
"--<tab> -- b" => %w(--opt1 --opt2),
|
|
64
|
-
"--<tab> --xyz" => %w(--opt1 --opt2),
|
|
65
|
+
"--<tab>" => %w(--opt1 --opt2 --multi),
|
|
66
|
+
"--<tab> a" => %w(--opt1 --opt2 --multi),
|
|
67
|
+
"--<tab> -- a" => %w(--opt1 --opt2 --multi),
|
|
68
|
+
"--<tab> -- b" => %w(--opt1 --opt2 --multi),
|
|
69
|
+
"--<tab> --xyz" => %w(--opt1 --opt2 --multi),
|
|
65
70
|
"--opt1 <tab> a" => %w(opt1v1 _opt1v2),
|
|
66
71
|
"--opt1 o<tab> a" => %w(opt1v1),
|
|
67
72
|
|
|
@@ -96,17 +101,25 @@ describe SimpleScripting::TabCompletion do
|
|
|
96
101
|
|
|
97
102
|
context "escaped cases" do
|
|
98
103
|
ESCAPED_CASES = {
|
|
99
|
-
|
|
104
|
+
'\ <tab>' => [" _argv1spc"],
|
|
100
105
|
'\-<tab>' => %w(), # this is the result of typing `command "\-<tab>`
|
|
101
106
|
'a \-<tab>' => %w(--arg2v3),
|
|
102
107
|
}
|
|
103
108
|
|
|
104
|
-
ESCAPED_CASES.each do |symbolic_commandline_options,
|
|
105
|
-
it "should output the entries for #{symbolic_commandline_options.inspect}"
|
|
109
|
+
ESCAPED_CASES.each do |symbolic_commandline_options, expected_entries|
|
|
110
|
+
it "should output the entries for #{symbolic_commandline_options.inspect}" do
|
|
111
|
+
expect(symbolic_commandline_options).to complete_with(expected_entries)
|
|
112
|
+
end
|
|
106
113
|
end
|
|
107
114
|
end # context "escaped cases"
|
|
108
115
|
|
|
109
|
-
it "should support multiple values for an option"
|
|
116
|
+
it "should support multiple values for an option" do
|
|
117
|
+
expect("--multi multiv1,m<tab>").to complete_with(%w(multiv1))
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "should use the next cursor marker when the commandline contains the base one" do
|
|
121
|
+
expect("a<tab0> <tab>").to complete_with(%w(arg2v1 arg2v2 --arg2v3))
|
|
122
|
+
end
|
|
110
123
|
|
|
111
124
|
it "should keep parsing also when --help is passed" do
|
|
112
125
|
expect("--help a<tab>").to complete_with(%w(arg1v1 arg1v2))
|
|
@@ -114,14 +127,16 @@ describe SimpleScripting::TabCompletion do
|
|
|
114
127
|
end # context "with a correct configuration"
|
|
115
128
|
|
|
116
129
|
context "with an incorrect configuration" do
|
|
117
|
-
INCORRECT_CASES =
|
|
118
|
-
"a b <tab>", # too many args
|
|
119
|
-
"-O<tab>",
|
|
120
|
-
|
|
130
|
+
INCORRECT_CASES = {
|
|
131
|
+
"a b <tab>" => "Command error!: Too many arguments\n", # too many args
|
|
132
|
+
"-O<tab>" => "Command error!: invalid option: -<tab0>\n", # no values for this option
|
|
133
|
+
}
|
|
121
134
|
|
|
122
|
-
INCORRECT_CASES.each do |symbolic_commandline_options|
|
|
135
|
+
INCORRECT_CASES.each do |symbolic_commandline_options, expected_error_message|
|
|
123
136
|
it "should not output any entries for #{symbolic_commandline_options.inspect}" do
|
|
124
|
-
expect
|
|
137
|
+
expect {
|
|
138
|
+
expect(symbolic_commandline_options).to not_complete
|
|
139
|
+
}.to output(expected_error_message).to_stdout
|
|
125
140
|
end
|
|
126
141
|
end
|
|
127
142
|
end # context "with an incorrect configuration"
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: simple_scripting
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.16.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Saverio Miroddi
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 2026-07-07 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: parseconfig
|
|
@@ -38,6 +37,20 @@ dependencies:
|
|
|
38
37
|
- - "~>"
|
|
39
38
|
- !ruby/object:Gem::Version
|
|
40
39
|
version: 0.6.1
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: base64
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.3'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0.3'
|
|
41
54
|
- !ruby/object:Gem::Dependency
|
|
42
55
|
name: rake
|
|
43
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -94,11 +107,10 @@ files:
|
|
|
94
107
|
- spec/simple_scripting/configuration_spec.rb
|
|
95
108
|
- spec/simple_scripting/tab_completion_spec.rb
|
|
96
109
|
- spec/spec_helper.rb
|
|
97
|
-
homepage: https://github.com/
|
|
110
|
+
homepage: https://github.com/64kramsystem/simple_scripting
|
|
98
111
|
licenses:
|
|
99
112
|
- GPL-3.0
|
|
100
113
|
metadata: {}
|
|
101
|
-
post_install_message:
|
|
102
114
|
rdoc_options: []
|
|
103
115
|
require_paths:
|
|
104
116
|
- lib
|
|
@@ -106,15 +118,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
106
118
|
requirements:
|
|
107
119
|
- - ">="
|
|
108
120
|
- !ruby/object:Gem::Version
|
|
109
|
-
version: 2.
|
|
121
|
+
version: 3.2.0
|
|
110
122
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
123
|
requirements:
|
|
112
124
|
- - ">="
|
|
113
125
|
- !ruby/object:Gem::Version
|
|
114
126
|
version: '0'
|
|
115
127
|
requirements: []
|
|
116
|
-
rubygems_version:
|
|
117
|
-
signing_key:
|
|
128
|
+
rubygems_version: 4.0.10
|
|
118
129
|
specification_version: 4
|
|
119
130
|
summary: Library for simplifying some typical scripting functionalities.
|
|
120
131
|
test_files:
|