toys-core 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/docs/guide.md +7 -7
- data/lib/toys/arg_parser.rb +7 -2
- data/lib/toys/core.rb +1 -1
- data/lib/toys/dsl/flag.rb +14 -11
- data/lib/toys/dsl/flag_group.rb +12 -9
- data/lib/toys/dsl/tool.rb +47 -15
- data/lib/toys/flag.rb +15 -12
- data/lib/toys/loader.rb +97 -9
- data/lib/toys/source_info.rb +92 -29
- data/lib/toys/standard_mixins/gems.rb +24 -5
- data/lib/toys/tool_definition.rb +10 -9
- data/lib/toys/utils/git_cache.rb +17 -2
- metadata +18 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: be1392e6cb8f49c19e9472449943cf873209b939d11cdc1e788bb58a1083c013
|
|
4
|
+
data.tar.gz: d78818495765389e99111032e863dc21d4fa982dfde07780a5b1ff9cc96743df
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 63ca5e62ed30e068d7b75a0b08e3916fc19466d92ca165d52f8f333e9538f96358027fdd676690042c5eb55d2e2569c3c1a8b8d9c32dd33f7ec3d4036230bc81
|
|
7
|
+
data.tar.gz: 61821ef2ffde83d82469c6a8d38ba092f1efc2a3254a1f848d877fda09a888fc932274e27c742c53d0480a55abc1b49fc6266359ceeca31e8851e16dac3ce0e8
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
+
### v0.17.0 / 2025-11-07
|
|
4
|
+
|
|
5
|
+
Toys-Core 0.17 supports several significant new pieces of functionality:
|
|
6
|
+
|
|
7
|
+
* Support for loading tools from Rubygems. The load_gem directive loads tools from the "toys" directory in a gem, installing the gem if necessary. This makes it easy to distribute tools, securely and versioned, as gems.
|
|
8
|
+
* Flag handlers can now take an optional third argument, the entire options hash. This enables significantly more powerful behavior during flag parsing, such as letting flags affect the behavior of other flags.
|
|
9
|
+
|
|
10
|
+
Additional new features:
|
|
11
|
+
|
|
12
|
+
* When using the :gems mixin, you can now specify installation options such as on_missing not only when you include the mixin, but also when you declare the gem.
|
|
13
|
+
* Added support for an environment variable `TOYS_GIT_CACHE_WRITABLE` to disable the read-only behavior of git cache sources. This improves compatibility with environments that want to delete caches.
|
|
14
|
+
|
|
15
|
+
Other fixes and documentation:
|
|
16
|
+
|
|
17
|
+
* Added the standard logger gem to the toys-core dependencies to silence Ruby 3.5 warnings.
|
|
18
|
+
* Updated the user guide to cover new features and fix some internal links
|
|
19
|
+
|
|
3
20
|
### v0.16.0 / 2025-10-31
|
|
4
21
|
|
|
5
22
|
* ADDED: Updated minimum Ruby version to 2.7
|
data/docs/guide.md
CHANGED
|
@@ -159,7 +159,7 @@ If only `foo` is requested, the loader will execute the `tool "foo" do` block
|
|
|
159
159
|
to get that tool definition, but will not execute the `tool "bar" do` block.
|
|
160
160
|
|
|
161
161
|
We will discuss more about the features of the loader below in the section on
|
|
162
|
-
[defining functionality](#
|
|
162
|
+
[defining functionality](#defining-functionality).
|
|
163
163
|
|
|
164
164
|
#### Building context
|
|
165
165
|
|
|
@@ -209,16 +209,16 @@ Generally, you control CLI features by passing arguments to its constructor.
|
|
|
209
209
|
These features include:
|
|
210
210
|
|
|
211
211
|
* How to find toys files and related code and data. See the section on
|
|
212
|
-
[defining functionality](#
|
|
212
|
+
[defining functionality](#defining-functionality).
|
|
213
213
|
* Middleware, providing common behavior for all tools. See the section on
|
|
214
|
-
[customizing the middleware stack](#
|
|
214
|
+
[customizing the middleware stack](#customizing-default-behavior).
|
|
215
215
|
* Common mixins and templates available to all tools. See the section on
|
|
216
|
-
[how to define mixins and templates](#
|
|
216
|
+
[how to define mixins and templates](#defining-mixins-and-templates).
|
|
217
217
|
* How logs, errors, and signals are reported. See the section on
|
|
218
|
-
[customizing tool output](#
|
|
218
|
+
[customizing tool output](#customizing-tool-output).
|
|
219
219
|
* How the executable interacts with the shell, including setting up tab
|
|
220
220
|
completion. See the
|
|
221
|
-
[corresponding section](#
|
|
221
|
+
[corresponding section](#shell-and-command-line-integration).
|
|
222
222
|
|
|
223
223
|
Each of the actual parameters is covered in detail in the documentation for
|
|
224
224
|
{Toys::CLI#initialize}. The configuration of a CLI cannot be changed once the
|
|
@@ -663,7 +663,7 @@ execute.
|
|
|
663
663
|
# This is a context key that will be used to store the "--show-timing"
|
|
664
664
|
# flag state. We can use `Object.new` to ensure that the key is unique
|
|
665
665
|
# across other middlewares and tool definitions.
|
|
666
|
-
KEY = Object.new
|
|
666
|
+
KEY = Object.new.freeze
|
|
667
667
|
|
|
668
668
|
# This method intercepts tool configuration. We use it to add a flag that
|
|
669
669
|
# enables timing display.
|
data/lib/toys/arg_parser.rb
CHANGED
|
@@ -416,7 +416,7 @@ module Toys
|
|
|
416
416
|
private
|
|
417
417
|
|
|
418
418
|
REMAINING_HANDLER = ->(val, prev) { prev.is_a?(::Array) ? prev << val : [val] }
|
|
419
|
-
ARG_HANDLER = ->(val
|
|
419
|
+
ARG_HANDLER = ->(val) { val }
|
|
420
420
|
private_constant :REMAINING_HANDLER, :ARG_HANDLER
|
|
421
421
|
|
|
422
422
|
def initial_data(cli, tool, default_data)
|
|
@@ -551,7 +551,12 @@ module Toys
|
|
|
551
551
|
value = accept.convert(*Array(match))
|
|
552
552
|
end
|
|
553
553
|
if handler
|
|
554
|
-
|
|
554
|
+
args = [value, @data[key], @data]
|
|
555
|
+
if handler.lambda?
|
|
556
|
+
limit = handler.arity.negative? ? -handler.arity - 1 : handler.arity
|
|
557
|
+
args = args[...limit]
|
|
558
|
+
end
|
|
559
|
+
value = handler.call(*args)
|
|
555
560
|
end
|
|
556
561
|
@data[key] = value
|
|
557
562
|
end
|
data/lib/toys/core.rb
CHANGED
data/lib/toys/dsl/flag.rb
CHANGED
|
@@ -114,17 +114,20 @@ module Toys
|
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
##
|
|
117
|
-
# Set the optional handler
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
# You
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
#
|
|
117
|
+
# Set the optional handler that customizes how a value is set or updated
|
|
118
|
+
# when the flag is parsed.
|
|
119
|
+
#
|
|
120
|
+
# A handler is a proc that takes up to three arguments: the given value,
|
|
121
|
+
# the previous value, and a hash containing all the data collected so far
|
|
122
|
+
# during argument parsing. It must return the new value for the flag. You
|
|
123
|
+
# You may pass the handler as a Proc (or an object responding to the
|
|
124
|
+
# `call` method) or you may provide a block.
|
|
125
|
+
#
|
|
126
|
+
# You may also specify a predefined named handler. The `:set` handler
|
|
127
|
+
# (the default) replaces the previous value (effectively
|
|
128
|
+
# `-> (val) { val }`). The `:push` handler expects the previous value to
|
|
129
|
+
# be an array and pushes the given value onto it; it should be combined
|
|
130
|
+
# with setting the default value to `[]` and is intended for
|
|
128
131
|
# "multi-valued" flags.
|
|
129
132
|
#
|
|
130
133
|
# @param handler [Proc,:set,:push]
|
data/lib/toys/dsl/flag_group.rb
CHANGED
|
@@ -147,15 +147,18 @@ module Toys
|
|
|
147
147
|
# @param default [Object] The default value. This is the value that will
|
|
148
148
|
# be set in the context if this flag is not provided on the command
|
|
149
149
|
# line. Defaults to `nil`.
|
|
150
|
-
# @param handler [Proc,nil,:set,:push] An optional handler
|
|
151
|
-
#
|
|
152
|
-
#
|
|
153
|
-
#
|
|
154
|
-
#
|
|
155
|
-
#
|
|
156
|
-
#
|
|
157
|
-
#
|
|
158
|
-
# `
|
|
150
|
+
# @param handler [Proc,nil,:set,:push] An optional handler that
|
|
151
|
+
# customizes how a value is set or updated when the flag is parsed.
|
|
152
|
+
# A handler is a proc that takes up to three arguments: the given
|
|
153
|
+
# value, the previous value, and a hash containing all the data
|
|
154
|
+
# collected so far during argument parsing. The proc must return the
|
|
155
|
+
# new value for the flag.
|
|
156
|
+
# You may also specify a predefined named handler. The `:set` handler
|
|
157
|
+
# (the default) replaces the previous value (effectively
|
|
158
|
+
# `-> (val) { val }`). The `:push` handler expects the previous value
|
|
159
|
+
# to be an array and pushes the given value onto it; it should be
|
|
160
|
+
# combined with setting the default value to `[]` and is intended for
|
|
161
|
+
# "multi-valued" flags.
|
|
159
162
|
# @param complete_flags [Object] A specifier for shell tab completion
|
|
160
163
|
# for flag names associated with this flag. By default, a
|
|
161
164
|
# {Toys::Flag::DefaultCompletion} is used, which provides the flag's
|
data/lib/toys/dsl/tool.rb
CHANGED
|
@@ -436,9 +436,12 @@ module Toys
|
|
|
436
436
|
# current commit if already loading from git, or to `HEAD`.
|
|
437
437
|
# @param as [String] Load into the given tool/namespace. If omitted,
|
|
438
438
|
# configuration will be loaded into the current namespace.
|
|
439
|
-
# @param update [Boolean]
|
|
440
|
-
# is a SHA).
|
|
441
|
-
# names, are up to date.
|
|
439
|
+
# @param update [Boolean,Integer] Whether and when to force-fetch from
|
|
440
|
+
# the remote (unless the commit is a SHA). Force-fetching will ensure
|
|
441
|
+
# that symbolic commits, such as branch names or HEAD, are up to date.
|
|
442
|
+
# You can pass `true` or `false` to specify whether to update, or an
|
|
443
|
+
# integer to update if the last update was done at least that many
|
|
444
|
+
# seconds ago. Default is false.
|
|
442
445
|
#
|
|
443
446
|
# @return [self]
|
|
444
447
|
#
|
|
@@ -453,9 +456,35 @@ module Toys
|
|
|
453
456
|
raise ToolDefinitionError, "Git remote not specified" unless remote
|
|
454
457
|
path ||= ""
|
|
455
458
|
commit ||= source_info.git_commit || "HEAD"
|
|
456
|
-
@__loader.load_git(source_info, remote, path, commit,
|
|
457
|
-
@__words, @__remaining_words, @__priority
|
|
458
|
-
|
|
459
|
+
@__loader.load_git(source_info, remote, path, commit, update,
|
|
460
|
+
@__words, @__remaining_words, @__priority)
|
|
461
|
+
self
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
##
|
|
465
|
+
# Load configuration from a gem, as if its contents were inserted at the
|
|
466
|
+
# current location.
|
|
467
|
+
#
|
|
468
|
+
# @param name [String] Name of the gem
|
|
469
|
+
# @param version [String,Array<String>] Version requirements for the gem.
|
|
470
|
+
# @param path [String] Optional path within the gem to the file or
|
|
471
|
+
# directory to load. Defaults to the root of the gem's toys directory.
|
|
472
|
+
# @param toys_dir [String] Optional override for the gem's toys
|
|
473
|
+
# directory name. If not specified, the default specified by the gem
|
|
474
|
+
# will be used.
|
|
475
|
+
# @param as [String] Load into the given tool/namespace. If omitted,
|
|
476
|
+
# configuration will be loaded into the current namespace.
|
|
477
|
+
#
|
|
478
|
+
def load_gem(name, version: nil, path: nil, toys_dir: nil, as: nil)
|
|
479
|
+
if as
|
|
480
|
+
tool(as) do
|
|
481
|
+
load_gem(name, version: version, path: path, toys_dir: toys_dir)
|
|
482
|
+
end
|
|
483
|
+
return self
|
|
484
|
+
end
|
|
485
|
+
path ||= ""
|
|
486
|
+
@__loader.load_gem(source_info, name, version, toys_dir, path,
|
|
487
|
+
@__words, @__remaining_words, @__priority)
|
|
459
488
|
self
|
|
460
489
|
end
|
|
461
490
|
|
|
@@ -946,15 +975,18 @@ module Toys
|
|
|
946
975
|
# @param default [Object] The default value. This is the value that will
|
|
947
976
|
# be set in the context if this flag is not provided on the command
|
|
948
977
|
# line. Defaults to `nil`.
|
|
949
|
-
# @param handler [Proc,nil,:set,:push] An optional handler
|
|
950
|
-
#
|
|
951
|
-
#
|
|
952
|
-
#
|
|
953
|
-
#
|
|
954
|
-
#
|
|
955
|
-
#
|
|
956
|
-
#
|
|
957
|
-
# `
|
|
978
|
+
# @param handler [Proc,nil,:set,:push] An optional handler that
|
|
979
|
+
# customizes how a value is set or updated when the flag is parsed.
|
|
980
|
+
# A handler is a proc that takes up to three arguments: the given
|
|
981
|
+
# value, the previous value, and a hash containing all the data
|
|
982
|
+
# collected so far during argument parsing. The proc must return the
|
|
983
|
+
# new value for the flag.
|
|
984
|
+
# You may also specify a predefined named handler. The `:set` handler
|
|
985
|
+
# (the default) replaces the previous value (effectively
|
|
986
|
+
# `-> (val) { val }`). The `:push` handler expects the previous value
|
|
987
|
+
# to be an array and pushes the given value onto it; it should be
|
|
988
|
+
# combined with setting the default value to `[]` and is intended for
|
|
989
|
+
# "multi-valued" flags.
|
|
958
990
|
# @param complete_flags [Object] A specifier for shell tab completion
|
|
959
991
|
# for flag names associated with this flag. By default, a
|
|
960
992
|
# {Toys::Flag::DefaultCompletion} is used, which provides the flag's
|
data/lib/toys/flag.rb
CHANGED
|
@@ -374,16 +374,16 @@ module Toys
|
|
|
374
374
|
# The set handler replaces the previous value.
|
|
375
375
|
# @return [Proc]
|
|
376
376
|
#
|
|
377
|
-
SET_HANDLER =
|
|
377
|
+
SET_HANDLER = proc { |val| val }
|
|
378
378
|
|
|
379
379
|
##
|
|
380
380
|
# The push handler pushes the given value using the `<<` operator.
|
|
381
381
|
# @return [Proc]
|
|
382
382
|
#
|
|
383
|
-
PUSH_HANDLER =
|
|
383
|
+
PUSH_HANDLER = proc { |val, prev| prev.nil? ? [val] : prev << val }
|
|
384
384
|
|
|
385
385
|
##
|
|
386
|
-
# The default handler is the set handler,
|
|
386
|
+
# The default handler is the set handler, which replaces the previous value.
|
|
387
387
|
# @return [Proc]
|
|
388
388
|
#
|
|
389
389
|
DEFAULT_HANDLER = SET_HANDLER
|
|
@@ -401,15 +401,18 @@ module Toys
|
|
|
401
401
|
# @param default [Object] The default value. This is the value that will
|
|
402
402
|
# be set in the context if this flag is not provided on the command
|
|
403
403
|
# line. Defaults to `nil`.
|
|
404
|
-
# @param handler [Proc,nil,:set,:push] An optional handler
|
|
405
|
-
#
|
|
406
|
-
#
|
|
407
|
-
#
|
|
408
|
-
#
|
|
409
|
-
#
|
|
410
|
-
#
|
|
411
|
-
#
|
|
412
|
-
# `
|
|
404
|
+
# @param handler [Proc,nil,:set,:push] An optional handler that customizes
|
|
405
|
+
# how a value is set or updated when the flag is parsed.
|
|
406
|
+
# A handler is a proc that takes up to three arguments: the given
|
|
407
|
+
# value, the previous value, and a hash containing all the data
|
|
408
|
+
# collected so far during argument parsing. The proc must return the
|
|
409
|
+
# new value for the flag.
|
|
410
|
+
# You may also specify a predefined named handler. The `:set` handler
|
|
411
|
+
# (the default) replaces the previous value (effectively
|
|
412
|
+
# `-> (val) { val }`). The `:push` handler expects the previous value
|
|
413
|
+
# to be an array and pushes the given value onto it; it should be
|
|
414
|
+
# combined with setting `default: []` and is intended for
|
|
415
|
+
# "multi-valued" flags.
|
|
413
416
|
# @param complete_flags [Object] A specifier for shell tab completion for
|
|
414
417
|
# flag names associated with this flag. By default, a
|
|
415
418
|
# {Toys::Flag::DefaultCompletion} is used, which provides the flag's
|
data/lib/toys/loader.rb
CHANGED
|
@@ -49,7 +49,8 @@ module Toys
|
|
|
49
49
|
mixin_lookup: nil,
|
|
50
50
|
middleware_lookup: nil,
|
|
51
51
|
template_lookup: nil,
|
|
52
|
-
git_cache: nil
|
|
52
|
+
git_cache: nil,
|
|
53
|
+
gems_util: nil)
|
|
53
54
|
if index_file_name && ::File.extname(index_file_name) != ".rb"
|
|
54
55
|
raise ::ArgumentError, "Illegal index file name #{index_file_name.inspect}"
|
|
55
56
|
end
|
|
@@ -73,6 +74,7 @@ module Toys
|
|
|
73
74
|
@middleware_stack = Middleware.stack(middleware_stack)
|
|
74
75
|
@delimiter_handler = DelimiterHandler.new(extra_delimiters)
|
|
75
76
|
@git_cache = git_cache
|
|
77
|
+
@gems_util = gems_util
|
|
76
78
|
get_tool([], -999_999)
|
|
77
79
|
end
|
|
78
80
|
|
|
@@ -204,8 +206,7 @@ module Toys
|
|
|
204
206
|
high_priority: false,
|
|
205
207
|
update: false,
|
|
206
208
|
context_directory: nil)
|
|
207
|
-
|
|
208
|
-
path = git_cache.get(git_remote, path: git_path, commit: git_commit, update: update)
|
|
209
|
+
path = resolve_git_path(git_remote, git_path, git_commit, update)
|
|
209
210
|
@mutex.synchronize do
|
|
210
211
|
raise "Cannot add a git source after tool loading has started" if @loading_started
|
|
211
212
|
priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
|
|
@@ -219,6 +220,43 @@ module Toys
|
|
|
219
220
|
self
|
|
220
221
|
end
|
|
221
222
|
|
|
223
|
+
##
|
|
224
|
+
# Add a configuration gem source to the loader.
|
|
225
|
+
#
|
|
226
|
+
# @param gem_name [String] The name of the gem
|
|
227
|
+
# @param gem_version [String,Array<String>] The version requirements
|
|
228
|
+
# @param gem_path [String] The path from the gem's toys directory to the
|
|
229
|
+
# relevant file or directory. Specify the empty string to use the
|
|
230
|
+
# entire toys directory.
|
|
231
|
+
# @param high_priority [Boolean] If true, add this path at the top of the
|
|
232
|
+
# priority list. Defaults to false, indicating the new path should be
|
|
233
|
+
# at the bottom of the priority list.
|
|
234
|
+
# @param gem_toys_dir [String] The name of the toys directory. Optional.
|
|
235
|
+
# Defaults to the directory specified in the gem's metadata, or the
|
|
236
|
+
# value "toys".
|
|
237
|
+
# @param context_directory [String,nil] The context directory for tools
|
|
238
|
+
# loaded from this source. You can pass a directory path as a string,
|
|
239
|
+
# or `nil` to denote no context. Defaults to `nil`.
|
|
240
|
+
# @return [self]
|
|
241
|
+
#
|
|
242
|
+
def add_gem(gem_name, gem_version, gem_path,
|
|
243
|
+
high_priority: false,
|
|
244
|
+
gem_toys_dir: nil,
|
|
245
|
+
context_directory: nil)
|
|
246
|
+
gem_version, gem_path, path = resolve_gem_info(gem_name, gem_version, gem_toys_dir, gem_path)
|
|
247
|
+
@mutex.synchronize do
|
|
248
|
+
raise "Cannot add a gem source after tool loading has started" if @loading_started
|
|
249
|
+
priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
|
|
250
|
+
source = SourceInfo.create_gem_root(gem_name, gem_version, gem_path, path, priority,
|
|
251
|
+
context_directory: context_directory,
|
|
252
|
+
data_dir_name: @data_dir_name,
|
|
253
|
+
lib_dir_name: @lib_dir_name)
|
|
254
|
+
@roots_by_priority[priority] = source
|
|
255
|
+
@worklist << [source, [], priority]
|
|
256
|
+
end
|
|
257
|
+
self
|
|
258
|
+
end
|
|
259
|
+
|
|
222
260
|
##
|
|
223
261
|
# Given a list of command line arguments, find the appropriate tool to
|
|
224
262
|
# handle the command, loading it from the configuration if necessary.
|
|
@@ -439,8 +477,7 @@ module Toys
|
|
|
439
477
|
#
|
|
440
478
|
def load_path(parent_source, path, words, remaining_words, priority)
|
|
441
479
|
if parent_source.git_remote
|
|
442
|
-
raise LoaderError,
|
|
443
|
-
"Git source #{parent_source.source_name} tried to load from the local file system"
|
|
480
|
+
raise LoaderError, "Git source #{parent_source.source_name} tried to load from the local file system"
|
|
444
481
|
end
|
|
445
482
|
source = parent_source.absolute_child(path)
|
|
446
483
|
@mutex.synchronize do
|
|
@@ -454,16 +491,30 @@ module Toys
|
|
|
454
491
|
#
|
|
455
492
|
# @private This interface is internal and subject to change without warning.
|
|
456
493
|
#
|
|
457
|
-
def load_git(parent_source, git_remote, git_path, git_commit,
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
path = git_cache.get(git_remote, path: git_path, commit: git_commit, update: update)
|
|
494
|
+
def load_git(parent_source, git_remote, git_path, git_commit, update,
|
|
495
|
+
words, remaining_words, priority)
|
|
496
|
+
path = resolve_git_path(git_remote, git_path, git_commit, update)
|
|
461
497
|
source = parent_source.git_child(git_remote, git_path, git_commit, path)
|
|
462
498
|
@mutex.synchronize do
|
|
463
499
|
load_validated_path(source, words, remaining_words, priority)
|
|
464
500
|
end
|
|
465
501
|
end
|
|
466
502
|
|
|
503
|
+
##
|
|
504
|
+
# Load configuration from the given gem. This is called from the `load_gem`
|
|
505
|
+
# directive in the DSL.
|
|
506
|
+
#
|
|
507
|
+
# @private This interface is internal and subject to change without warning.
|
|
508
|
+
#
|
|
509
|
+
def load_gem(parent_source, gem_name, gem_version, gem_toys_dir, gem_path,
|
|
510
|
+
words, remaining_words, priority)
|
|
511
|
+
gem_version, gem_path, path = resolve_gem_info(gem_name, gem_version, gem_toys_dir, gem_path)
|
|
512
|
+
source = parent_source.gem_child(gem_name, gem_version, gem_path, path)
|
|
513
|
+
@mutex.synchronize do
|
|
514
|
+
load_validated_path(source, words, remaining_words, priority)
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
|
|
467
518
|
##
|
|
468
519
|
# Load a subtool block. Called from the `tool` directive in the DSL.
|
|
469
520
|
#
|
|
@@ -478,6 +529,7 @@ module Toys
|
|
|
478
529
|
|
|
479
530
|
@git_cache_mutex = ::Mutex.new
|
|
480
531
|
@default_git_cache = nil
|
|
532
|
+
@default_gems_util = nil
|
|
481
533
|
|
|
482
534
|
##
|
|
483
535
|
# Get a global default GitCache.
|
|
@@ -493,6 +545,20 @@ module Toys
|
|
|
493
545
|
end
|
|
494
546
|
end
|
|
495
547
|
|
|
548
|
+
##
|
|
549
|
+
# Get a global default Gems utility.
|
|
550
|
+
#
|
|
551
|
+
# @private This interface is internal and subject to change without warning.
|
|
552
|
+
#
|
|
553
|
+
def self.default_gems_util
|
|
554
|
+
@git_cache_mutex.synchronize do
|
|
555
|
+
@default_gems_util ||= begin
|
|
556
|
+
require "toys/utils/gems"
|
|
557
|
+
Utils::Gems.new
|
|
558
|
+
end
|
|
559
|
+
end
|
|
560
|
+
end
|
|
561
|
+
|
|
496
562
|
##
|
|
497
563
|
# Determine the next setting for remaining_words, given a word.
|
|
498
564
|
#
|
|
@@ -640,6 +706,28 @@ module Toys
|
|
|
640
706
|
|
|
641
707
|
private
|
|
642
708
|
|
|
709
|
+
##
|
|
710
|
+
# Resolve the file system path to the given object in the git cache
|
|
711
|
+
#
|
|
712
|
+
def resolve_git_path(git_remote, git_path, git_commit, update)
|
|
713
|
+
git_cache = @git_cache || Loader.default_git_cache
|
|
714
|
+
git_cache.get(git_remote, path: git_path, commit: git_commit, update: update)
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
##
|
|
718
|
+
# Resolve information for a gem source.
|
|
719
|
+
#
|
|
720
|
+
def resolve_gem_info(gem_name, gem_version, gem_toys_dir, gem_path)
|
|
721
|
+
gems_util = @gems_util || Loader.default_gems_util
|
|
722
|
+
gems_util.activate(gem_name, *Array(gem_version))
|
|
723
|
+
gem_spec = ::Gem.loaded_specs[gem_name]
|
|
724
|
+
raise LoaderError, "Unable to find gem #{gem_name}" unless gem_spec&.gem_dir
|
|
725
|
+
gem_toys_dir ||= gem_spec.metadata["toys_dir"] || "toys"
|
|
726
|
+
gem_path = gem_path ? ::File.join(gem_toys_dir, gem_path) : gem_toys_dir
|
|
727
|
+
path = ::File.join(gem_spec.gem_dir, gem_path)
|
|
728
|
+
[gem_spec.version, gem_path, path]
|
|
729
|
+
end
|
|
730
|
+
|
|
643
731
|
##
|
|
644
732
|
# Return a snapshot of all the current tool definitions that have been
|
|
645
733
|
# loaded. No additional loading is done. The returned array is not in any
|
data/lib/toys/source_info.rb
CHANGED
|
@@ -11,6 +11,7 @@ module Toys
|
|
|
11
11
|
# * A toys directory
|
|
12
12
|
# * A single toys file
|
|
13
13
|
# * A file or directory loaded from git
|
|
14
|
+
# * A file or directory loaded from a gem
|
|
14
15
|
# * A config block passed directly to the CLI
|
|
15
16
|
# * A tool block within a toys file
|
|
16
17
|
#
|
|
@@ -143,6 +144,32 @@ module Toys
|
|
|
143
144
|
#
|
|
144
145
|
attr_reader :git_commit
|
|
145
146
|
|
|
147
|
+
##
|
|
148
|
+
# The gem name. This is set if the source, or one of its ancestors, comes
|
|
149
|
+
# from a gem.
|
|
150
|
+
#
|
|
151
|
+
# @return [String] The gem name.
|
|
152
|
+
# @return [nil] if this source is not from a gem.
|
|
153
|
+
#
|
|
154
|
+
attr_reader :gem_name
|
|
155
|
+
|
|
156
|
+
##
|
|
157
|
+
# The gem version. This is set if the source, or one of its ancestors,
|
|
158
|
+
# comes from a gem.
|
|
159
|
+
#
|
|
160
|
+
# @return [Gem::Version] The gem version.
|
|
161
|
+
# @return [nil] if this source is not from a gem.
|
|
162
|
+
#
|
|
163
|
+
attr_reader :gem_version
|
|
164
|
+
|
|
165
|
+
##
|
|
166
|
+
# The path within the gem, including the toys root directory in the gem.
|
|
167
|
+
#
|
|
168
|
+
# @return [String] The path.
|
|
169
|
+
# @return [nil] if this source is not from a gem.
|
|
170
|
+
#
|
|
171
|
+
attr_reader :gem_path
|
|
172
|
+
|
|
146
173
|
##
|
|
147
174
|
# A user-visible name of this source.
|
|
148
175
|
#
|
|
@@ -191,8 +218,10 @@ module Toys
|
|
|
191
218
|
#
|
|
192
219
|
# @private This interface is internal and subject to change without warning.
|
|
193
220
|
#
|
|
194
|
-
def initialize(parent, priority, context_directory,
|
|
195
|
-
|
|
221
|
+
def initialize(parent, priority, context_directory,
|
|
222
|
+
source_type, source_path, source_proc,
|
|
223
|
+
git_remote, git_path, git_commit, gem_name, gem_version, gem_path,
|
|
224
|
+
source_name, data_dir_name, lib_dir_name)
|
|
196
225
|
@parent = parent
|
|
197
226
|
@root = parent&.root || self
|
|
198
227
|
@priority = priority
|
|
@@ -204,7 +233,10 @@ module Toys
|
|
|
204
233
|
@git_remote = git_remote
|
|
205
234
|
@git_path = git_path
|
|
206
235
|
@git_commit = git_commit
|
|
207
|
-
@
|
|
236
|
+
@gem_name = gem_name
|
|
237
|
+
@gem_version = gem_version
|
|
238
|
+
@gem_path = gem_path
|
|
239
|
+
@source_name = source_name || default_source_name
|
|
208
240
|
@data_dir_name = data_dir_name
|
|
209
241
|
@lib_dir_name = lib_dir_name
|
|
210
242
|
@data_dir = find_special_dir(data_dir_name)
|
|
@@ -220,18 +252,12 @@ module Toys
|
|
|
220
252
|
unless source_type == :directory
|
|
221
253
|
raise LoaderError, "relative_child is valid only on a directory source"
|
|
222
254
|
end
|
|
223
|
-
child_path = ::File.join(source_path, filename)
|
|
224
|
-
child_path, type = SourceInfo.check_path(child_path, true)
|
|
255
|
+
child_path, type = SourceInfo.check_path(::File.join(source_path, filename), true)
|
|
225
256
|
return nil unless child_path
|
|
226
|
-
child_git_path = ::File.join(git_path, filename) if git_path
|
|
227
|
-
|
|
228
|
-
if git_path
|
|
229
|
-
"git(remote=#{git_remote} path=#{child_git_path} commit=#{git_commit})"
|
|
230
|
-
else
|
|
231
|
-
child_path
|
|
232
|
-
end
|
|
257
|
+
child_git_path = git_path.empty? ? filename : ::File.join(git_path, filename) if git_path
|
|
258
|
+
child_gem_path = gem_path.empty? ? filename : ::File.join(gem_path, filename) if gem_path
|
|
233
259
|
SourceInfo.new(self, priority, context_directory, type, child_path, nil,
|
|
234
|
-
git_remote, child_git_path, git_commit,
|
|
260
|
+
git_remote, child_git_path, git_commit, gem_name, gem_version, child_gem_path,
|
|
235
261
|
source_name, @data_dir_name, @lib_dir_name)
|
|
236
262
|
end
|
|
237
263
|
|
|
@@ -242,8 +268,8 @@ module Toys
|
|
|
242
268
|
#
|
|
243
269
|
def absolute_child(child_path, source_name: nil)
|
|
244
270
|
child_path, type = SourceInfo.check_path(child_path, false)
|
|
245
|
-
|
|
246
|
-
|
|
271
|
+
SourceInfo.new(self, priority, context_directory, type, child_path, nil,
|
|
272
|
+
nil, nil, nil, nil, nil, nil,
|
|
247
273
|
source_name, @data_dir_name, @lib_dir_name)
|
|
248
274
|
end
|
|
249
275
|
|
|
@@ -252,13 +278,22 @@ module Toys
|
|
|
252
278
|
#
|
|
253
279
|
# @private This interface is internal and subject to change without warning.
|
|
254
280
|
#
|
|
255
|
-
def git_child(child_git_remote, child_git_path, child_git_commit, child_path,
|
|
256
|
-
|
|
281
|
+
def git_child(child_git_remote, child_git_path, child_git_commit, child_path, source_name: nil)
|
|
282
|
+
child_path, type = SourceInfo.check_path(child_path, false)
|
|
283
|
+
SourceInfo.new(self, priority, context_directory, type, child_path, nil,
|
|
284
|
+
child_git_remote, child_git_path, child_git_commit, nil, nil, nil,
|
|
285
|
+
source_name, @data_dir_name, @lib_dir_name)
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
##
|
|
289
|
+
# Create a child SourceInfo with a gem source.
|
|
290
|
+
#
|
|
291
|
+
# @private This interface is internal and subject to change without warning.
|
|
292
|
+
#
|
|
293
|
+
def gem_child(child_gem_name, child_gem_version, child_gem_path, child_path, source_name: nil)
|
|
257
294
|
child_path, type = SourceInfo.check_path(child_path, false)
|
|
258
|
-
source_name ||=
|
|
259
|
-
"git(remote=#{child_git_remote} path=#{child_git_path} commit=#{child_git_commit})"
|
|
260
295
|
SourceInfo.new(self, priority, context_directory, type, child_path, nil,
|
|
261
|
-
|
|
296
|
+
nil, nil, nil, child_gem_name, child_gem_version, child_gem_path,
|
|
262
297
|
source_name, @data_dir_name, @lib_dir_name)
|
|
263
298
|
end
|
|
264
299
|
|
|
@@ -270,7 +305,7 @@ module Toys
|
|
|
270
305
|
def proc_child(child_proc, source_name: nil)
|
|
271
306
|
source_name ||= self.source_name
|
|
272
307
|
SourceInfo.new(self, priority, context_directory, :proc, source_path, child_proc,
|
|
273
|
-
git_remote, git_path, git_commit,
|
|
308
|
+
git_remote, git_path, git_commit, gem_name, gem_version, gem_path,
|
|
274
309
|
source_name, @data_dir_name, @lib_dir_name)
|
|
275
310
|
end
|
|
276
311
|
|
|
@@ -291,8 +326,8 @@ module Toys
|
|
|
291
326
|
when :path
|
|
292
327
|
context_directory = source_path
|
|
293
328
|
end
|
|
294
|
-
|
|
295
|
-
|
|
329
|
+
new(nil, priority, context_directory, type, source_path, nil,
|
|
330
|
+
nil, nil, nil, nil, nil, nil,
|
|
296
331
|
source_name, data_dir_name, lib_dir_name)
|
|
297
332
|
end
|
|
298
333
|
|
|
@@ -307,9 +342,25 @@ module Toys
|
|
|
307
342
|
lib_dir_name: nil,
|
|
308
343
|
source_name: nil)
|
|
309
344
|
source_path, type = check_path(source_path, false)
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
345
|
+
new(nil, priority, context_directory, type, source_path, nil,
|
|
346
|
+
git_remote, git_path, git_commit, nil, nil, nil,
|
|
347
|
+
source_name, data_dir_name, lib_dir_name)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
##
|
|
351
|
+
# Create a root source info for a loaded gem.
|
|
352
|
+
#
|
|
353
|
+
# @private This interface is internal and subject to change without warning.
|
|
354
|
+
#
|
|
355
|
+
def self.create_gem_root(gem_name, gem_version, gem_path, source_path, priority,
|
|
356
|
+
context_directory: nil,
|
|
357
|
+
data_dir_name: nil,
|
|
358
|
+
lib_dir_name: nil,
|
|
359
|
+
source_name: nil)
|
|
360
|
+
source_path, type = check_path(source_path, false)
|
|
361
|
+
new(nil, priority, context_directory, type, source_path, nil,
|
|
362
|
+
nil, nil, nil, gem_name, gem_version, gem_path,
|
|
363
|
+
source_name, data_dir_name, lib_dir_name)
|
|
313
364
|
end
|
|
314
365
|
|
|
315
366
|
##
|
|
@@ -322,9 +373,9 @@ module Toys
|
|
|
322
373
|
data_dir_name: nil,
|
|
323
374
|
lib_dir_name: nil,
|
|
324
375
|
source_name: nil)
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
376
|
+
new(nil, priority, context_directory, :proc, nil, source_proc,
|
|
377
|
+
nil, nil, nil, nil, nil, nil,
|
|
378
|
+
source_name, data_dir_name, lib_dir_name)
|
|
328
379
|
end
|
|
329
380
|
|
|
330
381
|
##
|
|
@@ -354,6 +405,18 @@ module Toys
|
|
|
354
405
|
|
|
355
406
|
private
|
|
356
407
|
|
|
408
|
+
def default_source_name
|
|
409
|
+
if @git_remote
|
|
410
|
+
"git(remote=#{@git_remote} path=#{@git_path} commit=#{@git_commit})"
|
|
411
|
+
elsif @gem_name
|
|
412
|
+
"gem(name=#{@gem_name} version=#{@gem_version} path=#{@gem_path})"
|
|
413
|
+
elsif @source_type == :proc
|
|
414
|
+
"(code block #{@source_proc.object_id})"
|
|
415
|
+
else
|
|
416
|
+
@source_path
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
|
|
357
420
|
def find_special_dir(dir_name)
|
|
358
421
|
return nil if @source_type != :directory || dir_name.nil?
|
|
359
422
|
dir = ::File.join(@source_path, dir_name)
|
|
@@ -22,8 +22,8 @@ module Toys
|
|
|
22
22
|
#
|
|
23
23
|
# tool "my_tool" do
|
|
24
24
|
# include :gems
|
|
25
|
-
# gem "nokogiri", "~> 1.15"
|
|
26
25
|
# def run
|
|
26
|
+
# gem "nokogiri", "~> 1.15"
|
|
27
27
|
# # Do stuff with Nokogiri
|
|
28
28
|
# end
|
|
29
29
|
# end
|
|
@@ -33,6 +33,18 @@ module Toys
|
|
|
33
33
|
#
|
|
34
34
|
# include :gems, on_missing: :error
|
|
35
35
|
#
|
|
36
|
+
# You can also pass options to the {#gem} mixin method itself:
|
|
37
|
+
#
|
|
38
|
+
# tool "my_tool" do
|
|
39
|
+
# include :gems
|
|
40
|
+
# def run
|
|
41
|
+
# # If the gem is not installed, error out instead of asking to
|
|
42
|
+
# # install it.
|
|
43
|
+
# gem "nokogiri", "~> 1.15", on_missing: :error
|
|
44
|
+
# # Do stuff with Nokogiri
|
|
45
|
+
# end
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
36
48
|
# See {Toys::Utils::Gems#initialize} for a list of supported options.
|
|
37
49
|
#
|
|
38
50
|
module Gems
|
|
@@ -54,8 +66,8 @@ module Toys
|
|
|
54
66
|
# @param requirements [String...] Version requirements
|
|
55
67
|
# @return [void]
|
|
56
68
|
#
|
|
57
|
-
def gem(name, *requirements)
|
|
58
|
-
self.class.
|
|
69
|
+
def gem(name, *requirements, **options)
|
|
70
|
+
self.class.gem(name, *requirements, **options)
|
|
59
71
|
end
|
|
60
72
|
|
|
61
73
|
on_include do |**opts|
|
|
@@ -76,8 +88,15 @@ module Toys
|
|
|
76
88
|
##
|
|
77
89
|
# @private
|
|
78
90
|
#
|
|
79
|
-
def self.gem(name, *requirements)
|
|
80
|
-
|
|
91
|
+
def self.gem(name, *requirements, **options)
|
|
92
|
+
gems_util =
|
|
93
|
+
if options.empty?
|
|
94
|
+
gems
|
|
95
|
+
else
|
|
96
|
+
require "toys/utils/gems"
|
|
97
|
+
Utils::Gems.new(**options)
|
|
98
|
+
end
|
|
99
|
+
gems_util.activate(name, *requirements)
|
|
81
100
|
end
|
|
82
101
|
end
|
|
83
102
|
end
|
data/lib/toys/tool_definition.rb
CHANGED
|
@@ -1012,15 +1012,16 @@ module Toys
|
|
|
1012
1012
|
# @param default [Object] The default value. This is the value that will
|
|
1013
1013
|
# be set in the context if this flag is not provided on the command
|
|
1014
1014
|
# line. Defaults to `nil`.
|
|
1015
|
-
# @param handler [Proc,nil,:set,:push] An optional handler
|
|
1016
|
-
#
|
|
1017
|
-
# arguments
|
|
1018
|
-
#
|
|
1019
|
-
#
|
|
1020
|
-
#
|
|
1021
|
-
#
|
|
1022
|
-
#
|
|
1023
|
-
# `default: []`
|
|
1015
|
+
# @param handler [Proc,nil,:set,:push] An optional handler that customizes
|
|
1016
|
+
# how a value is set or updated. A handler is a proc that takes up to
|
|
1017
|
+
# three arguments: the given value, the previous value, and a hash
|
|
1018
|
+
# containing all the data collected so far during argument parsing. It
|
|
1019
|
+
# must return the new value that should be set. You may also specify a
|
|
1020
|
+
# predefined named handler. The `:set` handler (the default) replaces
|
|
1021
|
+
# the previous value (effectively `-> (val) { val }`). The `:push`
|
|
1022
|
+
# handler expects the previous value to be an array and pushes the
|
|
1023
|
+
# given value onto it; it should be combined with setting `default: []`
|
|
1024
|
+
# and is intended for "multi-valued" flags.
|
|
1024
1025
|
# @param complete_flags [Object] A specifier for shell tab completion
|
|
1025
1026
|
# for flag names associated with this flag. By default, a
|
|
1026
1027
|
# {Toys::Flag::DefaultCompletion} is used, which provides the flag's
|
data/lib/toys/utils/git_cache.rb
CHANGED
|
@@ -274,6 +274,21 @@ module Toys
|
|
|
274
274
|
end
|
|
275
275
|
end
|
|
276
276
|
|
|
277
|
+
##
|
|
278
|
+
# Returns whether shared source files are writable by default.
|
|
279
|
+
# Normally, shared sources are made read-only to protect them from being
|
|
280
|
+
# modified accidentally since multiple clients may be accessing them.
|
|
281
|
+
# However, you can disable this feature by setting the environment
|
|
282
|
+
# variable `TOYS_GIT_CACHE_WRITABLE` to any non-empty value. This can be
|
|
283
|
+
# useful in environments that want to clean up temporary directories and
|
|
284
|
+
# are being hindered by read-only files.
|
|
285
|
+
#
|
|
286
|
+
# @return [boolean]
|
|
287
|
+
#
|
|
288
|
+
def self.sources_writable?
|
|
289
|
+
!::ENV["TOYS_GIT_CACHE_WRITABLE"].to_s.empty?
|
|
290
|
+
end
|
|
291
|
+
|
|
277
292
|
##
|
|
278
293
|
# Access a git cache.
|
|
279
294
|
#
|
|
@@ -318,7 +333,7 @@ module Toys
|
|
|
318
333
|
# @param into [String] If provided, copies the specified files into the
|
|
319
334
|
# given directory path. If omitted or `nil`, populates and returns a
|
|
320
335
|
# shared source file or directory.
|
|
321
|
-
# @param update [Boolean,Integer] Whether to update
|
|
336
|
+
# @param update [Boolean,Integer] Whether to update non-SHA commit
|
|
322
337
|
# references if they were previously loaded. This is useful, for
|
|
323
338
|
# example, if the commit is `HEAD` or a branch name. Pass `true` or
|
|
324
339
|
# `false` to specify whether to update, or an integer to update if
|
|
@@ -569,7 +584,7 @@ module Toys
|
|
|
569
584
|
::FileUtils.mkdir_p(source_path)
|
|
570
585
|
::FileUtils.chmod_R("u+w", source_path, force: true)
|
|
571
586
|
copy_from_repo(repo_path, source_path, sha, path)
|
|
572
|
-
::FileUtils.chmod_R("a-w", source_path, force: true)
|
|
587
|
+
::FileUtils.chmod_R("a-w", source_path, force: true) unless GitCache.sources_writable?
|
|
573
588
|
end
|
|
574
589
|
repo_lock.access_source!(sha, path)
|
|
575
590
|
path == "." ? source_path : ::File.join(source_path, path)
|
metadata
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: toys-core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.17.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel Azuma
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
-
dependencies:
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: logger
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.4'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.4'
|
|
12
26
|
description: Toys-Core is the command line tool framework underlying Toys. It can
|
|
13
27
|
be used to create command line executables using the Toys DSL and classes.
|
|
14
28
|
email:
|
|
@@ -78,10 +92,10 @@ homepage: https://github.com/dazuma/toys
|
|
|
78
92
|
licenses:
|
|
79
93
|
- MIT
|
|
80
94
|
metadata:
|
|
81
|
-
changelog_uri: https://dazuma.github.io/toys/gems/toys-core/v0.
|
|
95
|
+
changelog_uri: https://dazuma.github.io/toys/gems/toys-core/v0.17.0/file.CHANGELOG.html
|
|
82
96
|
source_code_uri: https://github.com/dazuma/toys/tree/main/toys-core
|
|
83
97
|
bug_tracker_uri: https://github.com/dazuma/toys/issues
|
|
84
|
-
documentation_uri: https://dazuma.github.io/toys/gems/toys-core/v0.
|
|
98
|
+
documentation_uri: https://dazuma.github.io/toys/gems/toys-core/v0.17.0
|
|
85
99
|
rdoc_options: []
|
|
86
100
|
require_paths:
|
|
87
101
|
- lib
|