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/command_dsl.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Cri
|
4
|
-
|
5
4
|
# The command DSL is a class that is used for building and modifying
|
6
5
|
# commands.
|
7
6
|
class CommandDSL
|
7
|
+
# @return [Cri::Command] The built command
|
8
|
+
attr_reader :command
|
8
9
|
|
9
10
|
# Creates a new DSL, intended to be used for building a single command. A
|
10
11
|
# {CommandDSL} instance is not reusable; create a new instance if you want
|
@@ -12,15 +13,10 @@ module Cri
|
|
12
13
|
#
|
13
14
|
# @param [Cri::Command, nil] command The command to modify, or nil if a
|
14
15
|
# new command should be created
|
15
|
-
def initialize(command=nil)
|
16
|
+
def initialize(command = nil)
|
16
17
|
@command = command || Cri::Command.new
|
17
18
|
end
|
18
19
|
|
19
|
-
# @return [Cri::Command] The built command
|
20
|
-
def command
|
21
|
-
@command
|
22
|
-
end
|
23
|
-
|
24
20
|
# Adds a subcommand to the current command. The command can either be
|
25
21
|
# given explicitly, or a block can be given that defines the command.
|
26
22
|
#
|
@@ -29,7 +25,7 @@ module Cri
|
|
29
25
|
# added as a subcommand
|
30
26
|
#
|
31
27
|
# @return [void]
|
32
|
-
def subcommand(command=nil, &block)
|
28
|
+
def subcommand(command = nil, &block)
|
33
29
|
if command.nil?
|
34
30
|
command = Cri::Command.define(&block)
|
35
31
|
end
|
@@ -108,13 +104,17 @@ module Cri
|
|
108
104
|
# @option params [Boolean] :multiple Whether or not the option should
|
109
105
|
# be multi-valued
|
110
106
|
#
|
107
|
+
# @option params [Boolean] :hidden Whether or not the option should
|
108
|
+
# be printed in the help output
|
109
|
+
#
|
111
110
|
# @return [void]
|
112
|
-
def option(short, long, desc, params={}, &block)
|
111
|
+
def option(short, long, desc, params = {}, &block)
|
113
112
|
requiredness = params.fetch(:argument, :forbidden)
|
114
113
|
multiple = params.fetch(:multiple, false)
|
114
|
+
hidden = params.fetch(:hidden, false)
|
115
115
|
|
116
116
|
if short.nil? && long.nil?
|
117
|
-
|
117
|
+
fail ArgumentError, 'short and long options cannot both be nil'
|
118
118
|
end
|
119
119
|
|
120
120
|
@command.option_definitions << {
|
@@ -124,6 +124,7 @@ module Cri
|
|
124
124
|
:argument => requiredness,
|
125
125
|
:multiple => multiple,
|
126
126
|
:block => block,
|
127
|
+
:hidden => hidden,
|
127
128
|
}
|
128
129
|
end
|
129
130
|
alias_method :opt, :option
|
@@ -140,12 +141,15 @@ module Cri
|
|
140
141
|
# @option params [Boolean] :multiple Whether or not the option should
|
141
142
|
# be multi-valued
|
142
143
|
#
|
144
|
+
# @option params [Boolean] :hidden Whether or not the option should
|
145
|
+
# be printed in the help output
|
146
|
+
#
|
143
147
|
# @return [void]
|
144
148
|
#
|
145
149
|
# @see {#option}
|
146
|
-
def required(short, long, desc, params={}, &block)
|
150
|
+
def required(short, long, desc, params = {}, &block)
|
147
151
|
params = params.merge(:argument => :required)
|
148
|
-
|
152
|
+
option(short, long, desc, params, &block)
|
149
153
|
end
|
150
154
|
|
151
155
|
# Adds a new option with a forbidden argument to the command. If a block
|
@@ -160,12 +164,15 @@ module Cri
|
|
160
164
|
# @option params [Boolean] :multiple Whether or not the option should
|
161
165
|
# be multi-valued
|
162
166
|
#
|
167
|
+
# @option params [Boolean] :hidden Whether or not the option should
|
168
|
+
# be printed in the help output
|
169
|
+
#
|
163
170
|
# @return [void]
|
164
171
|
#
|
165
172
|
# @see {#option}
|
166
|
-
def flag(short, long, desc, params={}, &block)
|
173
|
+
def flag(short, long, desc, params = {}, &block)
|
167
174
|
params = params.merge(:argument => :forbidden)
|
168
|
-
|
175
|
+
option(short, long, desc, params, &block)
|
169
176
|
end
|
170
177
|
alias_method :forbidden, :flag
|
171
178
|
|
@@ -181,12 +188,15 @@ module Cri
|
|
181
188
|
# @option params [Boolean] :multiple Whether or not the option should
|
182
189
|
# be multi-valued
|
183
190
|
#
|
191
|
+
# @option params [Boolean] :hidden Whether or not the option should
|
192
|
+
# be printed in the help output
|
193
|
+
#
|
184
194
|
# @return [void]
|
185
195
|
#
|
186
196
|
# @see {#option}
|
187
|
-
def optional(short, long, desc, params={}, &block)
|
197
|
+
def optional(short, long, desc, params = {}, &block)
|
188
198
|
params = params.merge(:argument => :optional)
|
189
|
-
|
199
|
+
option(short, long, desc, params, &block)
|
190
200
|
end
|
191
201
|
|
192
202
|
# Sets the run block to the given block. The given block should have two
|
@@ -203,8 +213,8 @@ module Cri
|
|
203
213
|
# @return [void]
|
204
214
|
def run(&block)
|
205
215
|
unless [2, 3].include?(block.arity)
|
206
|
-
|
207
|
-
|
216
|
+
fail ArgumentError,
|
217
|
+
'The block given to Cri::Command#run expects two or three args'
|
208
218
|
end
|
209
219
|
|
210
220
|
@command.block = block
|
data/lib/cri/command_runner.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Cri
|
4
|
-
|
5
4
|
# A command runner is responsible for the execution of a command. Using it
|
6
5
|
# is optional, but it is useful for commands whose execution block is large.
|
7
6
|
class CommandRunner
|
8
|
-
|
9
7
|
# @return [Hash] A hash contain the options and their values
|
10
8
|
attr_reader :options
|
11
9
|
|
@@ -33,7 +31,7 @@ module Cri
|
|
33
31
|
#
|
34
32
|
# @return [void]
|
35
33
|
def call
|
36
|
-
|
34
|
+
run
|
37
35
|
end
|
38
36
|
|
39
37
|
# Performs the actual execution of the command.
|
@@ -42,9 +40,7 @@ module Cri
|
|
42
40
|
#
|
43
41
|
# @abstract
|
44
42
|
def run
|
45
|
-
|
43
|
+
fail NotImplementedError, 'Cri::CommandRunner subclasses must implement #run'
|
46
44
|
end
|
47
|
-
|
48
45
|
end
|
49
|
-
|
50
46
|
end
|
@@ -14,8 +14,8 @@ flag :v, :verbose, 'show more detailed help'
|
|
14
14
|
|
15
15
|
run do |opts, args, cmd|
|
16
16
|
if cmd.supercommand.nil?
|
17
|
-
|
18
|
-
|
17
|
+
fail NoHelpAvailableError,
|
18
|
+
'No help available because the help command has no supercommand'
|
19
19
|
end
|
20
20
|
|
21
21
|
is_verbose = opts.fetch(:verbose, false)
|
data/lib/cri/core_ext.rb
CHANGED
data/lib/cri/core_ext/string.rb
CHANGED
@@ -2,36 +2,34 @@
|
|
2
2
|
|
3
3
|
require 'colored'
|
4
4
|
|
5
|
-
module Cri
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
5
|
+
module Cri
|
6
|
+
module CoreExtensions
|
7
|
+
# @deprecated
|
8
|
+
module String
|
9
|
+
# @see Cri::StringFormatter#to_paragraphs
|
10
|
+
def to_paragraphs
|
11
|
+
Cri::StringFormatter.new.to_paragraphs(self)
|
12
|
+
end
|
13
|
+
|
14
|
+
# @see Cri::StringFormatter#to_paragraphs
|
15
|
+
def wrap_and_indent(width, indentation)
|
16
|
+
Cri::StringFormatter.new.wrap_and_indent(self, width, indentation)
|
17
|
+
end
|
18
|
+
|
19
|
+
# @see Cri::StringFormatter#format_as_title
|
20
|
+
def formatted_as_title
|
21
|
+
Cri::StringFormatter.new.format_as_title(self)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @see Cri::StringFormatter#format_as_command
|
25
|
+
def formatted_as_command
|
26
|
+
Cri::StringFormatter.new.format_as_command(self)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @see Cri::StringFormatter#format_as_option
|
30
|
+
def formatted_as_option
|
31
|
+
Cri::StringFormatter.new.format_as_option(self)
|
32
|
+
end
|
23
33
|
end
|
24
|
-
|
25
|
-
# @see Cri::StringFormatter#format_as_command
|
26
|
-
def formatted_as_command
|
27
|
-
Cri::StringFormatter.new.format_as_command(self)
|
28
|
-
end
|
29
|
-
|
30
|
-
# @see Cri::StringFormatter#format_as_option
|
31
|
-
def formatted_as_option
|
32
|
-
Cri::StringFormatter.new.format_as_option(self)
|
33
|
-
end
|
34
|
-
|
35
34
|
end
|
36
|
-
|
37
35
|
end
|
data/lib/cri/help_renderer.rb
CHANGED
@@ -1,10 +1,17 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Cri
|
4
|
-
|
5
4
|
# The {HelpRenderer} class is responsible for generating a string containing
|
6
5
|
# the help for a given command, intended to be printed on the command line.
|
7
6
|
class HelpRenderer
|
7
|
+
# The line width of the help output
|
8
|
+
LINE_WIDTH = 78
|
9
|
+
|
10
|
+
# The indentation of descriptions
|
11
|
+
DESC_INDENT = 4
|
12
|
+
|
13
|
+
# The spacing between an option name and option description
|
14
|
+
OPT_DESC_SPACING = 6
|
8
15
|
|
9
16
|
# Creates a new help renderer for the given command.
|
10
17
|
#
|
@@ -12,7 +19,7 @@ module Cri
|
|
12
19
|
#
|
13
20
|
# @option params [Boolean] :verbose true if the help output should be
|
14
21
|
# verbose, false otherwise.
|
15
|
-
def initialize(cmd, params={})
|
22
|
+
def initialize(cmd, params = {})
|
16
23
|
@cmd = cmd
|
17
24
|
@is_verbose = params.fetch(:verbose, false)
|
18
25
|
@io = params.fetch(:io, $stdout)
|
@@ -40,32 +47,32 @@ module Cri
|
|
40
47
|
def append_summary(text)
|
41
48
|
return if @cmd.summary.nil?
|
42
49
|
|
43
|
-
text << fmt.format_as_title(
|
50
|
+
text << fmt.format_as_title('name', @io) << "\n"
|
44
51
|
text << " #{fmt.format_as_command(@cmd.name, @io)} - #{@cmd.summary}" << "\n"
|
45
52
|
unless @cmd.aliases.empty?
|
46
|
-
text <<
|
53
|
+
text << ' aliases: ' << @cmd.aliases.map { |a| fmt.format_as_command(a, @io) }.join(' ') << "\n"
|
47
54
|
end
|
48
55
|
end
|
49
56
|
|
50
57
|
def append_usage(text)
|
51
58
|
return if @cmd.usage.nil?
|
52
59
|
|
53
|
-
path = [
|
60
|
+
path = [@cmd.supercommand]
|
54
61
|
path.unshift(path[0].supercommand) until path[0].nil?
|
55
62
|
formatted_usage = @cmd.usage.gsub(/^([^\s]+)/) { |m| fmt.format_as_command(m, @io) }
|
56
63
|
full_usage = path[1..-1].map { |c| fmt.format_as_command(c.name, @io) + ' ' }.join + formatted_usage
|
57
64
|
|
58
65
|
text << "\n"
|
59
|
-
text << fmt.format_as_title(
|
60
|
-
text << fmt.wrap_and_indent(full_usage,
|
66
|
+
text << fmt.format_as_title('usage', @io) << "\n"
|
67
|
+
text << fmt.wrap_and_indent(full_usage, LINE_WIDTH, DESC_INDENT) << "\n"
|
61
68
|
end
|
62
69
|
|
63
70
|
def append_description(text)
|
64
71
|
return if @cmd.description.nil?
|
65
72
|
|
66
73
|
text << "\n"
|
67
|
-
text << fmt.format_as_title(
|
68
|
-
text << fmt.wrap_and_indent(@cmd.description,
|
74
|
+
text << fmt.format_as_title('description', @io) << "\n"
|
75
|
+
text << fmt.wrap_and_indent(@cmd.description, LINE_WIDTH, DESC_INDENT) + "\n"
|
69
76
|
end
|
70
77
|
|
71
78
|
def append_subcommands(text)
|
@@ -80,13 +87,15 @@ module Cri
|
|
80
87
|
|
81
88
|
# Command
|
82
89
|
shown_subcommands.sort_by { |cmd| cmd.name }.each do |cmd|
|
83
|
-
text <<
|
84
|
-
|
85
|
-
|
90
|
+
text <<
|
91
|
+
format(
|
92
|
+
" %-#{length + DESC_INDENT}s %s\n",
|
93
|
+
fmt.format_as_command(cmd.name, @io),
|
94
|
+
cmd.summary)
|
86
95
|
end
|
87
96
|
|
88
97
|
# Hidden notice
|
89
|
-
|
98
|
+
unless @is_verbose
|
90
99
|
diff = @cmd.subcommands.size - shown_subcommands.size
|
91
100
|
case diff
|
92
101
|
when 0
|
@@ -98,12 +107,34 @@ module Cri
|
|
98
107
|
end
|
99
108
|
end
|
100
109
|
|
110
|
+
def length_for_opt_defs(opt_defs)
|
111
|
+
opt_defs.map do |opt_def|
|
112
|
+
string = ''
|
113
|
+
|
114
|
+
# Always pretend there is a short option
|
115
|
+
string << '-X'
|
116
|
+
|
117
|
+
if opt_def[:long]
|
118
|
+
string << ' --' + opt_def[:long]
|
119
|
+
end
|
120
|
+
|
121
|
+
case opt_def[:argument]
|
122
|
+
when :required
|
123
|
+
string << '=<value>'
|
124
|
+
when :optional
|
125
|
+
string << '=[<value>]'
|
126
|
+
end
|
127
|
+
|
128
|
+
string.size
|
129
|
+
end.max
|
130
|
+
end
|
131
|
+
|
101
132
|
def append_options(text)
|
102
133
|
groups = { 'options' => @cmd.option_definitions }
|
103
134
|
if @cmd.supercommand
|
104
135
|
groups["options for #{@cmd.supercommand.name}"] = @cmd.supercommand.global_option_definitions
|
105
136
|
end
|
106
|
-
length = groups.values.inject(&:+)
|
137
|
+
length = length_for_opt_defs(groups.values.inject(&:+))
|
107
138
|
groups.keys.sort.each do |name|
|
108
139
|
defs = groups[name]
|
109
140
|
append_option_group(text, name, defs, length)
|
@@ -119,19 +150,70 @@ module Cri
|
|
119
150
|
|
120
151
|
ordered_defs = defs.sort_by { |x| x[:short] || x[:long] }
|
121
152
|
ordered_defs.each do |opt_def|
|
122
|
-
|
123
|
-
|
153
|
+
unless opt_def[:hidden]
|
154
|
+
text << format_opt_def(opt_def, length)
|
155
|
+
text << fmt.wrap_and_indent(opt_def[:desc], LINE_WIDTH, length + OPT_DESC_SPACING + DESC_INDENT, true) << "\n"
|
156
|
+
end
|
124
157
|
end
|
125
158
|
end
|
126
159
|
|
127
|
-
def
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
160
|
+
def short_value_postfix_for(opt_def)
|
161
|
+
value_postfix =
|
162
|
+
case opt_def[:argument]
|
163
|
+
when :required
|
164
|
+
'<value>'
|
165
|
+
when :optional
|
166
|
+
'[<value>]'
|
167
|
+
else
|
168
|
+
nil
|
169
|
+
end
|
170
|
+
|
171
|
+
if value_postfix
|
172
|
+
opt_def[:long] ? '' : ' ' + value_postfix
|
173
|
+
else
|
174
|
+
''
|
175
|
+
end
|
133
176
|
end
|
134
177
|
|
135
|
-
|
178
|
+
def long_value_postfix_for(opt_def)
|
179
|
+
value_postfix =
|
180
|
+
case opt_def[:argument]
|
181
|
+
when :required
|
182
|
+
'=<value>'
|
183
|
+
when :optional
|
184
|
+
'[=<value>]'
|
185
|
+
else
|
186
|
+
nil
|
187
|
+
end
|
188
|
+
|
189
|
+
if value_postfix
|
190
|
+
opt_def[:long] ? value_postfix : ''
|
191
|
+
else
|
192
|
+
''
|
193
|
+
end
|
194
|
+
end
|
136
195
|
|
196
|
+
def format_opt_def(opt_def, length)
|
197
|
+
short_value_postfix = short_value_postfix_for(opt_def)
|
198
|
+
long_value_postfix = long_value_postfix_for(opt_def)
|
199
|
+
|
200
|
+
opt_text = ''
|
201
|
+
opt_text_len = 0
|
202
|
+
if opt_def[:short]
|
203
|
+
opt_text << fmt.format_as_option('-' + opt_def[:short], @io)
|
204
|
+
opt_text << short_value_postfix
|
205
|
+
opt_text << ' '
|
206
|
+
opt_text_len += 1 + opt_def[:short].size + short_value_postfix.size + 1
|
207
|
+
else
|
208
|
+
opt_text << ' '
|
209
|
+
opt_text_len += 3
|
210
|
+
end
|
211
|
+
opt_text << fmt.format_as_option('--' + opt_def[:long], @io) if opt_def[:long]
|
212
|
+
opt_text << long_value_postfix
|
213
|
+
opt_text_len += 2 + opt_def[:long].size if opt_def[:long]
|
214
|
+
opt_text_len += long_value_postfix.size
|
215
|
+
|
216
|
+
' ' + opt_text + ' ' * (length + OPT_DESC_SPACING - opt_text_len)
|
217
|
+
end
|
218
|
+
end
|
137
219
|
end
|