toys 0.13.1 → 0.14.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 76a2e6d85d0d1baa0dc154bc6572bbd600c855c9e3cf6c6e64594630a33c58ed
4
- data.tar.gz: c701ff4fd53eb2a7dd85561b4c19e1614a43a9788f73abdbeaa86e8a9a17fb68
3
+ metadata.gz: 5605477c7993fad15f1c79740f8de6b90f4aef19becdc00a236e71853cb4d9a8
4
+ data.tar.gz: 1c63369d5c4302356f412e25904bd6db43ca93b9896c687c6ae5bbfd4fe24fc8
5
5
  SHA512:
6
- metadata.gz: da3b8b9da43c2bc81e286206fa649fc994b5083376357cf3c4f7d3289b8d8d928da8faffa42a32e4313074a24666ec40a1eabc73d8b1c82304bc6cd1eed99feb
7
- data.tar.gz: 8f36a970ed0292a604dcb28cb5a48ad4697fc0a5fbb27389f6dc937e9ea543488c2b28bfc1c148f93044eb26aed4f03c8911f9df921fafb54f352020144f041b
6
+ metadata.gz: c312c18f2fa8d57f6dbeb8def6b434a611030387e289a370bcb96faadf90a49b948cfc7ac17b14f9e7b97a942a85b133d384ad37705aed9b833fca3f650517d1
7
+ data.tar.gz: 1f7125dbf036acb25b0352de30b68f95c229610572c754b8cdf3e1c633e03f5a6318eeb5de56df1d229402f4915d16aacb794d62fcd571007a604aec4ad6070f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # Release History
2
2
 
3
+ ### v0.14.2 / 2022-10-09
4
+
5
+ * ADDED: The tool directive supports the delegate_relative argument, as a preferred alternative over alias_tool.
6
+ * FIXED: The toys file reference now properly appears in error messages on Ruby 3.1.
7
+ * FIXED: Error messages show the correct toys file line number on TruffleRuby.
8
+ * FIXED: Inspect strings for tool classes are less opaque and include the tool name.
9
+ * FIXED: The presence of an acceptor forces an ambiguous flag to take a value rather than erroring.
10
+
11
+ ### v0.14.1 / 2022-10-03
12
+
13
+ * FIXED: Fixed a crash due to a missing file in the gem
14
+
15
+ ### v0.14.0 / 2022-10-03
16
+
17
+ Toys 0.14.0 is a major release with new system tools for introspecting defined tools, pager support, support for tees and pipes in the Exec utility, some cleanup of the behavior of Acceptors, and other improvements.
18
+
19
+ Fixes that are potentially breaking:
20
+
21
+ * Disallowed acceptors on flags that are explicitly boolean.
22
+ * Acceptors no longer sometimes apply to the boolean setting of a flag with an optional value.
23
+
24
+ New functionality:
25
+
26
+ * Implemented new builtins for obtaining information about defined tools: `system tools show` and `system tools list`.
27
+ * Implemented a utility class and mixin for output pagers.
28
+ * Builtin commands that display data can format as either YAML or JSON.
29
+ * The Exec utility and mixin can tee (i.e. duplicate and split) output streams.
30
+ * The Exec utility and mixin can take pipes as input and output streams.
31
+ * The Exec mixin provides a `verbosity_flags` convenience method.
32
+
33
+ Fixes:
34
+
35
+ * The `system test` builtin no longer requires toys to be installed as a gem.
36
+ * Fixed a failure when passing a relative path to `system test --directory`.
37
+ * Contents of preload directories are loaded in sorted order.
38
+ * Various clarifications, fixes, and updates to the users guide and documentation.
39
+
3
40
  ### v0.13.1 / 2022-03-01
4
41
 
5
42
  * FIXED: Bundler integration no longer fails if a bundle was locked to a different version of a builtin gem
data/README.md CHANGED
@@ -19,7 +19,7 @@ gem. For more info on using toys-core, see
19
19
 
20
20
  Here's a tutorial to help you get a feel of what Toys can do.
21
21
 
22
- ### Install Toys
22
+ ### Install and try out Toys
23
23
 
24
24
  Install the **toys** gem using:
25
25
 
@@ -50,14 +50,6 @@ Toys does not yet specially implement tab completion for zsh or other shells.
50
50
  However, if you are using zsh, installing bash completion using `bashcompinit`
51
51
  *mostly* works.
52
52
 
53
- Toys requires Ruby 2.4 or later.
54
-
55
- Most parts of Toys work on JRuby. However, JRuby is not recommended because of
56
- JVM boot latency, lack of support for Kernel#fork, and other issues.
57
-
58
- Most parts of Toys work on TruffleRuby. However, TruffleRuby is not recommended
59
- because it has a few known bugs that affect Toys.
60
-
61
53
  ### Write your first tool
62
54
 
63
55
  You can define tools by creating a *Toys file*. Go into any directory, and,
@@ -244,8 +236,7 @@ you may need to add Toys to your Gemfile and use Bundler to invoke Toys (i.e.
244
236
  `bundle exec toys test`). This is because Toys is just calling the Rake API to
245
237
  run your task, and the Rake task might require the bundle. However, when Toys
246
238
  is not wrapping Rake, typical practice is actually *not* to use `bundle exec`.
247
- Toys provides its own mechanisms to setup a bundle, or to activate and even
248
- install individual gems.
239
+ Toys provides its own mechanisms to manage bundles or install gems for you.
249
240
 
250
241
  So far, we've made Toys a front-end for your Rake tasks. This may be useful by
251
242
  itself. Toys lets you pass command line arguments "normally" to tools, whereas
@@ -256,11 +247,11 @@ than Rake does.
256
247
  But you also might find Toys a more natural way to *write* tasks, and indeed
257
248
  you can often rewrite an entire Rakefile as a Toys file and get quite a bit of
258
249
  benefit in readability and maintainability. For an example, see the
259
- [Toys file for the Toys repo itself](https://github.com/dazuma/toys/blob/main/toys/.toys.rb).
260
- It contains the Toys scripts that I use to develop, test, and release Toys
261
- itself. Yes, Toys is self-hosted. You'll notice most of this Toys file consists
262
- of template expansions. Toys provides templates for a lot of common build,
263
- test, and release tasks for Ruby projects.
250
+ [Toys file for the Toys gem itself](https://github.com/dazuma/toys/blob/main/toys/.toys/.toys.rb).
251
+ It contains Toys scripts that I use to develop, test, and release Toys itself.
252
+ Yes, Toys is self-hosted. You'll notice most of this Toys file consists of
253
+ template expansions. Toys provides templates for a lot of common build, test,
254
+ and release tasks for Ruby projects.
264
255
 
265
256
  If you're feeling adventurous, try translating some of your Rake tasks into
266
257
  native Toys tools. You can do so in your existing `.toys.rb` file. Keep the
@@ -305,7 +296,7 @@ them. Furthermore, when writing new scripts, I was repeating the same
305
296
  OptionParser boilerplate and common functionality.
306
297
 
307
298
  Toys was designed to address those problems by providing a framework for
308
- writing *and organizing* your own command line scripts. You provide the actual
299
+ writing and organizing your own command line scripts. You provide the actual
309
300
  functionality by writing Toys files, and Toys takes care of all the other
310
301
  details expected from a good command line tool. It provides a streamlined
311
302
  interface for defining and handling command line flags and positional
@@ -320,6 +311,16 @@ scripts written for Toys can be invoked and passed arguments and flags using
320
311
  familiar unix command line conventions. The Toys github repo itself comes with
321
312
  Toys scripts instead of Rakefiles.
322
313
 
314
+ ## System requirements
315
+
316
+ Toys requires Ruby 2.4 or later.
317
+
318
+ Most parts of Toys work on JRuby. However, JRuby is not recommended because of
319
+ JVM boot latency, lack of support for Kernel#fork, and other issues.
320
+
321
+ Most parts of Toys work on TruffleRuby. However, TruffleRuby is not recommended
322
+ because it has a few known bugs that affect Toys.
323
+
323
324
  ## License
324
325
 
325
326
  Copyright 2019-2022 Daniel Azuma and the Toys contributors
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc "A set of system commands for Toys"
4
+
5
+ long_desc "Contains tools that inspect, configure, and update Toys itself."
6
+
7
+ tool "version" do
8
+ desc "Print the current Toys version"
9
+
10
+ def run
11
+ puts ::Toys::VERSION
12
+ end
13
+ end
14
+
15
+ mixin "output-tools" do
16
+ on_include do
17
+ flag :output_format, "--format=FORMAT" do
18
+ accept ["json", "json-compact", "yaml"]
19
+ desc 'The output format. Recognized values are "yaml" (the default), "json", and ' \
20
+ '"json-compact".'
21
+ end
22
+ end
23
+
24
+ def generate_output(object)
25
+ case output_format
26
+ when "json"
27
+ require "json"
28
+ ::JSON.pretty_generate(object)
29
+ when "json-compact"
30
+ require "json"
31
+ ::JSON.generate(object)
32
+ when nil, "yaml"
33
+ require "psych"
34
+ ::Psych.dump(object)
35
+ else
36
+ logger.error("Unknown output format: #{format}")
37
+ exit(1)
38
+ end
39
+ end
40
+ end
@@ -23,15 +23,16 @@ tool "list" do
23
23
  desc "The base directory for the cache. Optional. Defaults to the standard cache directory."
24
24
  end
25
25
 
26
+ include "output-tools"
27
+
26
28
  def run
27
- require "psych"
28
29
  require "toys/utils/git_cache"
29
30
  git_cache = ::Toys::Utils::GitCache.new(cache_dir: cache_dir)
30
31
  output = {
31
32
  "cache_dir" => git_cache.cache_dir,
32
33
  "remotes" => git_cache.remotes,
33
34
  }
34
- puts(::Psych.dump(output))
35
+ puts(generate_output(output))
35
36
  end
36
37
  end
37
38
 
@@ -48,8 +49,9 @@ tool "show" do
48
49
  desc "The base directory for the cache. Optional. Defaults to the standard cache directory."
49
50
  end
50
51
 
52
+ include "output-tools"
53
+
51
54
  def run
52
- require "psych"
53
55
  require "toys/utils/git_cache"
54
56
  git_cache = ::Toys::Utils::GitCache.new(cache_dir: cache_dir)
55
57
  info = git_cache.repo_info(remote)
@@ -57,7 +59,7 @@ tool "show" do
57
59
  logger.fatal("Unknown remote: #{remote}")
58
60
  exit(1)
59
61
  end
60
- puts(::Psych.dump(info.to_h))
62
+ puts(generate_output(info.to_h))
61
63
  end
62
64
  end
63
65
 
@@ -130,8 +132,9 @@ tool "remove" do
130
132
  desc "Remove all repositories. Required unless specific remotes are provided."
131
133
  end
132
134
 
135
+ include "output-tools"
136
+
133
137
  def run
134
- require "psych"
135
138
  require "toys/utils/git_cache"
136
139
  if remotes.empty? == !all
137
140
  logger.fatal("You must specify at least one remote to clear, or --all to clear all remotes.")
@@ -142,7 +145,7 @@ tool "remove" do
142
145
  output = {
143
146
  "removed" => removed,
144
147
  }
145
- puts(::Psych.dump(output))
148
+ puts(generate_output(output))
146
149
  end
147
150
  end
148
151
 
@@ -175,8 +178,9 @@ tool "remove-refs" do
175
178
  desc "The base directory for the cache. Optional. Defaults to the standard cache directory."
176
179
  end
177
180
 
181
+ include "output-tools"
182
+
178
183
  def run
179
- require "psych"
180
184
  require "toys/utils/git_cache"
181
185
  git_cache = ::Toys::Utils::GitCache.new(cache_dir: cache_dir)
182
186
  removed = git_cache.remove_refs(remote, refs: refs)
@@ -188,7 +192,7 @@ tool "remove-refs" do
188
192
  "remote" => remote,
189
193
  "removed_refs" => removed.map(&:to_h),
190
194
  }
191
- puts(::Psych.dump(output))
195
+ puts(generate_output(output))
192
196
  end
193
197
  end
194
198
 
@@ -220,8 +224,9 @@ tool "remove-sources" do
220
224
  desc "The base directory for the cache. Optional. Defaults to the standard cache directory."
221
225
  end
222
226
 
227
+ include "output-tools"
228
+
223
229
  def run
224
- require "psych"
225
230
  require "toys/utils/git_cache"
226
231
  git_cache = ::Toys::Utils::GitCache.new(cache_dir: cache_dir)
227
232
  removed = git_cache.remove_sources(remote, commits: commits)
@@ -233,6 +238,6 @@ tool "remove-sources" do
233
238
  "remote" => remote,
234
239
  "removed_sources" => removed.map(&:to_h),
235
240
  }
236
- puts(::Psych.dump(output))
241
+ puts(generate_output(output))
237
242
  end
238
243
  end
@@ -30,7 +30,6 @@ include :terminal
30
30
 
31
31
  def run
32
32
  load_minitest_gems
33
- ::Dir.chdir(tool_dir)
34
33
  test_files = find_test_files
35
34
  result = exec_ruby(ruby_args, in: :controller, log_cmd: "Starting minitest...") do |controller|
36
35
  controller.in.puts("gem 'minitest', '= #{::Minitest::VERSION}'")
@@ -43,7 +42,6 @@ def run
43
42
  controller.in.puts("gem 'minitest-rg', '= #{::MiniTest::RG::VERSION}'")
44
43
  controller.in.puts("require 'minitest/rg'")
45
44
  end
46
- controller.in.puts("gem 'toys', '= #{::Toys::VERSION}'")
47
45
  controller.in.puts("require 'toys'")
48
46
  controller.in.puts("require 'toys/testing'")
49
47
  if directory
@@ -79,7 +77,8 @@ end
79
77
  def find_test_files
80
78
  glob = ".test/**/test_*.rb"
81
79
  glob = "**/#{glob}" if recursive
82
- test_files = ::Dir.glob(glob)
80
+ glob = "#{tool_dir}/#{glob}"
81
+ test_files = Dir.glob(glob)
83
82
  if test_files.empty?
84
83
  logger.warn("No test files found")
85
84
  exit
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ mixin "tool-methods" do
4
+ def format_tool(tool, namespace, detailed: false)
5
+ output = {
6
+ "name" => tool.full_name[namespace.length..-1].join(" "),
7
+ "desc" => tool.desc.to_s,
8
+ "runnable" => tool.runnable?,
9
+ }
10
+ if detailed
11
+ output["exists"] = true
12
+ output["long_desc"] = tool.long_desc.map(&:to_s)
13
+ end
14
+ output
15
+ end
16
+
17
+ def choose_loader(local, from_dir)
18
+ return cli.loader unless local || from_dir
19
+ if from_dir
20
+ unless ::File.directory?(from_dir)
21
+ logger.fatal("Not a directory: #{from_dir}")
22
+ exit(1)
23
+ end
24
+ if ::File.basename(from_dir) == ".toys"
25
+ from_dir = ::File.dirname(from_dir)
26
+ elsif from_dir =~ %r{(^|/)\.toys/}
27
+ logger.fatal("Directory is inside a toys directory: #{from_dir}")
28
+ exit(1)
29
+ end
30
+ end
31
+ special_cli = if local
32
+ cli.child.add_search_path(from_dir || ::Dir.getwd)
33
+ else
34
+ ::Toys::StandardCLI.new(cur_dir: from_dir)
35
+ end
36
+ special_cli.loader
37
+ end
38
+ end
39
+
40
+ desc "Tools that introspect available tools"
41
+
42
+ long_desc \
43
+ "Tools that introspect the available tools."
44
+
45
+ tool "list" do
46
+ desc "Output a list of the tools under the given namespace."
47
+
48
+ long_desc \
49
+ "Outputs a list of the tools under the given namespace, in YAML format, to the standard " \
50
+ "output stream."
51
+
52
+ remaining_args :namespace
53
+
54
+ flag :local do
55
+ desc "List only tools defined locally in the current directory."
56
+ end
57
+ flag :recursive, "--[no-]recursive" do
58
+ desc "Recursively list subtools"
59
+ end
60
+ flag :flatten, "--[no-]flatten" do
61
+ desc "Display a flattened list of tools"
62
+ end
63
+ flag :from_dir, "--dir=PATH" do
64
+ desc "List tools from the given directory."
65
+ end
66
+ flag :show_all, "--all" do
67
+ desc "Show all tools, including hidden tools and non-runnable namespaces"
68
+ end
69
+
70
+ include "tool-methods"
71
+ include "output-tools"
72
+
73
+ def run
74
+ loader = choose_loader(local, from_dir)
75
+ words = namespace
76
+ words = loader.split_path(words.first) if words.size == 1
77
+ tool_list = loader.list_subtools(words,
78
+ recursive: recursive,
79
+ include_hidden: show_all,
80
+ include_namespaces: show_all || !flatten,
81
+ include_non_runnable: show_all)
82
+ output = {
83
+ "namespace" => words.join(" "),
84
+ "tools" => [],
85
+ }
86
+ if flatten
87
+ output["tools"] = tool_list.map { |tool| format_tool(tool, words) }
88
+ else
89
+ format_tool_list(tool_list, output, words)
90
+ end
91
+ puts(generate_output(output))
92
+ end
93
+
94
+ def format_tool_list(tool_list, toplevel, cur_ns)
95
+ stack = [toplevel]
96
+ tool_list.each do |tool|
97
+ tool_name_size = tool.full_name.size
98
+ while cur_ns.size >= tool_name_size
99
+ stack.pop
100
+ cur_ns.pop
101
+ end
102
+ formatted = format_tool(tool, cur_ns)
103
+ (stack.last["tools"] ||= []) << formatted
104
+ stack.push(formatted)
105
+ cur_ns.push(tool.full_name.last)
106
+ end
107
+ end
108
+ end
109
+
110
+ tool "show" do
111
+ desc "Show detailed information about a single tool"
112
+
113
+ long_desc \
114
+ "Outputs details about the given tool, in YAML format, to the standard output stream."
115
+
116
+ remaining_args :name
117
+
118
+ flag :local do
119
+ desc "Show only tools defined locally in the current directory."
120
+ end
121
+ flag :from_dir, "--dir=PATH" do
122
+ desc "Show a tool accessible from the given directory."
123
+ end
124
+
125
+ include "tool-methods"
126
+ include "output-tools"
127
+
128
+ def run
129
+ loader = choose_loader(local, from_dir)
130
+ words = name
131
+ words = loader.split_path(words.first) if words.size == 1
132
+ tool = loader.lookup_specific(words)
133
+ output =
134
+ if tool.nil?
135
+ {
136
+ "name" => words.join(" "),
137
+ "exists" => false,
138
+ }
139
+ else
140
+ format_tool(tool, [], detailed: true)
141
+ end
142
+ puts(generate_output(output))
143
+ exit(tool.nil? ? 1 : 0)
144
+ end
145
+ end
@@ -84,7 +84,7 @@ module Toys
84
84
  # When given a valid input, return an array in which the first element is
85
85
  # the original input string, and the remaining elements (which may be
86
86
  # empty) comprise any additional information that may be useful during
87
- # conversion. If there is no additional information, you may return the
87
+ # conversion. If there is no additional information, you can return the
88
88
  # original input string by itself without wrapping in an array.
89
89
  #
90
90
  # When given an invalid input, return a falsy value such as `nil`.
@@ -96,8 +96,7 @@ module Toys
96
96
  # as the only array element, indicating all inputs are valid. You can
97
97
  # override this method to provide a different validation function.
98
98
  #
99
- # @param str [String,nil] The input argument string. May be `nil` if the
100
- # value is optional and not provided.
99
+ # @param str [String] The input argument string.
101
100
  # @return [String,Array,nil]
102
101
  #
103
102
  def match(str)
@@ -111,8 +110,7 @@ module Toys
111
110
  # original input string and any other values returned from {#match}. It
112
111
  # must return the final converted value to use.
113
112
  #
114
- # @param str [String,nil] Original argument string. May be `nil` if the
115
- # value is optional and not provided.
113
+ # @param str [String] Original argument string.
116
114
  # @param extra [Object...] Zero or more additional arguments comprising
117
115
  # additional elements returned from the match function.
118
116
  # @return [Object] The converted argument as it should be stored in the
@@ -337,9 +335,7 @@ module Toys
337
335
  #
338
336
  NUMERIC_CONVERTER =
339
337
  proc do |s|
340
- if s.nil?
341
- nil
342
- elsif s.include?("/")
338
+ if s.include?("/")
343
339
  Rational(s)
344
340
  elsif s.include?(".") || (s.include?("e") && s !~ /\A-?0x/)
345
341
  Float(s)
@@ -348,6 +344,41 @@ module Toys
348
344
  end
349
345
  end
350
346
 
347
+ ##
348
+ # A set of strings that are considered true for boolean acceptors.
349
+ # Currently set to `["+", "true", "yes"]`.
350
+ # @return [Array<String>]
351
+ #
352
+ TRUE_STRINGS = ["+", "true", "yes"].freeze
353
+
354
+ ##
355
+ # A set of strings that are considered false for boolean acceptors.
356
+ # Currently set to `["-", "false", "no", "nil"]`.
357
+ # @return [Array<String>]
358
+ #
359
+ FALSE_STRINGS = ["-", "false", "no", "nil"].freeze
360
+
361
+ ##
362
+ # A converter proc that handles boolean strings. Recognizes {TRUE_STRINGS}
363
+ # and {FALSE_STRINGS}. Useful for Simple acceptors.
364
+ # @return [Proc]
365
+ #
366
+ BOOLEAN_CONVERTER =
367
+ proc do |s|
368
+ if s.empty?
369
+ REJECT
370
+ else
371
+ s = s.downcase
372
+ if TRUE_STRINGS.any? { |t| t.start_with?(s) }
373
+ true
374
+ elsif FALSE_STRINGS.any? { |f| f.start_with?(s) }
375
+ false
376
+ else
377
+ REJECT
378
+ end
379
+ end
380
+ end
381
+
351
382
  class << self
352
383
  ##
353
384
  # Lookup a standard acceptor name recognized by OptionParser.
@@ -427,6 +458,17 @@ module Toys
427
458
  def create(spec = nil, **options, &block)
428
459
  # Source available in the toys-core gem
429
460
  end
461
+
462
+ ##
463
+ # Take the various ways to express an acceptor spec, and convert them to
464
+ # a canonical form expressed as a single object. This is called from the
465
+ # DSL to generate a spec object that can be stored.
466
+ #
467
+ # @private This interface is internal and subject to change without warning.
468
+ #
469
+ def scalarize_spec(spec, options, block)
470
+ # Source available in the toys-core gem
471
+ end
430
472
  end
431
473
  end
432
474
  end
@@ -193,9 +193,9 @@ module Toys
193
193
  end
194
194
 
195
195
  ##
196
- # Make a clone with the same settings but no no config blocks and no paths
197
- # in the loader. This is sometimes useful for calling another tool that has
198
- # to be loaded from a different configuration.
196
+ # Make a clone with the same settings but no config blocks and no paths in
197
+ # the loader. This is sometimes useful for calling another tool that has to
198
+ # be loaded from a different configuration.
199
199
  #
200
200
  # @param opts [keywords] Any configuration arguments that should be
201
201
  # modified from the original. See {#initialize} for a list of
@@ -325,5 +325,16 @@ module Toys
325
325
  def self.create(spec = nil, **options, &block)
326
326
  # Source available in the toys-core gem
327
327
  end
328
+
329
+ ##
330
+ # Take the various ways to express a completion spec, and convert them to a
331
+ # canonical form expressed as a single object. This is called from the DSL
332
+ # DSL to generate a spec object that can be stored.
333
+ #
334
+ # @private This interface is internal and subject to change without warning.
335
+ #
336
+ def self.scalarize_spec(spec, options, block)
337
+ # Source available in the toys-core gem
338
+ end
328
339
  end
329
340
  end
@@ -297,7 +297,7 @@ module Toys
297
297
  end
298
298
 
299
299
  ##
300
- # Exit immediately with the given status code
300
+ # Exit immediately with the given status code.
301
301
  #
302
302
  # @param code [Integer] The status code, which should be 0 for no error,
303
303
  # or nonzero for an error condition. Default is 0.
@@ -308,7 +308,8 @@ module Toys
308
308
  end
309
309
 
310
310
  ##
311
- # Exit immediately with the given status code
311
+ # Exit immediately with the given status code. This class method can be
312
+ # called if the instance method is or could be replaced by the tool.
312
313
  #
313
314
  # @param code [Integer] The status code, which should be 0 for no error,
314
315
  # or nonzero for an error condition. Default is 0.
@@ -317,5 +318,18 @@ module Toys
317
318
  def self.exit(code = 0)
318
319
  # Source available in the toys-core gem
319
320
  end
321
+
322
+ ##
323
+ # Create a Context object. Applications generally will not need to create
324
+ # these objects directly; they are created by the tool when it is preparing
325
+ # for execution.
326
+ #
327
+ # @param data [Hash]
328
+ #
329
+ # @private This interface is internal and subject to change without warning.
330
+ #
331
+ def initialize(data)
332
+ # Source available in the toys-core gem
333
+ end
320
334
  end
321
335
  end
@@ -9,6 +9,6 @@ module Toys
9
9
  # Current version of Toys core.
10
10
  # @return [String]
11
11
  #
12
- VERSION = "0.13.1"
12
+ VERSION = "0.14.2"
13
13
  end
14
14
  end
@@ -263,7 +263,8 @@ module Toys
263
263
  # end
264
264
  # end
265
265
  #
266
- # The following example defines a tool that runs one of its subtools.
266
+ # The following example uses `delegate_to` to define a tool that runs one
267
+ # of its subtools.
267
268
  #
268
269
  # tool "test", delegate_to: ["test", "unit"] do
269
270
  # tool "unit" do
@@ -284,19 +285,22 @@ module Toys
284
285
  # delegate to another tool, specified by the full path. This path may
285
286
  # be given as an array of strings, or a single string possibly
286
287
  # delimited by path separators.
288
+ # @param delegate_relative [String,Array<String>] Optional. Similar to
289
+ # delegate_to, but takes a delegate name relative to the context in
290
+ # which this tool is being defined.
287
291
  # @param block [Proc] Defines the subtool.
288
292
  # @return [self]
289
293
  #
290
- def tool(words, if_defined: :combine, delegate_to: nil, &block)
294
+ def tool(words, if_defined: :combine, delegate_to: nil, delegate_relative: nil, &block)
291
295
  # Source available in the toys-core gem
292
296
  end
293
297
 
294
298
  ##
295
299
  # Create an alias, representing an "alternate name" for a tool.
296
300
  #
297
- # This is functionally equivalent to creating a subtool with the
298
- # `delegate_to` option, except that `alias_tool` takes a _relative_ name
299
- # for the delegate.
301
+ # Note: This is functionally equivalent to creating a tool with the
302
+ # `:delegate_relative` option. As such, `alias_tool` is considered
303
+ # deprecated.
300
304
  #
301
305
  # ### Example
302
306
  #
@@ -309,20 +313,24 @@ module Toys
309
313
  # end
310
314
  # end
311
315
  # alias_tool "t", "test"
316
+ # # Note: the following is preferred over alias_tool:
317
+ # # tool "t", delegate_relative: "test"
312
318
  #
313
319
  # @param word [String] The name of the alias
314
320
  # @param target [String,Array<String>] Relative path to the target of the
315
321
  # alias. This path may be given as an array of strings, or a single
316
322
  # string possibly delimited by path separators.
317
323
  # @return [self]
324
+ # @deprecated Use {#tool} and pass `:delegate_relative` instead
318
325
  #
319
326
  def alias_tool(word, target)
320
327
  # Source available in the toys-core gem
321
328
  end
322
329
 
323
330
  ##
324
- # Causes the current tool to delegate to another tool. When run, it
325
- # simply invokes the target tool with the same arguments.
331
+ # Causes the current tool to delegate to another tool, specified by the
332
+ # full tool name. When run, it simply invokes the target tool with the
333
+ # same arguments.
326
334
  #
327
335
  # ### Example
328
336
  #