toys-core 0.3.6 → 0.3.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/lib/toys-core.rb +20 -5
- data/lib/toys/cli.rb +39 -32
- data/lib/toys/core_version.rb +1 -1
- data/lib/toys/{tool → definition}/acceptor.rb +21 -15
- data/lib/toys/{utils/line_output.rb → definition/alias.rb} +47 -59
- data/lib/toys/{tool/arg_definition.rb → definition/arg.rb} +17 -7
- data/lib/toys/{tool/flag_definition.rb → definition/flag.rb} +19 -9
- data/lib/toys/definition/tool.rb +574 -0
- data/lib/toys/dsl/arg.rb +118 -0
- data/lib/toys/dsl/flag.rb +132 -0
- data/lib/toys/dsl/tool.rb +521 -0
- data/lib/toys/errors.rb +2 -2
- data/lib/toys/helpers.rb +3 -3
- data/lib/toys/helpers/exec.rb +31 -25
- data/lib/toys/helpers/fileutils.rb +8 -2
- data/lib/toys/helpers/highline.rb +8 -1
- data/lib/toys/{alias.rb → helpers/terminal.rb} +44 -53
- data/lib/toys/input_file.rb +61 -0
- data/lib/toys/loader.rb +87 -77
- data/lib/toys/middleware.rb +3 -3
- data/lib/toys/middleware/add_verbosity_flags.rb +22 -20
- data/lib/toys/middleware/base.rb +53 -5
- data/lib/toys/middleware/handle_usage_errors.rb +9 -12
- data/lib/toys/middleware/set_default_descriptions.rb +6 -7
- data/lib/toys/middleware/show_help.rb +71 -67
- data/lib/toys/middleware/show_root_version.rb +9 -9
- data/lib/toys/runner.rb +157 -0
- data/lib/toys/template.rb +4 -3
- data/lib/toys/templates.rb +2 -2
- data/lib/toys/templates/clean.rb +2 -2
- data/lib/toys/templates/gem_build.rb +5 -5
- data/lib/toys/templates/minitest.rb +2 -2
- data/lib/toys/templates/rubocop.rb +2 -2
- data/lib/toys/templates/yardoc.rb +2 -2
- data/lib/toys/tool.rb +168 -625
- data/lib/toys/utils/exec.rb +19 -18
- data/lib/toys/utils/gems.rb +140 -0
- data/lib/toys/utils/help_text.rb +25 -20
- data/lib/toys/utils/terminal.rb +412 -0
- data/lib/toys/utils/wrappable_string.rb +3 -1
- metadata +15 -24
- data/lib/toys/config_dsl.rb +0 -699
- data/lib/toys/context.rb +0 -290
- data/lib/toys/helpers/spinner.rb +0 -142
data/lib/toys/dsl/arg.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
# Copyright 2018 Daniel Azuma
|
2
|
+
#
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# * Neither the name of the copyright holder, nor the names of any other
|
14
|
+
# contributors to this software, may be used to endorse or promote products
|
15
|
+
# derived from this software without specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
18
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
21
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
22
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
23
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
24
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
25
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
26
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
27
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
;
|
29
|
+
|
30
|
+
module Toys
|
31
|
+
module DSL
|
32
|
+
##
|
33
|
+
# DSL for an arg definition block. Lets you set arg attributes in a block
|
34
|
+
# instead of a long series of keyword arguments.
|
35
|
+
#
|
36
|
+
class Arg
|
37
|
+
## @private
|
38
|
+
def initialize(accept, default, display_name, desc, long_desc)
|
39
|
+
@accept = accept
|
40
|
+
@default = default
|
41
|
+
@display_name = display_name
|
42
|
+
@desc = desc
|
43
|
+
@long_desc = long_desc
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Set the OptionParser acceptor.
|
48
|
+
# @param [Object] accept
|
49
|
+
#
|
50
|
+
def accept(accept)
|
51
|
+
@accept = accept
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Set the default value.
|
57
|
+
# @param [Object] default
|
58
|
+
#
|
59
|
+
def default(default)
|
60
|
+
@default = default
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# Set the name of this arg as it appears in help screens.
|
66
|
+
# @param [String] display_name
|
67
|
+
#
|
68
|
+
def display_name(display_name)
|
69
|
+
@handler = display_name
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Set the short description. See {Toys::DSL::Tool#desc} for the allowed
|
75
|
+
# formats.
|
76
|
+
#
|
77
|
+
# @param [String,Array<String>,Toys::Utils::WrappableString] desc
|
78
|
+
#
|
79
|
+
def desc(desc)
|
80
|
+
@desc = desc
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# Adds to the long description. This may be called multiple times, and
|
86
|
+
# the results are cumulative. See {Toys::DSL::Tool#long_desc} for the
|
87
|
+
# allowed formats.
|
88
|
+
#
|
89
|
+
# @param [String,Array<String>,Toys::Utils::WrappableString...] long_desc
|
90
|
+
#
|
91
|
+
def long_desc(*long_desc)
|
92
|
+
@long_desc += long_desc
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
## @private
|
97
|
+
def _add_required_to(tool, key)
|
98
|
+
tool.add_required_arg(key,
|
99
|
+
accept: @accept, display_name: @display_name,
|
100
|
+
desc: @desc, long_desc: @long_desc)
|
101
|
+
end
|
102
|
+
|
103
|
+
## @private
|
104
|
+
def _add_optional_to(tool, key)
|
105
|
+
tool.add_optional_arg(key,
|
106
|
+
accept: @accept, default: @default, display_name: @display_name,
|
107
|
+
desc: @desc, long_desc: @long_desc)
|
108
|
+
end
|
109
|
+
|
110
|
+
## @private
|
111
|
+
def _set_remaining_on(tool, key)
|
112
|
+
tool.set_remaining_args(key,
|
113
|
+
accept: @accept, default: @default, display_name: @display_name,
|
114
|
+
desc: @desc, long_desc: @long_desc)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# Copyright 2018 Daniel Azuma
|
2
|
+
#
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# * Neither the name of the copyright holder, nor the names of any other
|
14
|
+
# contributors to this software, may be used to endorse or promote products
|
15
|
+
# derived from this software without specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
18
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
21
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
22
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
23
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
24
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
25
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
26
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
27
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
;
|
29
|
+
|
30
|
+
module Toys
|
31
|
+
module DSL
|
32
|
+
##
|
33
|
+
# DSL for a flag definition block. Lets you set flag attributes in a block
|
34
|
+
# instead of a long series of keyword arguments.
|
35
|
+
#
|
36
|
+
class Flag
|
37
|
+
## @private
|
38
|
+
def initialize(flags, accept, default, handler, report_collisions, desc, long_desc)
|
39
|
+
@flags = flags
|
40
|
+
@accept = accept
|
41
|
+
@default = default
|
42
|
+
@handler = handler
|
43
|
+
@report_collisions = report_collisions
|
44
|
+
@desc = desc
|
45
|
+
@long_desc = long_desc
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Add flags in OptionParser format. This may be called multiple times,
|
50
|
+
# and the results are cumulative.
|
51
|
+
# @param [String...] flags
|
52
|
+
#
|
53
|
+
def flags(*flags)
|
54
|
+
@flags += flags
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Set the OptionParser acceptor.
|
60
|
+
# @param [Object] accept
|
61
|
+
#
|
62
|
+
def accept(accept)
|
63
|
+
@accept = accept
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Set the default value.
|
69
|
+
# @param [Object] default
|
70
|
+
#
|
71
|
+
def default(default)
|
72
|
+
@default = default
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# Set the optional handler for setting/updating the value when a flag is
|
78
|
+
# parsed. It should be a Proc taking two arguments, the new given value
|
79
|
+
# and the previous value, and it should return the new value that should
|
80
|
+
# be set.
|
81
|
+
#
|
82
|
+
# @param [Proc] handler
|
83
|
+
#
|
84
|
+
def handler(handler)
|
85
|
+
@handler = handler
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# Set whether to raise an exception if a flag is requested that is
|
91
|
+
# already in use or marked as disabled.
|
92
|
+
#
|
93
|
+
# @param [Boolean] setting
|
94
|
+
#
|
95
|
+
def report_collisions(setting)
|
96
|
+
@report_collisions = setting
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# Set the short description. See {Toys::DSL::Tool#desc} for the allowed
|
102
|
+
# formats.
|
103
|
+
#
|
104
|
+
# @param [String,Array<String>,Toys::Utils::WrappableString] desc
|
105
|
+
#
|
106
|
+
def desc(desc)
|
107
|
+
@desc = desc
|
108
|
+
self
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Adds to the long description. This may be called multiple times, and
|
113
|
+
# the results are cumulative. See {Toys::DSL::Tool#long_desc} for the
|
114
|
+
# allowed formats.
|
115
|
+
#
|
116
|
+
# @param [String,Array<String>,Toys::Utils::WrappableString...] long_desc
|
117
|
+
#
|
118
|
+
def long_desc(*long_desc)
|
119
|
+
@long_desc += long_desc
|
120
|
+
self
|
121
|
+
end
|
122
|
+
|
123
|
+
## @private
|
124
|
+
def _add_to(tool, key)
|
125
|
+
tool.add_flag(key, @flags,
|
126
|
+
accept: @accept, default: @default, handler: @handler,
|
127
|
+
report_collisions: @report_collisions,
|
128
|
+
desc: @desc, long_desc: @long_desc)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,521 @@
|
|
1
|
+
# Copyright 2018 Daniel Azuma
|
2
|
+
#
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# * Neither the name of the copyright holder, nor the names of any other
|
14
|
+
# contributors to this software, may be used to endorse or promote products
|
15
|
+
# derived from this software without specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
18
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
21
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
22
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
23
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
24
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
25
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
26
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
27
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
;
|
29
|
+
|
30
|
+
module Toys
|
31
|
+
module DSL
|
32
|
+
##
|
33
|
+
# This class defines the DSL for a toys configuration file.
|
34
|
+
#
|
35
|
+
# A toys configuration defines one or more named tools. It provides syntax
|
36
|
+
# for setting the description, defining flags and arguments, specifying
|
37
|
+
# how to execute the tool, and requesting helper modules and other services.
|
38
|
+
# It also lets you define subtools, nested arbitrarily deep, using blocks.
|
39
|
+
#
|
40
|
+
# Generally the DSL is invoked from the {Loader}. Applications should not
|
41
|
+
# need to create instances of DSL::Tool directly.
|
42
|
+
#
|
43
|
+
# ## Simple example
|
44
|
+
#
|
45
|
+
# Create a file called `.toys.rb` in the current directory, with the
|
46
|
+
# following contents:
|
47
|
+
#
|
48
|
+
# tool "greet" do
|
49
|
+
# desc "Prints a simple greeting"
|
50
|
+
#
|
51
|
+
# optional_arg :recipient, default: "world"
|
52
|
+
#
|
53
|
+
# script do
|
54
|
+
# puts "Hello, #{self[:recipient]}!"
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# Now you can execute it using:
|
59
|
+
#
|
60
|
+
# toys greet
|
61
|
+
#
|
62
|
+
# or try:
|
63
|
+
#
|
64
|
+
# toys greet rubyists
|
65
|
+
#
|
66
|
+
module Tool
|
67
|
+
## @private
|
68
|
+
def method_added(meth)
|
69
|
+
cur_tool = DSL::Tool.activate_tool(self)
|
70
|
+
cur_tool.mark_runnable if cur_tool && meth == :run
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Create an acceptor that can be passed into a flag or arg. An acceptor
|
75
|
+
# validates and/or converts a string parameter to a Ruby object. This
|
76
|
+
# acceptor may, for the current tool, be referenced by the name you provide
|
77
|
+
# when you create a flag or arg.
|
78
|
+
#
|
79
|
+
# An acceptor contains a validator, which parses and validates the string
|
80
|
+
# syntax of an argument, and a converter, which takes the validation
|
81
|
+
# results and returns a final value for the context data.
|
82
|
+
#
|
83
|
+
# The validator may be either a regular expression or a list of valid
|
84
|
+
# inputs.
|
85
|
+
#
|
86
|
+
# If the validator is a regular expression, it is matched against the
|
87
|
+
# argument string and succeeds only if the expression covers the *entire*
|
88
|
+
# string. The elements of the MatchData (i.e. the string matched, plus any
|
89
|
+
# captures) are then passed into the conversion function.
|
90
|
+
#
|
91
|
+
# If the validator is an array, the *string form* of the array elements
|
92
|
+
# (i.e. the results of calling to_s on each element) are considered the
|
93
|
+
# valid values for the argument. This is useful for enums, for example.
|
94
|
+
# In this case, the input is converted to the original array element, and
|
95
|
+
# any converter function you provide is ignored.
|
96
|
+
#
|
97
|
+
# If you provide no validator, then no validation takes place and all
|
98
|
+
# argument strings are considered valid. The string itself is passed on to
|
99
|
+
# the converter.
|
100
|
+
#
|
101
|
+
# The converter should be a proc that takes as its arguments the results
|
102
|
+
# of validation. For example, if you use a regular expression validator,
|
103
|
+
# the converter should take a series of strings arguments, the first of
|
104
|
+
# which is the full input string, and the rest of which are captures.
|
105
|
+
# If you provide no converter, no conversion is done and the input string
|
106
|
+
# is considered the final value. You may also provide the converter as a
|
107
|
+
# block.
|
108
|
+
#
|
109
|
+
# @param [String] name The acceptor name.
|
110
|
+
# @param [Regexp,Array,nil] validator The validator.
|
111
|
+
# @param [Proc,nil] converter The validator.
|
112
|
+
#
|
113
|
+
def acceptor(name, validator = nil, converter = nil, &block)
|
114
|
+
cur_tool = DSL::Tool.activate_tool(self)
|
115
|
+
return self if cur_tool.nil?
|
116
|
+
accept =
|
117
|
+
case validator
|
118
|
+
when ::Regexp
|
119
|
+
Definition::PatternAcceptor.new(name, validator, converter, &block)
|
120
|
+
when ::Array
|
121
|
+
Definition::EnumAcceptor.new(name, validator)
|
122
|
+
when nil
|
123
|
+
Definition::Acceptor.new(name, converter, &block)
|
124
|
+
else
|
125
|
+
raise ToolDefinitionError, "Illegal validator: #{validator.inspect}"
|
126
|
+
end
|
127
|
+
cur_tool.add_acceptor(accept)
|
128
|
+
self
|
129
|
+
end
|
130
|
+
|
131
|
+
##
|
132
|
+
# Create a named helper module.
|
133
|
+
# This module may be included by name in this tool or any subtool.
|
134
|
+
#
|
135
|
+
# You should pass a block and define methods in that block.
|
136
|
+
#
|
137
|
+
# @param [String] name Name of the helper
|
138
|
+
#
|
139
|
+
def helper(name, &block)
|
140
|
+
cur_tool = DSL::Tool.activate_tool(self)
|
141
|
+
cur_tool.add_helper(name, ::Module.new(&block)) if cur_tool
|
142
|
+
self
|
143
|
+
end
|
144
|
+
|
145
|
+
##
|
146
|
+
# Create a subtool. You must provide a block defining the subtool.
|
147
|
+
#
|
148
|
+
# If the subtool is already defined (either as a tool or a namespace), the
|
149
|
+
# old definition is discarded and replaced with the new definition.
|
150
|
+
#
|
151
|
+
# @param [String] word The name of the subtool
|
152
|
+
#
|
153
|
+
def tool(word, &block)
|
154
|
+
word = word.to_s
|
155
|
+
subtool_words = @__words + [word]
|
156
|
+
next_remaining = Loader.next_remaining_words(@__remaining_words, word)
|
157
|
+
subtool_class = @__loader.get_tool_definition(subtool_words, @__priority).tool_class
|
158
|
+
DSL::Tool.prepare(subtool_class, next_remaining, @__path) do
|
159
|
+
subtool_class.class_eval(&block)
|
160
|
+
end
|
161
|
+
self
|
162
|
+
end
|
163
|
+
alias name tool
|
164
|
+
|
165
|
+
##
|
166
|
+
# Create an alias in the current namespace.
|
167
|
+
#
|
168
|
+
# @param [String] word The name of the alias
|
169
|
+
# @param [String] target The target of the alias
|
170
|
+
#
|
171
|
+
def alias_tool(word, target)
|
172
|
+
@__loader.make_alias(@__words + [word.to_s], @__words + [target.to_s], @__priority)
|
173
|
+
self
|
174
|
+
end
|
175
|
+
|
176
|
+
##
|
177
|
+
# Create an alias of the current tool.
|
178
|
+
#
|
179
|
+
# @param [String] word The name of the alias
|
180
|
+
#
|
181
|
+
def alias_as(word)
|
182
|
+
if @__words.empty?
|
183
|
+
raise ToolDefinitionError, "Cannot make an alias of the root."
|
184
|
+
end
|
185
|
+
@__loader.make_alias(@__words[0..-2] + [word.to_s], @__words, @__priority)
|
186
|
+
self
|
187
|
+
end
|
188
|
+
|
189
|
+
##
|
190
|
+
# Include another config file or directory at the current location.
|
191
|
+
#
|
192
|
+
# @param [String] path The file or directory to include.
|
193
|
+
#
|
194
|
+
def load(path)
|
195
|
+
@__loader.include_path(path, @__words, @__remaining_words, @__priority)
|
196
|
+
self
|
197
|
+
end
|
198
|
+
|
199
|
+
##
|
200
|
+
# Expand the given template in the current location.
|
201
|
+
#
|
202
|
+
# The template may be specified as a class or a well-known template name.
|
203
|
+
# You may also provide arguments to pass to the template.
|
204
|
+
#
|
205
|
+
# @param [Class,String,Symbol] template_class The template, either as a
|
206
|
+
# class or a well-known name.
|
207
|
+
# @param [Object...] args Template arguments
|
208
|
+
#
|
209
|
+
def expand(template_class, *args)
|
210
|
+
unless template_class.is_a?(::Class)
|
211
|
+
name = template_class.to_s
|
212
|
+
template_class = Templates.lookup!(name)
|
213
|
+
end
|
214
|
+
template = template_class.new(*args)
|
215
|
+
yield template if block_given?
|
216
|
+
class_exec(template, &template_class.expander)
|
217
|
+
self
|
218
|
+
end
|
219
|
+
|
220
|
+
##
|
221
|
+
# Set the short description for the current tool. The short description is
|
222
|
+
# displayed with the tool in a subtool list. You may also use the
|
223
|
+
# equivalent method `short_desc`.
|
224
|
+
#
|
225
|
+
# The description is a {Toys::Utils::WrappableString}, which may be word-
|
226
|
+
# wrapped when displayed in a help screen. You may pass a
|
227
|
+
# {Toys::Utils::WrappableString} directly to this method, or you may pass
|
228
|
+
# any input that can be used to construct a wrappable string:
|
229
|
+
#
|
230
|
+
# * If you pass a String, its whitespace will be compacted (i.e. tabs,
|
231
|
+
# newlines, and multiple consecutive whitespace will be turned into a
|
232
|
+
# single space), and it will be word-wrapped on whitespace.
|
233
|
+
# * If you pass an Array of Strings, each string will be considered a
|
234
|
+
# literal word that cannot be broken, and wrapping will be done across
|
235
|
+
# the strings in the array. In this case, whitespace is not compacted.
|
236
|
+
#
|
237
|
+
# For example, if you pass in a sentence as a simple string, it may be
|
238
|
+
# word wrapped when displayed:
|
239
|
+
#
|
240
|
+
# desc "This sentence may be wrapped."
|
241
|
+
#
|
242
|
+
# To specify a sentence that should never be word-wrapped, pass it as the
|
243
|
+
# sole element of a string array:
|
244
|
+
#
|
245
|
+
# desc ["This sentence will not be wrapped."]
|
246
|
+
#
|
247
|
+
# @param [Toys::Utils::WrappableString,String,Array<String>] str
|
248
|
+
#
|
249
|
+
def desc(str)
|
250
|
+
cur_tool = DSL::Tool.activate_tool(self)
|
251
|
+
cur_tool.desc = str if cur_tool
|
252
|
+
self
|
253
|
+
end
|
254
|
+
alias short_desc desc
|
255
|
+
|
256
|
+
##
|
257
|
+
# Set the long description for the current tool. The long description is
|
258
|
+
# displayed in the usage documentation for the tool itself.
|
259
|
+
#
|
260
|
+
# A long description is a series of descriptions, which are generally
|
261
|
+
# displayed in a series of lines/paragraphs. Each individual description
|
262
|
+
# uses the form described in the {Toys::DSL::Tool#desc} documentation, and
|
263
|
+
# may be word-wrapped when displayed. To insert a blank line, include an
|
264
|
+
# empty string as one of the descriptions.
|
265
|
+
#
|
266
|
+
# Example:
|
267
|
+
#
|
268
|
+
# long_desc "This is an initial paragraph that might be word wrapped.",
|
269
|
+
# "This next paragraph is followed by a blank line.",
|
270
|
+
# "",
|
271
|
+
# ["This line will not be wrapped."]
|
272
|
+
#
|
273
|
+
# @param [Toys::Utils::WrappableString,String,Array<String>...] strs
|
274
|
+
#
|
275
|
+
def long_desc(*strs)
|
276
|
+
cur_tool = DSL::Tool.activate_tool(self)
|
277
|
+
cur_tool.long_desc = strs if cur_tool
|
278
|
+
self
|
279
|
+
end
|
280
|
+
|
281
|
+
##
|
282
|
+
# Add a flag to the current tool. Each flag must specify a key which
|
283
|
+
# the script may use to obtain the flag value from the context.
|
284
|
+
# You may then provide the flags themselves in `OptionParser` form.
|
285
|
+
#
|
286
|
+
# Attributes of the flag may be passed in as arguments to this method, or
|
287
|
+
# set in a block passed to this method.
|
288
|
+
#
|
289
|
+
# @param [Symbol] key The key to use to retrieve the value from the
|
290
|
+
# execution context.
|
291
|
+
# @param [String...] flags The flags in OptionParser format.
|
292
|
+
# @param [Object] accept An acceptor that validates and/or converts the
|
293
|
+
# value. You may provide either the name of an acceptor you have
|
294
|
+
# defined, or one of the default acceptors provided by OptionParser.
|
295
|
+
# Optional. If not specified, accepts any value as a string.
|
296
|
+
# @param [Object] default The default value. This is the value that will
|
297
|
+
# be set in the context if this flag is not provided on the command
|
298
|
+
# line. Defaults to `nil`.
|
299
|
+
# @param [Proc,nil] handler An optional handler for setting/updating the
|
300
|
+
# value. If given, it should take two arguments, the new given value
|
301
|
+
# and the previous value, and it should return the new value that
|
302
|
+
# should be set. The default handler simply replaces the previous
|
303
|
+
# value. i.e. the default is effectively `-> (val, _prev) { val }`.
|
304
|
+
# @param [Boolean] report_collisions Raise an exception if a flag is
|
305
|
+
# requested that is already in use or marked as unusable. Default is
|
306
|
+
# true.
|
307
|
+
# @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
|
308
|
+
# description for the flag. See {Toys::DSL::Tool#desc} for a
|
309
|
+
# description of the allowed formats. Defaults to the empty string.
|
310
|
+
# @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
|
311
|
+
# Long description for the flag. See {Toys::DSL::Tool#long_desc} for
|
312
|
+
# a description of the allowed formats. (But note that this param
|
313
|
+
# takes an Array of description lines, rather than a series of
|
314
|
+
# arguments.) Defaults to the empty array.
|
315
|
+
# @yieldparam flag_dsl [Toys::DSL::Flag] An object that lets you
|
316
|
+
# configure this flag in a block.
|
317
|
+
#
|
318
|
+
def flag(key, *flags,
|
319
|
+
accept: nil, default: nil, handler: nil,
|
320
|
+
report_collisions: true,
|
321
|
+
desc: nil, long_desc: nil)
|
322
|
+
cur_tool = DSL::Tool.activate_tool(self)
|
323
|
+
return self if cur_tool.nil?
|
324
|
+
flag_dsl = DSL::Flag.new(flags, accept, default, handler, report_collisions,
|
325
|
+
desc, long_desc)
|
326
|
+
yield flag_dsl if block_given?
|
327
|
+
flag_dsl._add_to(cur_tool, key)
|
328
|
+
self
|
329
|
+
end
|
330
|
+
|
331
|
+
##
|
332
|
+
# Add a required positional argument to the current tool. You must specify
|
333
|
+
# a key which the script may use to obtain the argument value from the
|
334
|
+
# context.
|
335
|
+
#
|
336
|
+
# @param [Symbol] key The key to use to retrieve the value from the
|
337
|
+
# execution context.
|
338
|
+
# @param [Object] accept An acceptor that validates and/or converts the
|
339
|
+
# value. You may provide either the name of an acceptor you have
|
340
|
+
# defined, or one of the default acceptors provided by OptionParser.
|
341
|
+
# Optional. If not specified, accepts any value as a string.
|
342
|
+
# @param [String] display_name A name to use for display (in help text and
|
343
|
+
# error reports). Defaults to the key in upper case.
|
344
|
+
# @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
|
345
|
+
# description for the flag. See {Toys::DSL::Tool#desc} for a
|
346
|
+
# description of the allowed formats. Defaults to the empty string.
|
347
|
+
# @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
|
348
|
+
# Long description for the flag. See {Toys::DSL::Tool#long_desc} for
|
349
|
+
# a description of the allowed formats. (But note that this param
|
350
|
+
# takes an Array of description lines, rather than a series of
|
351
|
+
# arguments.) Defaults to the empty array.
|
352
|
+
# @yieldparam arg_dsl [Toys::DSL::Arg] An object that lets you configure
|
353
|
+
# this argument in a block.
|
354
|
+
#
|
355
|
+
def required_arg(key, accept: nil, display_name: nil, desc: nil, long_desc: nil)
|
356
|
+
cur_tool = DSL::Tool.activate_tool(self)
|
357
|
+
return self if cur_tool.nil?
|
358
|
+
arg_dsl = DSL::Arg.new(accept, nil, display_name, desc, long_desc)
|
359
|
+
yield arg_dsl if block_given?
|
360
|
+
arg_dsl._add_required_to(cur_tool, key)
|
361
|
+
self
|
362
|
+
end
|
363
|
+
alias required required_arg
|
364
|
+
|
365
|
+
##
|
366
|
+
# Add an optional positional argument to the current tool. You must specify
|
367
|
+
# a key which the script may use to obtain the argument value from the
|
368
|
+
# context. If an optional argument is not given on the command line, the
|
369
|
+
# value is set to the given default.
|
370
|
+
#
|
371
|
+
# @param [Symbol] key The key to use to retrieve the value from the
|
372
|
+
# execution context.
|
373
|
+
# @param [Object] default The default value. This is the value that will
|
374
|
+
# be set in the context if this argument is not provided on the command
|
375
|
+
# line. Defaults to `nil`.
|
376
|
+
# @param [Object] accept An acceptor that validates and/or converts the
|
377
|
+
# value. You may provide either the name of an acceptor you have
|
378
|
+
# defined, or one of the default acceptors provided by OptionParser.
|
379
|
+
# Optional. If not specified, accepts any value as a string.
|
380
|
+
# @param [String] display_name A name to use for display (in help text and
|
381
|
+
# error reports). Defaults to the key in upper case.
|
382
|
+
# @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
|
383
|
+
# description for the flag. See {Toys::DSL::Tool#desc} for a
|
384
|
+
# description of the allowed formats. Defaults to the empty string.
|
385
|
+
# @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
|
386
|
+
# Long description for the flag. See {Toys::DSL::Tool#long_desc} for
|
387
|
+
# a description of the allowed formats. (But note that this param
|
388
|
+
# takes an Array of description lines, rather than a series of
|
389
|
+
# arguments.) Defaults to the empty array.
|
390
|
+
# @yieldparam arg_dsl [Toys::DSL::Arg] An object that lets you configure
|
391
|
+
# this argument in a block.
|
392
|
+
#
|
393
|
+
def optional_arg(key, default: nil, accept: nil, display_name: nil,
|
394
|
+
desc: nil, long_desc: nil)
|
395
|
+
cur_tool = DSL::Tool.activate_tool(self)
|
396
|
+
return self if cur_tool.nil?
|
397
|
+
arg_dsl = DSL::Arg.new(accept, default, display_name, desc, long_desc)
|
398
|
+
yield arg_dsl if block_given?
|
399
|
+
arg_dsl._add_optional_to(cur_tool, key)
|
400
|
+
self
|
401
|
+
end
|
402
|
+
alias optional optional_arg
|
403
|
+
|
404
|
+
##
|
405
|
+
# Specify what should be done with unmatched positional arguments. You must
|
406
|
+
# specify a key which the script may use to obtain the remaining args from
|
407
|
+
# the context.
|
408
|
+
#
|
409
|
+
# @param [Symbol] key The key to use to retrieve the value from the
|
410
|
+
# execution context.
|
411
|
+
# @param [Object] default The default value. This is the value that will
|
412
|
+
# be set in the context if no unmatched arguments are provided on the
|
413
|
+
# command line. Defaults to the empty array `[]`.
|
414
|
+
# @param [Object] accept An acceptor that validates and/or converts the
|
415
|
+
# value. You may provide either the name of an acceptor you have
|
416
|
+
# defined, or one of the default acceptors provided by OptionParser.
|
417
|
+
# Optional. If not specified, accepts any value as a string.
|
418
|
+
# @param [String] display_name A name to use for display (in help text and
|
419
|
+
# error reports). Defaults to the key in upper case.
|
420
|
+
# @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
|
421
|
+
# description for the flag. See {Toys::DSL::Tool#desc} for a
|
422
|
+
# description of the allowed formats. Defaults to the empty string.
|
423
|
+
# @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
|
424
|
+
# Long description for the flag. See {Toys::DSL::Tool#long_desc} for
|
425
|
+
# a description of the allowed formats. (But note that this param
|
426
|
+
# takes an Array of description lines, rather than a series of
|
427
|
+
# arguments.) Defaults to the empty array.
|
428
|
+
# @yieldparam arg_dsl [Toys::DSL::Arg] An object that lets you configure
|
429
|
+
# this argument in a block.
|
430
|
+
#
|
431
|
+
def remaining_args(key, default: [], accept: nil, display_name: nil,
|
432
|
+
desc: nil, long_desc: nil)
|
433
|
+
cur_tool = DSL::Tool.activate_tool(self)
|
434
|
+
return self if cur_tool.nil?
|
435
|
+
arg_dsl = DSL::Arg.new(accept, default, display_name, desc, long_desc)
|
436
|
+
yield arg_dsl if block_given?
|
437
|
+
arg_dsl._set_remaining_on(cur_tool, key)
|
438
|
+
self
|
439
|
+
end
|
440
|
+
alias remaining remaining_args
|
441
|
+
|
442
|
+
##
|
443
|
+
# Specify how to run this tool. You may do this by providing a block to
|
444
|
+
# this directive, or by defining the `run` method in the tool.
|
445
|
+
#
|
446
|
+
def run(&block)
|
447
|
+
define_method(:run, &block)
|
448
|
+
self
|
449
|
+
end
|
450
|
+
|
451
|
+
##
|
452
|
+
# Specify that the given module should be mixed into this tool, and its
|
453
|
+
# methods made available when running the tool.
|
454
|
+
#
|
455
|
+
# You may provide either a module, the string name of a helper that you
|
456
|
+
# have defined in this tool or one of its ancestors, or the symbol name
|
457
|
+
# of a well-known helper.
|
458
|
+
#
|
459
|
+
# @param [Module,Symbol,String] mod Module or module name.
|
460
|
+
#
|
461
|
+
def include(mod)
|
462
|
+
cur_tool = DSL::Tool.activate_tool(self)
|
463
|
+
return if cur_tool.nil?
|
464
|
+
name = mod.to_s
|
465
|
+
if mod.is_a?(::String)
|
466
|
+
mod = cur_tool.resolve_helper(mod)
|
467
|
+
elsif mod.is_a?(::Symbol)
|
468
|
+
mod = Helpers.lookup!(name)
|
469
|
+
end
|
470
|
+
if mod.nil?
|
471
|
+
raise ToolDefinitionError, "Module not found: #{name.inspect}"
|
472
|
+
end
|
473
|
+
super(mod)
|
474
|
+
end
|
475
|
+
|
476
|
+
## @private
|
477
|
+
def self.new_class(words, priority, loader)
|
478
|
+
tool_class = ::Class.new(::Toys::Tool)
|
479
|
+
tool_class.extend(DSL::Tool)
|
480
|
+
tool_class.instance_variable_set(:@__words, words)
|
481
|
+
tool_class.instance_variable_set(:@__priority, priority)
|
482
|
+
tool_class.instance_variable_set(:@__loader, loader)
|
483
|
+
tool_class.instance_variable_set(:@__remaining_words, nil)
|
484
|
+
tool_class.instance_variable_set(:@__path, nil)
|
485
|
+
tool_class
|
486
|
+
end
|
487
|
+
|
488
|
+
## @private
|
489
|
+
def self.activate_tool(tool_class)
|
490
|
+
path = tool_class.instance_variable_get(:@__path)
|
491
|
+
cur_tool =
|
492
|
+
if tool_class.instance_variable_defined?(:@__cur_tool)
|
493
|
+
tool_class.instance_variable_get(:@__cur_tool)
|
494
|
+
else
|
495
|
+
loader = tool_class.instance_variable_get(:@__loader)
|
496
|
+
words = tool_class.instance_variable_get(:@__words)
|
497
|
+
priority = tool_class.instance_variable_get(:@__priority)
|
498
|
+
cur_tool = loader.activate_tool_definition(words, priority)
|
499
|
+
if cur_tool.is_a?(Definition::Alias)
|
500
|
+
raise ToolDefinitionError,
|
501
|
+
"Cannot configure #{words.join(' ').inspect} because it is an alias"
|
502
|
+
end
|
503
|
+
tool_class.instance_variable_set(:@__cur_tool, cur_tool)
|
504
|
+
cur_tool
|
505
|
+
end
|
506
|
+
cur_tool.lock_source_path(path) if cur_tool
|
507
|
+
cur_tool
|
508
|
+
end
|
509
|
+
|
510
|
+
## @private
|
511
|
+
def self.prepare(tool_class, remaining_words, path)
|
512
|
+
tool_class.instance_variable_set(:@__remaining_words, remaining_words)
|
513
|
+
tool_class.instance_variable_set(:@__path, path)
|
514
|
+
yield
|
515
|
+
ensure
|
516
|
+
tool_class.instance_variable_set(:@__remaining_words, nil)
|
517
|
+
tool_class.instance_variable_set(:@__path, nil)
|
518
|
+
end
|
519
|
+
end
|
520
|
+
end
|
521
|
+
end
|