toys-core 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,205 +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 group of flags with the same requirement settings.
|
36
|
-
#
|
37
|
-
class FlagGroup
|
38
|
-
##
|
39
|
-
# Create a flag group.
|
40
|
-
# @private
|
41
|
-
#
|
42
|
-
def initialize(name, desc, long_desc)
|
43
|
-
@name = name
|
44
|
-
@desc = Utils::WrappableString.make(desc || default_desc)
|
45
|
-
@long_desc = Utils::WrappableString.make_array(long_desc || default_long_desc)
|
46
|
-
@flag_definitions = []
|
47
|
-
end
|
48
|
-
|
49
|
-
##
|
50
|
-
# Returns the symbolic name for this group
|
51
|
-
# @return [String,Symbol,nil]
|
52
|
-
#
|
53
|
-
attr_reader :name
|
54
|
-
|
55
|
-
##
|
56
|
-
# Returns the short description string.
|
57
|
-
# @return [Toys::Utils::WrappableString]
|
58
|
-
#
|
59
|
-
attr_reader :desc
|
60
|
-
|
61
|
-
##
|
62
|
-
# Returns the long description strings as an array.
|
63
|
-
# @return [Array<Toys::Utils::WrappableString>]
|
64
|
-
#
|
65
|
-
attr_reader :long_desc
|
66
|
-
|
67
|
-
##
|
68
|
-
# Returns an array of flags that are in this group.
|
69
|
-
# Do not modify the returned array.
|
70
|
-
# @return [Array<Toys::Definition::Flag>]
|
71
|
-
#
|
72
|
-
attr_reader :flag_definitions
|
73
|
-
|
74
|
-
##
|
75
|
-
# Returns true if this group is empty
|
76
|
-
# @return [Boolean]
|
77
|
-
#
|
78
|
-
def empty?
|
79
|
-
flag_definitions.empty?
|
80
|
-
end
|
81
|
-
|
82
|
-
## @private
|
83
|
-
def <<(flag)
|
84
|
-
flag_definitions << flag
|
85
|
-
end
|
86
|
-
|
87
|
-
## @private
|
88
|
-
def default_desc
|
89
|
-
"Flags"
|
90
|
-
end
|
91
|
-
|
92
|
-
## @private
|
93
|
-
def default_long_desc
|
94
|
-
nil
|
95
|
-
end
|
96
|
-
|
97
|
-
## @private
|
98
|
-
def validation_error(_seen)
|
99
|
-
nil
|
100
|
-
end
|
101
|
-
|
102
|
-
##
|
103
|
-
# A FlagGroup containing all required flags
|
104
|
-
#
|
105
|
-
class Required < FlagGroup
|
106
|
-
## @private
|
107
|
-
def validation_error(seen)
|
108
|
-
flag_definitions.each do |flag|
|
109
|
-
unless seen.include?(flag.key)
|
110
|
-
return "Flag \"#{flag.display_name}\" is required"
|
111
|
-
end
|
112
|
-
end
|
113
|
-
nil
|
114
|
-
end
|
115
|
-
|
116
|
-
## @private
|
117
|
-
def default_desc
|
118
|
-
"Required Flags"
|
119
|
-
end
|
120
|
-
|
121
|
-
## @private
|
122
|
-
def default_long_desc
|
123
|
-
"These flags are required."
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
##
|
128
|
-
# A FlagGroup containing all optional flags
|
129
|
-
#
|
130
|
-
class Optional < FlagGroup
|
131
|
-
end
|
132
|
-
|
133
|
-
##
|
134
|
-
# A FlagGroup in which exactly one flag must be set
|
135
|
-
#
|
136
|
-
class ExactlyOne < FlagGroup
|
137
|
-
## @private
|
138
|
-
def validation_error(seen)
|
139
|
-
set_flag = nil
|
140
|
-
flag_definitions.each do |flag|
|
141
|
-
if seen.include?(flag.key)
|
142
|
-
if set_flag
|
143
|
-
return "Exactly one out of group \"#{desc}\" is required, but both" \
|
144
|
-
" \"#{set_flag.display_name}\" and \"#{flag.display_name}\" were set"
|
145
|
-
else
|
146
|
-
set_flag = flag
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
return "Exactly one out of group \"#{desc}\" is required" unless set_flag
|
151
|
-
nil
|
152
|
-
end
|
153
|
-
|
154
|
-
## @private
|
155
|
-
def default_long_desc
|
156
|
-
"Exactly one of these flags must be set."
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
##
|
161
|
-
# A FlagGroup in which at most one flag must be set
|
162
|
-
#
|
163
|
-
class AtMostOne < FlagGroup
|
164
|
-
## @private
|
165
|
-
def validation_error(seen)
|
166
|
-
set_flag = nil
|
167
|
-
flag_definitions.each do |flag|
|
168
|
-
if seen.include?(flag.key)
|
169
|
-
if set_flag
|
170
|
-
return "At most one out of group \"#{desc}\" is required, but both" \
|
171
|
-
" \"#{set_flag.display_name}\" and \"#{flag.display_name}\" were set"
|
172
|
-
else
|
173
|
-
set_flag = flag
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
nil
|
178
|
-
end
|
179
|
-
|
180
|
-
## @private
|
181
|
-
def default_long_desc
|
182
|
-
"At most one of these flags must be set."
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
##
|
187
|
-
# A FlagGroup in which at least one flag must be set
|
188
|
-
#
|
189
|
-
class AtLeastOne < FlagGroup
|
190
|
-
## @private
|
191
|
-
def validation_error(seen)
|
192
|
-
flag_definitions.each do |flag|
|
193
|
-
return nil if seen.include?(flag.key)
|
194
|
-
end
|
195
|
-
"At least one out of group \"#{desc}\" is required"
|
196
|
-
end
|
197
|
-
|
198
|
-
## @private
|
199
|
-
def default_long_desc
|
200
|
-
"At least one of these flags must be set."
|
201
|
-
end
|
202
|
-
end
|
203
|
-
end
|
204
|
-
end
|
205
|
-
end
|
@@ -1,190 +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
|
-
# Information about source toys directories and files.
|
36
|
-
#
|
37
|
-
class SourceInfo
|
38
|
-
##
|
39
|
-
# Create a SourceInfo.
|
40
|
-
# @private
|
41
|
-
#
|
42
|
-
def initialize(parent, context_directory, source, source_type, source_name, data_dir_name)
|
43
|
-
@parent = parent
|
44
|
-
@context_directory = context_directory
|
45
|
-
@source = source
|
46
|
-
@source_type = source_type
|
47
|
-
@source_path = source if source.is_a?(::String)
|
48
|
-
@source_proc = source if source.is_a?(::Proc)
|
49
|
-
@source_name = source_name
|
50
|
-
@data_dir =
|
51
|
-
if data_dir_name && @source_path
|
52
|
-
dir = ::File.join(::File.dirname(@source_path), data_dir_name)
|
53
|
-
dir if ::File.directory?(dir) && ::File.readable?(dir)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
##
|
58
|
-
# Return the parent SourceInfo, or nil if this is the root.
|
59
|
-
# @return [Toys::Definition::SourceInfo,nil]
|
60
|
-
#
|
61
|
-
attr_reader :parent
|
62
|
-
|
63
|
-
##
|
64
|
-
# Return the context directory path (normally the directory containing
|
65
|
-
# the toplevel toys file or directory). May return nil if there is no
|
66
|
-
# context (e.g. the tool is being defined from a block).
|
67
|
-
# @return [String,nil]
|
68
|
-
#
|
69
|
-
attr_reader :context_directory
|
70
|
-
|
71
|
-
##
|
72
|
-
# Return the source, which may be a path or a proc.
|
73
|
-
# @return [String,Proc]
|
74
|
-
#
|
75
|
-
attr_reader :source
|
76
|
-
|
77
|
-
##
|
78
|
-
# Return the type of source.
|
79
|
-
# @return [:file,:directory,:proc]
|
80
|
-
#
|
81
|
-
attr_reader :source_type
|
82
|
-
|
83
|
-
##
|
84
|
-
# Return the path of the current source file or directory, or nil if this
|
85
|
-
# source is not a file system path.
|
86
|
-
# @return [String,nil]
|
87
|
-
#
|
88
|
-
attr_reader :source_path
|
89
|
-
|
90
|
-
##
|
91
|
-
# Return the source proc, or nil if this source is not a proc.
|
92
|
-
# @return [Proc,nil]
|
93
|
-
#
|
94
|
-
attr_reader :source_proc
|
95
|
-
|
96
|
-
##
|
97
|
-
# Return the user-visible name of this source.
|
98
|
-
# @return [String]
|
99
|
-
#
|
100
|
-
attr_reader :source_name
|
101
|
-
alias to_s source_name
|
102
|
-
|
103
|
-
##
|
104
|
-
# Return the absolute path to the given data file or directory.
|
105
|
-
#
|
106
|
-
# @param [String] path The relative path to find
|
107
|
-
# @param [nil,:file,:directory] type Type of file system object to find,
|
108
|
-
# or nil to return any type.
|
109
|
-
# @return [String,nil] Absolute path of the result, or nil if not found.
|
110
|
-
#
|
111
|
-
def find_data(path, type: nil)
|
112
|
-
if @data_dir
|
113
|
-
full_path = ::File.join(@data_dir, path)
|
114
|
-
case type
|
115
|
-
when :file
|
116
|
-
return full_path if ::File.file?(full_path)
|
117
|
-
when :directory
|
118
|
-
return full_path if ::File.directory?(full_path)
|
119
|
-
else
|
120
|
-
return full_path if ::File.readable?(full_path)
|
121
|
-
end
|
122
|
-
end
|
123
|
-
parent&.find_data(path, type: type)
|
124
|
-
end
|
125
|
-
|
126
|
-
##
|
127
|
-
# Create a child SourceInfo relative to the parent path.
|
128
|
-
# @private
|
129
|
-
#
|
130
|
-
def relative_child(filename, data_dir_name)
|
131
|
-
raise "Cannot create relative child of a proc" unless source_path
|
132
|
-
child_path = ::File.join(source_path, filename)
|
133
|
-
child_path, type = SourceInfo.check_path(child_path, true)
|
134
|
-
return nil unless child_path
|
135
|
-
SourceInfo.new(self, context_directory, child_path, type, child_path, data_dir_name)
|
136
|
-
end
|
137
|
-
|
138
|
-
##
|
139
|
-
# Create a child SourceInfo with an absolute path.
|
140
|
-
# @private
|
141
|
-
#
|
142
|
-
def absolute_child(child_path)
|
143
|
-
child_path, type = SourceInfo.check_path(child_path, false)
|
144
|
-
SourceInfo.new(self, context_directory, child_path, type, child_path, nil)
|
145
|
-
end
|
146
|
-
|
147
|
-
##
|
148
|
-
# Create a root source info for a file path.
|
149
|
-
# @private
|
150
|
-
#
|
151
|
-
def self.create_path_root(source_path)
|
152
|
-
source_path, type = check_path(source_path, false)
|
153
|
-
context_directory = ::File.dirname(source_path)
|
154
|
-
new(nil, context_directory, source_path, type, source_path, nil)
|
155
|
-
end
|
156
|
-
|
157
|
-
##
|
158
|
-
# Create a root source info for a proc.
|
159
|
-
# @private
|
160
|
-
#
|
161
|
-
def self.create_proc_root(source_proc, source_name)
|
162
|
-
new(nil, nil, source_proc, :proc, source_name, nil)
|
163
|
-
end
|
164
|
-
|
165
|
-
##
|
166
|
-
# Check a path and determine the canonical path and type.
|
167
|
-
# @private
|
168
|
-
#
|
169
|
-
def self.check_path(path, lenient)
|
170
|
-
path = ::File.expand_path(path)
|
171
|
-
unless ::File.readable?(path)
|
172
|
-
raise LoaderError, "Cannot read: #{path}" unless lenient
|
173
|
-
return [nil, nil]
|
174
|
-
end
|
175
|
-
if ::File.file?(path)
|
176
|
-
unless ::File.extname(path) == ".rb"
|
177
|
-
raise LoaderError, "File is not a ruby file: #{path}" unless lenient
|
178
|
-
return [nil, nil]
|
179
|
-
end
|
180
|
-
[path, :file]
|
181
|
-
elsif ::File.directory?(path)
|
182
|
-
[path, :directory]
|
183
|
-
else
|
184
|
-
raise LoaderError, "Unknown type: #{path}" unless lenient
|
185
|
-
[nil, nil]
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
190
|
-
end
|
data/lib/toys/definition/tool.rb
DELETED
@@ -1,842 +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
|
-
require "set"
|
34
|
-
|
35
|
-
module Toys
|
36
|
-
module Definition
|
37
|
-
##
|
38
|
-
# A Tool is a single command that can be invoked using Toys.
|
39
|
-
# It has a name, a series of one or more words that you use to identify
|
40
|
-
# the tool on the command line. It also has a set of formal flags and
|
41
|
-
# command line arguments supported, and a block that gets run when the
|
42
|
-
# tool is executed.
|
43
|
-
#
|
44
|
-
class Tool
|
45
|
-
##
|
46
|
-
# Built-in acceptors (i.e. those recognized by OptionParser).
|
47
|
-
# You can reference these acceptors directly. Otherwise, you have to add
|
48
|
-
# one explicitly to the tool using {Tool#add_acceptor}.
|
49
|
-
#
|
50
|
-
OPTPARSER_ACCEPTORS = ::Set.new(
|
51
|
-
[
|
52
|
-
::Object,
|
53
|
-
::NilClass,
|
54
|
-
::String,
|
55
|
-
::Integer,
|
56
|
-
::Float,
|
57
|
-
::Numeric,
|
58
|
-
::TrueClass,
|
59
|
-
::FalseClass,
|
60
|
-
::Array,
|
61
|
-
::Regexp,
|
62
|
-
::OptionParser::DecimalInteger,
|
63
|
-
::OptionParser::OctalInteger,
|
64
|
-
::OptionParser::DecimalNumeric
|
65
|
-
]
|
66
|
-
).freeze
|
67
|
-
|
68
|
-
##
|
69
|
-
# Create a new tool.
|
70
|
-
# @private
|
71
|
-
#
|
72
|
-
def initialize(loader, parent, full_name, priority, middleware_stack)
|
73
|
-
@parent = parent
|
74
|
-
@full_name = full_name.dup.freeze
|
75
|
-
@priority = priority
|
76
|
-
@middleware_stack = middleware_stack
|
77
|
-
|
78
|
-
@acceptors = {}
|
79
|
-
@mixins = {}
|
80
|
-
@templates = {}
|
81
|
-
|
82
|
-
reset_definition(loader)
|
83
|
-
end
|
84
|
-
|
85
|
-
##
|
86
|
-
# Reset the definition of this tool, deleting all definition data but
|
87
|
-
# leaving named acceptors, mixins, and templates intact.
|
88
|
-
# Should be called only from the DSL.
|
89
|
-
# @private
|
90
|
-
#
|
91
|
-
def reset_definition(loader)
|
92
|
-
@tool_class = DSL::Tool.new_class(@full_name, @priority, loader)
|
93
|
-
|
94
|
-
@source_info = nil
|
95
|
-
@definition_finished = false
|
96
|
-
|
97
|
-
@desc = Utils::WrappableString.new("")
|
98
|
-
@long_desc = []
|
99
|
-
|
100
|
-
@default_data = {}
|
101
|
-
@used_flags = []
|
102
|
-
@initializers = []
|
103
|
-
|
104
|
-
default_flag_group = Definition::FlagGroup.new(nil, nil, nil)
|
105
|
-
@flag_groups = [default_flag_group]
|
106
|
-
@flag_group_names = {nil => default_flag_group}
|
107
|
-
|
108
|
-
@flag_definitions = []
|
109
|
-
@required_arg_definitions = []
|
110
|
-
@optional_arg_definitions = []
|
111
|
-
@remaining_args_definition = nil
|
112
|
-
|
113
|
-
@disable_argument_parsing = false
|
114
|
-
@includes_modules = false
|
115
|
-
@custom_context_directory = nil
|
116
|
-
end
|
117
|
-
|
118
|
-
##
|
119
|
-
# Return the name of the tool as an array of strings.
|
120
|
-
# This array may not be modified.
|
121
|
-
# @return [Array<String>]
|
122
|
-
#
|
123
|
-
attr_reader :full_name
|
124
|
-
|
125
|
-
##
|
126
|
-
# Return the priority of this tool definition.
|
127
|
-
# @return [Integer]
|
128
|
-
#
|
129
|
-
attr_reader :priority
|
130
|
-
|
131
|
-
##
|
132
|
-
# Return the tool class.
|
133
|
-
# @return [Class]
|
134
|
-
#
|
135
|
-
attr_reader :tool_class
|
136
|
-
|
137
|
-
##
|
138
|
-
# Returns the short description string.
|
139
|
-
# @return [Toys::Utils::WrappableString]
|
140
|
-
#
|
141
|
-
attr_reader :desc
|
142
|
-
|
143
|
-
##
|
144
|
-
# Returns the long description strings as an array.
|
145
|
-
# @return [Array<Toys::Utils::WrappableString>]
|
146
|
-
#
|
147
|
-
attr_reader :long_desc
|
148
|
-
|
149
|
-
##
|
150
|
-
# Return a list of all defined flag groups, in order.
|
151
|
-
# @return [Array<Toys::Definition::FlagGroup>]
|
152
|
-
#
|
153
|
-
attr_reader :flag_groups
|
154
|
-
|
155
|
-
##
|
156
|
-
# Return a list of all defined flags.
|
157
|
-
# @return [Array<Toys::Definition::Flag>]
|
158
|
-
#
|
159
|
-
attr_reader :flag_definitions
|
160
|
-
|
161
|
-
##
|
162
|
-
# Return a list of all defined required positional arguments.
|
163
|
-
# @return [Array<Toys::Definition::Arg>]
|
164
|
-
#
|
165
|
-
attr_reader :required_arg_definitions
|
166
|
-
|
167
|
-
##
|
168
|
-
# Return a list of all defined optional positional arguments.
|
169
|
-
# @return [Array<Toys::Definition::Arg>]
|
170
|
-
#
|
171
|
-
attr_reader :optional_arg_definitions
|
172
|
-
|
173
|
-
##
|
174
|
-
# Return the remaining arguments specification, or `nil` if remaining
|
175
|
-
# arguments are currently not supported by this tool.
|
176
|
-
# @return [Toys::Definition::Arg,nil]
|
177
|
-
#
|
178
|
-
attr_reader :remaining_args_definition
|
179
|
-
|
180
|
-
##
|
181
|
-
# Return a list of flags that have been used in the flag definitions.
|
182
|
-
# @return [Array<String>]
|
183
|
-
#
|
184
|
-
attr_reader :used_flags
|
185
|
-
|
186
|
-
##
|
187
|
-
# Return the default argument data.
|
188
|
-
# @return [Hash]
|
189
|
-
#
|
190
|
-
attr_reader :default_data
|
191
|
-
|
192
|
-
##
|
193
|
-
# Returns the middleware stack
|
194
|
-
# @return [Array<Object>]
|
195
|
-
#
|
196
|
-
attr_reader :middleware_stack
|
197
|
-
|
198
|
-
##
|
199
|
-
# Returns info on the source of this tool, or nil if the source is not
|
200
|
-
# defined.
|
201
|
-
# @return [Toys::Definition::SourceInfo,nil]
|
202
|
-
#
|
203
|
-
attr_reader :source_info
|
204
|
-
|
205
|
-
##
|
206
|
-
# Returns the custom context directory set for this tool, or nil if none
|
207
|
-
# is set.
|
208
|
-
# @return [String,nil]
|
209
|
-
#
|
210
|
-
attr_reader :custom_context_directory
|
211
|
-
|
212
|
-
##
|
213
|
-
# Returns the local name of this tool.
|
214
|
-
# @return [String]
|
215
|
-
#
|
216
|
-
def simple_name
|
217
|
-
full_name.last
|
218
|
-
end
|
219
|
-
|
220
|
-
##
|
221
|
-
# Returns a displayable name of this tool, generally the full name
|
222
|
-
# delimited by spaces.
|
223
|
-
# @return [String]
|
224
|
-
#
|
225
|
-
def display_name
|
226
|
-
full_name.join(" ")
|
227
|
-
end
|
228
|
-
|
229
|
-
##
|
230
|
-
# Returns true if this tool is a root tool.
|
231
|
-
# @return [Boolean]
|
232
|
-
#
|
233
|
-
def root?
|
234
|
-
full_name.empty?
|
235
|
-
end
|
236
|
-
|
237
|
-
##
|
238
|
-
# Returns true if this tool is marked as runnable.
|
239
|
-
# @return [Boolean]
|
240
|
-
#
|
241
|
-
def runnable?
|
242
|
-
tool_class.public_instance_methods(false).include?(:run)
|
243
|
-
end
|
244
|
-
|
245
|
-
##
|
246
|
-
# Returns true if this tool has at least one included module.
|
247
|
-
# @return [Boolean]
|
248
|
-
#
|
249
|
-
def includes_modules?
|
250
|
-
@includes_modules
|
251
|
-
end
|
252
|
-
|
253
|
-
##
|
254
|
-
# Returns true if there is a specific description set for this tool.
|
255
|
-
# @return [Boolean]
|
256
|
-
#
|
257
|
-
def includes_description?
|
258
|
-
!long_desc.empty? || !desc.empty?
|
259
|
-
end
|
260
|
-
|
261
|
-
##
|
262
|
-
# Returns true if at least one flag or positional argument is defined
|
263
|
-
# for this tool.
|
264
|
-
# @return [Boolean]
|
265
|
-
#
|
266
|
-
def includes_arguments?
|
267
|
-
!default_data.empty? || !flag_definitions.empty? ||
|
268
|
-
!required_arg_definitions.empty? || !optional_arg_definitions.empty? ||
|
269
|
-
!remaining_args_definition.nil?
|
270
|
-
end
|
271
|
-
|
272
|
-
##
|
273
|
-
# Returns true if this tool has any definition information.
|
274
|
-
# @return [Boolean]
|
275
|
-
#
|
276
|
-
def includes_definition?
|
277
|
-
includes_arguments? || runnable? || argument_parsing_disabled? ||
|
278
|
-
includes_modules? || includes_description?
|
279
|
-
end
|
280
|
-
|
281
|
-
##
|
282
|
-
# Returns true if this tool's definition has been finished and is locked.
|
283
|
-
# @return [Boolean]
|
284
|
-
#
|
285
|
-
def definition_finished?
|
286
|
-
@definition_finished
|
287
|
-
end
|
288
|
-
|
289
|
-
##
|
290
|
-
# Returns true if this tool has disabled argument parsing.
|
291
|
-
# @return [Boolean]
|
292
|
-
#
|
293
|
-
def argument_parsing_disabled?
|
294
|
-
@disable_argument_parsing
|
295
|
-
end
|
296
|
-
|
297
|
-
##
|
298
|
-
# Returns all arg definitions in order: required, optional, remaining.
|
299
|
-
# @return [Array<Toys::Definition::Arg>]
|
300
|
-
#
|
301
|
-
def arg_definitions
|
302
|
-
result = required_arg_definitions + optional_arg_definitions
|
303
|
-
result << remaining_args_definition if remaining_args_definition
|
304
|
-
result
|
305
|
-
end
|
306
|
-
|
307
|
-
##
|
308
|
-
# Returns a list of all custom acceptors used by this tool.
|
309
|
-
# @return [Array<Toys::Definition::Acceptor>]
|
310
|
-
#
|
311
|
-
def custom_acceptors
|
312
|
-
result = []
|
313
|
-
flag_definitions.each do |f|
|
314
|
-
result << f.accept if f.accept.is_a?(Acceptor)
|
315
|
-
end
|
316
|
-
arg_definitions.each do |a|
|
317
|
-
result << a.accept if a.accept.is_a?(Acceptor)
|
318
|
-
end
|
319
|
-
result.uniq
|
320
|
-
end
|
321
|
-
|
322
|
-
##
|
323
|
-
# Resolve the given acceptor. You may pass in a
|
324
|
-
# {Toys::Definition::Acceptor}, an acceptor name, a well-known acceptor
|
325
|
-
# understood by OptionParser, or `nil`.
|
326
|
-
#
|
327
|
-
# Returns either `nil` or an acceptor that is usable by OptionParser.
|
328
|
-
#
|
329
|
-
# If an acceptor name is given, it may be resolved by this tool or any of
|
330
|
-
# its ancestors. Raises {Toys::ToolDefinitionError} if the name is not
|
331
|
-
# recognized.
|
332
|
-
#
|
333
|
-
# @param [Object] accept An acceptor input.
|
334
|
-
# @return [Object] The resolved acceptor.
|
335
|
-
#
|
336
|
-
def resolve_acceptor(accept)
|
337
|
-
return accept if accept.nil? || accept.is_a?(Acceptor)
|
338
|
-
name = accept
|
339
|
-
accept = @acceptors.fetch(name) do |k|
|
340
|
-
if @parent
|
341
|
-
@parent.resolve_acceptor(k)
|
342
|
-
elsif OPTPARSER_ACCEPTORS.include?(k)
|
343
|
-
k
|
344
|
-
end
|
345
|
-
end
|
346
|
-
if accept.nil?
|
347
|
-
raise ToolDefinitionError, "Unknown acceptor: #{name.inspect}"
|
348
|
-
end
|
349
|
-
accept
|
350
|
-
end
|
351
|
-
|
352
|
-
##
|
353
|
-
# Get the named template from this tool or its ancestors.
|
354
|
-
#
|
355
|
-
# @param [String] name The template name
|
356
|
-
# @return [Class,nil] The template class, or `nil` if not found.
|
357
|
-
#
|
358
|
-
def resolve_template(name)
|
359
|
-
@templates.fetch(name.to_s) { |k| @parent ? @parent.resolve_template(k) : nil }
|
360
|
-
end
|
361
|
-
|
362
|
-
##
|
363
|
-
# Get the named mixin from this tool or its ancestors.
|
364
|
-
#
|
365
|
-
# @param [String] name The mixin name
|
366
|
-
# @return [Module,nil] The mixin module, or `nil` if not found.
|
367
|
-
#
|
368
|
-
def resolve_mixin(name)
|
369
|
-
@mixins.fetch(name.to_s) { |k| @parent ? @parent.resolve_mixin(k) : nil }
|
370
|
-
end
|
371
|
-
|
372
|
-
##
|
373
|
-
# Include the given mixin in the tool class.
|
374
|
-
#
|
375
|
-
# @param [String,Symbol,Module] name The mixin name or module
|
376
|
-
#
|
377
|
-
def include_mixin(name)
|
378
|
-
tool_class.include(name)
|
379
|
-
self
|
380
|
-
end
|
381
|
-
|
382
|
-
##
|
383
|
-
# Sets the path to the file that defines this tool.
|
384
|
-
# A tool may be defined from at most one path. If a different path is
|
385
|
-
# already set, raises {Toys::ToolDefinitionError}
|
386
|
-
#
|
387
|
-
# @param [Toys::Definition::SourceInfo] source Source info
|
388
|
-
#
|
389
|
-
def lock_source(source)
|
390
|
-
if source_info && source_info.source != source.source
|
391
|
-
raise ToolDefinitionError,
|
392
|
-
"Cannot redefine tool #{display_name.inspect} in #{source.source_name}" \
|
393
|
-
" (already defined in #{source_info.source_name})"
|
394
|
-
end
|
395
|
-
@source_info = source
|
396
|
-
end
|
397
|
-
|
398
|
-
##
|
399
|
-
# Set the short description string.
|
400
|
-
#
|
401
|
-
# The description may be provided as a {Toys::Utils::WrappableString}, a
|
402
|
-
# single string (which will be wrapped), or an array of strings, which will
|
403
|
-
# be interpreted as string fragments that will be concatenated and wrapped.
|
404
|
-
#
|
405
|
-
# @param [Toys::Utils::WrappableString,String,Array<String>] desc
|
406
|
-
#
|
407
|
-
def desc=(desc)
|
408
|
-
check_definition_state
|
409
|
-
@desc = Utils::WrappableString.make(desc)
|
410
|
-
end
|
411
|
-
|
412
|
-
##
|
413
|
-
# Set the long description strings.
|
414
|
-
#
|
415
|
-
# Each string may be provided as a {Toys::Utils::WrappableString}, a single
|
416
|
-
# string (which will be wrapped), or an array of strings, which will be
|
417
|
-
# interpreted as string fragments that will be concatenated and wrapped.
|
418
|
-
#
|
419
|
-
# @param [Array<Toys::Utils::WrappableString,String,Array<String>>] long_desc
|
420
|
-
#
|
421
|
-
def long_desc=(long_desc)
|
422
|
-
check_definition_state
|
423
|
-
@long_desc = Utils::WrappableString.make_array(long_desc)
|
424
|
-
end
|
425
|
-
|
426
|
-
##
|
427
|
-
# Append long description strings.
|
428
|
-
#
|
429
|
-
# Each string may be provided as a {Toys::Utils::WrappableString}, a single
|
430
|
-
# string (which will be wrapped), or an array of strings, which will be
|
431
|
-
# interpreted as string fragments that will be concatenated and wrapped.
|
432
|
-
#
|
433
|
-
# @param [Array<Toys::Utils::WrappableString,String,Array<String>>] long_desc
|
434
|
-
#
|
435
|
-
def append_long_desc(long_desc)
|
436
|
-
check_definition_state
|
437
|
-
@long_desc += Utils::WrappableString.make_array(long_desc)
|
438
|
-
end
|
439
|
-
|
440
|
-
##
|
441
|
-
# Add an acceptor to the tool. This acceptor may be refereneced by name
|
442
|
-
# when adding a flag or an arg.
|
443
|
-
#
|
444
|
-
# @param [Toys::Definition::Acceptor] acceptor The acceptor to add.
|
445
|
-
#
|
446
|
-
def add_acceptor(acceptor)
|
447
|
-
if @acceptors.key?(acceptor.name)
|
448
|
-
raise ToolDefinitionError,
|
449
|
-
"An acceptor named #{acceptor.name.inspect} has already been" \
|
450
|
-
" defined in tool #{display_name.inspect}."
|
451
|
-
end
|
452
|
-
@acceptors[acceptor.name] = acceptor
|
453
|
-
self
|
454
|
-
end
|
455
|
-
|
456
|
-
##
|
457
|
-
# Add a named mixin module to this tool.
|
458
|
-
#
|
459
|
-
# @param [String] name The name of the mixin.
|
460
|
-
# @param [Module] mixin_module The mixin module.
|
461
|
-
#
|
462
|
-
def add_mixin(name, mixin_module)
|
463
|
-
name = name.to_s
|
464
|
-
if @mixins.key?(name)
|
465
|
-
raise ToolDefinitionError,
|
466
|
-
"A mixin named #{name.inspect} has already been defined in tool" \
|
467
|
-
" #{display_name.inspect}."
|
468
|
-
end
|
469
|
-
@mixins[name] = mixin_module
|
470
|
-
self
|
471
|
-
end
|
472
|
-
|
473
|
-
##
|
474
|
-
# Add a named template class to this tool.
|
475
|
-
#
|
476
|
-
# @param [String] name The name of the template.
|
477
|
-
# @param [Class] template_class The template class.
|
478
|
-
#
|
479
|
-
def add_template(name, template_class)
|
480
|
-
name = name.to_s
|
481
|
-
if @templates.key?(name)
|
482
|
-
raise ToolDefinitionError,
|
483
|
-
"A template named #{name.inspect} has already been defined in tool" \
|
484
|
-
" #{display_name.inspect}."
|
485
|
-
end
|
486
|
-
@templates[name] = template_class
|
487
|
-
self
|
488
|
-
end
|
489
|
-
|
490
|
-
##
|
491
|
-
# Disable argument parsing for this tool
|
492
|
-
#
|
493
|
-
def disable_argument_parsing
|
494
|
-
check_definition_state
|
495
|
-
if includes_arguments?
|
496
|
-
raise ToolDefinitionError,
|
497
|
-
"Cannot disable argument parsing for tool #{display_name.inspect}" \
|
498
|
-
" because arguments have already been defined."
|
499
|
-
end
|
500
|
-
@disable_argument_parsing = true
|
501
|
-
self
|
502
|
-
end
|
503
|
-
|
504
|
-
##
|
505
|
-
# Add a flag group to the group list.
|
506
|
-
#
|
507
|
-
# @param [Symbol] type The type of group. Allowed values: `:required`,
|
508
|
-
# `:optional`, `:exactly_one`, `:at_most_one`, `:at_least_one`.
|
509
|
-
# Default is `:optional`.
|
510
|
-
# @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
|
511
|
-
# description for the group. See {Toys::Definition::Tool#desc=} for a
|
512
|
-
# description of allowed formats. Defaults to `"Flags"`.
|
513
|
-
# @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
|
514
|
-
# Long description for the flag group. See
|
515
|
-
# {Toys::Definition::Tool#long_desc=} for a description of allowed
|
516
|
-
# formats. Defaults to the empty array.
|
517
|
-
# @param [String,Symbol,nil] name The name of the group, or nil for no
|
518
|
-
# name.
|
519
|
-
# @param [Boolean] report_collisions If `true`, raise an exception if a
|
520
|
-
# the given name is already taken. If `false`, ignore. Default is
|
521
|
-
# `true`.
|
522
|
-
# @param [Boolean] prepend If `true`, prepend rather than append the
|
523
|
-
# group to the list. Default is `false`.
|
524
|
-
#
|
525
|
-
def add_flag_group(type: :optional, desc: nil, long_desc: nil,
|
526
|
-
name: nil, report_collisions: true, prepend: false)
|
527
|
-
if !name.nil? && @flag_group_names.key?(name)
|
528
|
-
return self unless report_collisions
|
529
|
-
raise ToolDefinitionError, "Flag group #{name} already exists"
|
530
|
-
end
|
531
|
-
unless type.is_a?(::Class)
|
532
|
-
type = Utils::ModuleLookup.to_module_name(type)
|
533
|
-
type = Definition::FlagGroup.const_get(type)
|
534
|
-
end
|
535
|
-
group = type.new(name, desc, long_desc)
|
536
|
-
@flag_group_names[name] = group unless name.nil?
|
537
|
-
if prepend
|
538
|
-
@flag_groups.unshift(group)
|
539
|
-
else
|
540
|
-
@flag_groups.push(group)
|
541
|
-
end
|
542
|
-
self
|
543
|
-
end
|
544
|
-
|
545
|
-
##
|
546
|
-
# Add a flag to the current tool. Each flag must specify a key which
|
547
|
-
# the script may use to obtain the flag value from the context.
|
548
|
-
# You may then provide the flags themselves in `OptionParser` form.
|
549
|
-
#
|
550
|
-
# @param [String,Symbol] key The key to use to retrieve the value from
|
551
|
-
# the execution context.
|
552
|
-
# @param [Array<String>] flags The flags in OptionParser format.
|
553
|
-
# @param [Object] accept An acceptor that validates and/or converts the
|
554
|
-
# value. You may provide either the name of an acceptor you have
|
555
|
-
# defined, or one of the default acceptors provided by OptionParser.
|
556
|
-
# Optional. If not specified, accepts any value as a string.
|
557
|
-
# @param [Object] default The default value. This is the value that will
|
558
|
-
# be set in the context if this flag is not provided on the command
|
559
|
-
# line. Defaults to `nil`.
|
560
|
-
# @param [Proc,nil] handler An optional handler for setting/updating the
|
561
|
-
# value. If given, it should take two arguments, the new given value
|
562
|
-
# and the previous value, and it should return the new value that
|
563
|
-
# should be set. The default handler simply replaces the previous
|
564
|
-
# value. i.e. the default is effectively `-> (val, _prev) { val }`.
|
565
|
-
# @param [Boolean] report_collisions Raise an exception if a flag is
|
566
|
-
# requested that is already in use or marked as disabled. Default is
|
567
|
-
# true.
|
568
|
-
# @param [Toys::Definition::FlagGroup,String,Symbol,nil] group Group for
|
569
|
-
# this flag. You may provide a group name, a FlagGroup object, or
|
570
|
-
# `nil` which denotes the default group.
|
571
|
-
# @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
|
572
|
-
# description for the flag. See {Toys::Definition::Tool#desc=} for a
|
573
|
-
# description of allowed formats. Defaults to the empty string.
|
574
|
-
# @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
|
575
|
-
# Long description for the flag. See
|
576
|
-
# {Toys::Definition::Tool#long_desc=} for a description of allowed
|
577
|
-
# formats. Defaults to the empty array.
|
578
|
-
# @param [String] display_name A display name for this flag, used in help
|
579
|
-
# text and error messages.
|
580
|
-
#
|
581
|
-
def add_flag(key, flags = [],
|
582
|
-
accept: nil, default: nil, handler: nil,
|
583
|
-
report_collisions: true, group: nil,
|
584
|
-
desc: nil, long_desc: nil, display_name: nil)
|
585
|
-
unless group.is_a?(Definition::FlagGroup)
|
586
|
-
group_name = group
|
587
|
-
group = @flag_group_names[group_name]
|
588
|
-
raise ToolDefinitionError, "No such flag group: #{group_name.inspect}" if group.nil?
|
589
|
-
end
|
590
|
-
check_definition_state(is_arg: true)
|
591
|
-
accept = resolve_acceptor(accept)
|
592
|
-
flag_def = Definition::Flag.new(key, flags, @used_flags, report_collisions,
|
593
|
-
accept, handler, default, display_name, group)
|
594
|
-
flag_def.desc = desc if desc
|
595
|
-
flag_def.long_desc = long_desc if long_desc
|
596
|
-
if flag_def.active?
|
597
|
-
@flag_definitions << flag_def
|
598
|
-
group << flag_def
|
599
|
-
end
|
600
|
-
@default_data[key] = default
|
601
|
-
self
|
602
|
-
end
|
603
|
-
|
604
|
-
##
|
605
|
-
# Mark one or more flags as disabled, preventing their use by any
|
606
|
-
# subsequent flag definition. This may be used to prevent middleware from
|
607
|
-
# defining a particular flag.
|
608
|
-
#
|
609
|
-
# @param [String...] flags The flags to disable
|
610
|
-
#
|
611
|
-
def disable_flag(*flags)
|
612
|
-
check_definition_state(is_arg: true)
|
613
|
-
flags = flags.uniq
|
614
|
-
intersection = @used_flags & flags
|
615
|
-
unless intersection.empty?
|
616
|
-
raise ToolDefinitionError, "Cannot disable flags already used: #{intersection.inspect}"
|
617
|
-
end
|
618
|
-
@used_flags.concat(flags)
|
619
|
-
self
|
620
|
-
end
|
621
|
-
|
622
|
-
##
|
623
|
-
# Add a required positional argument to the current tool. You must specify
|
624
|
-
# a key which the script may use to obtain the argument value from the
|
625
|
-
# context.
|
626
|
-
#
|
627
|
-
# @param [String,Symbol] key The key to use to retrieve the value from
|
628
|
-
# the execution context.
|
629
|
-
# @param [Object] accept An acceptor that validates and/or converts the
|
630
|
-
# value. You may provide either the name of an acceptor you have
|
631
|
-
# defined, or one of the default acceptors provided by OptionParser.
|
632
|
-
# Optional. If not specified, accepts any value as a string.
|
633
|
-
# @param [String] display_name A name to use for display (in help text and
|
634
|
-
# error reports). Defaults to the key in upper case.
|
635
|
-
# @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
|
636
|
-
# description for the arg. See {Toys::Definition::Tool#desc=} for a
|
637
|
-
# description of allowed formats. Defaults to the empty string.
|
638
|
-
# @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
|
639
|
-
# Long description for the arg. See
|
640
|
-
# {Toys::Definition::Tool#long_desc=} for a description of allowed
|
641
|
-
# formats. Defaults to the empty array.
|
642
|
-
#
|
643
|
-
def add_required_arg(key, accept: nil, display_name: nil, desc: nil, long_desc: nil)
|
644
|
-
check_definition_state(is_arg: true)
|
645
|
-
accept = resolve_acceptor(accept)
|
646
|
-
arg_def = Definition::Arg.new(key, :required, accept, nil, desc, long_desc, display_name)
|
647
|
-
@required_arg_definitions << arg_def
|
648
|
-
self
|
649
|
-
end
|
650
|
-
|
651
|
-
##
|
652
|
-
# Add an optional positional argument to the current tool. You must specify
|
653
|
-
# a key which the script may use to obtain the argument value from the
|
654
|
-
# context. If an optional argument is not given on the command line, the
|
655
|
-
# value is set to the given default.
|
656
|
-
#
|
657
|
-
# @param [String,Symbol] key The key to use to retrieve the value from
|
658
|
-
# the execution context.
|
659
|
-
# @param [Object] default The default value. This is the value that will
|
660
|
-
# be set in the context if this argument is not provided on the command
|
661
|
-
# line. Defaults to `nil`.
|
662
|
-
# @param [Object] accept An acceptor that validates and/or converts the
|
663
|
-
# value. You may provide either the name of an acceptor you have
|
664
|
-
# defined, or one of the default acceptors provided by OptionParser.
|
665
|
-
# Optional. If not specified, accepts any value as a string.
|
666
|
-
# @param [String] display_name A name to use for display (in help text and
|
667
|
-
# error reports). Defaults to the key in upper case.
|
668
|
-
# @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
|
669
|
-
# description for the arg. See {Toys::Definition::Tool#desc=} for a
|
670
|
-
# description of allowed formats. Defaults to the empty string.
|
671
|
-
# @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
|
672
|
-
# Long description for the arg. See
|
673
|
-
# {Toys::Definition::Tool#long_desc=} for a description of allowed
|
674
|
-
# formats. Defaults to the empty array.
|
675
|
-
#
|
676
|
-
def add_optional_arg(key, default: nil, accept: nil, display_name: nil,
|
677
|
-
desc: nil, long_desc: nil)
|
678
|
-
check_definition_state(is_arg: true)
|
679
|
-
accept = resolve_acceptor(accept)
|
680
|
-
arg_def = Definition::Arg.new(key, :optional, accept, default,
|
681
|
-
desc, long_desc, display_name)
|
682
|
-
@optional_arg_definitions << arg_def
|
683
|
-
@default_data[key] = default
|
684
|
-
self
|
685
|
-
end
|
686
|
-
|
687
|
-
##
|
688
|
-
# Specify what should be done with unmatched positional arguments. You must
|
689
|
-
# specify a key which the script may use to obtain the remaining args
|
690
|
-
# from the context.
|
691
|
-
#
|
692
|
-
# @param [String,Symbol] key The key to use to retrieve the value from
|
693
|
-
# the execution context.
|
694
|
-
# @param [Object] default The default value. This is the value that will
|
695
|
-
# be set in the context if no unmatched arguments are provided on the
|
696
|
-
# command line. Defaults to the empty array `[]`.
|
697
|
-
# @param [Object] accept An acceptor that validates and/or converts the
|
698
|
-
# value. You may provide either the name of an acceptor you have
|
699
|
-
# defined, or one of the default acceptors provided by OptionParser.
|
700
|
-
# Optional. If not specified, accepts any value as a string.
|
701
|
-
# @param [String] display_name A name to use for display (in help text and
|
702
|
-
# error reports). Defaults to the key in upper case.
|
703
|
-
# @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
|
704
|
-
# description for the arg. See {Toys::Definition::Tool#desc=} for a
|
705
|
-
# description of allowed formats. Defaults to the empty string.
|
706
|
-
# @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
|
707
|
-
# Long description for the arg. See
|
708
|
-
# {Toys::Definition::Tool#long_desc=} for a description of allowed
|
709
|
-
# formats. Defaults to the empty array.
|
710
|
-
#
|
711
|
-
def set_remaining_args(key, default: [], accept: nil, display_name: nil,
|
712
|
-
desc: nil, long_desc: nil)
|
713
|
-
check_definition_state(is_arg: true)
|
714
|
-
accept = resolve_acceptor(accept)
|
715
|
-
arg_def = Definition::Arg.new(key, :remaining, accept, default,
|
716
|
-
desc, long_desc, display_name)
|
717
|
-
@remaining_args_definition = arg_def
|
718
|
-
@default_data[key] = default
|
719
|
-
self
|
720
|
-
end
|
721
|
-
|
722
|
-
##
|
723
|
-
# Set the runnable block
|
724
|
-
#
|
725
|
-
# @param [Proc] proc The runnable block
|
726
|
-
#
|
727
|
-
def runnable=(proc)
|
728
|
-
@tool_class.to_run(&proc)
|
729
|
-
end
|
730
|
-
|
731
|
-
##
|
732
|
-
# Add an initializer.
|
733
|
-
#
|
734
|
-
# @param [Proc] proc The initializer block
|
735
|
-
# @param [Object...] args Arguments to pass to the initializer
|
736
|
-
#
|
737
|
-
def add_initializer(proc, *args)
|
738
|
-
check_definition_state
|
739
|
-
@initializers << [proc, args]
|
740
|
-
self
|
741
|
-
end
|
742
|
-
|
743
|
-
##
|
744
|
-
# Set the custom context directory.
|
745
|
-
#
|
746
|
-
# @param [String] dir
|
747
|
-
#
|
748
|
-
def custom_context_directory=(dir)
|
749
|
-
check_definition_state
|
750
|
-
@custom_context_directory = dir
|
751
|
-
end
|
752
|
-
|
753
|
-
##
|
754
|
-
# Return the effective context directory.
|
755
|
-
# If there is a custom context directory, uses that. Otherwise, looks for
|
756
|
-
# a custom context directory up the tool ancestor chain. If none is
|
757
|
-
# found, uses the default context directory from the source info. It is
|
758
|
-
# possible for there to be no context directory at all, in which case,
|
759
|
-
# returns nil.
|
760
|
-
#
|
761
|
-
# @return [String,nil]
|
762
|
-
#
|
763
|
-
def context_directory
|
764
|
-
lookup_custom_context_directory || source_info&.context_directory
|
765
|
-
end
|
766
|
-
|
767
|
-
##
|
768
|
-
# Lookup the custom context directory in this tool and its ancestors.
|
769
|
-
# @private
|
770
|
-
#
|
771
|
-
def lookup_custom_context_directory
|
772
|
-
custom_context_directory || @parent&.lookup_custom_context_directory
|
773
|
-
end
|
774
|
-
|
775
|
-
##
|
776
|
-
# Mark this tool as having at least one module included
|
777
|
-
# @private
|
778
|
-
#
|
779
|
-
def mark_includes_modules
|
780
|
-
check_definition_state
|
781
|
-
@includes_modules = true
|
782
|
-
self
|
783
|
-
end
|
784
|
-
|
785
|
-
##
|
786
|
-
# Complete definition and run middleware configs. Should be called from
|
787
|
-
# the Loader only.
|
788
|
-
# @private
|
789
|
-
#
|
790
|
-
def finish_definition(loader)
|
791
|
-
unless @definition_finished
|
792
|
-
ContextualError.capture("Error installing tool middleware!", tool_name: full_name) do
|
793
|
-
config_proc = proc {}
|
794
|
-
middleware_stack.reverse_each do |middleware|
|
795
|
-
config_proc = make_config_proc(middleware, loader, config_proc)
|
796
|
-
end
|
797
|
-
config_proc.call
|
798
|
-
end
|
799
|
-
flag_groups.each do |flag_group|
|
800
|
-
flag_group.flag_definitions.sort_by!(&:sort_str)
|
801
|
-
end
|
802
|
-
@definition_finished = true
|
803
|
-
end
|
804
|
-
self
|
805
|
-
end
|
806
|
-
|
807
|
-
##
|
808
|
-
# Run all initializers against a tool. Should be called from the Runner
|
809
|
-
# only.
|
810
|
-
# @private
|
811
|
-
#
|
812
|
-
def run_initializers(tool)
|
813
|
-
@initializers.each do |func, args|
|
814
|
-
tool.instance_exec(*args, &func)
|
815
|
-
end
|
816
|
-
end
|
817
|
-
|
818
|
-
##
|
819
|
-
# Check that the tool can still be defined. Should be called internally
|
820
|
-
# or from the DSL only.
|
821
|
-
# @private
|
822
|
-
#
|
823
|
-
def check_definition_state(is_arg: false)
|
824
|
-
if @definition_finished
|
825
|
-
raise ToolDefinitionError,
|
826
|
-
"Defintion of tool #{display_name.inspect} is already finished"
|
827
|
-
end
|
828
|
-
if is_arg && argument_parsing_disabled?
|
829
|
-
raise ToolDefinitionError,
|
830
|
-
"Tool #{display_name.inspect} has disabled argument parsing"
|
831
|
-
end
|
832
|
-
self
|
833
|
-
end
|
834
|
-
|
835
|
-
private
|
836
|
-
|
837
|
-
def make_config_proc(middleware, loader, next_config)
|
838
|
-
proc { middleware.config(self, loader, &next_config) }
|
839
|
-
end
|
840
|
-
end
|
841
|
-
end
|
842
|
-
end
|