toys 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Travis-CI Build Status](https://travis-ci.org/dazuma/toys.svg)](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
|