toys 0.3.0 → 0.3.1
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/.yardopts +11 -0
- data/README.md +5 -2
- data/lib/toys.rb +2 -2
- data/lib/toys/builtins/system.rb +4 -0
- data/lib/toys/cli.rb +90 -3
- data/lib/toys/config_dsl.rb +432 -0
- data/lib/toys/context.rb +99 -0
- data/lib/toys/helpers.rb +11 -0
- data/lib/toys/helpers/exec.rb +180 -2
- data/lib/toys/loader.rb +165 -27
- data/lib/toys/middleware.rb +14 -0
- data/lib/toys/middleware/base.rb +7 -1
- data/lib/toys/middleware/set_verbosity.rb +3 -0
- data/lib/toys/middleware/{group_default.rb → show_group_usage.rb} +16 -5
- data/lib/toys/middleware/{show_tool_help.rb → show_tool_usage.rb} +8 -1
- data/lib/toys/middleware/show_usage_errors.rb +6 -0
- data/lib/toys/template.rb +73 -0
- data/lib/toys/templates.rb +14 -0
- data/lib/toys/templates/clean.rb +13 -2
- data/lib/toys/templates/gem_build.rb +19 -1
- data/lib/toys/templates/minitest.rb +17 -2
- data/lib/toys/templates/rubocop.rb +14 -1
- data/lib/toys/templates/yardoc.rb +15 -1
- data/lib/toys/tool.rb +405 -77
- data/lib/toys/utils/usage.rb +55 -11
- data/lib/toys/version.rb +1 -1
- metadata +6 -5
- data/lib/toys/builder.rb +0 -227
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1dcbd6fabf227072fda60d4acebcc09ae1a8af3d10db580bcea3d4a56a6623fd
|
4
|
+
data.tar.gz: bdc7a717793c190af3cb0c470cfbd97078779478e1528297397303c88af1b6a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74b91bdd53c8ca7aec7ba1b1d5ed9d98e2f3a826846ea9d41975391d504e14047d25f6db9551318568fabb81194dd3bf896183316a50e2fe63f92a4e0811ee9c
|
7
|
+
data.tar.gz: 81617b1a8aff51c05cae72cc79e4fffd5030af0176f50f389e3e235334161efbbb9f6ca60e5273f15a9a00cf76bbbfc675aec0a906c87110991e760c9ff1e2da
|
data/.yardopts
ADDED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Toys
|
2
2
|
|
3
|
+
[](https://travis-ci.org/dazuma/toys/)
|
4
|
+
|
3
5
|
Toys is a Ruby library and command line tool that lets you build your own
|
4
6
|
command line suite of tools (with commands and subcommands) using a Ruby DSL.
|
5
7
|
You can define commands globally or configure special commands scoped to
|
@@ -29,11 +31,12 @@ The source can be found on Github at
|
|
29
31
|
|
30
32
|
### TODO items
|
31
33
|
|
32
|
-
* Document methods
|
33
|
-
* Write overall documentation
|
34
34
|
* Decide about highline integration
|
35
35
|
* Output formats middleware
|
36
36
|
* System paths tool
|
37
|
+
* Search function in group help
|
38
|
+
* Split out toys-core gem
|
39
|
+
* Write overall documentation
|
37
40
|
|
38
41
|
## License
|
39
42
|
|
data/lib/toys.rb
CHANGED
@@ -35,13 +35,13 @@
|
|
35
35
|
#
|
36
36
|
module Toys
|
37
37
|
##
|
38
|
-
# Namespace for common utility classes
|
38
|
+
# Namespace for common utility classes.
|
39
39
|
#
|
40
40
|
module Utils; end
|
41
41
|
end
|
42
42
|
|
43
|
-
require "toys/builder"
|
44
43
|
require "toys/cli"
|
44
|
+
require "toys/config_dsl"
|
45
45
|
require "toys/context"
|
46
46
|
require "toys/errors"
|
47
47
|
require "toys/helpers"
|
data/lib/toys/builtins/system.rb
CHANGED
@@ -43,6 +43,7 @@ tool "update" do
|
|
43
43
|
use :exec
|
44
44
|
|
45
45
|
execute do
|
46
|
+
logger.info "Checking rubygems for the latest toys release..."
|
46
47
|
version_info = capture("gem query -q -r -e toys")
|
47
48
|
if version_info =~ /toys\s\((.+)\)/
|
48
49
|
latest_version = ::Gem::Version.new($1)
|
@@ -50,6 +51,9 @@ tool "update" do
|
|
50
51
|
if latest_version > cur_version
|
51
52
|
logger.warn("Updating toys from #{cur_version} to #{latest_version}...")
|
52
53
|
sh("gem install toys")
|
54
|
+
elsif latest_version < cur_version
|
55
|
+
logger.warn("Toys is already at experimental version #{cur_version}, which is later than" \
|
56
|
+
" the latest released version #{latest_version}")
|
53
57
|
else
|
54
58
|
logger.warn("Toys is already at the latest version: #{latest_version}")
|
55
59
|
end
|
data/lib/toys/cli.rb
CHANGED
@@ -31,7 +31,9 @@ require "logger"
|
|
31
31
|
|
32
32
|
module Toys
|
33
33
|
##
|
34
|
-
# A Toys-based CLI
|
34
|
+
# A Toys-based CLI.
|
35
|
+
#
|
36
|
+
# Use this class to implement a CLI using Toys.
|
35
37
|
#
|
36
38
|
class CLI
|
37
39
|
##
|
@@ -75,6 +77,38 @@ module Toys
|
|
75
77
|
" globally or scoped to specific directories that you choose." \
|
76
78
|
" For detailed information, see https://www.rubydoc.info/gems/toys".freeze
|
77
79
|
|
80
|
+
##
|
81
|
+
# Create a CLI
|
82
|
+
#
|
83
|
+
# @param [String,nil] binary_name The binary name displayed in help text.
|
84
|
+
# Optional. Defaults to the ruby program name.
|
85
|
+
# @param [Logger,nil] logger The logger to use. If not provided, a default
|
86
|
+
# logger that writes to `STDERR` is used.
|
87
|
+
# @param [String,nil] config_dir_name A directory with this name that
|
88
|
+
# appears in the loader path, is treated as a configuration directory
|
89
|
+
# whose contents are loaded into the toys configuration. Optional.
|
90
|
+
# If not provided, toplevel configuration directories are disabled.
|
91
|
+
# The default toys CLI sets this to `".toys"`.
|
92
|
+
# @param [String,nil] config_file_name A file with this name that appears
|
93
|
+
# in the loader path, is treated as a toplevel configuration file
|
94
|
+
# whose contents are loaded into the toys configuration. Optional.
|
95
|
+
# If not provided, toplevel configuration files are disabled.
|
96
|
+
# The default toys CLI sets this to `".toys.rb"`.
|
97
|
+
# @param [String,nil] index_file_name A file with this name that appears
|
98
|
+
# in any configuration directory (not just a toplevel directory) is
|
99
|
+
# loaded first as a standalone configuration file. If not provided,
|
100
|
+
# standalone configuration files are disabled.
|
101
|
+
# The default toys CLI sets this to `".toys.rb"`.
|
102
|
+
# @param [String,nil] preload_file_name A file with this name that appears
|
103
|
+
# in any configuration directory (not just a toplevel directory) is
|
104
|
+
# loaded before any configuration files. It is not treated as a
|
105
|
+
# configuration file in that the configuration DSL is not honored. You
|
106
|
+
# may use such a file to define auxiliary Ruby modules and classes that
|
107
|
+
# used by the tools defined in that directory.
|
108
|
+
# @param [Array] middleware An array of middleware that will be used by
|
109
|
+
# default for all tools loaded by this CLI.
|
110
|
+
# @param [String] root_desc The description of the root tool.
|
111
|
+
#
|
78
112
|
def initialize(
|
79
113
|
binary_name: nil,
|
80
114
|
logger: nil,
|
@@ -97,16 +131,41 @@ module Toys
|
|
97
131
|
@context_base = Context::Base.new(@loader, binary_name, logger)
|
98
132
|
end
|
99
133
|
|
134
|
+
##
|
135
|
+
# Add one or more configuration files/directories to the loader.
|
136
|
+
#
|
137
|
+
# If a CLI has a default tool set, it might use this to point to the
|
138
|
+
# directory that defines those tools. For example, the default Toys CLI
|
139
|
+
# uses this to load the builtin tools from the `builtins` directory.
|
140
|
+
#
|
141
|
+
# @param [String,Array<String>] paths One or more paths to add.
|
142
|
+
#
|
100
143
|
def add_config_paths(paths)
|
101
144
|
@loader.add_config_paths(paths)
|
102
145
|
self
|
103
146
|
end
|
104
147
|
|
148
|
+
##
|
149
|
+
# Add one or more path directories to the loader. These directories are
|
150
|
+
# searched for config directories and config files. Typically a CLI may
|
151
|
+
# include the current directory, or the user's home directory, `/etc` or
|
152
|
+
# other configuration-centric directories here.
|
153
|
+
#
|
154
|
+
# @param [String,Array<String>] paths One or more paths to add.
|
155
|
+
#
|
105
156
|
def add_paths(paths)
|
106
157
|
@loader.add_paths(paths)
|
107
158
|
self
|
108
159
|
end
|
109
160
|
|
161
|
+
##
|
162
|
+
# Add the given path and all ancestor directories to the loader as paths.
|
163
|
+
# You may optionally provide a stopping point using the `base` argument,
|
164
|
+
# which, if present, will be the _last_ directory added.
|
165
|
+
#
|
166
|
+
# @param [String] path The first directory to add
|
167
|
+
# @param [String] base The last directory to add. Defaults to `"/"`.
|
168
|
+
#
|
110
169
|
def add_path_hierarchy(path = nil, base = "/")
|
111
170
|
path ||= ::Dir.pwd
|
112
171
|
paths = []
|
@@ -121,6 +180,11 @@ module Toys
|
|
121
180
|
self
|
122
181
|
end
|
123
182
|
|
183
|
+
##
|
184
|
+
# Add a standard set of paths. This includes the contents of the
|
185
|
+
# `TOYS_PATH` environment variable if present, the current user's home
|
186
|
+
# directory, and any system configuration directories such as `/etc`.
|
187
|
+
#
|
124
188
|
def add_standard_paths
|
125
189
|
toys_path = ::ENV["TOYS_PATH"].to_s.split(::File::PATH_SEPARATOR)
|
126
190
|
if toys_path.empty?
|
@@ -131,11 +195,19 @@ module Toys
|
|
131
195
|
self
|
132
196
|
end
|
133
197
|
|
198
|
+
##
|
199
|
+
# Run the CLI with the given command line arguments.
|
200
|
+
#
|
134
201
|
def run(*args)
|
135
202
|
exit(@context_base.run(args.flatten, verbosity: 0))
|
136
203
|
end
|
137
204
|
|
138
205
|
class << self
|
206
|
+
##
|
207
|
+
# Configure and create the standard Toys CLI.
|
208
|
+
#
|
209
|
+
# @return [Toys::CLI]
|
210
|
+
#
|
139
211
|
def create_standard
|
140
212
|
cli = new(
|
141
213
|
binary_name: DEFAULT_BINARY_NAME,
|
@@ -152,15 +224,30 @@ module Toys
|
|
152
224
|
cli
|
153
225
|
end
|
154
226
|
|
227
|
+
##
|
228
|
+
# Returns a default set of middleware used by the standard Toys CLI.
|
229
|
+
# This middleware handles usage errors, provides a behavior for groups
|
230
|
+
# that displays the group command list, provides a `--help` option for
|
231
|
+
# showing individual tool documentation, and provides `--verbose` and
|
232
|
+
# `--quiet` switches for setting the verbosity, which in turn controls
|
233
|
+
# the logger level.
|
234
|
+
#
|
235
|
+
# @return [Array]
|
236
|
+
#
|
155
237
|
def default_middleware_stack
|
156
238
|
[
|
157
239
|
Middleware.lookup(:show_usage_errors).new,
|
158
|
-
Middleware.lookup(:
|
159
|
-
Middleware.lookup(:
|
240
|
+
Middleware.lookup(:show_group_usage).new,
|
241
|
+
Middleware.lookup(:show_tool_usage).new,
|
160
242
|
Middleware.lookup(:set_verbosity).new
|
161
243
|
]
|
162
244
|
end
|
163
245
|
|
246
|
+
##
|
247
|
+
# Returns a default logger that logs to `STDERR`.
|
248
|
+
#
|
249
|
+
# @return [Logger]
|
250
|
+
#
|
164
251
|
def default_logger
|
165
252
|
logger = ::Logger.new(::STDERR)
|
166
253
|
logger.formatter = proc do |severity, time, _progname, msg|
|
@@ -0,0 +1,432 @@
|
|
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
|
+
##
|
32
|
+
# This class defines the DSL for a toys configuration file.
|
33
|
+
#
|
34
|
+
# A toys configuration defines one or more named tools. It provides syntax
|
35
|
+
# for setting the description, defining switches and arguments, specifying
|
36
|
+
# how to execute the tool, and requesting helper modules and other services.
|
37
|
+
# It also lets you define subtools, nested arbitrarily deep, using blocks.
|
38
|
+
#
|
39
|
+
# Generally ConfigDSL is invoked from the {Loader}. Applications should not
|
40
|
+
# need to create instances of ConfigDSL directly.
|
41
|
+
#
|
42
|
+
# ## Simple example
|
43
|
+
#
|
44
|
+
# Create a file called `.toys.rb` in the current directory, with the
|
45
|
+
# following contents:
|
46
|
+
#
|
47
|
+
# tool "greet" do
|
48
|
+
# desc "Prints a simple greeting"
|
49
|
+
#
|
50
|
+
# optional_arg :recipient, default: "world"
|
51
|
+
#
|
52
|
+
# execute do
|
53
|
+
# puts "Hello, #{self[:recipient]}!"
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# Now you can execute it using:
|
58
|
+
#
|
59
|
+
# toys greet
|
60
|
+
#
|
61
|
+
# or try:
|
62
|
+
#
|
63
|
+
# toys greet rubyists
|
64
|
+
#
|
65
|
+
class ConfigDSL
|
66
|
+
##
|
67
|
+
# Create an instance of the DSL.
|
68
|
+
# @private
|
69
|
+
#
|
70
|
+
# @param [String] path The path to the config file being evaluated
|
71
|
+
# @param [Toys::Tool] tool The tool being defined at the top level
|
72
|
+
# @param [Array<String>,nil] remaining_words Arguments remaining in the
|
73
|
+
# current lookup.
|
74
|
+
# @param [Integer] priority Priority of this configuration
|
75
|
+
# @param [Toys::Loader] loader Current active loader
|
76
|
+
# @param [:tool,:append,:group] type Type of tool being configured
|
77
|
+
#
|
78
|
+
# @return [Toys::ConfigDSL]
|
79
|
+
#
|
80
|
+
def initialize(path, tool, remaining_words, priority, loader, type)
|
81
|
+
@path = path
|
82
|
+
@tool = tool
|
83
|
+
@remaining_words = remaining_words
|
84
|
+
@priority = priority
|
85
|
+
@loader = loader
|
86
|
+
@type = type
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# Create a subtool.
|
91
|
+
#
|
92
|
+
# If the subtool is not an alias, you must provide a block defining the
|
93
|
+
# subtool.
|
94
|
+
#
|
95
|
+
# If the subtool is already defined (either as a tool or a group), the old
|
96
|
+
# definition is discarded and replaced with the new definition. If the old
|
97
|
+
# tool was a group, all its descendants are also discarded, recursively.
|
98
|
+
#
|
99
|
+
# @param [String] word The name of the subtool
|
100
|
+
# @param [String,nil] alias_of If set, this subtool is set to be an alias
|
101
|
+
# of the given subtool name. Defaults to `nil`, indicating the subtool
|
102
|
+
# is not an alias.
|
103
|
+
#
|
104
|
+
def tool(word, alias_of: nil, &block)
|
105
|
+
word = word.to_s
|
106
|
+
subtool = @loader.get_or_create_tool(@tool.full_name + [word], @priority, assume_parent: true)
|
107
|
+
return self if subtool.nil?
|
108
|
+
if alias_of
|
109
|
+
if block
|
110
|
+
raise ToolDefinitionError, "Cannot take a block with alias_of"
|
111
|
+
end
|
112
|
+
subtool.make_alias_of_word(alias_of.to_s)
|
113
|
+
return self
|
114
|
+
end
|
115
|
+
next_remaining = Loader.next_remaining_words(@remaining_words, word)
|
116
|
+
ConfigDSL.evaluate(@path, subtool, next_remaining, @priority, @loader, :tool, block)
|
117
|
+
self
|
118
|
+
end
|
119
|
+
alias name tool
|
120
|
+
|
121
|
+
##
|
122
|
+
# Append subtools to an existing group.
|
123
|
+
#
|
124
|
+
# Pass a group name to this method to "reopen" that group. You must provide
|
125
|
+
# a block. In that block, you may not modify any properties of the group
|
126
|
+
# itself, but you may add or replace subtools within the group.
|
127
|
+
#
|
128
|
+
# @param [String] word The name of the group.
|
129
|
+
#
|
130
|
+
def append(word, &block)
|
131
|
+
word = word.to_s
|
132
|
+
subtool = @loader.get_or_create_tool(@tool.full_name + [word], nil, assume_parent: true)
|
133
|
+
next_remaining = Loader.next_remaining_words(@remaining_words, word)
|
134
|
+
ConfigDSL.evaluate(@path, subtool, next_remaining, @priority, @loader, :append, block)
|
135
|
+
self
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# Create a group subtool. You must provide a block defining the group's
|
140
|
+
# properties and contents.
|
141
|
+
#
|
142
|
+
# If the subtool is already defined (either as a tool or a group), the old
|
143
|
+
# definition is discarded and replaced with the new definition.
|
144
|
+
#
|
145
|
+
# @param [String] word The name of the group
|
146
|
+
#
|
147
|
+
def group(word, &block)
|
148
|
+
word = word.to_s
|
149
|
+
subtool = @loader.get_or_create_tool(@tool.full_name + [word], @priority, assume_parent: true)
|
150
|
+
return self if subtool.nil?
|
151
|
+
next_remaining = Loader.next_remaining_words(@remaining_words, word)
|
152
|
+
ConfigDSL.evaluate(@path, subtool, next_remaining, @priority, @loader, :group, block)
|
153
|
+
self
|
154
|
+
end
|
155
|
+
|
156
|
+
##
|
157
|
+
# Create an alias of the current tool.
|
158
|
+
#
|
159
|
+
# @param [String] word The name of the alias
|
160
|
+
#
|
161
|
+
def alias_as(word)
|
162
|
+
if @tool.root?
|
163
|
+
raise ToolDefinitionError, "Cannot make an alias of the root tool"
|
164
|
+
end
|
165
|
+
if @type == :group || @type == :append
|
166
|
+
raise ToolDefinitionError, "Cannot make an alias of a group"
|
167
|
+
end
|
168
|
+
alias_name = @tool.full_name.slice(0..-2) + [word.to_s]
|
169
|
+
alias_tool = @loader.get_or_create_tool(alias_name, @priority)
|
170
|
+
alias_tool.make_alias_of(@tool.simple_name) if alias_tool
|
171
|
+
self
|
172
|
+
end
|
173
|
+
|
174
|
+
##
|
175
|
+
# Make the current tool an alias of the given sibling
|
176
|
+
#
|
177
|
+
# @param [String] word The name of the sibling tool to alias
|
178
|
+
#
|
179
|
+
def alias_of(word)
|
180
|
+
if @tool.root?
|
181
|
+
raise ToolDefinitionError, "Cannot make the root tool an alias"
|
182
|
+
end
|
183
|
+
if @type == :group || @type == :append
|
184
|
+
raise ToolDefinitionError, "Cannot make a group an alias"
|
185
|
+
end
|
186
|
+
@tool.make_alias_of(word.to_s)
|
187
|
+
self
|
188
|
+
end
|
189
|
+
|
190
|
+
##
|
191
|
+
# Include another config file or directory at the current location.
|
192
|
+
#
|
193
|
+
# @param [String] path The file or directory to include.
|
194
|
+
#
|
195
|
+
def include(path)
|
196
|
+
@tool.yield_definition do
|
197
|
+
@loader.include_path(path, @tool.full_name, @remaining_words, @priority)
|
198
|
+
end
|
199
|
+
self
|
200
|
+
end
|
201
|
+
|
202
|
+
##
|
203
|
+
# Expand the given template in the current location.
|
204
|
+
#
|
205
|
+
# The template may be specified as a class or a well-known template name.
|
206
|
+
# You may also provide arguments to pass to the template.
|
207
|
+
#
|
208
|
+
# @param [Class,String,Symbol] template_class The template, either as a
|
209
|
+
# class or a well-known name.
|
210
|
+
# @param [Object...] args Template arguments
|
211
|
+
#
|
212
|
+
def expand(template_class, *args)
|
213
|
+
unless template_class.is_a?(::Class)
|
214
|
+
name = template_class.to_s
|
215
|
+
template_class = Templates.lookup(name)
|
216
|
+
if template_class.nil?
|
217
|
+
raise ToolDefinitionError, "Template not found: #{name.inspect}"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
template = template_class.new(*args)
|
221
|
+
yield template if block_given?
|
222
|
+
instance_exec(template, &template_class.expander)
|
223
|
+
self
|
224
|
+
end
|
225
|
+
|
226
|
+
##
|
227
|
+
# Set the long description for the current tool. The long description is
|
228
|
+
# displayed in the usage documentation for the tool itself.
|
229
|
+
#
|
230
|
+
# @param [String] desc The long description string.
|
231
|
+
#
|
232
|
+
def long_desc(desc)
|
233
|
+
if @type == :append
|
234
|
+
raise ToolDefinitionError, "Cannot set the description when appending"
|
235
|
+
end
|
236
|
+
@tool.long_desc = desc
|
237
|
+
self
|
238
|
+
end
|
239
|
+
|
240
|
+
##
|
241
|
+
# Set the short description for the current tool. The short description is
|
242
|
+
# displayed with the tool in a command list. You may also use the
|
243
|
+
# equivalent method `short_desc`.
|
244
|
+
#
|
245
|
+
# @param [String] desc The short description string.
|
246
|
+
#
|
247
|
+
def desc(desc)
|
248
|
+
if @type == :append
|
249
|
+
raise ToolDefinitionError, "Cannot set the description when appending"
|
250
|
+
end
|
251
|
+
@tool.desc = desc
|
252
|
+
self
|
253
|
+
end
|
254
|
+
alias short_desc desc
|
255
|
+
|
256
|
+
##
|
257
|
+
# Add a switch to the current tool. Each switch must specify a key which
|
258
|
+
# the executor may use to obtain the switch value from the context.
|
259
|
+
# You may then provide the switches themselves in `OptionParser` form.
|
260
|
+
#
|
261
|
+
# @param [Symbol] key The key to use to retrieve the value from the
|
262
|
+
# execution context.
|
263
|
+
# @param [String...] switches The switches in OptionParser format.
|
264
|
+
# @param [Object,nil] accept An OptionParser acceptor. Optional.
|
265
|
+
# @param [Object] default The default value. This is the value that will
|
266
|
+
# be set in the context if this switch is not provided on the command
|
267
|
+
# line. Defaults to `nil`.
|
268
|
+
# @param [String,nil] doc The documentation for the switch, which appears
|
269
|
+
# in the usage documentation. Defaults to `nil` for no documentation.
|
270
|
+
# @param [Boolean] only_unique If true, any switches that are already
|
271
|
+
# defined in this tool are removed from this switch. For example, if
|
272
|
+
# an earlier switch uses `-a`, and this switch wants to use both
|
273
|
+
# `-a` and `-b`, then only `-b` will be assigned to this switch.
|
274
|
+
# Defaults to false.
|
275
|
+
# @param [Proc,nil] handler An optional handler for setting/updating the
|
276
|
+
# value. If given, it should take two arguments, the new given value
|
277
|
+
# and the previous value, and it should return the new value that
|
278
|
+
# should be set. The default handler simply replaces the previous
|
279
|
+
# value. i.e. the default is effectively `-> (val, _prev) { val }`.
|
280
|
+
#
|
281
|
+
def switch(key, *switches,
|
282
|
+
accept: nil, default: nil, doc: nil, only_unique: false, handler: nil)
|
283
|
+
if @type == :append
|
284
|
+
raise ToolDefinitionError, "Cannot add a switch when appending"
|
285
|
+
end
|
286
|
+
@tool.add_switch(key, *switches,
|
287
|
+
accept: accept, default: default, doc: doc,
|
288
|
+
only_unique: only_unique, handler: handler)
|
289
|
+
self
|
290
|
+
end
|
291
|
+
|
292
|
+
##
|
293
|
+
# Add a required positional argument to the current tool. You must specify
|
294
|
+
# a key which the executor may use to obtain the argument value from the
|
295
|
+
# context.
|
296
|
+
#
|
297
|
+
# @param [Symbol] key The key to use to retrieve the value from the
|
298
|
+
# execution context.
|
299
|
+
# @param [Object,nil] accept An OptionParser acceptor. Optional.
|
300
|
+
# @param [String,nil] doc The documentation for the switch, which appears
|
301
|
+
# in the usage documentation. Defaults to `nil` for no documentation.
|
302
|
+
#
|
303
|
+
def required_arg(key, accept: nil, doc: nil)
|
304
|
+
if @type == :append
|
305
|
+
raise ToolDefinitionError, "Cannot add an argument when appending"
|
306
|
+
end
|
307
|
+
@tool.add_required_arg(key, accept: accept, doc: doc)
|
308
|
+
self
|
309
|
+
end
|
310
|
+
|
311
|
+
##
|
312
|
+
# Add an optional positional argument to the current tool. You must specify
|
313
|
+
# a key which the executor may use to obtain the argument value from the
|
314
|
+
# context. If an optional argument is not given on the command line, the
|
315
|
+
# value is set to the given default.
|
316
|
+
#
|
317
|
+
# @param [Symbol] key The key to use to retrieve the value from the
|
318
|
+
# execution context.
|
319
|
+
# @param [Object,nil] accept An OptionParser acceptor. Optional.
|
320
|
+
# @param [Object] default The default value. This is the value that will
|
321
|
+
# be set in the context if this argument is not provided on the command
|
322
|
+
# line. Defaults to `nil`.
|
323
|
+
# @param [String,nil] doc The documentation for the argument, which appears
|
324
|
+
# in the usage documentation. Defaults to `nil` for no documentation.
|
325
|
+
#
|
326
|
+
def optional_arg(key, accept: nil, default: nil, doc: nil)
|
327
|
+
if @type == :append
|
328
|
+
raise ToolDefinitionError, "Cannot add an argument when appending"
|
329
|
+
end
|
330
|
+
@tool.add_optional_arg(key, accept: accept, default: default, doc: doc)
|
331
|
+
self
|
332
|
+
end
|
333
|
+
|
334
|
+
##
|
335
|
+
# Specify what should be done with unmatched positional arguments. You must
|
336
|
+
# specify a key which the executor may use to obtain the remaining args
|
337
|
+
# from the context.
|
338
|
+
#
|
339
|
+
# @param [Symbol] key The key to use to retrieve the value from the
|
340
|
+
# execution context.
|
341
|
+
# @param [Object,nil] accept An OptionParser acceptor. Optional.
|
342
|
+
# @param [Object] default The default value. This is the value that will
|
343
|
+
# be set in the context if no unmatched arguments are provided on the
|
344
|
+
# command line. Defaults to the empty array `[]`.
|
345
|
+
# @param [String,nil] doc The documentation for the remaining arguments,
|
346
|
+
# which appears in the usage documentation. Defaults to `nil` for no
|
347
|
+
# documentation.
|
348
|
+
#
|
349
|
+
def remaining_args(key, accept: nil, default: [], doc: nil)
|
350
|
+
if @type == :append
|
351
|
+
raise ToolDefinitionError, "Cannot add an argument when appending"
|
352
|
+
end
|
353
|
+
@tool.set_remaining_args(key, accept: accept, default: default, doc: doc)
|
354
|
+
self
|
355
|
+
end
|
356
|
+
|
357
|
+
##
|
358
|
+
# Specify the executor for this tool. This is a block that will be called,
|
359
|
+
# with `self` set to a {Toys::Context}.
|
360
|
+
#
|
361
|
+
def execute(&block)
|
362
|
+
if @type == :group || @type == :append
|
363
|
+
raise ToolDefinitionError, "Cannot set the executor of a group"
|
364
|
+
end
|
365
|
+
@tool.executor = block
|
366
|
+
self
|
367
|
+
end
|
368
|
+
|
369
|
+
##
|
370
|
+
# Define a helper method that may be called from this tool's executor.
|
371
|
+
# You must provide a name for the method, and a block for the method
|
372
|
+
# definition.
|
373
|
+
#
|
374
|
+
# @param [String,Symbol] name Name of the method. May not begin with an
|
375
|
+
# underscore.
|
376
|
+
#
|
377
|
+
def helper(name, &block)
|
378
|
+
if @type == :group || @type == :append
|
379
|
+
raise ToolDefinitionError, "Cannot define a helper method to a group"
|
380
|
+
end
|
381
|
+
@tool.add_helper(name, &block)
|
382
|
+
self
|
383
|
+
end
|
384
|
+
|
385
|
+
##
|
386
|
+
# Specify that the given module should be mixed in to this tool's executor.
|
387
|
+
# Effectively, the module is added to the {Toys::Context} object.
|
388
|
+
# You may either provide a module directly, or specify the name of a
|
389
|
+
# well-known module.
|
390
|
+
#
|
391
|
+
# @param [Module,Symbol] mod Module or name of well-known module.
|
392
|
+
#
|
393
|
+
def use(mod)
|
394
|
+
if @type == :group || @type == :append
|
395
|
+
raise ToolDefinitionError, "Cannot use a helper module in a group"
|
396
|
+
end
|
397
|
+
@tool.use_module(mod)
|
398
|
+
self
|
399
|
+
end
|
400
|
+
|
401
|
+
## @private
|
402
|
+
def _binding
|
403
|
+
binding
|
404
|
+
end
|
405
|
+
|
406
|
+
## @private
|
407
|
+
def self.evaluate(path, tool, remaining_words, priority, loader, type, source)
|
408
|
+
dsl = new(path, tool, remaining_words, priority, loader, type)
|
409
|
+
if type == :append
|
410
|
+
eval_source(dsl, path, source)
|
411
|
+
else
|
412
|
+
tool.defining_from(path) do
|
413
|
+
eval_source(dsl, path, source)
|
414
|
+
tool.finish_definition
|
415
|
+
end
|
416
|
+
end
|
417
|
+
tool
|
418
|
+
end
|
419
|
+
|
420
|
+
## @private
|
421
|
+
def self.eval_source(dsl, path, source)
|
422
|
+
case source
|
423
|
+
when String
|
424
|
+
# rubocop:disable Security/Eval
|
425
|
+
eval(source, dsl._binding, path, 1)
|
426
|
+
# rubocop:enable Security/Eval
|
427
|
+
when ::Proc
|
428
|
+
dsl.instance_eval(&source)
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|