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
data/lib/toys/middleware.rb
CHANGED
@@ -1,32 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright
|
3
|
+
# Copyright 2019 Daniel Azuma
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
6
11
|
#
|
7
|
-
#
|
8
|
-
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
9
14
|
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
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.
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
+
# IN THE SOFTWARE.
|
30
22
|
;
|
31
23
|
|
32
24
|
module Toys
|
@@ -65,11 +57,11 @@ module Toys
|
|
65
57
|
# This basic implementation does nothing and simply yields to the next
|
66
58
|
# middleware.
|
67
59
|
#
|
68
|
-
# @param [Toys::
|
69
|
-
#
|
70
|
-
# @
|
60
|
+
# @param tool [Toys::Tool] The tool definition to modify.
|
61
|
+
# @param loader [Toys::Loader] The loader that loaded this tool.
|
62
|
+
# @return [void]
|
71
63
|
#
|
72
|
-
def config(
|
64
|
+
def config(tool, loader) # rubocop:disable Lint/UnusedMethodArgument
|
73
65
|
yield
|
74
66
|
end
|
75
67
|
|
@@ -86,14 +78,15 @@ module Toys
|
|
86
78
|
#
|
87
79
|
# Like a tool's `run` method, this method's return value is unused. If
|
88
80
|
# you want to output from a tool, write to stdout or stderr. If you want
|
89
|
-
# to set the exit status code, call {Toys::
|
81
|
+
# to set the exit status code, call {Toys::Context#exit} on the context.
|
90
82
|
#
|
91
83
|
# This basic implementation does nothing and simply yields to the next
|
92
84
|
# middleware.
|
93
85
|
#
|
94
|
-
# @param [Toys::
|
86
|
+
# @param context [Toys::Context] The tool execution context.
|
87
|
+
# @return [void]
|
95
88
|
#
|
96
|
-
def run(
|
89
|
+
def run(context) # rubocop:disable Lint/UnusedMethodArgument
|
97
90
|
yield
|
98
91
|
end
|
99
92
|
end
|
data/lib/toys/mixin.rb
CHANGED
@@ -1,32 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright
|
3
|
+
# Copyright 2019 Daniel Azuma
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
6
11
|
#
|
7
|
-
#
|
8
|
-
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
9
14
|
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
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.
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
+
# IN THE SOFTWARE.
|
30
22
|
;
|
31
23
|
|
32
24
|
module Toys
|
@@ -36,7 +28,7 @@ module Toys
|
|
36
28
|
# A mixin is a collection of methods that are available to be called from a
|
37
29
|
# tool implementation (i.e. its run method). The mixin is added to the tool
|
38
30
|
# class, so it has access to the same methods that can be called by the tool,
|
39
|
-
# such as {Toys::
|
31
|
+
# such as {Toys::Context#get}.
|
40
32
|
#
|
41
33
|
# ## Usage
|
42
34
|
#
|
@@ -44,19 +36,24 @@ module Toys
|
|
44
36
|
# the methods you want to be available.
|
45
37
|
#
|
46
38
|
# If you want to perform some initialization specific to the mixin, you can
|
47
|
-
# provide
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
# the
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
39
|
+
# provide an *initializer* block and/or an *inclusion* block. These can be
|
40
|
+
# specified by calling the module methods defined in
|
41
|
+
# {Toys::Mixin::ModuleMethods}.
|
42
|
+
#
|
43
|
+
# The initializer block is called when the tool context is instantiated
|
44
|
+
# in preparation for execution. It has access to context methods such as
|
45
|
+
# {Toys::Context#get}, and can perform setup for the tool execution itself,
|
46
|
+
# such as initializing some persistent state and storing it in the tool using
|
47
|
+
# {Toys::Context#set}. The initializer block is passed any extra arguments
|
48
|
+
# that were provided to the `include` directive. Define the initializer by
|
49
|
+
# calling {Toys::Mixin::ModuleMethods#on_initialize}.
|
50
|
+
#
|
51
|
+
# The inclusion block is called in the context of your tool class when your
|
52
|
+
# mixin is included. It is also passed any extra arguments that were provided
|
53
|
+
# to the `include` directive. It can be used to issue directives to define
|
54
|
+
# tools or other objects in the DSL, or even enhance the DSL by defining DSL
|
55
|
+
# methods specific to the mixin. Define the inclusion block by calling
|
56
|
+
# {Toys::Mixin::ModuleMethods#on_include}.
|
60
57
|
#
|
61
58
|
# ## Example
|
62
59
|
#
|
@@ -68,14 +65,16 @@ module Toys
|
|
68
65
|
# module MyCounterMixin
|
69
66
|
# include Toys::Mixin
|
70
67
|
#
|
71
|
-
# # Initialize the counter.
|
72
|
-
# #
|
73
|
-
#
|
68
|
+
# # Initialize the counter. Notice that the initializer is evaluated
|
69
|
+
# # in the context of the runtime context, so has access to the runtime
|
70
|
+
# # context state.
|
71
|
+
# on_initialize do |start = 0|
|
74
72
|
# set(:counter_value, start)
|
75
73
|
# end
|
76
74
|
#
|
77
|
-
# # Mixin methods are
|
78
|
-
# # the
|
75
|
+
# # Mixin methods are evaluated in the runtime context and so have
|
76
|
+
# # access to the runtime context state, just as if you had defined
|
77
|
+
# # them in your tool.
|
79
78
|
# def counter_value
|
80
79
|
# get(:counter_value)
|
81
80
|
# end
|
@@ -100,9 +99,23 @@ module Toys
|
|
100
99
|
# end
|
101
100
|
#
|
102
101
|
module Mixin
|
102
|
+
##
|
103
|
+
# Create a mixin module with the given block.
|
104
|
+
#
|
105
|
+
# @param block [Proc] Defines the mixin module.
|
106
|
+
# @return [Class]
|
107
|
+
#
|
108
|
+
def self.create(&block)
|
109
|
+
mixin_mod = ::Module.new do
|
110
|
+
include ::Toys::Mixin
|
111
|
+
end
|
112
|
+
mixin_mod.module_eval(&block) if block
|
113
|
+
mixin_mod
|
114
|
+
end
|
115
|
+
|
103
116
|
## @private
|
104
117
|
def self.included(mod)
|
105
|
-
return if mod.respond_to?(:
|
118
|
+
return if mod.respond_to?(:on_initialize)
|
106
119
|
mod.extend(ModuleMethods)
|
107
120
|
end
|
108
121
|
|
@@ -111,32 +124,50 @@ module Toys
|
|
111
124
|
#
|
112
125
|
module ModuleMethods
|
113
126
|
##
|
114
|
-
#
|
115
|
-
#
|
127
|
+
# Set the initializer for this mixin. This block is evaluated in the
|
128
|
+
# runtime context before execution, and is passed any arguments provided
|
129
|
+
# to the `include` directive. It can perform any runtime initialization
|
130
|
+
# needed by the mixin.
|
131
|
+
#
|
132
|
+
# @param block [Proc] Sets the initializer proc.
|
133
|
+
# @return [self]
|
116
134
|
#
|
117
|
-
def
|
118
|
-
self.
|
135
|
+
def on_initialize(&block)
|
136
|
+
self.initializer = block
|
137
|
+
self
|
119
138
|
end
|
120
139
|
|
121
140
|
##
|
122
|
-
#
|
123
|
-
#
|
141
|
+
# The initializer proc for this mixin. This proc is evaluated in the
|
142
|
+
# runtime context before execution, and is passed any arguments provided
|
143
|
+
# to the `include` directive. It can perform any runtime initialization
|
144
|
+
# needed by the mixin.
|
124
145
|
#
|
125
|
-
|
126
|
-
|
127
|
-
|
146
|
+
# @return [Proc] The iniitiliazer for this mixin.
|
147
|
+
#
|
148
|
+
attr_accessor :initializer
|
128
149
|
|
129
150
|
##
|
130
|
-
#
|
131
|
-
#
|
151
|
+
# Set an inclusion proc for this mixin. This block is evaluated in the
|
152
|
+
# tool class immediately after the mixin is included, and is passed any
|
153
|
+
# arguments provided to the `include` directive.
|
132
154
|
#
|
133
|
-
|
155
|
+
# @param block [Proc] Sets the inclusion proc.
|
156
|
+
# @return [self]
|
157
|
+
#
|
158
|
+
def on_include(&block)
|
159
|
+
self.inclusion = block
|
160
|
+
self
|
161
|
+
end
|
134
162
|
|
135
163
|
##
|
136
|
-
#
|
137
|
-
#
|
164
|
+
# The inclusion proc for this mixin. This block is evaluated in the tool
|
165
|
+
# class immediately after the mixin is included, and is passed any
|
166
|
+
# arguments provided to the `include` directive.
|
167
|
+
#
|
168
|
+
# @return [Proc] The inclusion procedure for this mixin.
|
138
169
|
#
|
139
|
-
attr_accessor :
|
170
|
+
attr_accessor :inclusion
|
140
171
|
end
|
141
172
|
end
|
142
173
|
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2019 Daniel Azuma
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
+
# IN THE SOFTWARE.
|
22
|
+
;
|
23
|
+
|
24
|
+
module Toys
|
25
|
+
##
|
26
|
+
# A helper module that provides methods to do module lookups. This is
|
27
|
+
# used to obtain named helpers, middleware, and templates from the
|
28
|
+
# respective modules.
|
29
|
+
#
|
30
|
+
class ModuleLookup
|
31
|
+
class << self
|
32
|
+
##
|
33
|
+
# Convert the given string to a path element. Specifically, converts
|
34
|
+
# to `lower_snake_case`.
|
35
|
+
#
|
36
|
+
# @param str [String,Symbol] String to convert.
|
37
|
+
# @return [String] Converted string
|
38
|
+
#
|
39
|
+
def to_path_name(str)
|
40
|
+
str = str.to_s.sub(/^_/, "").sub(/_$/, "").gsub(/_+/, "_")
|
41
|
+
while str.sub!(/([^_])([A-Z])/, "\\1_\\2") do end
|
42
|
+
str.downcase
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Convert the given string to a module name. Specifically, converts
|
47
|
+
# to `UpperCamelCase`, and then to a symbol.
|
48
|
+
#
|
49
|
+
# @param str [String,Symbol] String to convert.
|
50
|
+
# @return [Symbol] Converted name
|
51
|
+
#
|
52
|
+
def to_module_name(str)
|
53
|
+
str = str.to_s.sub(/^_/, "").sub(/_$/, "").gsub(/_+/, "_")
|
54
|
+
str.to_s.gsub(/(?:^|_)([a-zA-Z])/) { ::Regexp.last_match(1).upcase }.to_sym
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Given a require path, return the module expected to be defined.
|
59
|
+
#
|
60
|
+
# @param path [String] File path, delimited by forward slash
|
61
|
+
# @return [Module] The module loaded from that path
|
62
|
+
#
|
63
|
+
def path_to_module(path)
|
64
|
+
path.split("/").reduce(::Object) do |running_mod, seg|
|
65
|
+
mod_name = to_module_name(seg)
|
66
|
+
unless running_mod.constants.include?(mod_name)
|
67
|
+
raise ::NameError, "Module #{running_mod.name}::#{mod_name} not found"
|
68
|
+
end
|
69
|
+
running_mod.const_get(mod_name)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Create an empty ModuleLookup
|
76
|
+
#
|
77
|
+
def initialize
|
78
|
+
@paths = []
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Add a lookup path for modules.
|
83
|
+
#
|
84
|
+
# @param path_base [String] The base require path
|
85
|
+
# @param module_base [Module] The base module, or `nil` (the default) to
|
86
|
+
# infer a default from the path base.
|
87
|
+
# @param high_priority [Boolean] If true, add to the head of the lookup
|
88
|
+
# path, otherwise add to the end.
|
89
|
+
# @return [self]
|
90
|
+
#
|
91
|
+
def add_path(path_base, module_base: nil, high_priority: false)
|
92
|
+
module_base ||= ModuleLookup.path_to_module(path_base)
|
93
|
+
if high_priority
|
94
|
+
@paths.unshift([path_base, module_base])
|
95
|
+
else
|
96
|
+
@paths << [path_base, module_base]
|
97
|
+
end
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# Obtain a named module. Returns `nil` if the name is not present.
|
103
|
+
#
|
104
|
+
# @param name [String,Symbol] The name of the module to return.
|
105
|
+
# @return [Module] The specified module
|
106
|
+
#
|
107
|
+
def lookup(name)
|
108
|
+
@paths.each do |path_base, module_base|
|
109
|
+
path = "#{path_base}/#{ModuleLookup.to_path_name(name)}"
|
110
|
+
begin
|
111
|
+
require path
|
112
|
+
rescue ::LoadError
|
113
|
+
next
|
114
|
+
end
|
115
|
+
mod_name = ModuleLookup.to_module_name(name)
|
116
|
+
unless module_base.constants.include?(mod_name)
|
117
|
+
raise ::NameError,
|
118
|
+
"File #{path.inspect} did not define #{module_base.name}::#{mod_name}"
|
119
|
+
end
|
120
|
+
return module_base.const_get(mod_name)
|
121
|
+
end
|
122
|
+
nil
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2019 Daniel Azuma
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
+
# IN THE SOFTWARE.
|
22
|
+
;
|
23
|
+
|
24
|
+
module Toys
|
25
|
+
##
|
26
|
+
# Representation of a formal positional argument
|
27
|
+
#
|
28
|
+
class PositionalArg
|
29
|
+
##
|
30
|
+
# Create a PositionalArg definition.
|
31
|
+
# This argument list is subject to change. Use {Toys::PositionalArg.create}
|
32
|
+
# instead for a more stable interface.
|
33
|
+
# @private
|
34
|
+
#
|
35
|
+
def initialize(key, type, acceptor, default, completion, desc, long_desc, display_name)
|
36
|
+
@key = key
|
37
|
+
@type = type
|
38
|
+
@acceptor = Acceptor.create(acceptor)
|
39
|
+
@default = default
|
40
|
+
@completion = Completion.create(completion)
|
41
|
+
@desc = WrappableString.make(desc)
|
42
|
+
@long_desc = WrappableString.make_array(long_desc)
|
43
|
+
@display_name = display_name || key.to_s.tr("-", "_").gsub(/\W/, "").upcase
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Create a PositionalArg definition.
|
48
|
+
#
|
49
|
+
# @param key [String,Symbol] The key to use to retrieve the value from
|
50
|
+
# the execution context.
|
51
|
+
# @param type [Symbol] The type of arg. Valid values are `:required`,
|
52
|
+
# `:optional`, and `:remaining`.
|
53
|
+
# @param accept [Object] An acceptor that validates and/or converts the
|
54
|
+
# value. See {Toys::Acceptor.create} for recognized formats. Optional.
|
55
|
+
# If not specified, defaults to {Toys::Acceptor::DEFAULT}.
|
56
|
+
# @param complete [Object] A specifier for shell tab completion. See
|
57
|
+
# {Toys::Completion.create} for recognized formats.
|
58
|
+
# @param display_name [String] A name to use for display (in help text and
|
59
|
+
# error reports). Defaults to the key in upper case.
|
60
|
+
# @param desc [String,Array<String>,Toys::WrappableString] Short
|
61
|
+
# description for the flag. See {Toys::DSL::Tool#desc} for a
|
62
|
+
# description of the allowed formats. Defaults to the empty string.
|
63
|
+
# @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
|
64
|
+
# Long description for the flag. See {Toys::DSL::Tool#long_desc} for
|
65
|
+
# a description of the allowed formats. (But note that this param
|
66
|
+
# takes an Array of description lines, rather than a series of
|
67
|
+
# arguments.) Defaults to the empty array.
|
68
|
+
# @return [Toys::PositionalArg]
|
69
|
+
#
|
70
|
+
def self.create(key, type,
|
71
|
+
accept: nil, default: nil, complete: nil, desc: nil,
|
72
|
+
long_desc: nil, display_name: nil)
|
73
|
+
new(key, type, accept, default, complete, desc, long_desc, display_name)
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# The key for this arg.
|
78
|
+
# @return [Symbol]
|
79
|
+
#
|
80
|
+
attr_reader :key
|
81
|
+
|
82
|
+
##
|
83
|
+
# Type of this argument.
|
84
|
+
# @return [:required,:optional,:remaining]
|
85
|
+
#
|
86
|
+
attr_reader :type
|
87
|
+
|
88
|
+
##
|
89
|
+
# The effective acceptor.
|
90
|
+
# @return [Toys::Acceptor::Base]
|
91
|
+
#
|
92
|
+
attr_accessor :acceptor
|
93
|
+
|
94
|
+
##
|
95
|
+
# The default value, which may be `nil`.
|
96
|
+
# @return [Object]
|
97
|
+
#
|
98
|
+
attr_reader :default
|
99
|
+
|
100
|
+
##
|
101
|
+
# The proc that determines shell completions for the value.
|
102
|
+
# @return [Proc,Toys::Completion::Base]
|
103
|
+
#
|
104
|
+
attr_reader :completion
|
105
|
+
|
106
|
+
##
|
107
|
+
# The short description string.
|
108
|
+
#
|
109
|
+
# When reading, this is always returned as a {Toys::WrappableString}.
|
110
|
+
#
|
111
|
+
# When setting, the description may be provided as any of the following:
|
112
|
+
# * A {Toys::WrappableString}.
|
113
|
+
# * A normal String, which will be transformed into a
|
114
|
+
# {Toys::WrappableString} using spaces as word delimiters.
|
115
|
+
# * An Array of String, which will be transformed into a
|
116
|
+
# {Toys::WrappableString} where each array element represents an
|
117
|
+
# individual word for wrapping.
|
118
|
+
#
|
119
|
+
# @return [Toys::WrappableString]
|
120
|
+
#
|
121
|
+
attr_reader :desc
|
122
|
+
|
123
|
+
##
|
124
|
+
# The long description strings.
|
125
|
+
#
|
126
|
+
# When reading, this is returned as an Array of {Toys::WrappableString}
|
127
|
+
# representing the lines in the description.
|
128
|
+
#
|
129
|
+
# When setting, the description must be provided as an Array where *each
|
130
|
+
# element* may be any of the following:
|
131
|
+
# * A {Toys::WrappableString} representing one line.
|
132
|
+
# * A normal String representing a line. This will be transformed into a
|
133
|
+
# {Toys::WrappableString} using spaces as word delimiters.
|
134
|
+
# * An Array of String representing a line. This will be transformed into
|
135
|
+
# a {Toys::WrappableString} where each array element represents an
|
136
|
+
# individual word for wrapping.
|
137
|
+
#
|
138
|
+
# @return [Array<Toys::WrappableString>]
|
139
|
+
#
|
140
|
+
attr_reader :long_desc
|
141
|
+
|
142
|
+
##
|
143
|
+
# The displayable name.
|
144
|
+
# @return [String]
|
145
|
+
#
|
146
|
+
attr_accessor :display_name
|
147
|
+
|
148
|
+
##
|
149
|
+
# Set the short description string.
|
150
|
+
#
|
151
|
+
# See {#desc} for details.
|
152
|
+
#
|
153
|
+
# @param desc [Toys::WrappableString,String,Array<String>]
|
154
|
+
#
|
155
|
+
def desc=(desc)
|
156
|
+
@desc = WrappableString.make(desc)
|
157
|
+
end
|
158
|
+
|
159
|
+
##
|
160
|
+
# Set the long description strings.
|
161
|
+
#
|
162
|
+
# See {#long_desc} for details.
|
163
|
+
#
|
164
|
+
# @param long_desc [Array<Toys::WrappableString,String,Array<String>>]
|
165
|
+
#
|
166
|
+
def long_desc=(long_desc)
|
167
|
+
@long_desc = WrappableString.make_array(long_desc)
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# Append long description strings.
|
172
|
+
#
|
173
|
+
# You must pass an array of lines in the long description. See {#long_desc}
|
174
|
+
# for details on how each line may be represented.
|
175
|
+
#
|
176
|
+
# @param long_desc [Array<Toys::WrappableString,String,Array<String>>]
|
177
|
+
# @return [self]
|
178
|
+
#
|
179
|
+
def append_long_desc(long_desc)
|
180
|
+
@long_desc.concat(WrappableString.make_array(long_desc))
|
181
|
+
self
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|