mini-cli 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/mini-cli.rb +140 -97
- data/lib/mini-cli/version.rb +1 -1
- data/lib/mini_cli.rb +1 -0
- data/test/mini-cli/main_test.rb +22 -2
- data/test/mini-cli/run_test.rb +6 -4
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6954edd798ee19570124490d3c139443e54cf9db86da1cbf5e1ecf053f5b4de6
|
4
|
+
data.tar.gz: e9c458ab08eb813875df22053ce3c5fd99c651bbe24f60d787a6d0968936d204
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad9884fa0f18600b69212a8f3b3c36108838d510879cde3d2a4525009439a3574c8eae1d7d7d4b567647fce04aa5cfaeee3fdceec8529381defc2533c1166559
|
7
|
+
data.tar.gz: 287198992331abcd2fee44165775cad3a6905c15eb1fac71bee380d706ba5f2067b3b2d3498f9a7bc4f07fe92c447e417c854313b247f0b81db304f0810a582b
|
data/lib/mini-cli.rb
CHANGED
@@ -1,36 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module MiniCli
|
4
|
-
def self.included(
|
5
|
-
|
6
|
-
const_set(:
|
4
|
+
def self.included(mod)
|
5
|
+
source = caller_locations(1, 1).first.absolute_path
|
6
|
+
mod.const_set(:MiniCli__, Instance.new(source))
|
7
7
|
end
|
8
8
|
|
9
9
|
def name(name = nil)
|
10
|
-
|
11
|
-
@__name = name || File.basename(MiniCli::SRC, '.*')
|
10
|
+
__minicli__.name(name)
|
12
11
|
end
|
13
12
|
|
14
13
|
def help(helptext, *args)
|
15
|
-
|
14
|
+
__minicli__.help(helptext, args)
|
16
15
|
end
|
17
16
|
|
18
17
|
def show_help
|
19
|
-
|
20
|
-
true
|
18
|
+
__minicli__.show_help
|
21
19
|
end
|
22
20
|
|
23
|
-
def
|
24
|
-
|
21
|
+
def show_errors?
|
22
|
+
__minicli__.show_errors
|
23
|
+
end
|
24
|
+
|
25
|
+
def show_errors=(value)
|
26
|
+
__minicli__.show_errors = value ? true : false
|
27
|
+
end
|
28
|
+
|
29
|
+
def error(code, message = nil)
|
30
|
+
$stderr.puts("#{name}: #{message}") if message && show_errors?
|
25
31
|
exit(code)
|
26
32
|
end
|
27
33
|
|
28
34
|
def parse_argv(argv = nil, &argv_converter)
|
29
|
-
return
|
35
|
+
return __minicli__.converter = argv_converter if argv_converter
|
30
36
|
argv ||= ARGV.dup
|
31
37
|
exit(show_help) if argv.index('--help') || argv.index('-h')
|
32
|
-
|
33
|
-
defined?(@__argv_converter) ? @__argv_converter.call(args) || args : args
|
38
|
+
__minicli__.convert(__minicli__.parse(argv, method(:error).to_proc))
|
34
39
|
end
|
35
40
|
|
36
41
|
def main(args = nil)
|
@@ -43,121 +48,159 @@ module MiniCli
|
|
43
48
|
|
44
49
|
private
|
45
50
|
|
46
|
-
def
|
47
|
-
|
51
|
+
def __minicli__
|
52
|
+
self.class::MiniCli__
|
48
53
|
end
|
49
54
|
|
50
|
-
class
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
+
class Instance
|
56
|
+
attr_reader :source
|
57
|
+
attr_writer :converter
|
58
|
+
attr_accessor :show_errors
|
59
|
+
|
60
|
+
def initialize(source)
|
61
|
+
@source = source
|
62
|
+
@name = File.basename(source, '.*')
|
63
|
+
@parser = @converter = nil
|
64
|
+
@show_errors = true
|
55
65
|
end
|
56
66
|
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
67
|
+
def name(name = nil)
|
68
|
+
name ? @name = name.to_s : @name
|
69
|
+
end
|
70
|
+
|
71
|
+
def help(helptext, args)
|
72
|
+
@parser = ArgvParser.new(helptext, args)
|
73
|
+
end
|
74
|
+
|
75
|
+
def show_help
|
76
|
+
parser.show_help(@name)
|
77
|
+
true
|
65
78
|
end
|
66
79
|
|
67
80
|
def parse(argv, error)
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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)
|
81
|
+
parser.parse(argv, error)
|
82
|
+
end
|
83
|
+
|
84
|
+
def convert(args)
|
85
|
+
@converter ? @converter.call(args) || args : args
|
87
86
|
end
|
88
87
|
|
89
88
|
private
|
90
89
|
|
91
|
-
def
|
92
|
-
@
|
90
|
+
def parser
|
91
|
+
@parser ||= ArgvParser.new(nil, [])
|
93
92
|
end
|
94
93
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
94
|
+
class ArgvParser
|
95
|
+
def initialize(helptext, args)
|
96
|
+
@helptext = helptext.to_s
|
97
|
+
@args = args.flatten.map!(&:to_s).uniq
|
98
|
+
@options = nil
|
99
|
+
end
|
100
|
+
|
101
|
+
def show_help(name)
|
102
|
+
parse_help! unless @options
|
103
|
+
print("Usage: #{name}")
|
104
|
+
print(' [OPTIONS]') unless @options.empty?
|
105
|
+
print(' ', @args.join(' ')) unless @args.empty?
|
106
|
+
puts
|
107
|
+
puts(nil, 'Options:') unless @options.empty?
|
108
|
+
puts(@helptext) unless @helptext.empty?
|
109
|
+
end
|
110
|
+
|
111
|
+
def parse(argv, error)
|
112
|
+
@error, @result = error, {}
|
113
|
+
parse_help! unless @options
|
114
|
+
process(parse_argv(argv))
|
115
|
+
end
|
99
116
|
|
117
|
+
private
|
118
|
+
|
119
|
+
def parse_argv(argv)
|
120
|
+
arguments = []
|
121
|
+
while (arg = argv.shift)
|
122
|
+
case arg
|
123
|
+
when '--'
|
124
|
+
arguments += argv
|
125
|
+
break
|
126
|
+
when /\A--([[[:alnum:]]-]+)\z/
|
127
|
+
handle_option(Regexp.last_match[1], argv)
|
128
|
+
when /\A-([[:alnum:]]+)\z/
|
129
|
+
parse_options(Regexp.last_match[1], argv)
|
130
|
+
else
|
131
|
+
arguments << arg
|
132
|
+
end
|
133
|
+
end
|
134
|
+
arguments
|
135
|
+
end
|
136
|
+
|
137
|
+
def error(msg)
|
138
|
+
@error[1, msg]
|
139
|
+
end
|
140
|
+
|
141
|
+
def process(arguments)
|
142
|
+
@args.each do |arg|
|
143
|
+
process_arg(arg, arguments.shift) unless arg.index('..')
|
144
|
+
end
|
145
|
+
arguments.unshift(@result['FILES']) if @result.key?('FILES')
|
146
|
+
@result['FILES'] = arguments
|
147
|
+
@result
|
148
|
+
end
|
149
|
+
|
150
|
+
def process_arg(arg, value)
|
100
151
|
if arg.start_with?('[')
|
101
152
|
@result[arg[1..-2]] = value if value
|
102
153
|
else
|
103
154
|
@result[arg] = value || error("parameter expected - #{arg}")
|
104
155
|
end
|
105
156
|
end
|
106
|
-
arguments.unshift(@result['FILES']) if @result.key?('FILES')
|
107
|
-
@result['FILES'] = arguments
|
108
|
-
@result
|
109
|
-
end
|
110
157
|
|
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
|
158
|
+
def handle_option(option, argv, test = ->(k) { option == k })
|
159
|
+
key = @options[option] || error("unknown option - #{option}")
|
160
|
+
@result[key] = test[key] and return
|
123
161
|
@result[key] = value = argv.shift
|
124
|
-
|
125
|
-
error("parameter #{key} expected -
|
162
|
+
return unless value.nil? || value.start_with?('-')
|
163
|
+
error("parameter #{key} expected - --#{option}")
|
126
164
|
end
|
127
|
-
end
|
128
165
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
166
|
+
def parse_options(options, argv)
|
167
|
+
test = ->(k) { k == k.downcase }
|
168
|
+
options.each_char { |opt| handle_option(opt, argv, test) }
|
169
|
+
end
|
170
|
+
|
171
|
+
def parse_help!
|
172
|
+
@options = {}
|
173
|
+
@helptext.each_line do |line|
|
174
|
+
case line
|
175
|
+
when /-([[:alnum:]]), --([[[:alnum:]]-]+) ([[:upper:]]+)\s+\S+/
|
176
|
+
option_with_argument(Regexp.last_match)
|
177
|
+
when /--([[[:alnum:]]-]+) ([[:upper:]]+)\s+\S+/
|
178
|
+
short_option_with_argument(Regexp.last_match)
|
179
|
+
when /-([[:alnum:]]), --([[[:alnum:]]-]+)\s+\S+/
|
180
|
+
option(Regexp.last_match)
|
181
|
+
when /--([[[:alnum:]]-]+)\s+\S+/
|
182
|
+
short_option(Regexp.last_match)
|
183
|
+
end
|
141
184
|
end
|
142
185
|
end
|
143
|
-
end
|
144
186
|
|
145
|
-
|
146
|
-
|
147
|
-
|
187
|
+
def option_with_argument(match)
|
188
|
+
@options[match[1]] = @options[match[2]] = match[3]
|
189
|
+
end
|
148
190
|
|
149
|
-
|
150
|
-
|
151
|
-
|
191
|
+
def short_option_with_argument(match)
|
192
|
+
@options[match[1]] = match[2]
|
193
|
+
end
|
152
194
|
|
153
|
-
|
154
|
-
|
155
|
-
|
195
|
+
def option(match)
|
196
|
+
@options[match[1]] = @options[match[2]] = match[2]
|
197
|
+
end
|
156
198
|
|
157
|
-
|
158
|
-
|
199
|
+
def short_option(match)
|
200
|
+
@options[match[1]] = match[1]
|
201
|
+
end
|
159
202
|
end
|
160
203
|
end
|
161
204
|
|
162
|
-
private_constant(:
|
205
|
+
private_constant(:Instance)
|
163
206
|
end
|
data/lib/mini-cli/version.rb
CHANGED
data/lib/mini_cli.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'mini-cli'
|
data/test/mini-cli/main_test.rb
CHANGED
@@ -1,16 +1,29 @@
|
|
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
|
+
error
|
17
|
+
help
|
18
|
+
main
|
19
|
+
name
|
20
|
+
parse_argv
|
21
|
+
run
|
22
|
+
show_errors=
|
23
|
+
show_errors?
|
24
|
+
show_help
|
25
|
+
]
|
26
|
+
methods = (subject.methods - Object.instance_methods).sort!
|
14
27
|
assert_equal(expected_methods, methods)
|
15
28
|
end
|
16
29
|
|
@@ -22,6 +35,13 @@ class MainTest < Test
|
|
22
35
|
assert_stop_with_error(21, 'error') { subject.error(21, :error) }
|
23
36
|
end
|
24
37
|
|
38
|
+
def test_no_error
|
39
|
+
subject.show_errors = false
|
40
|
+
|
41
|
+
assert_output(nil, nil) { subject.error(666, 'some error text') }
|
42
|
+
assert_equal([666], subject.exit_args)
|
43
|
+
end
|
44
|
+
|
25
45
|
def test_help_simple
|
26
46
|
subject.help 'Some helptext'
|
27
47
|
expected_text = <<~EXPECTED
|
data/test/mini-cli/run_test.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../helper'
|
2
4
|
|
3
5
|
class RunTest < Test
|
4
6
|
def test_simple
|
5
7
|
result = subject.run('pwd')
|
6
|
-
assert_equal(Dir.pwd
|
8
|
+
assert_equal("#{Dir.pwd}\n", result)
|
7
9
|
end
|
8
10
|
|
9
11
|
def test_simple_error
|
@@ -15,14 +17,14 @@ class RunTest < Test
|
|
15
17
|
home = Dir.home
|
16
18
|
refute(home == Dir.pwd)
|
17
19
|
result = subject.run('pwd', chdir: home)
|
18
|
-
assert_equal(home
|
20
|
+
assert_equal("#{home}\n", result)
|
19
21
|
end
|
20
22
|
|
21
23
|
def test_status
|
22
24
|
status, result = subject.run('pwd', status: true)
|
23
25
|
assert_instance_of(Process::Status, status)
|
24
26
|
assert(status.success?)
|
25
|
-
assert_equal(Dir.pwd
|
27
|
+
assert_equal("#{Dir.pwd}\n", result)
|
26
28
|
end
|
27
29
|
|
28
30
|
def test_status_error
|
@@ -39,7 +41,7 @@ class RunTest < Test
|
|
39
41
|
end
|
40
42
|
|
41
43
|
def test_stdin_stream
|
42
|
-
stream = StringIO.new(
|
44
|
+
stream = StringIO.new('Hello World')
|
43
45
|
result = subject.run('cat', stdin_data: stream)
|
44
46
|
assert_equal(stream.string, result)
|
45
47
|
end
|
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.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Blumtritt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -68,6 +68,7 @@ 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
|