cri 2.6.1 → 2.7.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/Gemfile +1 -0
- data/Gemfile.lock +46 -21
- data/LICENSE +1 -1
- data/NEWS.md +12 -0
- data/README.adoc +1 -1
- data/Rakefile +7 -2
- data/cri.gemspec +5 -7
- data/lib/cri.rb +0 -2
- data/lib/cri/argument_array.rb +0 -4
- data/lib/cri/command.rb +31 -40
- data/lib/cri/command_dsl.rb +28 -18
- data/lib/cri/command_runner.rb +2 -6
- data/lib/cri/commands/basic_help.rb +2 -2
- data/lib/cri/commands/basic_root.rb +1 -1
- data/lib/cri/core_ext.rb +3 -1
- data/lib/cri/core_ext/string.rb +28 -30
- data/lib/cri/help_renderer.rb +105 -23
- data/lib/cri/option_parser.rb +13 -17
- data/lib/cri/platform.rb +1 -5
- data/lib/cri/string_formatter.rb +14 -9
- data/lib/cri/version.rb +1 -3
- data/test/helper.rb +38 -38
- data/test/test_argument_array.rb +7 -7
- data/test/test_base.rb +4 -4
- data/test/test_basic_help.rb +47 -47
- data/test/test_basic_root.rb +10 -10
- data/test/test_command.rb +424 -347
- data/test/test_command_dsl.rb +174 -150
- data/test/test_command_runner.rb +22 -23
- data/test/test_option_parser.rb +212 -224
- data/test/test_string_formatter.rb +97 -82
- metadata +3 -3
data/lib/cri/option_parser.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Cri
|
4
|
-
|
5
4
|
# Cri::OptionParser is used for parsing commandline options.
|
6
5
|
#
|
7
6
|
# Option definitions are hashes with the keys `:short`, `:long` and
|
@@ -56,7 +55,6 @@ module Cri
|
|
56
55
|
# }
|
57
56
|
# }
|
58
57
|
class OptionParser
|
59
|
-
|
60
58
|
# Error that will be raised when an unknown option is encountered.
|
61
59
|
class IllegalOptionError < Cri::Error
|
62
60
|
end
|
@@ -104,7 +102,7 @@ module Cri
|
|
104
102
|
#
|
105
103
|
# @return [Cri::OptionParser] The option parser self
|
106
104
|
def self.parse(arguments_and_options, definitions)
|
107
|
-
|
105
|
+
new(arguments_and_options, definitions).run
|
108
106
|
end
|
109
107
|
|
110
108
|
# Creates a new parser with the given options/arguments and definitions.
|
@@ -169,9 +167,9 @@ module Cri
|
|
169
167
|
|
170
168
|
if e == '--'
|
171
169
|
handle_dashdash(e)
|
172
|
-
elsif e =~ /^--./
|
170
|
+
elsif e =~ /^--./ && !@no_more_options
|
173
171
|
handle_dashdash_option(e)
|
174
|
-
elsif e =~ /^-./
|
172
|
+
elsif e =~ /^-./ && !@no_more_options
|
175
173
|
handle_dash_option(e)
|
176
174
|
else
|
177
175
|
add_argument(e)
|
@@ -182,7 +180,7 @@ module Cri
|
|
182
180
|
@running = false
|
183
181
|
end
|
184
182
|
|
185
|
-
|
183
|
+
private
|
186
184
|
|
187
185
|
def handle_dashdash(e)
|
188
186
|
add_argument(e)
|
@@ -192,8 +190,8 @@ module Cri
|
|
192
190
|
def handle_dashdash_option(e)
|
193
191
|
# Get option key, and option value if included
|
194
192
|
if e =~ /^--([^=]+)=(.+)$/
|
195
|
-
option_key =
|
196
|
-
option_value =
|
193
|
+
option_key = Regexp.last_match[1]
|
194
|
+
option_value = Regexp.last_match[2]
|
197
195
|
else
|
198
196
|
option_key = e[2..-1]
|
199
197
|
option_value = nil
|
@@ -201,9 +199,9 @@ module Cri
|
|
201
199
|
|
202
200
|
# Find definition
|
203
201
|
definition = @definitions.find { |d| d[:long] == option_key }
|
204
|
-
|
202
|
+
fail IllegalOptionError.new(option_key) if definition.nil?
|
205
203
|
|
206
|
-
if [
|
204
|
+
if [:required, :optional].include?(definition[:argument])
|
207
205
|
# Get option value if necessary
|
208
206
|
if option_value.nil?
|
209
207
|
option_value = find_option_value(definition, option_key)
|
@@ -225,12 +223,12 @@ module Cri
|
|
225
223
|
option_keys.each do |option_key|
|
226
224
|
# Find definition
|
227
225
|
definition = @definitions.find { |d| d[:short] == option_key }
|
228
|
-
|
226
|
+
fail IllegalOptionError.new(option_key) if definition.nil?
|
229
227
|
|
230
|
-
if option_keys.length > 1
|
228
|
+
if option_keys.length > 1 && definition[:argument] == :required
|
231
229
|
# This is a combined option and it requires an argument, so complain
|
232
|
-
|
233
|
-
elsif [
|
230
|
+
fail OptionRequiresAnArgumentError.new(option_key)
|
231
|
+
elsif [:required, :optional].include?(definition[:argument])
|
234
232
|
# Get option value
|
235
233
|
option_value = find_option_value(definition, option_key)
|
236
234
|
|
@@ -247,7 +245,7 @@ module Cri
|
|
247
245
|
option_value = @unprocessed_arguments_and_options.shift
|
248
246
|
if option_value.nil? || option_value =~ /^-/
|
249
247
|
if definition[:argument] == :required
|
250
|
-
|
248
|
+
fail OptionRequiresAnArgumentError.new(option_key)
|
251
249
|
else
|
252
250
|
@unprocessed_arguments_and_options.unshift(option_value)
|
253
251
|
option_value = true
|
@@ -275,7 +273,5 @@ module Cri
|
|
275
273
|
delegate.argument_added(value, self) unless delegate.nil?
|
276
274
|
end
|
277
275
|
end
|
278
|
-
|
279
276
|
end
|
280
|
-
|
281
277
|
end
|
data/lib/cri/platform.rb
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Cri
|
4
|
-
|
5
4
|
module Platform
|
6
|
-
|
7
5
|
# @return [Boolean] true if the current platform is Windows, false
|
8
6
|
# otherwise.
|
9
7
|
def self.windows?
|
10
|
-
|
8
|
+
RUBY_PLATFORM =~ /windows|bccwin|cygwin|djgpp|mingw|mswin|wince/i
|
11
9
|
end
|
12
10
|
|
13
11
|
# Checks whether colors can be enabled. For colors to be enabled, the given
|
@@ -24,7 +22,5 @@ module Cri
|
|
24
22
|
true
|
25
23
|
end
|
26
24
|
end
|
27
|
-
|
28
25
|
end
|
29
|
-
|
30
26
|
end
|
data/lib/cri/string_formatter.rb
CHANGED
@@ -3,18 +3,16 @@
|
|
3
3
|
require 'colored'
|
4
4
|
|
5
5
|
module Cri
|
6
|
-
|
7
6
|
class StringFormatter
|
8
|
-
|
9
7
|
# Extracts individual paragraphs (separated by two newlines).
|
10
8
|
#
|
11
9
|
# @param [String] s The string to format
|
12
10
|
#
|
13
11
|
# @return [Array<String>] A list of paragraphs in the string
|
14
12
|
def to_paragraphs(s)
|
15
|
-
lines = s.scan(/([^\n]+\n|[^\n]*$)/).map { |
|
13
|
+
lines = s.scan(/([^\n]+\n|[^\n]*$)/).map { |l| l[0].strip }
|
16
14
|
|
17
|
-
paragraphs = [
|
15
|
+
paragraphs = [[]]
|
18
16
|
lines.each do |line|
|
19
17
|
if line.empty?
|
20
18
|
paragraphs << []
|
@@ -36,15 +34,18 @@ module Cri
|
|
36
34
|
#
|
37
35
|
# @param [Number] indentation The number of spaces to indent each line.
|
38
36
|
#
|
37
|
+
# @param [Boolean] first_line_already_indented Whether or not the first
|
38
|
+
# line is already indented
|
39
|
+
#
|
39
40
|
# @return [String] The word-wrapped and indented string
|
40
|
-
def wrap_and_indent(s, width, indentation)
|
41
|
+
def wrap_and_indent(s, width, indentation, first_line_already_indented = false)
|
41
42
|
indented_width = width - indentation
|
42
43
|
indent = ' ' * indentation
|
43
44
|
# Split into paragraphs
|
44
45
|
paragraphs = to_paragraphs(s)
|
45
46
|
|
46
47
|
# Wrap and indent each paragraph
|
47
|
-
paragraphs.map do |paragraph|
|
48
|
+
text = paragraphs.map do |paragraph|
|
48
49
|
# Initialize
|
49
50
|
lines = []
|
50
51
|
line = ''
|
@@ -58,13 +59,19 @@ module Cri
|
|
58
59
|
end
|
59
60
|
|
60
61
|
# Add word to line
|
61
|
-
line += (line == '' ? '' : ' '
|
62
|
+
line += (line == '' ? '' : ' ') + word
|
62
63
|
end
|
63
64
|
lines << line
|
64
65
|
|
65
66
|
# Join lines
|
66
67
|
lines.map { |l| indent + l }.join("\n")
|
67
68
|
end.join("\n\n")
|
69
|
+
|
70
|
+
if first_line_already_indented
|
71
|
+
text[indentation..-1]
|
72
|
+
else
|
73
|
+
text
|
74
|
+
end
|
68
75
|
end
|
69
76
|
|
70
77
|
# @param [String] s The string to format
|
@@ -102,7 +109,5 @@ module Cri
|
|
102
109
|
s
|
103
110
|
end
|
104
111
|
end
|
105
|
-
|
106
112
|
end
|
107
|
-
|
108
113
|
end
|
data/lib/cri/version.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -10,45 +10,45 @@ require 'cri'
|
|
10
10
|
|
11
11
|
require 'stringio'
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
13
|
+
module Cri
|
14
|
+
class TestCase < Minitest::Test
|
15
|
+
def setup
|
16
|
+
@orig_io = capture_io
|
17
|
+
end
|
18
|
+
|
19
|
+
def teardown
|
20
|
+
uncapture_io(*@orig_io)
|
21
|
+
end
|
22
|
+
|
23
|
+
def capture_io_while(&block)
|
24
|
+
orig_io = capture_io
|
25
|
+
block.call
|
26
|
+
[$stdout.string, $stderr.string]
|
27
|
+
ensure
|
28
|
+
uncapture_io(*orig_io)
|
29
|
+
end
|
30
|
+
|
31
|
+
def lines(string)
|
32
|
+
string.scan(/^.*\n/).map { |s| s.chomp }
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def capture_io
|
38
|
+
orig_stdout = $stdout
|
39
|
+
orig_stderr = $stderr
|
40
|
+
|
41
|
+
$stdout = StringIO.new
|
42
|
+
$stderr = StringIO.new
|
43
|
+
|
44
|
+
[orig_stdout, orig_stderr]
|
45
|
+
end
|
46
|
+
|
47
|
+
def uncapture_io(orig_stdout, orig_stderr)
|
48
|
+
$stdout = orig_stdout
|
49
|
+
$stderr = orig_stderr
|
50
|
+
end
|
33
51
|
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def capture_io
|
38
|
-
orig_stdout = $stdout
|
39
|
-
orig_stderr = $stderr
|
40
|
-
|
41
|
-
$stdout = StringIO.new
|
42
|
-
$stderr = StringIO.new
|
43
|
-
|
44
|
-
[ orig_stdout, orig_stderr ]
|
45
|
-
end
|
46
|
-
|
47
|
-
def uncapture_io(orig_stdout, orig_stderr)
|
48
|
-
$stdout = orig_stdout
|
49
|
-
$stderr = orig_stderr
|
50
|
-
end
|
51
|
-
|
52
52
|
end
|
53
53
|
|
54
54
|
# Unexpected system exit is unexpected
|
data/test/test_argument_array.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
module Cri
|
4
|
+
class ArgumentArrayTestCase < Cri::TestCase
|
5
|
+
def test_initialize
|
6
|
+
arr = Cri::ArgumentArray.new(['foo', 'bar', '--', 'baz'])
|
7
|
+
assert_equal %w(foo bar baz), arr
|
8
|
+
assert_equal ['foo', 'bar', '--', 'baz'], arr.raw
|
9
|
+
end
|
9
10
|
end
|
10
|
-
|
11
11
|
end
|
data/test/test_base.rb
CHANGED
data/test/test_basic_help.rb
CHANGED
@@ -1,67 +1,67 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
3
|
+
module Cri
|
4
|
+
class BasicHelpTestCase < Cri::TestCase
|
5
|
+
def test_run_without_supercommand
|
6
|
+
cmd = Cri::Command.new_basic_help
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
assert_raises Cri::NoHelpAvailableError do
|
9
|
-
cmd.run([])
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def test_run_with_supercommand
|
14
|
-
cmd = Cri::Command.define do
|
15
|
-
name 'meh'
|
8
|
+
assert_raises Cri::NoHelpAvailableError do
|
9
|
+
cmd.run([])
|
10
|
+
end
|
16
11
|
end
|
17
12
|
|
18
|
-
|
19
|
-
|
13
|
+
def test_run_with_supercommand
|
14
|
+
cmd = Cri::Command.define do
|
15
|
+
name 'meh'
|
16
|
+
end
|
20
17
|
|
21
|
-
|
22
|
-
|
18
|
+
help_cmd = Cri::Command.new_basic_help
|
19
|
+
cmd.add_command(help_cmd)
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
name 'root'
|
27
|
-
summary 'I am root!'
|
21
|
+
help_cmd.run([])
|
22
|
+
end
|
28
23
|
|
29
|
-
|
30
|
-
|
31
|
-
|
24
|
+
def test_run_with_chain_of_commands
|
25
|
+
cmd = Cri::Command.define do
|
26
|
+
name 'root'
|
27
|
+
summary 'I am root!'
|
32
28
|
|
33
29
|
subcommand do
|
34
|
-
name '
|
35
|
-
summary 'I am
|
30
|
+
name 'foo'
|
31
|
+
summary 'I am foo!'
|
32
|
+
|
33
|
+
subcommand do
|
34
|
+
name 'subsubby'
|
35
|
+
summary 'I am subsubby!'
|
36
|
+
end
|
36
37
|
end
|
37
38
|
end
|
38
|
-
end
|
39
39
|
|
40
|
-
|
41
|
-
|
40
|
+
help_cmd = Cri::Command.new_basic_help
|
41
|
+
cmd.add_command(help_cmd)
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
43
|
+
# Simple call
|
44
|
+
stdout, stderr = capture_io_while do
|
45
|
+
help_cmd.run(['foo'])
|
46
|
+
end
|
47
|
+
assert_match(/I am foo!/m, stdout)
|
48
|
+
assert_equal('', stderr)
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
50
|
+
# Subcommand
|
51
|
+
stdout, stderr = capture_io_while do
|
52
|
+
help_cmd.run(%w(foo subsubby))
|
53
|
+
end
|
54
|
+
assert_match(/I am subsubby!/m, stdout)
|
55
|
+
assert_equal('', stderr)
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
57
|
+
# Non-existing subcommand
|
58
|
+
stdout, stderr = capture_io_while do
|
59
|
+
assert_raises SystemExit do
|
60
|
+
help_cmd.run(%w(foo mysterycmd))
|
61
|
+
end
|
61
62
|
end
|
63
|
+
assert_equal '', stdout
|
64
|
+
assert_match(/foo: unknown command 'mysterycmd'/, stderr)
|
62
65
|
end
|
63
|
-
assert_equal '', stdout
|
64
|
-
assert_match(/foo: unknown command 'mysterycmd'/, stderr)
|
65
66
|
end
|
66
|
-
|
67
67
|
end
|
data/test/test_basic_root.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
3
|
+
module Cri
|
4
|
+
class BasicRootTestCase < Cri::TestCase
|
5
|
+
def test_run_with_help
|
6
|
+
cmd = Cri::Command.new_basic_root
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
assert_raises SystemExit do
|
10
|
-
cmd.run(%w( -h ))
|
8
|
+
stdout, _stderr = capture_io_while do
|
9
|
+
assert_raises SystemExit do
|
10
|
+
cmd.run(%w( -h ))
|
11
|
+
end
|
11
12
|
end
|
12
|
-
end
|
13
13
|
|
14
|
-
|
14
|
+
assert stdout =~ /COMMANDS.*\n.*help.*show help/
|
15
|
+
end
|
15
16
|
end
|
16
|
-
|
17
17
|
end
|