toys-core 0.7.0 → 0.8.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/CHANGELOG.md +98 -0
- data/LICENSE.md +16 -24
- data/README.md +307 -59
- data/docs/guide.md +44 -4
- data/lib/toys-core.rb +58 -49
- data/lib/toys/acceptor.rb +672 -0
- data/lib/toys/alias.rb +106 -0
- data/lib/toys/arg_parser.rb +624 -0
- data/lib/toys/cli.rb +422 -181
- data/lib/toys/compat.rb +83 -0
- data/lib/toys/completion.rb +442 -0
- data/lib/toys/context.rb +354 -0
- data/lib/toys/core_version.rb +18 -26
- data/lib/toys/dsl/flag.rb +213 -56
- data/lib/toys/dsl/flag_group.rb +237 -51
- data/lib/toys/dsl/positional_arg.rb +210 -0
- data/lib/toys/dsl/tool.rb +968 -317
- data/lib/toys/errors.rb +46 -28
- data/lib/toys/flag.rb +821 -0
- data/lib/toys/flag_group.rb +282 -0
- data/lib/toys/input_file.rb +18 -26
- data/lib/toys/loader.rb +110 -100
- data/lib/toys/middleware.rb +24 -31
- data/lib/toys/mixin.rb +90 -59
- data/lib/toys/module_lookup.rb +125 -0
- data/lib/toys/positional_arg.rb +184 -0
- data/lib/toys/source_info.rb +192 -0
- data/lib/toys/standard_middleware/add_verbosity_flags.rb +38 -43
- data/lib/toys/standard_middleware/handle_usage_errors.rb +39 -40
- data/lib/toys/standard_middleware/set_default_descriptions.rb +111 -89
- data/lib/toys/standard_middleware/show_help.rb +130 -113
- data/lib/toys/standard_middleware/show_root_version.rb +29 -35
- data/lib/toys/standard_mixins/exec.rb +116 -78
- data/lib/toys/standard_mixins/fileutils.rb +16 -24
- data/lib/toys/standard_mixins/gems.rb +29 -30
- data/lib/toys/standard_mixins/highline.rb +34 -41
- data/lib/toys/standard_mixins/terminal.rb +72 -26
- data/lib/toys/template.rb +51 -35
- data/lib/toys/tool.rb +1161 -206
- data/lib/toys/utils/completion_engine.rb +171 -0
- data/lib/toys/utils/exec.rb +279 -182
- data/lib/toys/utils/gems.rb +58 -49
- data/lib/toys/utils/help_text.rb +117 -111
- data/lib/toys/utils/terminal.rb +69 -62
- data/lib/toys/wrappable_string.rb +162 -0
- metadata +24 -22
- data/lib/toys/definition/acceptor.rb +0 -191
- data/lib/toys/definition/alias.rb +0 -112
- data/lib/toys/definition/arg.rb +0 -140
- data/lib/toys/definition/flag.rb +0 -370
- data/lib/toys/definition/flag_group.rb +0 -205
- data/lib/toys/definition/source_info.rb +0 -190
- data/lib/toys/definition/tool.rb +0 -842
- data/lib/toys/dsl/arg.rb +0 -132
- data/lib/toys/runner.rb +0 -188
- data/lib/toys/standard_middleware.rb +0 -47
- data/lib/toys/utils/module_lookup.rb +0 -135
- data/lib/toys/utils/wrappable_string.rb +0 -165
@@ -1,191 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Copyright 2018 Daniel Azuma
|
4
|
-
#
|
5
|
-
# All rights reserved.
|
6
|
-
#
|
7
|
-
# Redistribution and use in source and binary forms, with or without
|
8
|
-
# modification, are permitted provided that the following conditions are met:
|
9
|
-
#
|
10
|
-
# * Redistributions of source code must retain the above copyright notice,
|
11
|
-
# this list of conditions and the following disclaimer.
|
12
|
-
# * Redistributions in binary form must reproduce the above copyright notice,
|
13
|
-
# this list of conditions and the following disclaimer in the documentation
|
14
|
-
# and/or other materials provided with the distribution.
|
15
|
-
# * Neither the name of the copyright holder, nor the names of any other
|
16
|
-
# contributors to this software, may be used to endorse or promote products
|
17
|
-
# derived from this software without specific prior written permission.
|
18
|
-
#
|
19
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
-
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
-
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
22
|
-
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
23
|
-
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
24
|
-
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
25
|
-
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
26
|
-
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
27
|
-
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
28
|
-
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
29
|
-
# POSSIBILITY OF SUCH DAMAGE.
|
30
|
-
;
|
31
|
-
|
32
|
-
module Toys
|
33
|
-
module Definition
|
34
|
-
##
|
35
|
-
# An Acceptor validates and converts arguments. It is designed to be
|
36
|
-
# compatible with the OptionParser accept mechanism.
|
37
|
-
#
|
38
|
-
# First, an acceptor validates an argument via the {#match} method. This
|
39
|
-
# method should determine whether the argument is valid, and return
|
40
|
-
# information that will help with conversion of the argument.
|
41
|
-
#
|
42
|
-
# Second, an acceptor converts the argument from the input string to its
|
43
|
-
# final form via the {#convert} method.
|
44
|
-
#
|
45
|
-
# Finally, an acceptor has a name that may appear in help text for flags
|
46
|
-
# and arguments that use it.
|
47
|
-
#
|
48
|
-
class Acceptor
|
49
|
-
##
|
50
|
-
# Create a base acceptor.
|
51
|
-
#
|
52
|
-
# The base acceptor does not do any validation (i.e. it accepts all
|
53
|
-
# arguments). You may subclass this object and override the {#match}
|
54
|
-
# method to change this behavior.
|
55
|
-
#
|
56
|
-
# The base acceptor lets you provide a converter as a proc. The proc
|
57
|
-
# should take one or more arguments, the first of which is the entire
|
58
|
-
# argument string, and the others of which are any additional values
|
59
|
-
# returned from validation. The converter should return the final
|
60
|
-
# converted value of the argument.
|
61
|
-
#
|
62
|
-
# The converter may be provided either as a proc in the `converter`
|
63
|
-
# parameter, or as a block. If neither is provided, the base acceptor
|
64
|
-
# performs no conversion and uses the argument string.
|
65
|
-
#
|
66
|
-
# @param [String] name A visible name for the acceptor, shown in help.
|
67
|
-
# @param [Proc] converter A converter function. May also be given as a
|
68
|
-
# block.
|
69
|
-
#
|
70
|
-
def initialize(name, converter = nil, &block)
|
71
|
-
@name = name.to_s
|
72
|
-
@converter = converter || block
|
73
|
-
end
|
74
|
-
|
75
|
-
##
|
76
|
-
# Name of the acceptor
|
77
|
-
# @return [String]
|
78
|
-
#
|
79
|
-
attr_reader :name
|
80
|
-
alias to_s name
|
81
|
-
|
82
|
-
##
|
83
|
-
# Validate the given input.
|
84
|
-
#
|
85
|
-
# You may override this method to specify a validation function. For a
|
86
|
-
# valid input, the function must return either the original argument
|
87
|
-
# string, or an array of which the first element is the original argument
|
88
|
-
# string, and the remaining elements may comprise additional information.
|
89
|
-
# All returned information is then passed to the conversion function.
|
90
|
-
# Note that a MatchInfo object is a legitimate return value since it
|
91
|
-
# duck-types the appropriate array.
|
92
|
-
#
|
93
|
-
# For an invalid input, you should return a falsy value.
|
94
|
-
#
|
95
|
-
# The default implementation simply returns the original argument string,
|
96
|
-
# indicating all inputs are valid.
|
97
|
-
#
|
98
|
-
# @param [String] str Input argument string
|
99
|
-
# @return [String,Array]
|
100
|
-
#
|
101
|
-
def match(str)
|
102
|
-
str
|
103
|
-
end
|
104
|
-
|
105
|
-
##
|
106
|
-
# Convert the given input. Uses the converter provided to this object's
|
107
|
-
# constructor. Subclasses may also override this method.
|
108
|
-
#
|
109
|
-
# @param [String] str Original argument string
|
110
|
-
# @param [Object...] extra Zero or more additional arguments comprising
|
111
|
-
# additional elements returned from the match function.
|
112
|
-
# @return [Object] The converted argument as it should be stored in the
|
113
|
-
# context data.
|
114
|
-
#
|
115
|
-
def convert(str, *extra)
|
116
|
-
@converter ? @converter.call(str, *extra) : str
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
##
|
121
|
-
# An acceptor that uses a regex to validate input.
|
122
|
-
#
|
123
|
-
class PatternAcceptor < Acceptor
|
124
|
-
##
|
125
|
-
# Create a pattern acceptor.
|
126
|
-
#
|
127
|
-
# You must provide a regular expression as a validator. You may also
|
128
|
-
# provide a converter proc. See {Toys::Definition::Acceptor} for details
|
129
|
-
# on the converter.
|
130
|
-
#
|
131
|
-
# @param [String] name A visible name for the acceptor, shown in help.
|
132
|
-
# @param [Regexp] regex Regular expression defining value values.
|
133
|
-
# @param [Proc] converter A converter function. May also be given as a
|
134
|
-
# block. Note that the converter will be passed all elements of
|
135
|
-
# the MatchInfo.
|
136
|
-
#
|
137
|
-
def initialize(name, regex, converter = nil, &block)
|
138
|
-
super(name, converter, &block)
|
139
|
-
@regex = regex
|
140
|
-
end
|
141
|
-
|
142
|
-
##
|
143
|
-
# Overrides {Toys::Definition::Acceptor#match} to use the given regex.
|
144
|
-
#
|
145
|
-
def match(str)
|
146
|
-
@regex.match(str)
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
##
|
151
|
-
# An acceptor that recognizes a fixed set of values.
|
152
|
-
#
|
153
|
-
# You provide a list of valid values. The input argument string will be
|
154
|
-
# matched against the string forms of these valid values. If it matches,
|
155
|
-
# the converter will return the actual value.
|
156
|
-
#
|
157
|
-
# For example, you could pass `[:one, :two, 3]` as the set of values. If
|
158
|
-
# an argument of `"two"` is passed in, the converter will yield a final
|
159
|
-
# value of the symbol `:two`. If an argument of "3" is passed in, the
|
160
|
-
# converter will yield the integer `3`. If an argument of "three" is
|
161
|
-
# passed in, the match will fail.
|
162
|
-
#
|
163
|
-
class EnumAcceptor < Acceptor
|
164
|
-
##
|
165
|
-
# Create an acceptor.
|
166
|
-
#
|
167
|
-
# @param [String] name A visible name for the acceptor, shown in help.
|
168
|
-
# @param [Array] values Valid values.
|
169
|
-
#
|
170
|
-
def initialize(name, values)
|
171
|
-
super(name)
|
172
|
-
@values = Array(values).map { |v| [v.to_s, v] }
|
173
|
-
end
|
174
|
-
|
175
|
-
##
|
176
|
-
# Overrides {Toys::Definition::Acceptor#match} to find the value.
|
177
|
-
#
|
178
|
-
def match(str)
|
179
|
-
@values.find { |s, _e| s == str }
|
180
|
-
end
|
181
|
-
|
182
|
-
##
|
183
|
-
# Overrides {Toys::Definition::Acceptor#convert} to return the original
|
184
|
-
# element.
|
185
|
-
#
|
186
|
-
def convert(_str, elem)
|
187
|
-
elem
|
188
|
-
end
|
189
|
-
end
|
190
|
-
end
|
191
|
-
end
|
@@ -1,112 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Copyright 2018 Daniel Azuma
|
4
|
-
#
|
5
|
-
# All rights reserved.
|
6
|
-
#
|
7
|
-
# Redistribution and use in source and binary forms, with or without
|
8
|
-
# modification, are permitted provided that the following conditions are met:
|
9
|
-
#
|
10
|
-
# * Redistributions of source code must retain the above copyright notice,
|
11
|
-
# this list of conditions and the following disclaimer.
|
12
|
-
# * Redistributions in binary form must reproduce the above copyright notice,
|
13
|
-
# this list of conditions and the following disclaimer in the documentation
|
14
|
-
# and/or other materials provided with the distribution.
|
15
|
-
# * Neither the name of the copyright holder, nor the names of any other
|
16
|
-
# contributors to this software, may be used to endorse or promote products
|
17
|
-
# derived from this software without specific prior written permission.
|
18
|
-
#
|
19
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
-
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
-
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
22
|
-
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
23
|
-
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
24
|
-
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
25
|
-
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
26
|
-
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
27
|
-
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
28
|
-
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
29
|
-
# POSSIBILITY OF SUCH DAMAGE.
|
30
|
-
;
|
31
|
-
|
32
|
-
module Toys
|
33
|
-
module Definition
|
34
|
-
##
|
35
|
-
# An alias is a name that refers to another name.
|
36
|
-
#
|
37
|
-
class Alias
|
38
|
-
##
|
39
|
-
# Create a new alias.
|
40
|
-
#
|
41
|
-
# @param [Array<String>] full_name The name of the alias.
|
42
|
-
# @param [String,Array<String>] target The name of the target. May either
|
43
|
-
# be a local reference (a single string) or a global reference (an
|
44
|
-
# array of strings)
|
45
|
-
#
|
46
|
-
def initialize(loader, full_name, target, priority)
|
47
|
-
@target_name =
|
48
|
-
if target.is_a?(::Array)
|
49
|
-
target.map(&:to_s)
|
50
|
-
else
|
51
|
-
full_name[0..-2] + [target.to_s]
|
52
|
-
end
|
53
|
-
@target_name.freeze
|
54
|
-
@full_name = full_name.map(&:to_s).freeze
|
55
|
-
@priority = priority
|
56
|
-
@tool_class = DSL::Tool.new_class(@full_name, priority, loader)
|
57
|
-
end
|
58
|
-
|
59
|
-
##
|
60
|
-
# Return the tool class.
|
61
|
-
# @return [Class]
|
62
|
-
#
|
63
|
-
attr_reader :tool_class
|
64
|
-
|
65
|
-
##
|
66
|
-
# Return the name of the tool as an array of strings.
|
67
|
-
# This array may not be modified.
|
68
|
-
# @return [Array<String>]
|
69
|
-
#
|
70
|
-
attr_reader :full_name
|
71
|
-
|
72
|
-
##
|
73
|
-
# Return the priority of this alias.
|
74
|
-
# @return [Integer]
|
75
|
-
#
|
76
|
-
attr_reader :priority
|
77
|
-
|
78
|
-
##
|
79
|
-
# Return the name of the target as an array of strings.
|
80
|
-
# This array may not be modified.
|
81
|
-
# @return [Array<String>]
|
82
|
-
#
|
83
|
-
attr_reader :target_name
|
84
|
-
|
85
|
-
##
|
86
|
-
# Returns the local name of this tool.
|
87
|
-
# @return [String]
|
88
|
-
#
|
89
|
-
def simple_name
|
90
|
-
full_name.last
|
91
|
-
end
|
92
|
-
|
93
|
-
##
|
94
|
-
# Returns a displayable name of this tool, generally the full name
|
95
|
-
# delimited by spaces.
|
96
|
-
# @return [String]
|
97
|
-
#
|
98
|
-
def display_name
|
99
|
-
full_name.join(" ")
|
100
|
-
end
|
101
|
-
|
102
|
-
##
|
103
|
-
# Returns a displayable name of the target, generally the full name
|
104
|
-
# delimited by spaces.
|
105
|
-
# @return [String]
|
106
|
-
#
|
107
|
-
def display_target
|
108
|
-
target_name.join(" ")
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
data/lib/toys/definition/arg.rb
DELETED
@@ -1,140 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Copyright 2018 Daniel Azuma
|
4
|
-
#
|
5
|
-
# All rights reserved.
|
6
|
-
#
|
7
|
-
# Redistribution and use in source and binary forms, with or without
|
8
|
-
# modification, are permitted provided that the following conditions are met:
|
9
|
-
#
|
10
|
-
# * Redistributions of source code must retain the above copyright notice,
|
11
|
-
# this list of conditions and the following disclaimer.
|
12
|
-
# * Redistributions in binary form must reproduce the above copyright notice,
|
13
|
-
# this list of conditions and the following disclaimer in the documentation
|
14
|
-
# and/or other materials provided with the distribution.
|
15
|
-
# * Neither the name of the copyright holder, nor the names of any other
|
16
|
-
# contributors to this software, may be used to endorse or promote products
|
17
|
-
# derived from this software without specific prior written permission.
|
18
|
-
#
|
19
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
-
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
-
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
22
|
-
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
23
|
-
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
24
|
-
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
25
|
-
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
26
|
-
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
27
|
-
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
28
|
-
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
29
|
-
# POSSIBILITY OF SUCH DAMAGE.
|
30
|
-
;
|
31
|
-
|
32
|
-
require "optparse"
|
33
|
-
|
34
|
-
module Toys
|
35
|
-
module Definition
|
36
|
-
##
|
37
|
-
# Representation of a formal positional argument
|
38
|
-
#
|
39
|
-
class Arg
|
40
|
-
##
|
41
|
-
# Create an Arg definition
|
42
|
-
# @private
|
43
|
-
#
|
44
|
-
def initialize(key, type, accept, default, desc, long_desc, display_name = nil)
|
45
|
-
@key = key
|
46
|
-
@type = type
|
47
|
-
@accept = accept
|
48
|
-
@default = default
|
49
|
-
@desc = Utils::WrappableString.make(desc)
|
50
|
-
@long_desc = Utils::WrappableString.make_array(long_desc)
|
51
|
-
@display_name = display_name || key.to_s.tr("-", "_").gsub(/\W/, "").upcase
|
52
|
-
end
|
53
|
-
|
54
|
-
##
|
55
|
-
# Returns the key.
|
56
|
-
# @return [Symbol]
|
57
|
-
#
|
58
|
-
attr_reader :key
|
59
|
-
|
60
|
-
##
|
61
|
-
# Type of this argument.
|
62
|
-
# @return [:required,:optional,:remaining]
|
63
|
-
#
|
64
|
-
attr_reader :type
|
65
|
-
|
66
|
-
##
|
67
|
-
# Returns the acceptor, which may be `nil`.
|
68
|
-
# @return [Object]
|
69
|
-
#
|
70
|
-
attr_accessor :accept
|
71
|
-
|
72
|
-
##
|
73
|
-
# Returns the default value, which may be `nil`.
|
74
|
-
# @return [Object]
|
75
|
-
#
|
76
|
-
attr_reader :default
|
77
|
-
|
78
|
-
##
|
79
|
-
# Returns the short description string.
|
80
|
-
# @return [Toys::Utils::WrappableString]
|
81
|
-
#
|
82
|
-
attr_reader :desc
|
83
|
-
|
84
|
-
##
|
85
|
-
# Returns the long description strings as an array.
|
86
|
-
# @return [Array<Toys::Utils::WrappableString>]
|
87
|
-
#
|
88
|
-
attr_reader :long_desc
|
89
|
-
|
90
|
-
##
|
91
|
-
# Returns the displayable name.
|
92
|
-
# @return [String]
|
93
|
-
#
|
94
|
-
attr_accessor :display_name
|
95
|
-
|
96
|
-
##
|
97
|
-
# Process the given value through the acceptor.
|
98
|
-
# May raise an exception if the acceptor rejected the input.
|
99
|
-
#
|
100
|
-
# @param [String] input Input value
|
101
|
-
# @return [Object] Accepted value
|
102
|
-
#
|
103
|
-
def process_value(input)
|
104
|
-
return input unless accept
|
105
|
-
result = input
|
106
|
-
optparse = ::OptionParser.new
|
107
|
-
optparse.accept(accept) if accept.is_a?(Acceptor)
|
108
|
-
optparse.on("--abc VALUE", accept) { |v| result = v }
|
109
|
-
optparse.parse(["--abc", input])
|
110
|
-
result
|
111
|
-
end
|
112
|
-
|
113
|
-
##
|
114
|
-
# Set the short description string.
|
115
|
-
#
|
116
|
-
# The description may be provided as a {Toys::Utils::WrappableString}, a
|
117
|
-
# single string (which will be wrapped), or an array of strings, which will
|
118
|
-
# be interpreted as string fragments that will be concatenated and wrapped.
|
119
|
-
#
|
120
|
-
# @param [Toys::Utils::WrappableString,String,Array<String>] desc
|
121
|
-
#
|
122
|
-
def desc=(desc)
|
123
|
-
@desc = Utils::WrappableString.make(desc)
|
124
|
-
end
|
125
|
-
|
126
|
-
##
|
127
|
-
# Set the long description strings.
|
128
|
-
#
|
129
|
-
# Each string may be provided as a {Toys::Utils::WrappableString}, a single
|
130
|
-
# string (which will be wrapped), or an array of strings, which will be
|
131
|
-
# interpreted as string fragments that will be concatenated and wrapped.
|
132
|
-
#
|
133
|
-
# @param [Array<Toys::Utils::WrappableString,String,Array<String>>] long_desc
|
134
|
-
#
|
135
|
-
def long_desc=(long_desc)
|
136
|
-
@long_desc = Utils::WrappableString.make_array(long_desc)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
data/lib/toys/definition/flag.rb
DELETED
@@ -1,370 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Copyright 2018 Daniel Azuma
|
4
|
-
#
|
5
|
-
# All rights reserved.
|
6
|
-
#
|
7
|
-
# Redistribution and use in source and binary forms, with or without
|
8
|
-
# modification, are permitted provided that the following conditions are met:
|
9
|
-
#
|
10
|
-
# * Redistributions of source code must retain the above copyright notice,
|
11
|
-
# this list of conditions and the following disclaimer.
|
12
|
-
# * Redistributions in binary form must reproduce the above copyright notice,
|
13
|
-
# this list of conditions and the following disclaimer in the documentation
|
14
|
-
# and/or other materials provided with the distribution.
|
15
|
-
# * Neither the name of the copyright holder, nor the names of any other
|
16
|
-
# contributors to this software, may be used to endorse or promote products
|
17
|
-
# derived from this software without specific prior written permission.
|
18
|
-
#
|
19
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
-
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
-
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
22
|
-
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
23
|
-
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
24
|
-
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
25
|
-
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
26
|
-
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
27
|
-
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
28
|
-
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
29
|
-
# POSSIBILITY OF SUCH DAMAGE.
|
30
|
-
;
|
31
|
-
|
32
|
-
module Toys
|
33
|
-
module Definition
|
34
|
-
##
|
35
|
-
# Representation of a single flag.
|
36
|
-
#
|
37
|
-
class FlagSyntax
|
38
|
-
##
|
39
|
-
# Parse flag syntax
|
40
|
-
# @param [String] str syntax.
|
41
|
-
#
|
42
|
-
def initialize(str)
|
43
|
-
case str
|
44
|
-
when /^(-([\?\w]))$/
|
45
|
-
setup(str, [$1], $1, $2, "-", nil, nil, nil, nil)
|
46
|
-
when /^(-([\?\w]))( ?)\[(\w+)\]$/
|
47
|
-
setup(str, [$1], $1, $2, "-", :value, :optional, $3, $4)
|
48
|
-
when /^(-([\?\w]))\[( )(\w+)\]$/
|
49
|
-
setup(str, [$1], $1, $2, "-", :value, :optional, $3, $4)
|
50
|
-
when /^(-([\?\w]))( ?)(\w+)$/
|
51
|
-
setup(str, [$1], $1, $2, "-", :value, :required, $3, $4)
|
52
|
-
when /^--\[no-\](\w[\?\w-]*)$/
|
53
|
-
setup(str, ["--#{$1}", "--no-#{$1}"], str, $1, "--", :boolean, nil, nil, nil)
|
54
|
-
when /^(--(\w[\?\w-]*))$/
|
55
|
-
setup(str, [$1], $1, $2, "--", nil, nil, nil, nil)
|
56
|
-
when /^(--(\w[\?\w-]*))([= ])\[(\w+)\]$/
|
57
|
-
setup(str, [$1], $1, $2, "--", :value, :optional, $3, $4)
|
58
|
-
when /^(--(\w[\?\w-]*))\[([= ])(\w+)\]$/
|
59
|
-
setup(str, [$1], $1, $2, "--", :value, :optional, $3, $4)
|
60
|
-
when /^(--(\w[\?\w-]*))([= ])(\w+)$/
|
61
|
-
setup(str, [$1], $1, $2, "--", :value, :required, $3, $4)
|
62
|
-
else
|
63
|
-
raise ToolDefinitionError, "Illegal flag: #{str.inspect}"
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
attr_reader :original_str
|
68
|
-
attr_reader :flags
|
69
|
-
attr_reader :str_without_value
|
70
|
-
attr_reader :sort_str
|
71
|
-
attr_reader :flag_style
|
72
|
-
attr_reader :flag_type
|
73
|
-
attr_reader :value_type
|
74
|
-
attr_reader :value_delim
|
75
|
-
attr_reader :value_label
|
76
|
-
attr_reader :canonical_str
|
77
|
-
|
78
|
-
## @private
|
79
|
-
def configure_canonical(canonical_flag_type, canonical_value_type,
|
80
|
-
canonical_value_label, canonical_value_delim)
|
81
|
-
return unless flag_type.nil?
|
82
|
-
@flag_type = canonical_flag_type
|
83
|
-
return unless canonical_flag_type == :value
|
84
|
-
@value_type = canonical_value_type
|
85
|
-
canonical_value_delim = "" if canonical_value_delim == "=" && flag_style == "-"
|
86
|
-
canonical_value_delim = "=" if canonical_value_delim == "" && flag_style == "--"
|
87
|
-
@value_delim = canonical_value_delim
|
88
|
-
@value_label = canonical_value_label
|
89
|
-
label = @value_type == :optional ? "[#{@value_label}]" : @value_label
|
90
|
-
@canonical_str = "#{str_without_value}#{@value_delim}#{label}"
|
91
|
-
end
|
92
|
-
|
93
|
-
private
|
94
|
-
|
95
|
-
def setup(original_str, flags, str_without_value, sort_str, flag_style, flag_type,
|
96
|
-
value_type, value_delim, value_label)
|
97
|
-
@original_str = original_str
|
98
|
-
@flags = flags
|
99
|
-
@str_without_value = str_without_value
|
100
|
-
@sort_str = sort_str
|
101
|
-
@flag_style = flag_style
|
102
|
-
@flag_type = flag_type
|
103
|
-
@value_type = value_type
|
104
|
-
@value_delim = value_delim
|
105
|
-
@value_label = value_label ? value_label.upcase : value_label
|
106
|
-
@canonical_str = original_str
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
##
|
111
|
-
# Representation of a formal set of flags that set a particular context
|
112
|
-
# key. The flags within a single Flag definition are synonyms.
|
113
|
-
#
|
114
|
-
class Flag
|
115
|
-
##
|
116
|
-
# The default handler replaces the previous value.
|
117
|
-
# @return [Proc]
|
118
|
-
#
|
119
|
-
DEFAULT_HANDLER = ->(val, _prev) { val }
|
120
|
-
|
121
|
-
##
|
122
|
-
# Create a Flag definition
|
123
|
-
# @private
|
124
|
-
#
|
125
|
-
def initialize(key, flags, used_flags, report_collisions, accept, handler,
|
126
|
-
default, display_name, group)
|
127
|
-
@group = group
|
128
|
-
@key = key
|
129
|
-
@flag_syntax = flags.map { |s| FlagSyntax.new(s) }
|
130
|
-
@accept = accept
|
131
|
-
@handler = handler || DEFAULT_HANDLER
|
132
|
-
@desc = Utils::WrappableString.make(desc)
|
133
|
-
@long_desc = Utils::WrappableString.make_array(long_desc)
|
134
|
-
@default = default
|
135
|
-
needs_val = (!accept.nil? && accept != ::TrueClass && accept != ::FalseClass) ||
|
136
|
-
(!default.nil? && default != true && default != false)
|
137
|
-
create_default_flag_if_needed(needs_val)
|
138
|
-
remove_used_flags(used_flags, report_collisions)
|
139
|
-
canonicalize(needs_val)
|
140
|
-
summarize(display_name)
|
141
|
-
end
|
142
|
-
|
143
|
-
##
|
144
|
-
# Returns the flag group containing this flag
|
145
|
-
# @return [Toys::Definition::FlagGroup]
|
146
|
-
#
|
147
|
-
attr_reader :group
|
148
|
-
|
149
|
-
##
|
150
|
-
# Returns the key.
|
151
|
-
# @return [Symbol]
|
152
|
-
#
|
153
|
-
attr_reader :key
|
154
|
-
|
155
|
-
##
|
156
|
-
# Returns an array of FlagSyntax for the flags.
|
157
|
-
# @return [Array<FlagSyntax>]
|
158
|
-
#
|
159
|
-
attr_reader :flag_syntax
|
160
|
-
|
161
|
-
##
|
162
|
-
# Returns the acceptor, which may be `nil`.
|
163
|
-
# @return [Object]
|
164
|
-
#
|
165
|
-
attr_reader :accept
|
166
|
-
|
167
|
-
##
|
168
|
-
# Returns the default value, which may be `nil`.
|
169
|
-
# @return [Object]
|
170
|
-
#
|
171
|
-
attr_reader :default
|
172
|
-
|
173
|
-
##
|
174
|
-
# Returns the short description string.
|
175
|
-
# @return [Toys::Utils::WrappableString]
|
176
|
-
#
|
177
|
-
attr_reader :desc
|
178
|
-
|
179
|
-
##
|
180
|
-
# Returns the long description strings as an array.
|
181
|
-
# @return [Array<Toys::Utils::WrappableString>]
|
182
|
-
#
|
183
|
-
attr_reader :long_desc
|
184
|
-
|
185
|
-
##
|
186
|
-
# Returns the handler for setting/updating the value.
|
187
|
-
# @return [Proc]
|
188
|
-
#
|
189
|
-
attr_reader :handler
|
190
|
-
|
191
|
-
##
|
192
|
-
# The type of flag. Possible values are `:boolean` for a simple boolean
|
193
|
-
# switch, or `:value` for a flag that sets a value.
|
194
|
-
# @return [:boolean,:value]
|
195
|
-
#
|
196
|
-
attr_reader :flag_type
|
197
|
-
|
198
|
-
##
|
199
|
-
# The type of value. Set to `:required` or `:optional` if the flag type
|
200
|
-
# is `:value`. Otherwise set to `nil`.
|
201
|
-
# @return [:required,:optional,nil]
|
202
|
-
#
|
203
|
-
attr_reader :value_type
|
204
|
-
|
205
|
-
##
|
206
|
-
# The string label for the value as it should display in help, or `nil`
|
207
|
-
# if the flag type is not `:value`.
|
208
|
-
# @return [String,nil]
|
209
|
-
#
|
210
|
-
attr_reader :value_label
|
211
|
-
|
212
|
-
##
|
213
|
-
# The value delimiter, which may be `""`, `" "`, or `"="`. Set to `nil`
|
214
|
-
# if the flag type is not `:value`.
|
215
|
-
# @return [String,nil]
|
216
|
-
#
|
217
|
-
attr_reader :value_delim
|
218
|
-
|
219
|
-
##
|
220
|
-
# Returns the display name of this flag.
|
221
|
-
# @return [String]
|
222
|
-
#
|
223
|
-
attr_reader :display_name
|
224
|
-
|
225
|
-
##
|
226
|
-
# Returns a string that can be used to sort this flag
|
227
|
-
# @return [String]
|
228
|
-
#
|
229
|
-
attr_reader :sort_str
|
230
|
-
|
231
|
-
##
|
232
|
-
# Returns an array of FlagSyntax including only single-dash flags
|
233
|
-
# @return [Array<FlagSyntax>]
|
234
|
-
#
|
235
|
-
def single_flag_syntax
|
236
|
-
@single_flag_syntax ||= flag_syntax.find_all { |ss| ss.flag_style == "-" }
|
237
|
-
end
|
238
|
-
|
239
|
-
##
|
240
|
-
# Returns an array of FlagSyntax including only double-dash flags
|
241
|
-
# @return [Array<FlagSyntax>]
|
242
|
-
#
|
243
|
-
def double_flag_syntax
|
244
|
-
@double_flag_syntax ||= flag_syntax.find_all { |ss| ss.flag_style == "--" }
|
245
|
-
end
|
246
|
-
|
247
|
-
##
|
248
|
-
# Returns the list of effective flags used.
|
249
|
-
# @return [Array<String>]
|
250
|
-
#
|
251
|
-
def effective_flags
|
252
|
-
@effective_flags ||= flag_syntax.map(&:flags).flatten
|
253
|
-
end
|
254
|
-
|
255
|
-
##
|
256
|
-
# Returns a list suitable for passing to OptionParser.
|
257
|
-
# @return [Array]
|
258
|
-
#
|
259
|
-
def optparser_info
|
260
|
-
@optparser_info ||= flag_syntax.map(&:canonical_str) + Array(accept)
|
261
|
-
end
|
262
|
-
|
263
|
-
##
|
264
|
-
# Returns true if this flag is active. That is, it has a nonempty
|
265
|
-
# flags list.
|
266
|
-
# @return [Boolean]
|
267
|
-
#
|
268
|
-
def active?
|
269
|
-
!effective_flags.empty?
|
270
|
-
end
|
271
|
-
|
272
|
-
##
|
273
|
-
# Set the short description string.
|
274
|
-
#
|
275
|
-
# The description may be provided as a {Toys::Utils::WrappableString}, a
|
276
|
-
# single string (which will be wrapped), or an array of strings, which will
|
277
|
-
# be interpreted as string fragments that will be concatenated and wrapped.
|
278
|
-
#
|
279
|
-
# @param [Toys::Utils::WrappableString,String,Array<String>] desc
|
280
|
-
#
|
281
|
-
def desc=(desc)
|
282
|
-
@desc = Utils::WrappableString.make(desc)
|
283
|
-
end
|
284
|
-
|
285
|
-
##
|
286
|
-
# Set the long description strings.
|
287
|
-
#
|
288
|
-
# Each string may be provided as a {Toys::Utils::WrappableString}, a single
|
289
|
-
# string (which will be wrapped), or an array of strings, which will be
|
290
|
-
# interpreted as string fragments that will be concatenated and wrapped.
|
291
|
-
#
|
292
|
-
# @param [Array<Toys::Utils::WrappableString,String,Array<String>>] long_desc
|
293
|
-
#
|
294
|
-
def long_desc=(long_desc)
|
295
|
-
@long_desc = Utils::WrappableString.make_array(long_desc)
|
296
|
-
end
|
297
|
-
|
298
|
-
private
|
299
|
-
|
300
|
-
def create_default_flag_if_needed(needs_val)
|
301
|
-
return unless @flag_syntax.empty?
|
302
|
-
canonical_flag = key.to_s.downcase.tr("_", "-").gsub(/[^a-z0-9-]/, "").sub(/^-+/, "")
|
303
|
-
unless canonical_flag.empty?
|
304
|
-
flag = needs_val ? "--#{canonical_flag} VALUE" : "--#{canonical_flag}"
|
305
|
-
@flag_syntax << FlagSyntax.new(flag)
|
306
|
-
end
|
307
|
-
end
|
308
|
-
|
309
|
-
def remove_used_flags(used_flags, report_collisions)
|
310
|
-
@flag_syntax.select! do |fs|
|
311
|
-
fs.flags.all? do |f|
|
312
|
-
collision = used_flags.include?(f)
|
313
|
-
if collision && report_collisions
|
314
|
-
raise ToolDefinitionError,
|
315
|
-
"Cannot use flag #{f.inspect} because it is already assigned or reserved."
|
316
|
-
end
|
317
|
-
!collision
|
318
|
-
end
|
319
|
-
end
|
320
|
-
used_flags.concat(effective_flags.uniq)
|
321
|
-
end
|
322
|
-
|
323
|
-
def canonicalize(needs_val)
|
324
|
-
@flag_type = needs_val ? :value : nil
|
325
|
-
@value_type = nil
|
326
|
-
@value_label = needs_val ? "VALUE" : nil
|
327
|
-
@value_delim = " "
|
328
|
-
single_flag_syntax.reverse_each do |flag|
|
329
|
-
analyze_flag_syntax(flag)
|
330
|
-
end
|
331
|
-
double_flag_syntax.reverse_each do |flag|
|
332
|
-
analyze_flag_syntax(flag)
|
333
|
-
end
|
334
|
-
@flag_type ||= :boolean
|
335
|
-
@value_type ||= :required if @flag_type == :value
|
336
|
-
flag_syntax.each do |flag|
|
337
|
-
flag.configure_canonical(@flag_type, @value_type, @value_label, @value_delim)
|
338
|
-
end
|
339
|
-
end
|
340
|
-
|
341
|
-
def analyze_flag_syntax(flag)
|
342
|
-
return if flag.flag_type.nil?
|
343
|
-
if !@flag_type.nil? && @flag_type != flag.flag_type
|
344
|
-
raise ToolDefinitionError, "Cannot have both value and boolean flags for #{key.inspect}"
|
345
|
-
end
|
346
|
-
@flag_type = flag.flag_type
|
347
|
-
return unless @flag_type == :value
|
348
|
-
if !@value_type.nil? && @value_type != flag.value_type
|
349
|
-
raise ToolDefinitionError,
|
350
|
-
"Cannot have both required and optional values for flag #{key.inspect}"
|
351
|
-
end
|
352
|
-
@value_type = flag.value_type
|
353
|
-
@value_label = flag.value_label
|
354
|
-
@value_delim = flag.value_delim
|
355
|
-
end
|
356
|
-
|
357
|
-
def summarize(name)
|
358
|
-
@display_name =
|
359
|
-
name ||
|
360
|
-
double_flag_syntax.first&.canonical_str ||
|
361
|
-
single_flag_syntax.first&.canonical_str ||
|
362
|
-
key.to_s
|
363
|
-
@sort_str =
|
364
|
-
double_flag_syntax.first&.sort_str ||
|
365
|
-
single_flag_syntax.first&.sort_str ||
|
366
|
-
""
|
367
|
-
end
|
368
|
-
end
|
369
|
-
end
|
370
|
-
end
|