mini-cli 0.1.2 → 0.5.1
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/lib/mini-cli.rb +184 -104
- data/lib/mini-cli/run.rb +36 -20
- data/lib/mini-cli/version.rb +1 -1
- data/lib/mini_cli.rb +1 -0
- data/mini-cli.gemspec +19 -19
- data/rakefile.rb +3 -5
- data/samples/custom_args.rb +14 -11
- data/test/apps/noop.rb +6 -0
- data/test/apps/sequence.rb +26 -0
- data/test/mini-cli/exec_test.rb +94 -0
- data/test/mini-cli/main_test.rb +36 -2
- data/test/mini-cli/ruby_run_test.rb +43 -0
- data/test/mini-cli/run_test.rb +31 -26
- metadata +12 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 299da5a35fccbc561453ee2dc9584f7aec510b1fe9dd34e8c7ae3221b6253dbb
|
4
|
+
data.tar.gz: 242f37afb8733342cafd7694d73de4247f2887a027cb4f33da210eff1dc81380
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da79647fb91708e556151c22296aa0febe509c9502bbbe327e6fb10cfd3cc22b851f402cede01e25d1df6b9e42382200cc7d34c28f8dab7fc1fac9984dda6d47
|
7
|
+
data.tar.gz: bb7bfb271ea6cbb2e30e4a0833746a30869e484ddc4e7d13c6f1c5f44acdca10d29081f0b52fe9c045685974d6ef6c188ce0e6147cd57f331deb020d74abeacb
|
data/lib/mini-cli.rb
CHANGED
@@ -1,163 +1,243 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module MiniCli
|
4
|
-
def self.included(
|
5
|
-
|
6
|
-
|
4
|
+
def self.included(mod)
|
5
|
+
mod.const_set(
|
6
|
+
:MiniCli__,
|
7
|
+
Instance.new(caller_locations(1, 1).first.absolute_path)
|
8
|
+
)
|
9
|
+
mod.private_constant(:MiniCli__)
|
7
10
|
end
|
8
11
|
|
9
12
|
def name(name = nil)
|
10
|
-
|
11
|
-
@__name = name || File.basename(MiniCli::SRC, '.*')
|
13
|
+
__minicli__.name(name)
|
12
14
|
end
|
13
15
|
|
14
16
|
def help(helptext, *args)
|
15
|
-
|
17
|
+
__minicli__.help(helptext, args)
|
16
18
|
end
|
17
19
|
|
18
20
|
def show_help
|
19
|
-
|
20
|
-
true
|
21
|
+
__minicli__.show_help
|
21
22
|
end
|
22
23
|
|
23
|
-
def
|
24
|
-
|
25
|
-
exit(code)
|
24
|
+
def show_errors?
|
25
|
+
__minicli__.show_errors
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
28
|
+
def show_errors=(value)
|
29
|
+
__minicli__.show_errors = value ? true : false
|
30
|
+
end
|
31
|
+
|
32
|
+
def error_code
|
33
|
+
__minicli__.error_code
|
34
|
+
end
|
35
|
+
|
36
|
+
def error(code, message = nil)
|
37
|
+
$stderr.puts("#{name}: #{message}") if message && show_errors?
|
38
|
+
exit(__minicli__.error_code = code)
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse_argv(argv = nil, &argv_converter)
|
42
|
+
return __minicli__.converter = argv_converter if argv_converter
|
43
|
+
argv = Array.new(argv || ARGV)
|
31
44
|
exit(show_help) if argv.index('--help') || argv.index('-h')
|
32
|
-
|
33
|
-
defined?(@__argv_converter) ? @__argv_converter.call(args) || args : args
|
45
|
+
__minicli__.main(argv, method(:error).to_proc)
|
34
46
|
end
|
35
47
|
|
36
|
-
def main
|
37
|
-
|
38
|
-
yield(
|
48
|
+
def main
|
49
|
+
__minicli__.run do
|
50
|
+
yield(parse_argv)
|
39
51
|
rescue Interrupt
|
40
52
|
error(130, 'aborted')
|
41
53
|
end
|
42
54
|
end
|
43
55
|
|
56
|
+
def before(&callback)
|
57
|
+
callback and __minicli__.before << callback
|
58
|
+
end
|
59
|
+
|
60
|
+
def after(&callback)
|
61
|
+
callback and __minicli__.after << callback
|
62
|
+
end
|
63
|
+
|
44
64
|
private
|
45
65
|
|
46
|
-
def
|
47
|
-
|
66
|
+
def __minicli__
|
67
|
+
self.class.const_get(:MiniCli__)
|
48
68
|
end
|
49
69
|
|
50
|
-
class
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
70
|
+
class Instance
|
71
|
+
attr_reader :source
|
72
|
+
attr_writer :converter
|
73
|
+
attr_accessor :show_errors, :before, :after, :error_code
|
74
|
+
|
75
|
+
def initialize(source)
|
76
|
+
@source = source
|
77
|
+
@name = File.basename(source, '.*')
|
78
|
+
@parser = @converter = @main_proc = nil
|
79
|
+
@before, @after = [], []
|
80
|
+
@show_errors = true
|
81
|
+
@before_ok = false
|
82
|
+
@error_code = 0
|
83
|
+
init_main
|
55
84
|
end
|
56
85
|
|
57
|
-
def
|
58
|
-
|
59
|
-
print("Usage: #{name}")
|
60
|
-
print(' [OPTIONS]') unless @options.empty?
|
61
|
-
print(' ', @args.join(' ')) unless @args.empty?
|
62
|
-
puts
|
63
|
-
puts(nil, 'Valid Options:') unless @options.empty?
|
64
|
-
puts(@helptext) unless @helptext.empty?
|
86
|
+
def name(name = nil)
|
87
|
+
name ? @name = name.to_s : @name
|
65
88
|
end
|
66
89
|
|
67
|
-
def
|
68
|
-
@
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
end
|
86
|
-
process(arguments)
|
90
|
+
def help(helptext, args)
|
91
|
+
@parser = ArgvParser.new(helptext, args)
|
92
|
+
end
|
93
|
+
|
94
|
+
def show_help
|
95
|
+
parser.show_help(@name)
|
96
|
+
true
|
97
|
+
end
|
98
|
+
|
99
|
+
def run(&main_proc)
|
100
|
+
@main_proc = main_proc
|
101
|
+
end
|
102
|
+
|
103
|
+
def main(argv, error)
|
104
|
+
@before.each(&:call)
|
105
|
+
@before_ok = true
|
106
|
+
args = parser.parse(argv, error)
|
107
|
+
@converter ? @converter.call(args) || args : args
|
87
108
|
end
|
88
109
|
|
89
110
|
private
|
90
111
|
|
91
|
-
def
|
92
|
-
|
112
|
+
def init_main
|
113
|
+
at_exit do
|
114
|
+
next if $! and not ($!.kind_of?(SystemExit) and $!.success?)
|
115
|
+
shutdown unless @after.empty?
|
116
|
+
@main_proc&.call
|
117
|
+
end
|
93
118
|
end
|
94
119
|
|
95
|
-
def
|
96
|
-
|
97
|
-
next if
|
98
|
-
|
120
|
+
def shutdown(pid = Process.pid)
|
121
|
+
at_exit do
|
122
|
+
next if Process.pid != pid
|
123
|
+
@after.reverse_each(&:call) if @before_ok
|
124
|
+
exit(@error_code)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def parser
|
129
|
+
@parser ||= ArgvParser.new(nil, [])
|
130
|
+
end
|
131
|
+
|
132
|
+
class ArgvParser
|
133
|
+
def initialize(helptext, args)
|
134
|
+
@helptext = helptext.to_s
|
135
|
+
@args = args.flatten.map!(&:to_s).uniq
|
136
|
+
@options = nil
|
137
|
+
end
|
138
|
+
|
139
|
+
def show_help(name)
|
140
|
+
parse_help! unless @options
|
141
|
+
print("Usage: #{name}")
|
142
|
+
print(' [OPTIONS]') unless @options.empty?
|
143
|
+
print(' ', @args.join(' ')) unless @args.empty?
|
144
|
+
puts
|
145
|
+
puts(nil, 'Options:') unless @options.empty?
|
146
|
+
puts(@helptext) unless @helptext.empty?
|
147
|
+
end
|
148
|
+
|
149
|
+
def parse(argv, error)
|
150
|
+
@error, @result = error, {}
|
151
|
+
parse_help! unless @options
|
152
|
+
process(parse_argv(argv))
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
def parse_argv(argv)
|
158
|
+
arguments = []
|
159
|
+
while (arg = argv.shift)
|
160
|
+
case arg
|
161
|
+
when '--'
|
162
|
+
break arguments += argv
|
163
|
+
when /\A--([[[:alnum:]]-]+)\z/
|
164
|
+
handle_option(Regexp.last_match[1], argv)
|
165
|
+
when /\A-([[:alnum:]]+)\z/
|
166
|
+
parse_options(Regexp.last_match[1], argv)
|
167
|
+
else
|
168
|
+
arguments << arg
|
169
|
+
end
|
170
|
+
end
|
171
|
+
arguments
|
172
|
+
end
|
99
173
|
|
174
|
+
def error(msg)
|
175
|
+
@error[1, msg]
|
176
|
+
end
|
177
|
+
|
178
|
+
def process(arguments)
|
179
|
+
@args.each do |arg|
|
180
|
+
process_arg(arg, arguments.shift) unless arg.index('..')
|
181
|
+
end
|
182
|
+
arguments.unshift(@result['FILES']) if @result.key?('FILES')
|
183
|
+
@result['FILES'] = arguments
|
184
|
+
@result
|
185
|
+
end
|
186
|
+
|
187
|
+
def process_arg(arg, value)
|
100
188
|
if arg.start_with?('[')
|
101
189
|
@result[arg[1..-2]] = value if value
|
102
190
|
else
|
103
191
|
@result[arg] = value || error("parameter expected - #{arg}")
|
104
192
|
end
|
105
193
|
end
|
106
|
-
arguments.unshift(@result['FILES']) if @result.key?('FILES')
|
107
|
-
@result['FILES'] = arguments
|
108
|
-
@result
|
109
|
-
end
|
110
194
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
@result[key] = value = argv.shift
|
115
|
-
return unless value.nil? || value.start_with?('-')
|
116
|
-
error("parameter #{key} expected - --#{option}")
|
117
|
-
end
|
118
|
-
|
119
|
-
def parse_options(options, argv)
|
120
|
-
options.each_char do |opt|
|
121
|
-
key = @options[opt] || error("unknown option - #{opt}")
|
122
|
-
next @result[key] = true if key == key.downcase
|
195
|
+
def handle_option(option, argv, test = ->(k) { option == k })
|
196
|
+
key = @options[option] || error("unknown option - #{option}")
|
197
|
+
@result[key] = test[key] and return
|
123
198
|
@result[key] = value = argv.shift
|
124
|
-
|
125
|
-
error("parameter #{key} expected -
|
199
|
+
return unless value.nil? || value.start_with?('-')
|
200
|
+
error("parameter #{key} expected - --#{option}")
|
126
201
|
end
|
127
|
-
end
|
128
202
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
203
|
+
def parse_options(options, argv)
|
204
|
+
test = ->(k) { k == k.downcase }
|
205
|
+
options.each_char { |opt| handle_option(opt, argv, test) }
|
206
|
+
end
|
207
|
+
|
208
|
+
def parse_help!
|
209
|
+
@options = {}
|
210
|
+
@helptext.each_line do |line|
|
211
|
+
case line
|
212
|
+
when /-([[:alnum:]]), --([[[:alnum:]]-]+) ([[:upper:]]+)\s+\S+/
|
213
|
+
option_with_argument(Regexp.last_match)
|
214
|
+
when /--([[[:alnum:]]-]+) ([[:upper:]]+)\s+\S+/
|
215
|
+
short_option_with_argument(Regexp.last_match)
|
216
|
+
when /-([[:alnum:]]), --([[[:alnum:]]-]+)\s+\S+/
|
217
|
+
option(Regexp.last_match)
|
218
|
+
when /--([[[:alnum:]]-]+)\s+\S+/
|
219
|
+
short_option(Regexp.last_match)
|
220
|
+
end
|
141
221
|
end
|
142
222
|
end
|
143
|
-
end
|
144
223
|
|
145
|
-
|
146
|
-
|
147
|
-
|
224
|
+
def option_with_argument(match)
|
225
|
+
@options[match[1]] = @options[match[2]] = match[3]
|
226
|
+
end
|
148
227
|
|
149
|
-
|
150
|
-
|
151
|
-
|
228
|
+
def short_option_with_argument(match)
|
229
|
+
@options[match[1]] = match[2]
|
230
|
+
end
|
152
231
|
|
153
|
-
|
154
|
-
|
155
|
-
|
232
|
+
def option(match)
|
233
|
+
@options[match[1]] = @options[match[2]] = match[2]
|
234
|
+
end
|
156
235
|
|
157
|
-
|
158
|
-
|
236
|
+
def short_option(match)
|
237
|
+
@options[match[1]] = match[1]
|
238
|
+
end
|
159
239
|
end
|
160
240
|
end
|
161
241
|
|
162
|
-
private_constant(:
|
242
|
+
private_constant(:Instance)
|
163
243
|
end
|
data/lib/mini-cli/run.rb
CHANGED
@@ -1,32 +1,48 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'open3'
|
4
|
-
|
5
3
|
module MiniCli
|
6
|
-
def run(*cmd)
|
7
|
-
|
8
|
-
opts
|
9
|
-
|
4
|
+
def run(*cmd, stdin_data: nil, status: false, chdir: nil)
|
5
|
+
in_read, in_write = IO.pipe
|
6
|
+
opts = { err: %i[child out], in: in_read }
|
7
|
+
opts[:chdir] = chdir if chdir
|
8
|
+
ret = IO.popen(*cmd, opts, &__run_proc(stdin_data, in_write))
|
9
|
+
status ? [Process.last_status, ret] : ret
|
10
|
+
rescue Errno::ENOENT
|
10
11
|
nil
|
12
|
+
ensure
|
13
|
+
in_read&.close
|
14
|
+
in_write&.close
|
11
15
|
end
|
12
16
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
result = Open3.capture3(*cmd)
|
17
|
-
result.shift unless opts.first == :stdout
|
18
|
-
result.pop unless opts.last == :status
|
19
|
-
result.size == 1 ? result.first : result
|
17
|
+
def run_ruby(*cmd, **run_options)
|
18
|
+
require 'shellwords'
|
19
|
+
run(Shellwords.join(RUBY_CMD_ + cmd), **run_options)
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
23
|
-
|
24
|
-
return result.last.success? if opts.empty?
|
25
|
-
return result if opts.size == 2
|
26
|
-
opts.first == :status ? result.last : result.first
|
22
|
+
def run_script(script, status: false, chdir: nil)
|
23
|
+
run_ruby(stdin_data: script, status: status, chdir: chdir)
|
27
24
|
end
|
28
25
|
|
29
|
-
|
30
|
-
|
26
|
+
private
|
27
|
+
|
28
|
+
def __run_proc(stdin_data, in_write)
|
29
|
+
return :read unless stdin_data
|
30
|
+
proc do |out|
|
31
|
+
in_write.sync = true
|
32
|
+
if stdin_data.respond_to?(:readpartial)
|
33
|
+
IO.copy_stream(stdin_data, in_write)
|
34
|
+
else
|
35
|
+
in_write.write(stdin_data)
|
36
|
+
end
|
37
|
+
in_write.close
|
38
|
+
out.read
|
39
|
+
end
|
31
40
|
end
|
41
|
+
|
42
|
+
RUBY_CMD_ =
|
43
|
+
%w[--disable gems --disable did_you_mean --disable rubyopt].unshift(
|
44
|
+
RbConfig.ruby
|
45
|
+
).freeze
|
46
|
+
|
47
|
+
private_constant(:RUBY_CMD_)
|
32
48
|
end
|
data/lib/mini-cli/version.rb
CHANGED
data/lib/mini_cli.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'mini-cli'
|
data/mini-cli.gemspec
CHANGED
@@ -2,30 +2,30 @@
|
|
2
2
|
|
3
3
|
require_relative './lib/mini-cli/version'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'mini-cli'
|
7
|
+
spec.version = MiniCli::VERSION
|
8
|
+
spec.required_ruby_version = '>= 2.5.0'
|
9
|
+
|
10
|
+
spec.summary = 'The lean CLI framework for Ruby'
|
11
|
+
spec.description = <<~DESC
|
10
12
|
This gem is a lean, easy to use CLI framework with a very small footprint.
|
11
13
|
It provides an easy to use argument parsing, help displaying and
|
12
14
|
minimalistic error handling.
|
13
15
|
DESC
|
14
|
-
gem.author = 'Mike Blumtritt'
|
15
|
-
gem.homepage = 'https://github.com/mblumtritt/mini-cli'
|
16
|
-
gem.metadata = {
|
17
|
-
'source_code_uri' => 'https://github.com/mblumtritt/mini-cli',
|
18
|
-
'bug_tracker_uri' => 'https://github.com/mblumtritt/mini-cli/issues'
|
19
|
-
}
|
20
16
|
|
21
|
-
|
17
|
+
spec.author = 'Mike Blumtritt'
|
18
|
+
spec.homepage = 'https://github.com/mblumtritt/mini-cli'
|
19
|
+
spec.metadata['source_code_uri'] = 'https://github.com/mblumtritt/mini-cli'
|
20
|
+
spec.metadata['bug_tracker_uri'] =
|
21
|
+
'https://github.com/mblumtritt/mini-cli/issues'
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
spec.add_development_dependency 'bundler'
|
24
|
+
spec.add_development_dependency 'minitest'
|
25
|
+
spec.add_development_dependency 'rake'
|
26
26
|
|
27
|
-
all_files =
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
all_files = Dir.chdir(__dir__) { `git ls-files -z`.split(0.chr) }
|
28
|
+
spec.test_files = all_files.grep(%r{^(spec|test)/})
|
29
|
+
spec.files = all_files - spec.test_files
|
30
|
+
spec.extra_rdoc_files = %w[README.md]
|
31
31
|
end
|
data/rakefile.rb
CHANGED
@@ -3,13 +3,11 @@
|
|
3
3
|
require 'rake/testtask'
|
4
4
|
require 'bundler/gem_tasks'
|
5
5
|
|
6
|
-
|
6
|
+
$stdout.sync = $stderr.sync = true
|
7
7
|
|
8
|
-
|
8
|
+
task(:default) { exec('rake --tasks') }
|
9
9
|
|
10
|
-
|
11
|
-
exec "#{$PROGRAM_NAME} --tasks"
|
12
|
-
end
|
10
|
+
CLOBBER << 'prj'
|
13
11
|
|
14
12
|
Rake::TestTask.new(:test) do |t|
|
15
13
|
t.ruby_opts = %w[-w]
|
data/samples/custom_args.rb
CHANGED
@@ -14,15 +14,18 @@ HELP
|
|
14
14
|
main { |cfg| cfg.each_pair { |key, value| puts("#{key}: #{value}") } }
|
15
15
|
|
16
16
|
parse_argv do |args|
|
17
|
-
Struct
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
17
|
+
Struct
|
18
|
+
.new(:target, :sources, :name, :url, :switch, :opt)
|
19
|
+
.new
|
20
|
+
.tap do |cfg|
|
21
|
+
cfg.target = args['TARGET']
|
22
|
+
cfg.sources = args['FILES'] # args['FILES'] is an array containing all surplus arguments
|
23
|
+
source = args['SOURCE'] || ENV['SOURCE']
|
24
|
+
cfg.sources.unshift(source) if source
|
25
|
+
cfg.sources << 'STDIN' if cfg.sources.empty?
|
26
|
+
cfg.name = args['NAME'] || 'default_name'
|
27
|
+
cfg.url = args['URL'] || 'www.url.test'
|
28
|
+
cfg.switch = args.key?('switch')
|
29
|
+
cfg.opt = args.key?('opt')
|
30
|
+
end
|
28
31
|
end
|
data/test/apps/noop.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# demonstrates the call sequence
|
3
|
+
|
4
|
+
require_relative '../../lib/mini-cli'
|
5
|
+
include(MiniCli)
|
6
|
+
|
7
|
+
help <<~HELP
|
8
|
+
-x, --exit early exit
|
9
|
+
-e, --error exit with error
|
10
|
+
HELP
|
11
|
+
|
12
|
+
main do |*args|
|
13
|
+
puts('main' + args.inspect)
|
14
|
+
exit if args.first['exit']
|
15
|
+
error(42, '!error!') if args.first['error']
|
16
|
+
end
|
17
|
+
|
18
|
+
before { |*args| puts('before_1' + args.inspect) }
|
19
|
+
after { |*args| puts('after_1' + args.inspect) }
|
20
|
+
before { |*args| puts('before_2' + args.inspect) }
|
21
|
+
after { |*args| puts('after_2' + args.inspect) }
|
22
|
+
|
23
|
+
parse_argv do |*args|
|
24
|
+
puts('parse_argv' + args.inspect)
|
25
|
+
args.first
|
26
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
require 'shellwords'
|
5
|
+
require_relative '../helper'
|
6
|
+
|
7
|
+
class ExecTest < Minitest::Test
|
8
|
+
def test_noop
|
9
|
+
assert_empty(assert_no_err('noop.rb'))
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_noop_help
|
13
|
+
assert_equal("Usage: noop\n", assert_no_err('noop.rb', '--help'))
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_sequence
|
17
|
+
expected = [
|
18
|
+
'before_1[]',
|
19
|
+
'before_2[]',
|
20
|
+
"parse_argv[{\"FILES\"=>[]}]",
|
21
|
+
"main[{\"FILES\"=>[]}]",
|
22
|
+
'after_2[]',
|
23
|
+
'after_1[]'
|
24
|
+
]
|
25
|
+
assert_equal(expected, assert_no_err('sequence.rb').split("\n"))
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_sequence_help
|
29
|
+
expected = [
|
30
|
+
'Usage: sequence [OPTIONS]',
|
31
|
+
'',
|
32
|
+
'Options:',
|
33
|
+
'-x, --exit early exit',
|
34
|
+
'-e, --error exit with error'
|
35
|
+
]
|
36
|
+
assert_equal(expected, assert_no_err('sequence.rb', '--help').split("\n"))
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_sequence_early_exit
|
40
|
+
expected = [
|
41
|
+
'before_1[]',
|
42
|
+
'before_2[]',
|
43
|
+
"parse_argv[{\"exit\"=>true, \"FILES\"=>[]}]",
|
44
|
+
"main[{\"exit\"=>true, \"FILES\"=>[]}]",
|
45
|
+
'after_2[]',
|
46
|
+
'after_1[]'
|
47
|
+
]
|
48
|
+
assert_equal(expected, assert_no_err('sequence.rb', '-x').split("\n"))
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_sequence_error
|
52
|
+
expected = [
|
53
|
+
'before_1[]',
|
54
|
+
'before_2[]',
|
55
|
+
"parse_argv[{\"error\"=>true, \"FILES\"=>[]}]",
|
56
|
+
"main[{\"error\"=>true, \"FILES\"=>[]}]",
|
57
|
+
'after_2[]',
|
58
|
+
'after_1[]'
|
59
|
+
]
|
60
|
+
std, err, status = invoke('sequence.rb', '-e')
|
61
|
+
assert_same(42, status.exitstatus)
|
62
|
+
assert_equal("sequence: !error!\n", err)
|
63
|
+
assert_equal(expected, std.split("\n"))
|
64
|
+
end
|
65
|
+
|
66
|
+
def assert_no_err(...)
|
67
|
+
std, err = assert_success(...)
|
68
|
+
assert_empty(err)
|
69
|
+
std
|
70
|
+
end
|
71
|
+
|
72
|
+
def assert_success(...)
|
73
|
+
std, err, status = invoke(...)
|
74
|
+
assert(status.success?)
|
75
|
+
return std, err
|
76
|
+
end
|
77
|
+
|
78
|
+
def invoke(name, *args)
|
79
|
+
Open3.capture3(
|
80
|
+
Shellwords.join(
|
81
|
+
[
|
82
|
+
RbConfig.ruby,
|
83
|
+
'--disable',
|
84
|
+
'gems',
|
85
|
+
'--disable',
|
86
|
+
'did_you_mean',
|
87
|
+
'--disable',
|
88
|
+
'rubyopt',
|
89
|
+
File.expand_path("../apps/#{name}", __dir__)
|
90
|
+
] + args
|
91
|
+
)
|
92
|
+
)
|
93
|
+
end
|
94
|
+
end
|
data/test/mini-cli/main_test.rb
CHANGED
@@ -1,16 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../helper'
|
2
4
|
|
3
5
|
class MainTest < Test
|
4
6
|
def test_defaults
|
5
7
|
assert_equal('helper', subject.name)
|
8
|
+
assert(subject.show_errors?)
|
6
9
|
assert_output("Usage: helper\n") { subject.show_help }
|
7
10
|
end
|
8
11
|
|
9
12
|
def test_methods
|
10
13
|
subject = Class.new { include MiniCli }.new
|
11
14
|
|
12
|
-
expected_methods = %i[
|
13
|
-
|
15
|
+
expected_methods = %i[
|
16
|
+
after
|
17
|
+
before
|
18
|
+
error
|
19
|
+
error_code
|
20
|
+
help
|
21
|
+
main
|
22
|
+
name
|
23
|
+
parse_argv
|
24
|
+
run
|
25
|
+
run_ruby
|
26
|
+
run_script
|
27
|
+
show_errors=
|
28
|
+
show_errors?
|
29
|
+
show_help
|
30
|
+
]
|
31
|
+
methods = (subject.methods - Object.instance_methods).sort!
|
14
32
|
assert_equal(expected_methods, methods)
|
15
33
|
end
|
16
34
|
|
@@ -22,6 +40,13 @@ class MainTest < Test
|
|
22
40
|
assert_stop_with_error(21, 'error') { subject.error(21, :error) }
|
23
41
|
end
|
24
42
|
|
43
|
+
def test_no_error
|
44
|
+
subject.show_errors = false
|
45
|
+
|
46
|
+
assert_output(nil, nil) { subject.error(666, 'some error text') }
|
47
|
+
assert_equal([666], subject.exit_args)
|
48
|
+
end
|
49
|
+
|
25
50
|
def test_help_simple
|
26
51
|
subject.help 'Some helptext'
|
27
52
|
expected_text = <<~EXPECTED
|
@@ -156,4 +181,13 @@ class MainTest < Test
|
|
156
181
|
result = subject.parse_argv(as_argv('-nup name port in out opt file'))
|
157
182
|
assert_equal(expected, result)
|
158
183
|
end
|
184
|
+
|
185
|
+
def test_run
|
186
|
+
call_sequence = []
|
187
|
+
subject.main { call_sequence << :main }
|
188
|
+
subject.before { call_sequence << :start_1 }
|
189
|
+
subject.after { call_sequence << :end_1 }
|
190
|
+
subject.before { call_sequence << :start_2 }
|
191
|
+
subject.after { call_sequence << :end_2 }
|
192
|
+
end
|
159
193
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../helper'
|
4
|
+
|
5
|
+
class RubyRunTest < Test
|
6
|
+
def test_simple
|
7
|
+
result = subject.run_script('puts("Hello World")')
|
8
|
+
assert_equal("Hello World\n", result)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_error
|
12
|
+
result = subject.run_script('UNDEFINED')
|
13
|
+
assert_match(/NameError/, result)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_chdir
|
17
|
+
home = Dir.home
|
18
|
+
refute(home == Dir.pwd)
|
19
|
+
result = subject.run_script('print Dir.pwd', chdir: home)
|
20
|
+
assert_equal(home, result)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_status
|
24
|
+
status, result = subject.run_script('print :Ok', status: true)
|
25
|
+
assert_instance_of(Process::Status, status)
|
26
|
+
assert(status.success?)
|
27
|
+
assert_equal('Ok', result)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_status_error
|
31
|
+
status, result = subject.run_script('print :Err;exit 42', status: true)
|
32
|
+
assert_instance_of(Process::Status, status)
|
33
|
+
refute(status.success?)
|
34
|
+
assert_same(42, status.exitstatus)
|
35
|
+
assert_equal('Err', result)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_stream
|
39
|
+
stream = StringIO.new('puts "Hello World"')
|
40
|
+
result = subject.run_script(stream)
|
41
|
+
assert_equal("Hello World\n", result)
|
42
|
+
end
|
43
|
+
end
|
data/test/mini-cli/run_test.rb
CHANGED
@@ -1,44 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../helper'
|
2
4
|
|
3
5
|
class RunTest < Test
|
4
|
-
def
|
5
|
-
|
6
|
-
|
6
|
+
def test_simple
|
7
|
+
result = subject.run('pwd')
|
8
|
+
assert_equal("#{Dir.pwd}\n", result)
|
7
9
|
end
|
8
10
|
|
9
|
-
def
|
10
|
-
|
11
|
-
|
11
|
+
def test_simple_error
|
12
|
+
result = subject.run('ls /no-valid-dir')
|
13
|
+
assert_match(/No such file or directory/, result)
|
14
|
+
end
|
12
15
|
|
13
|
-
|
14
|
-
|
16
|
+
def test_chdir
|
17
|
+
home = Dir.home
|
18
|
+
refute(home == Dir.pwd)
|
19
|
+
result = subject.run('pwd', chdir: home)
|
20
|
+
assert_equal("#{home}\n", result)
|
15
21
|
end
|
16
22
|
|
17
23
|
def test_status
|
18
|
-
result = subject.run('pwd',
|
19
|
-
assert_instance_of(TrueClass, result)
|
20
|
-
|
21
|
-
status = subject.run('pwd', status: true)
|
22
|
-
assert_instance_of(Process::Status, status)
|
23
|
-
|
24
|
-
out, status = subject.run('pwd', stdout: true, status: true)
|
25
|
-
assert_equal(@pwd, out)
|
24
|
+
status, result = subject.run('pwd', status: true)
|
26
25
|
assert_instance_of(Process::Status, status)
|
27
26
|
assert(status.success?)
|
27
|
+
assert_equal("#{Dir.pwd}\n", result)
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
31
|
-
|
32
|
-
assert_match(/No such file or directory/, err)
|
33
|
-
|
34
|
-
out, err = subject.run('ls /no-valid-dir', stdout: true, stderr: true)
|
35
|
-
assert_empty(out)
|
36
|
-
assert_match(/No such file or directory/, err)
|
37
|
-
|
38
|
-
err, status = subject.run('ls /no-valid-dir', stderr: true, status: true)
|
39
|
-
assert_match(/No such file or directory/, err)
|
30
|
+
def test_status_error
|
31
|
+
status, result = subject.run('ls /no-valid-dir', status: true)
|
40
32
|
assert_instance_of(Process::Status, status)
|
41
33
|
refute(status.success?)
|
34
|
+
assert_match(/No such file or directory/, result)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_stdin
|
38
|
+
string = 'Hello World'
|
39
|
+
result = subject.run('cat', stdin_data: string)
|
40
|
+
assert_equal(string, result)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_stdin_stream
|
44
|
+
stream = StringIO.new('Hello World')
|
45
|
+
result = subject.run('cat', stdin_data: stream)
|
46
|
+
assert_equal(stream.string, result)
|
42
47
|
end
|
43
48
|
|
44
49
|
def test_failure
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mini-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Blumtritt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-06-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -68,13 +68,18 @@ files:
|
|
68
68
|
- lib/mini-cli.rb
|
69
69
|
- lib/mini-cli/run.rb
|
70
70
|
- lib/mini-cli/version.rb
|
71
|
+
- lib/mini_cli.rb
|
71
72
|
- mini-cli.gemspec
|
72
73
|
- rakefile.rb
|
73
74
|
- samples/custom_args.rb
|
74
75
|
- samples/demo.rb
|
75
76
|
- samples/simple.rb
|
77
|
+
- test/apps/noop.rb
|
78
|
+
- test/apps/sequence.rb
|
76
79
|
- test/helper.rb
|
80
|
+
- test/mini-cli/exec_test.rb
|
77
81
|
- test/mini-cli/main_test.rb
|
82
|
+
- test/mini-cli/ruby_run_test.rb
|
78
83
|
- test/mini-cli/run_test.rb
|
79
84
|
homepage: https://github.com/mblumtritt/mini-cli
|
80
85
|
licenses: []
|
@@ -96,11 +101,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
96
101
|
- !ruby/object:Gem::Version
|
97
102
|
version: '0'
|
98
103
|
requirements: []
|
99
|
-
rubygems_version: 3.
|
104
|
+
rubygems_version: 3.2.15
|
100
105
|
signing_key:
|
101
106
|
specification_version: 4
|
102
107
|
summary: The lean CLI framework for Ruby
|
103
108
|
test_files:
|
109
|
+
- test/apps/noop.rb
|
110
|
+
- test/apps/sequence.rb
|
104
111
|
- test/helper.rb
|
112
|
+
- test/mini-cli/exec_test.rb
|
105
113
|
- test/mini-cli/main_test.rb
|
114
|
+
- test/mini-cli/ruby_run_test.rb
|
106
115
|
- test/mini-cli/run_test.rb
|