cri 2.7.1 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +21 -34
- data/NEWS.md +8 -0
- data/README.adoc +12 -0
- data/Rakefile +4 -7
- data/lib/cri/command.rb +37 -10
- data/lib/cri/command_dsl.rb +11 -0
- data/lib/cri/commands/basic_root.rb +1 -1
- data/lib/cri/option_parser.rb +2 -2
- data/lib/cri/version.rb +1 -1
- data/test/test_argument_array.rb +2 -0
- data/test/test_base.rb +2 -0
- data/test/test_basic_help.rb +2 -0
- data/test/test_basic_root.rb +14 -1
- data/test/test_command.rb +78 -5
- data/test/test_command_dsl.rb +2 -0
- data/test/test_command_runner.rb +2 -0
- data/test/test_option_parser.rb +2 -0
- data/test/test_string_formatter.rb +2 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef8c0cab47165e8e00e78ffa23c72a9569656a64
|
4
|
+
data.tar.gz: 59473eac71bfa27211fe462321d5fd64e997c694
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8e7c91fbb4550ec9744f18cf21ff9e2ee02dd324f3a85bc3e660442545ce766bda90d8fc003985f19ec845a26c7c7040c9cef9e69dbe5af461992f42540cbcb
|
7
|
+
data.tar.gz: 4008d8b904781cd7f1b8da699fa26b1f66842387517e43676db912d9d7551e232bb2fa3337600fe028c4f6a764cdacd3ef2f4cd5736e9212601f6b1d1e434b66
|
data/Gemfile.lock
CHANGED
@@ -7,54 +7,41 @@ PATH
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
asciidoctor (1.5.
|
10
|
+
asciidoctor (1.5.5)
|
11
11
|
ast (2.3.0)
|
12
12
|
colored (1.2)
|
13
|
-
coveralls (0.8.
|
14
|
-
json (
|
15
|
-
|
16
|
-
simplecov (~> 0.10.0)
|
13
|
+
coveralls (0.8.20)
|
14
|
+
json (>= 1.8, < 3)
|
15
|
+
simplecov (~> 0.14.1)
|
17
16
|
term-ansicolor (~> 1.3)
|
18
|
-
thor (~> 0.19.
|
17
|
+
thor (~> 0.19.4)
|
18
|
+
tins (~> 1.6)
|
19
19
|
docile (1.1.5)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
domain_name (~> 0.5)
|
24
|
-
json (1.8.2)
|
25
|
-
mime-types (2.5)
|
26
|
-
minitest (5.6.1)
|
27
|
-
netrc (0.10.3)
|
28
|
-
parser (2.3.3.1)
|
20
|
+
json (2.0.3)
|
21
|
+
minitest (5.10.1)
|
22
|
+
parser (2.4.0.0)
|
29
23
|
ast (~> 2.2)
|
30
24
|
powerpack (0.1.1)
|
31
|
-
rainbow (2.1
|
32
|
-
rake (
|
33
|
-
|
34
|
-
|
35
|
-
mime-types (>= 1.16, < 3.0)
|
36
|
-
netrc (~> 0.7)
|
37
|
-
rubocop (0.46.0)
|
38
|
-
parser (>= 2.3.1.1, < 3.0)
|
25
|
+
rainbow (2.2.1)
|
26
|
+
rake (12.0.0)
|
27
|
+
rubocop (0.48.0)
|
28
|
+
parser (>= 2.3.3.1, < 3.0)
|
39
29
|
powerpack (~> 0.1)
|
40
30
|
rainbow (>= 1.99.1, < 3.0)
|
41
31
|
ruby-progressbar (~> 1.7)
|
42
32
|
unicode-display_width (~> 1.0, >= 1.0.1)
|
43
33
|
ruby-progressbar (1.8.1)
|
44
|
-
simplecov (0.
|
34
|
+
simplecov (0.14.1)
|
45
35
|
docile (~> 1.1.0)
|
46
|
-
json (
|
36
|
+
json (>= 1.8, < 3)
|
47
37
|
simplecov-html (~> 0.10.0)
|
48
38
|
simplecov-html (0.10.0)
|
49
|
-
term-ansicolor (1.
|
39
|
+
term-ansicolor (1.5.0)
|
50
40
|
tins (~> 1.0)
|
51
|
-
thor (0.19.
|
52
|
-
tins (1.
|
53
|
-
|
54
|
-
|
55
|
-
unf_ext (0.0.7.1)
|
56
|
-
unicode-display_width (1.1.1)
|
57
|
-
yard (0.8.7.6)
|
41
|
+
thor (0.19.4)
|
42
|
+
tins (1.13.2)
|
43
|
+
unicode-display_width (1.1.3)
|
44
|
+
yard (0.9.8)
|
58
45
|
|
59
46
|
PLATFORMS
|
60
47
|
ruby
|
@@ -70,4 +57,4 @@ DEPENDENCIES
|
|
70
57
|
yard
|
71
58
|
|
72
59
|
BUNDLED WITH
|
73
|
-
1.
|
60
|
+
1.14.6
|
data/NEWS.md
CHANGED
data/README.adoc
CHANGED
@@ -9,6 +9,10 @@ link:http://inch-ci.org/github/ddfreyne/cri/[image:http://inch-ci.org/github/ddf
|
|
9
9
|
Cri is a library for building easy-to-use command-line tools with support for
|
10
10
|
nested commands.
|
11
11
|
|
12
|
+
== Requirements ==
|
13
|
+
|
14
|
+
Cri requires Ruby 2.1 or newer.
|
15
|
+
|
12
16
|
== Usage ==
|
13
17
|
|
14
18
|
The central concept in Cri is the _command_, which has option definitions as
|
@@ -189,6 +193,14 @@ root_cmd.add_command(cmd_commit)
|
|
189
193
|
root.cmd.add_command(cmd_init)
|
190
194
|
--------------------------------------------------------------------------------
|
191
195
|
|
196
|
+
You can specify a default subcommand. This subcommand will be executed when the
|
197
|
+
command has subcommands, and no subcommands are otherwise explicitly specified:
|
198
|
+
|
199
|
+
[source,ruby]
|
200
|
+
--------------------------------------------------------------------------------
|
201
|
+
default_subcommand 'compile'
|
202
|
+
--------------------------------------------------------------------------------
|
203
|
+
|
192
204
|
== Contributors ==
|
193
205
|
|
194
206
|
* Toon Willems
|
data/Rakefile
CHANGED
@@ -14,18 +14,15 @@ YARD::Rake::YardocTask.new(:doc) do |yard|
|
|
14
14
|
]
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
FileList['./test/**/test_*.rb', './test/**/*_spec.rb'].each do |fn|
|
21
|
-
require fn
|
22
|
-
end
|
17
|
+
Rake::TestTask.new(:test_unit) do |t|
|
18
|
+
t.test_files = Dir['test/**/*_spec.rb'] + Dir['test/**/test_*.rb']
|
19
|
+
t.libs << 'test'
|
23
20
|
end
|
24
21
|
|
25
22
|
RuboCop::RakeTask.new(:test_style) do |task|
|
26
23
|
task.options = %w(--display-cop-names --format simple)
|
27
24
|
end
|
28
25
|
|
29
|
-
task test:
|
26
|
+
task test: %i(test_unit test_style)
|
30
27
|
|
31
28
|
task default: :test
|
data/lib/cri/command.rb
CHANGED
@@ -38,6 +38,20 @@ module Cri
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
+
# Signals that Cri should abort execution. Unless otherwise specified using the `hard_exit`
|
42
|
+
# param, this exception will cause Cri to exit the running process.
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
class CriExitException < StandardError
|
46
|
+
def initialize(is_error:)
|
47
|
+
@is_error = is_error
|
48
|
+
end
|
49
|
+
|
50
|
+
def error?
|
51
|
+
@is_error
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
41
55
|
# @return [Cri::Command, nil] This command’s supercommand, or nil if the
|
42
56
|
# command has no supercommand
|
43
57
|
attr_accessor :supercommand
|
@@ -46,6 +60,9 @@ module Cri
|
|
46
60
|
attr_accessor :commands
|
47
61
|
alias subcommands commands
|
48
62
|
|
63
|
+
# @return [Symbol] The name of the default subcommand
|
64
|
+
attr_accessor :default_subcommand_name
|
65
|
+
|
49
66
|
# @return [String] The name
|
50
67
|
attr_accessor :name
|
51
68
|
|
@@ -123,6 +140,7 @@ module Cri
|
|
123
140
|
@aliases = Set.new
|
124
141
|
@commands = Set.new
|
125
142
|
@option_definitions = Set.new
|
143
|
+
@default_subcommand_name = nil
|
126
144
|
end
|
127
145
|
|
128
146
|
# Modifies the command using the DSL.
|
@@ -211,19 +229,21 @@ module Cri
|
|
211
229
|
# @param [String] name The full, partial or aliases name of the command
|
212
230
|
#
|
213
231
|
# @return [Cri::Command] The command with the given name
|
214
|
-
def command_named(name)
|
232
|
+
def command_named(name, hard_exit: true)
|
215
233
|
commands = commands_named(name)
|
216
234
|
|
217
235
|
if commands.empty?
|
218
236
|
$stderr.puts "#{self.name}: unknown command '#{name}'\n"
|
219
|
-
|
237
|
+
raise CriExitException.new(is_error: true)
|
220
238
|
elsif commands.size > 1
|
221
239
|
$stderr.puts "#{self.name}: '#{name}' is ambiguous:"
|
222
240
|
$stderr.puts " #{commands.map(&:name).sort.join(' ')}"
|
223
|
-
|
241
|
+
raise CriExitException.new(is_error: true)
|
224
242
|
else
|
225
243
|
commands[0]
|
226
244
|
end
|
245
|
+
rescue CriExitException => e
|
246
|
+
exit(e.error? ? 1 : 0) if hard_exit
|
227
247
|
end
|
228
248
|
|
229
249
|
# Runs the command with the given command-line arguments, possibly invoking
|
@@ -235,7 +255,7 @@ module Cri
|
|
235
255
|
# supercommand
|
236
256
|
#
|
237
257
|
# @return [void]
|
238
|
-
def run(opts_and_args, parent_opts = {})
|
258
|
+
def run(opts_and_args, parent_opts = {}, hard_exit: true)
|
239
259
|
# Parse up to command name
|
240
260
|
stuff = partition(opts_and_args)
|
241
261
|
opts_before_subcmd, subcmd_name, opts_and_args_after_subcmd = *stuff
|
@@ -248,14 +268,21 @@ module Cri
|
|
248
268
|
|
249
269
|
# Get command
|
250
270
|
if subcmd_name.nil?
|
251
|
-
|
252
|
-
|
271
|
+
if default_subcommand_name
|
272
|
+
subcmd_name = default_subcommand_name
|
273
|
+
else
|
274
|
+
$stderr.puts "#{name}: no command given"
|
275
|
+
raise CriExitException.new(is_error: true)
|
276
|
+
end
|
253
277
|
end
|
254
|
-
subcommand = command_named(subcmd_name)
|
278
|
+
subcommand = command_named(subcmd_name, hard_exit: hard_exit)
|
279
|
+
return if subcommand.nil?
|
255
280
|
|
256
281
|
# Run
|
257
|
-
subcommand.run(opts_and_args_after_subcmd, opts_before_subcmd)
|
282
|
+
subcommand.run(opts_and_args_after_subcmd, opts_before_subcmd, hard_exit: hard_exit)
|
258
283
|
end
|
284
|
+
rescue CriExitException => e
|
285
|
+
exit(e.error? ? 1 : 0) if hard_exit
|
259
286
|
end
|
260
287
|
|
261
288
|
# Runs the actual command with the given command-line arguments, not
|
@@ -343,10 +370,10 @@ module Cri
|
|
343
370
|
yield
|
344
371
|
rescue Cri::OptionParser::IllegalOptionError => e
|
345
372
|
$stderr.puts "#{name}: illegal option -- #{e}"
|
346
|
-
|
373
|
+
raise CriExitException.new(is_error: true)
|
347
374
|
rescue Cri::OptionParser::OptionRequiresAnArgumentError => e
|
348
375
|
$stderr.puts "#{name}: option requires an argument -- #{e}"
|
349
|
-
|
376
|
+
raise CriExitException.new(is_error: true)
|
350
377
|
end
|
351
378
|
end
|
352
379
|
end
|
data/lib/cri/command_dsl.rb
CHANGED
@@ -31,6 +31,17 @@ module Cri
|
|
31
31
|
@command.add_command(command)
|
32
32
|
end
|
33
33
|
|
34
|
+
# Sets the name of the default subcommand, i.e. the subcommand that will
|
35
|
+
# be executed if no subcommand is explicitly specified. This is `nil` by
|
36
|
+
# default, and will typically only be set for the root command.
|
37
|
+
#
|
38
|
+
# @param [String, nil] name The name of the default subcommand
|
39
|
+
#
|
40
|
+
# @return [void]
|
41
|
+
def default_subcommand(name)
|
42
|
+
@command.default_subcommand_name = name
|
43
|
+
end
|
44
|
+
|
34
45
|
# Sets the command name.
|
35
46
|
#
|
36
47
|
# @param [String] arg The new command name
|
data/lib/cri/option_parser.rb
CHANGED
@@ -199,7 +199,7 @@ module Cri
|
|
199
199
|
definition = @definitions.find { |d| d[:long] == option_key }
|
200
200
|
raise IllegalOptionError.new(option_key) if definition.nil?
|
201
201
|
|
202
|
-
if
|
202
|
+
if %i(required optional).include?(definition[:argument])
|
203
203
|
# Get option value if necessary
|
204
204
|
if option_value.nil?
|
205
205
|
option_value = find_option_value(definition, option_key)
|
@@ -226,7 +226,7 @@ module Cri
|
|
226
226
|
if option_keys.length > 1 && definition[:argument] == :required
|
227
227
|
# This is a combined option and it requires an argument, so complain
|
228
228
|
raise OptionRequiresAnArgumentError.new(option_key)
|
229
|
-
elsif
|
229
|
+
elsif %i(required optional).include?(definition[:argument])
|
230
230
|
# Get option value
|
231
231
|
option_value = find_option_value(definition, option_key)
|
232
232
|
|
data/lib/cri/version.rb
CHANGED
data/test/test_argument_array.rb
CHANGED
data/test/test_base.rb
CHANGED
data/test/test_basic_help.rb
CHANGED
data/test/test_basic_root.rb
CHANGED
@@ -1,12 +1,25 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
1
3
|
module Cri
|
2
4
|
class BasicRootTestCase < Cri::TestCase
|
3
5
|
def test_run_with_help
|
4
6
|
cmd = Cri::Command.new_basic_root
|
5
7
|
|
6
8
|
stdout, _stderr = capture_io_while do
|
7
|
-
assert_raises SystemExit do
|
9
|
+
err = assert_raises SystemExit do
|
8
10
|
cmd.run(%w(-h))
|
9
11
|
end
|
12
|
+
assert_equal 0, err.status
|
13
|
+
end
|
14
|
+
|
15
|
+
assert stdout =~ /COMMANDS.*\n.*help.*show help/
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_run_with_help_no_exit
|
19
|
+
cmd = Cri::Command.new_basic_root
|
20
|
+
|
21
|
+
stdout, _stderr = capture_io_while do
|
22
|
+
cmd.run(%w(-h), {}, hard_exit: false)
|
10
23
|
end
|
11
24
|
|
12
25
|
assert stdout =~ /COMMANDS.*\n.*help.*show help/
|
data/test/test_command.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
1
3
|
module Cri
|
2
4
|
class CommandTestCase < Cri::TestCase
|
3
5
|
def simple_cmd
|
@@ -145,9 +147,19 @@ module Cri
|
|
145
147
|
|
146
148
|
def test_invoke_simple_with_missing_opt_arg
|
147
149
|
out, err = capture_io_while do
|
148
|
-
assert_raises SystemExit do
|
150
|
+
err = assert_raises SystemExit do
|
149
151
|
simple_cmd.run(%w(-b))
|
150
152
|
end
|
153
|
+
assert_equal 1, err.status
|
154
|
+
end
|
155
|
+
|
156
|
+
assert_equal [], lines(out)
|
157
|
+
assert_equal ['moo: option requires an argument -- b'], lines(err)
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_invoke_simple_with_missing_opt_arg_no_exit
|
161
|
+
out, err = capture_io_while do
|
162
|
+
simple_cmd.run(%w(-b), {}, hard_exit: false)
|
151
163
|
end
|
152
164
|
|
153
165
|
assert_equal [], lines(out)
|
@@ -156,9 +168,19 @@ module Cri
|
|
156
168
|
|
157
169
|
def test_invoke_simple_with_illegal_opt
|
158
170
|
out, err = capture_io_while do
|
159
|
-
assert_raises SystemExit do
|
171
|
+
err = assert_raises SystemExit do
|
160
172
|
simple_cmd.run(%w(-z))
|
161
173
|
end
|
174
|
+
assert_equal 1, err.status
|
175
|
+
end
|
176
|
+
|
177
|
+
assert_equal [], lines(out)
|
178
|
+
assert_equal ['moo: illegal option -- z'], lines(err)
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_invoke_simple_with_illegal_opt_no_exit
|
182
|
+
out, err = capture_io_while do
|
183
|
+
simple_cmd.run(%w(-z), {}, hard_exit: false)
|
162
184
|
end
|
163
185
|
|
164
186
|
assert_equal [], lines(out)
|
@@ -176,9 +198,19 @@ module Cri
|
|
176
198
|
|
177
199
|
def test_invoke_nested_without_opts_or_args
|
178
200
|
out, err = capture_io_while do
|
179
|
-
assert_raises SystemExit do
|
201
|
+
err = assert_raises SystemExit do
|
180
202
|
nested_cmd.run(%w())
|
181
203
|
end
|
204
|
+
assert_equal 1, err.status
|
205
|
+
end
|
206
|
+
|
207
|
+
assert_equal [], lines(out)
|
208
|
+
assert_equal ['super: no command given'], lines(err)
|
209
|
+
end
|
210
|
+
|
211
|
+
def test_invoke_nested_without_opts_or_args_no_exit
|
212
|
+
out, err = capture_io_while do
|
213
|
+
nested_cmd.run(%w(), {}, hard_exit: false)
|
182
214
|
end
|
183
215
|
|
184
216
|
assert_equal [], lines(out)
|
@@ -196,9 +228,19 @@ module Cri
|
|
196
228
|
|
197
229
|
def test_invoke_nested_with_incorrect_command_name
|
198
230
|
out, err = capture_io_while do
|
199
|
-
assert_raises SystemExit do
|
231
|
+
err = assert_raises SystemExit do
|
200
232
|
nested_cmd.run(%w(oogabooga))
|
201
233
|
end
|
234
|
+
assert_equal 1, err.status
|
235
|
+
end
|
236
|
+
|
237
|
+
assert_equal [], lines(out)
|
238
|
+
assert_equal ["super: unknown command 'oogabooga'"], lines(err)
|
239
|
+
end
|
240
|
+
|
241
|
+
def test_invoke_nested_with_incorrect_command_name_no_exit
|
242
|
+
out, err = capture_io_while do
|
243
|
+
nested_cmd.run(%w(oogabooga), {}, hard_exit: false)
|
202
244
|
end
|
203
245
|
|
204
246
|
assert_equal [], lines(out)
|
@@ -207,9 +249,19 @@ module Cri
|
|
207
249
|
|
208
250
|
def test_invoke_nested_with_ambiguous_command_name
|
209
251
|
out, err = capture_io_while do
|
210
|
-
assert_raises SystemExit do
|
252
|
+
err = assert_raises SystemExit do
|
211
253
|
nested_cmd.run(%w(s))
|
212
254
|
end
|
255
|
+
assert_equal 1, err.status
|
256
|
+
end
|
257
|
+
|
258
|
+
assert_equal [], lines(out)
|
259
|
+
assert_equal ["super: 's' is ambiguous:", ' sink sub'], lines(err)
|
260
|
+
end
|
261
|
+
|
262
|
+
def test_invoke_nested_with_ambiguous_command_name_no_exit
|
263
|
+
out, err = capture_io_while do
|
264
|
+
nested_cmd.run(%w(s), {}, hard_exit: false)
|
213
265
|
end
|
214
266
|
|
215
267
|
assert_equal [], lines(out)
|
@@ -559,5 +611,26 @@ module Cri
|
|
559
611
|
|
560
612
|
assert_equal [bar, foo, qux], [foo, bar, qux].sort
|
561
613
|
end
|
614
|
+
|
615
|
+
def test_default_subcommand
|
616
|
+
subcommand = Cri::Command.define do
|
617
|
+
name 'sub'
|
618
|
+
|
619
|
+
run do |_opts, _args, _c|
|
620
|
+
$stdout.puts 'I am the subcommand!'
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
cmd = Cri::Command.define do
|
625
|
+
name 'super'
|
626
|
+
default_subcommand 'sub'
|
627
|
+
subcommand subcommand
|
628
|
+
end
|
629
|
+
|
630
|
+
out, _err = capture_io_while do
|
631
|
+
cmd.run([])
|
632
|
+
end
|
633
|
+
assert_equal "I am the subcommand!\n", out
|
634
|
+
end
|
562
635
|
end
|
563
636
|
end
|
data/test/test_command_dsl.rb
CHANGED
data/test/test_command_runner.rb
CHANGED
data/test/test_option_parser.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cri
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Denis Defreyne
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-04-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colored
|
@@ -101,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
101
101
|
version: '0'
|
102
102
|
requirements: []
|
103
103
|
rubyforge_project:
|
104
|
-
rubygems_version: 2.6.
|
104
|
+
rubygems_version: 2.6.11
|
105
105
|
signing_key:
|
106
106
|
specification_version: 4
|
107
107
|
summary: a library for building easy-to-use command-line tools
|