mini-cli 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/README.md +64 -0
- data/gems.rb +1 -0
- data/lib/mini-cli.rb +162 -0
- data/lib/mini-cli/run.rb +32 -0
- data/lib/mini-cli/version.rb +5 -0
- data/mini-cli.gemspec +31 -0
- data/rakefile.rb +18 -0
- data/samples/custom_args.rb +31 -0
- data/samples/demo.rb +22 -0
- data/samples/simple.rb +14 -0
- data/test/helper.rb +32 -0
- data/test/mini-cli/main_test.rb +178 -0
- data/test/mini-cli/run_test.rb +48 -0
- metadata +105 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3f71b96747e645a7e4ab1131c3e52840f63f22af4035d06b98f2d21172716d9b
|
4
|
+
data.tar.gz: a8ec28b1326a313f16ec8986f85a56b60f1b8c9832583324b79fd6a55c6a6228
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 72575dc73beedd924a2080b8cb436dcb11b78d9897e4f9eb87836dff6af028845d077c069540b06a5b7b93c6a2719c2de3a53a4e19996476a8c5871332fe97bb
|
7
|
+
data.tar.gz: 6848f5981ef7ce5bb4545a015b309f549450796a5d6530f4fd0f56fd3c315e7347da2afa318048facdb077db0eeee21676cd305b5cd17a863cb2037f5323cf32
|
data/.gitignore
ADDED
data/README.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# Mini Cli
|
2
|
+
|
3
|
+
This gem is a minimalistic, easy to use CLI framework with a very small footprint. I provides an easy to use argument parsing, help displaying, minimalistic error handling and some tools like executing external programs and gather their output.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
To use Mini CLI just install the gem with
|
8
|
+
|
9
|
+
```shell
|
10
|
+
gem install mini-cli
|
11
|
+
```
|
12
|
+
|
13
|
+
or include it to you project's gemspec:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'mini-cli'
|
17
|
+
```
|
18
|
+
|
19
|
+
## Sample
|
20
|
+
|
21
|
+
A very minimalistic program may look like this sample program:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
require 'mini-cli'
|
25
|
+
|
26
|
+
include MiniCli
|
27
|
+
|
28
|
+
help <<~HELP, %w[TARGET [SOURCE]]
|
29
|
+
-n, --name NAME option requires NAME argument, has shortcut
|
30
|
+
--url URL option requires URL argument
|
31
|
+
-s, --switch option without any argument, has shortcut
|
32
|
+
--opt option without any argument
|
33
|
+
|
34
|
+
This is a sample application only.
|
35
|
+
HELP
|
36
|
+
|
37
|
+
main do |args|
|
38
|
+
puts "TARGET: #{args['TARGET']}"
|
39
|
+
puts "SOURCE: #{args['SOURCE']}" if args.key?('SOURCE')
|
40
|
+
puts "NAME: #{args['NAME']}" if args.key?('NAME')
|
41
|
+
puts "URL: #{args['URL']}" if args.key?('URL')
|
42
|
+
puts "FILES: #{args['FILES']}" unless args['FILES'].empty?
|
43
|
+
puts '--switch was given' if args.key?('switch')
|
44
|
+
puts '--opt was given' if args.key?('opt')
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
The sample uses the powerful `#help` method to generate an argument parser which handles the command line for you. You only need to handle the given `Hash` parameter (named `args` in the sample) in the body of your `#main` block.
|
49
|
+
|
50
|
+
Executing the sample with `--help` or `-h` will provide following help screen:
|
51
|
+
|
52
|
+
```
|
53
|
+
Usage: sample [OPTIONS] TARGET [SOURCE]
|
54
|
+
|
55
|
+
Valid Options:
|
56
|
+
-n, --name NAME option requires NAME argument, has shortcut
|
57
|
+
--url URL option requires URL argument
|
58
|
+
-s, --switch option without any argument, has shortcut
|
59
|
+
--opt option without any argument
|
60
|
+
|
61
|
+
This is a sample application only.
|
62
|
+
```
|
63
|
+
|
64
|
+
See the `./samples` directory for more sample programs…
|
data/gems.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
gemspec
|
data/lib/mini-cli.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniCli
|
4
|
+
def self.included(_)
|
5
|
+
return if const_defined?(:SRC)
|
6
|
+
const_set(:SRC, caller_locations(1, 1).first.absolute_path)
|
7
|
+
end
|
8
|
+
|
9
|
+
def name(name = nil)
|
10
|
+
return name ? @__name = name : @__name if defined?(@__name)
|
11
|
+
@__name = name || File.basename(MiniCli::SRC, '.*')
|
12
|
+
end
|
13
|
+
|
14
|
+
def help(helptext, *args)
|
15
|
+
@__argv_parser = ArgvParser.new(helptext, args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def show_help
|
19
|
+
__argv_parser.show_help(name)
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def error(code, message)
|
24
|
+
$stderr.puts("#{name}: #{message}")
|
25
|
+
exit(code)
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse_argv(argv = nil, &block)
|
29
|
+
return @__argv_converter = block if block
|
30
|
+
argv ||= ARGV.dup
|
31
|
+
exit(show_help) if argv.index('--help') || argv.index('-h')
|
32
|
+
args = __argv_parser.parse(argv, method(:error).to_proc)
|
33
|
+
defined?(@__argv_converter) ? @__argv_converter.call(args) || args : args
|
34
|
+
end
|
35
|
+
|
36
|
+
def main(args = nil)
|
37
|
+
at_exit do
|
38
|
+
yield(args || parse_argv)
|
39
|
+
rescue Interrupt
|
40
|
+
error(130, 'aborted')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def __argv_parser
|
47
|
+
@__argv_parser ||= ArgvParser.new(nil, [])
|
48
|
+
end
|
49
|
+
|
50
|
+
class ArgvParser
|
51
|
+
def initialize(helptext, args)
|
52
|
+
@helptext = helptext.to_s
|
53
|
+
@args = args.flatten.map!(&:to_s).uniq
|
54
|
+
@options = nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def show_help(name)
|
58
|
+
parse_help! unless @options
|
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?
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse(argv, error)
|
68
|
+
@error = error
|
69
|
+
parse_help! unless @options
|
70
|
+
@result, arguments = {}, []
|
71
|
+
loop do
|
72
|
+
case arg = argv.shift
|
73
|
+
when nil
|
74
|
+
break
|
75
|
+
when '--'
|
76
|
+
arguments += argv
|
77
|
+
break
|
78
|
+
when /\A--([[[:alnum:]]-]+)\z/
|
79
|
+
handle_option(Regexp.last_match[1], argv)
|
80
|
+
when /\A-([[:alnum:]]+)\z/
|
81
|
+
parse_options(Regexp.last_match[1], argv)
|
82
|
+
else
|
83
|
+
arguments << arg
|
84
|
+
end
|
85
|
+
end
|
86
|
+
process(arguments)
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def error(msg)
|
92
|
+
@error.call(1, msg)
|
93
|
+
end
|
94
|
+
|
95
|
+
def process(arguments)
|
96
|
+
@args.each do |arg|
|
97
|
+
next if arg.index('..')
|
98
|
+
value = arguments.shift
|
99
|
+
if arg.start_with?('[')
|
100
|
+
@result[arg[1..-2]] = value if value
|
101
|
+
else
|
102
|
+
@result[arg] = value || error("parameter expected - #{arg}")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
arguments.unshift(@result['FILES']) if @result.key?('FILES')
|
106
|
+
@result['FILES'] = arguments
|
107
|
+
@result
|
108
|
+
end
|
109
|
+
|
110
|
+
def handle_option(option, argv)
|
111
|
+
key = @options[option] || error("unknown option - #{option}")
|
112
|
+
return @result[key] = true if option == key
|
113
|
+
@result[key] = value = argv.shift
|
114
|
+
return unless value.nil? || value.start_with?('-')
|
115
|
+
error("parameter #{key} expected - --#{option}")
|
116
|
+
end
|
117
|
+
|
118
|
+
def parse_options(options, argv)
|
119
|
+
options.each_char do |opt|
|
120
|
+
key = @options[opt] || error("unknown option - #{opt}")
|
121
|
+
next @result[key] = true if key == key.downcase
|
122
|
+
@result[key] = value = argv.shift
|
123
|
+
next unless value.nil? || value.start_with?('-')
|
124
|
+
error("parameter #{key} expected - -#{opt}")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def parse_help!
|
129
|
+
@options = {}
|
130
|
+
@helptext.each_line do |line|
|
131
|
+
case line
|
132
|
+
when /-([[:alnum:]]), --([[[:alnum:]]-]+) ([[:upper:]]+)\s+\S+/
|
133
|
+
named_option(Regexp.last_match)
|
134
|
+
when /--([[[:alnum:]]-]+) ([[:upper:]]+)\s+\S+/
|
135
|
+
named_option_short(Regexp.last_match)
|
136
|
+
when /-([[:alnum:]]), --([[[:alnum:]]-]+)\s+\S+/
|
137
|
+
option(Regexp.last_match)
|
138
|
+
when /--([[[:alnum:]]-]+)\s+\S+/
|
139
|
+
option_short(Regexp.last_match)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def named_option(match)
|
145
|
+
@options[match[1]] = @options[match[2]] = match[3]
|
146
|
+
end
|
147
|
+
|
148
|
+
def named_option_short(match)
|
149
|
+
@options[match[1]] = match[2]
|
150
|
+
end
|
151
|
+
|
152
|
+
def option(match)
|
153
|
+
@options[match[1]] = @options[match[2]] = match[2]
|
154
|
+
end
|
155
|
+
|
156
|
+
def option_short(match)
|
157
|
+
@options[match[1]] = match[1]
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
private_constant(:ArgvParser)
|
162
|
+
end
|
data/lib/mini-cli/run.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
|
5
|
+
module MiniCli
|
6
|
+
def run(*cmd)
|
7
|
+
opts = Hash === cmd.last ? __run_opt(cmd.last) : [:stdout]
|
8
|
+
opts.delete(:stderr) ? __run3(opts, cmd) : __run2(opts, cmd)
|
9
|
+
rescue SystemCallError
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def __run3(opts, cmd)
|
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
|
20
|
+
end
|
21
|
+
|
22
|
+
def __run2(opts, cmd)
|
23
|
+
result = Open3.capture2e(*cmd)
|
24
|
+
return result.last.success? if opts.empty?
|
25
|
+
return result if opts.size == 2
|
26
|
+
opts.first == :status ? result.last : result.first
|
27
|
+
end
|
28
|
+
|
29
|
+
def __run_opt(opts)
|
30
|
+
%i[stdout stderr status].keep_if{ |s| opts.delete(s) }
|
31
|
+
end
|
32
|
+
end
|
data/mini-cli.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './lib/mini-cli/version'
|
4
|
+
|
5
|
+
GemSpec = Gem::Specification.new do |gem|
|
6
|
+
gem.name = 'mini-cli'
|
7
|
+
gem.version = MiniCli::VERSION
|
8
|
+
gem.summary = 'The minimalistic CLI framework for Ruby'
|
9
|
+
gem.description = <<~DESC
|
10
|
+
This gem is a minimalistic, easy to use CLI framework with a very small
|
11
|
+
footprint. I provides an easy to use argument parsing, help displaying and
|
12
|
+
minimalistic error handling.
|
13
|
+
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
|
+
|
21
|
+
gem.required_ruby_version = '>= 2.5.0'
|
22
|
+
|
23
|
+
gem.add_development_dependency 'bundler'
|
24
|
+
gem.add_development_dependency 'minitest'
|
25
|
+
gem.add_development_dependency 'rake'
|
26
|
+
|
27
|
+
all_files = %x(git ls-files -z).split(0.chr)
|
28
|
+
gem.test_files = all_files.grep(%r{^(spec|test)/})
|
29
|
+
gem.files = all_files - gem.test_files
|
30
|
+
# gem.extra_rdoc_files = %w[README.MD]
|
31
|
+
end
|
data/rakefile.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'bundler/gem_tasks'
|
5
|
+
|
6
|
+
STDOUT.sync = STDERR.sync = true
|
7
|
+
|
8
|
+
CLOBBER << 'prj'
|
9
|
+
|
10
|
+
task :default do
|
11
|
+
exec "#{$PROGRAM_NAME} --tasks"
|
12
|
+
end
|
13
|
+
|
14
|
+
Rake::TestTask.new(:test) do |t|
|
15
|
+
t.ruby_opts = %w[-w]
|
16
|
+
t.verbose = true
|
17
|
+
t.test_files = FileList['test/**/*_test.rb']
|
18
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../lib/mini-cli'
|
4
|
+
|
5
|
+
include MiniCli
|
6
|
+
|
7
|
+
help <<~HELP, %w[TARGET [SOURCE]]
|
8
|
+
-n, --name NAME option requires NAME argument, has shortcut
|
9
|
+
--url URL option requires URL argument
|
10
|
+
-s, --switch option without any argument, has shortcut
|
11
|
+
--opt option without any argument
|
12
|
+
HELP
|
13
|
+
|
14
|
+
main do |cfg|
|
15
|
+
cfg.each_pair{ |key, value| puts("#{key}: #{value}") }
|
16
|
+
end
|
17
|
+
|
18
|
+
parse_argv do |args|
|
19
|
+
Struct.new(:target, :sources, :name, :url, :switch, :opt).new.tap do |cfg|
|
20
|
+
cfg.target = args['TARGET']
|
21
|
+
# args['FILES'] is an array containing all surplus arguments
|
22
|
+
cfg.sources = args['FILES']
|
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
|
31
|
+
end
|
data/samples/demo.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../lib/mini-cli'
|
4
|
+
|
5
|
+
include MiniCli
|
6
|
+
|
7
|
+
help <<~HELP, %w[TARGET [SOURCE]]
|
8
|
+
-n, --name NAME option requires NAME argument, has shortcut
|
9
|
+
--url URL option requires URL argument
|
10
|
+
-s, --switch option without any argument, has shortcut
|
11
|
+
--opt option without any argument
|
12
|
+
HELP
|
13
|
+
|
14
|
+
main do |args|
|
15
|
+
puts("TARGET: #{args['TARGET']}")
|
16
|
+
puts("SOURCE: #{args['SOURCE']}") if args.key?('SOURCE')
|
17
|
+
puts("NAME: #{args['NAME']}") if args.key?('NAME')
|
18
|
+
puts("URL: #{args['URL']}") if args.key?('URL')
|
19
|
+
puts("FILES: #{args['FILES']}") unless args['FILES'].empty?
|
20
|
+
puts('--switch was given') if args.key?('switch')
|
21
|
+
puts('--opt was given') if args.key?('opt')
|
22
|
+
end
|
data/samples/simple.rb
ADDED
data/test/helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'minitest/parallel'
|
5
|
+
|
6
|
+
require_relative '../lib/mini-cli'
|
7
|
+
require_relative '../lib/mini-cli/run'
|
8
|
+
|
9
|
+
class Test < Minitest::Test
|
10
|
+
parallelize_me!
|
11
|
+
attr_reader :subject
|
12
|
+
|
13
|
+
def setup
|
14
|
+
@subject = Class.new do
|
15
|
+
include MiniCli
|
16
|
+
attr_reader :exit_args
|
17
|
+
|
18
|
+
def exit(*args)
|
19
|
+
@exit_args = args
|
20
|
+
end
|
21
|
+
end.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def assert_stop_with_error(code, text, &block)
|
25
|
+
assert_output(nil, "helper: #{text}\n", &block)
|
26
|
+
assert_equal([code], subject.exit_args)
|
27
|
+
end
|
28
|
+
|
29
|
+
def as_argv(str)
|
30
|
+
str.split(' ')
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
|
3
|
+
class MainTest < Test
|
4
|
+
def test_defaults
|
5
|
+
assert_equal('helper', subject.name)
|
6
|
+
assert_output("Usage: helper\n"){ subject.show_help }
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_methods
|
10
|
+
subject = Class.new{ include MiniCli }.new
|
11
|
+
|
12
|
+
expected_methods = %i[
|
13
|
+
error
|
14
|
+
help
|
15
|
+
main
|
16
|
+
name
|
17
|
+
parse_argv
|
18
|
+
run
|
19
|
+
show_help
|
20
|
+
].sort!
|
21
|
+
methods = (subject.methods - Object.new.methods).sort!
|
22
|
+
assert_equal(expected_methods, methods)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_error
|
26
|
+
assert_stop_with_error(42, 'some error text') do
|
27
|
+
subject.error(42, 'some error text')
|
28
|
+
end
|
29
|
+
|
30
|
+
assert_stop_with_error(21, 'error') do
|
31
|
+
subject.error(21, :error)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_help_simple
|
36
|
+
subject.help 'Some helptext'
|
37
|
+
expected_text = <<~EXPECTED
|
38
|
+
Usage: helper
|
39
|
+
Some helptext
|
40
|
+
EXPECTED
|
41
|
+
|
42
|
+
assert_output(expected_text){ subject.show_help }
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_help_with_args
|
46
|
+
subject.help <<~HELP, 'ARG1', :ARG2
|
47
|
+
Some helptext comes
|
48
|
+
here
|
49
|
+
HELP
|
50
|
+
|
51
|
+
expected_text = <<~EXPECTED
|
52
|
+
Usage: helper ARG1 ARG2
|
53
|
+
Some helptext comes
|
54
|
+
here
|
55
|
+
EXPECTED
|
56
|
+
|
57
|
+
assert_output(expected_text){ subject.show_help }
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_argument_required
|
61
|
+
subject.help :text, :ARG
|
62
|
+
|
63
|
+
assert_stop_with_error(1, 'parameter expected - ARG') do
|
64
|
+
subject.parse_argv(as_argv(''))
|
65
|
+
end
|
66
|
+
|
67
|
+
expected = {'ARG' => 'arg', 'FILES' => []}
|
68
|
+
assert_equal(expected, subject.parse_argv(as_argv('arg')))
|
69
|
+
|
70
|
+
expected = {'ARG' => 'arg1', 'FILES' => %w[arg2 arg3]}
|
71
|
+
assert_equal(expected, subject.parse_argv(as_argv('arg1 arg2 arg3')))
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_name
|
75
|
+
subject.name 'test-42'
|
76
|
+
assert_equal('test-42', subject.name)
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_argument_optional
|
80
|
+
subject.help :text, 'ARG1', '[ARG2]'
|
81
|
+
|
82
|
+
assert_stop_with_error(1, 'parameter expected - ARG1') do
|
83
|
+
subject.parse_argv(as_argv(''))
|
84
|
+
end
|
85
|
+
|
86
|
+
expected = {'ARG1' => 'arg1', 'ARG2' => 'arg2', 'FILES' => []}
|
87
|
+
assert_equal(expected, subject.parse_argv(as_argv('arg1 arg2')))
|
88
|
+
|
89
|
+
expected = {'ARG1' => 'arg', 'FILES' => []}
|
90
|
+
assert_equal(expected, subject.parse_argv(as_argv('arg')))
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_options
|
94
|
+
subject.help <<~HELP
|
95
|
+
-n, --named NAME option NAME with shortcut
|
96
|
+
--named-long LNAME option LNAME without shortcut
|
97
|
+
-u, --unnamed option unnamed with shortcut
|
98
|
+
--un-named option un-named without shortcut
|
99
|
+
|
100
|
+
Some additional explaination can be here
|
101
|
+
HELP
|
102
|
+
|
103
|
+
expected = {'FILES' => []}
|
104
|
+
assert_equal(expected, subject.parse_argv(as_argv('')))
|
105
|
+
|
106
|
+
expected = {'NAME' => 'name', 'FILES' => []}
|
107
|
+
assert_equal(expected, subject.parse_argv(as_argv('--named name')))
|
108
|
+
assert_equal(expected, subject.parse_argv(as_argv('-n name')))
|
109
|
+
|
110
|
+
expected = {'LNAME' => 'long', 'FILES' => []}
|
111
|
+
assert_equal(expected, subject.parse_argv(as_argv('--named-long long')))
|
112
|
+
|
113
|
+
expected = {'unnamed' => true, 'FILES' => []}
|
114
|
+
assert_equal(expected, subject.parse_argv(as_argv('--unnamed')))
|
115
|
+
assert_equal(expected, subject.parse_argv(as_argv('-u')))
|
116
|
+
|
117
|
+
expected = {'un-named' => true, 'FILES' => []}
|
118
|
+
assert_equal(expected, subject.parse_argv(as_argv('--un-named')))
|
119
|
+
|
120
|
+
expected = {
|
121
|
+
'NAME' => 'name',
|
122
|
+
'LNAME' => 'long',
|
123
|
+
'unnamed' => true,
|
124
|
+
'un-named' => true,
|
125
|
+
'FILES' => %w[FILE1 FILE2]
|
126
|
+
}
|
127
|
+
|
128
|
+
result = subject.parse_argv(%w[
|
129
|
+
--named name
|
130
|
+
--named-long long
|
131
|
+
--unnamed
|
132
|
+
--un-named
|
133
|
+
FILE1
|
134
|
+
FILE2
|
135
|
+
])
|
136
|
+
assert_equal(expected, result)
|
137
|
+
|
138
|
+
result = subject.parse_argv(%w[
|
139
|
+
-nu name
|
140
|
+
--named-long long
|
141
|
+
--un-named
|
142
|
+
FILE1
|
143
|
+
FILE2
|
144
|
+
])
|
145
|
+
assert_equal(expected, result)
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_complex_options
|
149
|
+
subject.help <<~HELP, %w[INFILE OUTFILE [OPTFILE]]
|
150
|
+
-n, --named NAME key 'NAME'
|
151
|
+
-p, --port PORT key 'PORT'
|
152
|
+
-u, --un-named key 'unnamed'
|
153
|
+
HELP
|
154
|
+
|
155
|
+
expected = {
|
156
|
+
'INFILE' => 'in',
|
157
|
+
'OUTFILE' => 'out',
|
158
|
+
'NAME' => 'name',
|
159
|
+
'PORT' => 'port',
|
160
|
+
'un-named' => true,
|
161
|
+
'FILES' => []
|
162
|
+
}
|
163
|
+
result = subject.parse_argv(as_argv('-nup name port in out'))
|
164
|
+
assert_equal(expected, result)
|
165
|
+
|
166
|
+
expected = {
|
167
|
+
'INFILE' => 'in',
|
168
|
+
'OUTFILE' => 'out',
|
169
|
+
'OPTFILE' => 'opt',
|
170
|
+
'NAME' => 'name',
|
171
|
+
'PORT' => 'port',
|
172
|
+
'un-named' => true,
|
173
|
+
'FILES' => %w[file]
|
174
|
+
}
|
175
|
+
result = subject.parse_argv(as_argv('-nup name port in out opt file'))
|
176
|
+
assert_equal(expected, result)
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
|
3
|
+
class RunTest < Test
|
4
|
+
def setup
|
5
|
+
super
|
6
|
+
@pwd = Dir.pwd + "\n"
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_std_out_only
|
10
|
+
out = subject.run('pwd')
|
11
|
+
assert_equal(@pwd, out)
|
12
|
+
|
13
|
+
out = subject.run('pwd', stdout: true)
|
14
|
+
assert_equal(@pwd, out)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_status
|
18
|
+
result = subject.run('pwd', stdout: false)
|
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)
|
26
|
+
assert_instance_of(Process::Status, status)
|
27
|
+
assert(status.success?)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_std_error
|
31
|
+
err = subject.run('ls /no-valid-dir', stderr: true)
|
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)
|
40
|
+
assert_instance_of(Process::Status, status)
|
41
|
+
refute(status.success?)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_failure
|
45
|
+
result = subject.run('this-is-not-a-valid-command')
|
46
|
+
assert_instance_of(NilClass, result)
|
47
|
+
end
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mini-cli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mike Blumtritt
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-03-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: |
|
56
|
+
This gem is a minimalistic, easy to use CLI framework with a very small
|
57
|
+
footprint. I provides an easy to use argument parsing, help displaying and
|
58
|
+
minimalistic error handling.
|
59
|
+
email:
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- ".gitignore"
|
65
|
+
- README.md
|
66
|
+
- gems.rb
|
67
|
+
- lib/mini-cli.rb
|
68
|
+
- lib/mini-cli/run.rb
|
69
|
+
- lib/mini-cli/version.rb
|
70
|
+
- mini-cli.gemspec
|
71
|
+
- rakefile.rb
|
72
|
+
- samples/custom_args.rb
|
73
|
+
- samples/demo.rb
|
74
|
+
- samples/simple.rb
|
75
|
+
- test/helper.rb
|
76
|
+
- test/mini-cli/main_test.rb
|
77
|
+
- test/mini-cli/run_test.rb
|
78
|
+
homepage: https://github.com/mblumtritt/mini-cli
|
79
|
+
licenses: []
|
80
|
+
metadata:
|
81
|
+
source_code_uri: https://github.com/mblumtritt/mini-cli
|
82
|
+
bug_tracker_uri: https://github.com/mblumtritt/mini-cli/issues
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: 2.5.0
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubygems_version: 3.1.2
|
99
|
+
signing_key:
|
100
|
+
specification_version: 4
|
101
|
+
summary: The minimalistic CLI framework for Ruby
|
102
|
+
test_files:
|
103
|
+
- test/helper.rb
|
104
|
+
- test/mini-cli/main_test.rb
|
105
|
+
- test/mini-cli/run_test.rb
|