toys-core 0.15.6 → 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 +23 -1
- data/README.md +2 -2
- data/docs/guide.md +7 -7
- data/lib/toys/arg_parser.rb +9 -4
- data/lib/toys/cli.rb +8 -10
- data/lib/toys/compat.rb +11 -83
- data/lib/toys/completion.rb +3 -3
- 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/internal.rb +4 -3
- data/lib/toys/dsl/tool.rb +48 -16
- data/lib/toys/flag.rb +15 -12
- data/lib/toys/input_file.rb +3 -5
- data/lib/toys/loader.rb +99 -11
- data/lib/toys/middleware.rb +1 -1
- data/lib/toys/settings.rb +11 -2
- data/lib/toys/source_info.rb +92 -29
- data/lib/toys/standard_mixins/gems.rb +24 -5
- data/lib/toys/standard_mixins/highline.rb +12 -12
- data/lib/toys/tool_definition.rb +22 -28
- data/lib/toys/utils/exec.rb +33 -45
- data/lib/toys/utils/gems.rb +7 -7
- data/lib/toys/utils/git_cache.rb +23 -5
- data/lib/toys/utils/help_text.rb +4 -4
- data/lib/toys/utils/pager.rb +3 -5
- data/lib/toys/utils/terminal.rb +8 -12
- data/lib/toys/utils/xdg.rb +2 -2
- data/lib/toys-core.rb +2 -1
- metadata +21 -10
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.
|
|
@@ -288,7 +326,7 @@ module Toys
|
|
|
288
326
|
found_tools = all_cur_definitions.find_all do |tool|
|
|
289
327
|
name = tool.full_name
|
|
290
328
|
name.length > len && name.slice(0, len) == words &&
|
|
291
|
-
(include_hidden || name[len
|
|
329
|
+
(include_hidden || name[len..].none? { |word| word.start_with?("_") })
|
|
292
330
|
end
|
|
293
331
|
found_tools.sort_by!(&:full_name)
|
|
294
332
|
found_tools = filter_non_runnable_tools(found_tools, include_namespaces, include_non_runnable)
|
|
@@ -303,7 +341,7 @@ module Toys
|
|
|
303
341
|
# @param words [Array<String>] The name of the parent tool
|
|
304
342
|
# @return [Boolean]
|
|
305
343
|
#
|
|
306
|
-
def has_subtools?(words) # rubocop:disable Naming/
|
|
344
|
+
def has_subtools?(words) # rubocop:disable Naming/PredicatePrefix
|
|
307
345
|
load_for_prefix(words)
|
|
308
346
|
len = words.length
|
|
309
347
|
all_cur_definitions.any? do |tool|
|
|
@@ -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/middleware.rb
CHANGED
data/lib/toys/settings.rb
CHANGED
|
@@ -769,10 +769,19 @@ module Toys
|
|
|
769
769
|
attr_reader :default
|
|
770
770
|
attr_reader :group_class
|
|
771
771
|
|
|
772
|
+
##
|
|
773
|
+
# @return [boolean] Whether the field is a group
|
|
774
|
+
#
|
|
772
775
|
def group?
|
|
773
776
|
!@group_class.nil?
|
|
774
777
|
end
|
|
775
778
|
|
|
779
|
+
##
|
|
780
|
+
# Validate the given value.
|
|
781
|
+
#
|
|
782
|
+
# @return [Object] The validated value
|
|
783
|
+
# @raise [FieldError] If the value cannot be validated
|
|
784
|
+
#
|
|
776
785
|
def validate(value)
|
|
777
786
|
validated_value = @type.call(value)
|
|
778
787
|
if validated_value == ILLEGAL_VALUE
|
|
@@ -877,8 +886,8 @@ module Toys
|
|
|
877
886
|
raise ::ArgumentError, "Illegal settings field name: #{name}"
|
|
878
887
|
end
|
|
879
888
|
existing = public_instance_methods(false)
|
|
880
|
-
if existing.include?(name.to_sym) || existing.include?("#{name}="
|
|
881
|
-
existing.include?("#{name}_set?"
|
|
889
|
+
if existing.include?(name.to_sym) || existing.include?(:"#{name}=") ||
|
|
890
|
+
existing.include?(:"#{name}_set?") || existing.include?(:"#{name}_unset!")
|
|
882
891
|
raise ::ArgumentError, "Settings field already exists: #{name}"
|
|
883
892
|
end
|
|
884
893
|
name.to_sym
|
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
|
|
@@ -48,43 +48,43 @@ module Toys
|
|
|
48
48
|
##
|
|
49
49
|
# Calls [HighLine#agree](https://www.rubydoc.info/gems/highline/HighLine:agree)
|
|
50
50
|
#
|
|
51
|
-
def agree(
|
|
52
|
-
self[KEY].agree(
|
|
51
|
+
def agree(...)
|
|
52
|
+
self[KEY].agree(...)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
##
|
|
56
56
|
# Calls [HighLine#ask](https://www.rubydoc.info/gems/highline/HighLine:ask)
|
|
57
57
|
#
|
|
58
|
-
def ask(
|
|
59
|
-
self[KEY].ask(
|
|
58
|
+
def ask(...)
|
|
59
|
+
self[KEY].ask(...)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
##
|
|
63
63
|
# Calls [HighLine#choose](https://www.rubydoc.info/gems/highline/HighLine:choose)
|
|
64
64
|
#
|
|
65
|
-
def choose(
|
|
66
|
-
self[KEY].choose(
|
|
65
|
+
def choose(...)
|
|
66
|
+
self[KEY].choose(...)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
##
|
|
70
70
|
# Calls [HighLine#list](https://www.rubydoc.info/gems/highline/HighLine:list)
|
|
71
71
|
#
|
|
72
|
-
def list(
|
|
73
|
-
self[KEY].list(
|
|
72
|
+
def list(...)
|
|
73
|
+
self[KEY].list(...)
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
##
|
|
77
77
|
# Calls [HighLine#say](https://www.rubydoc.info/gems/highline/HighLine:say)
|
|
78
78
|
#
|
|
79
|
-
def say(
|
|
80
|
-
self[KEY].say(
|
|
79
|
+
def say(...)
|
|
80
|
+
self[KEY].say(...)
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
##
|
|
84
84
|
# Calls [HighLine#indent](https://www.rubydoc.info/gems/highline/HighLine:indent)
|
|
85
85
|
#
|
|
86
|
-
def indent(
|
|
87
|
-
self[KEY].indent(
|
|
86
|
+
def indent(...)
|
|
87
|
+
self[KEY].indent(...)
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
##
|
data/lib/toys/tool_definition.rb
CHANGED
|
@@ -552,9 +552,8 @@ module Toys
|
|
|
552
552
|
# @return [true,false]
|
|
553
553
|
#
|
|
554
554
|
def runnable?
|
|
555
|
-
@run_handler.is_a?(::
|
|
556
|
-
tool_class.public_instance_methods(false).include?(@run_handler)
|
|
557
|
-
@run_handler.is_a?(::Proc)
|
|
555
|
+
@run_handler.is_a?(::Proc) ||
|
|
556
|
+
(@run_handler.is_a?(::Symbol) && tool_class.public_instance_methods(false).include?(@run_handler))
|
|
558
557
|
end
|
|
559
558
|
|
|
560
559
|
##
|
|
@@ -608,8 +607,7 @@ module Toys
|
|
|
608
607
|
# @return [true,false]
|
|
609
608
|
#
|
|
610
609
|
def includes_arguments?
|
|
611
|
-
!
|
|
612
|
-
!required_args.empty? || !optional_args.empty? ||
|
|
610
|
+
!flags.empty? || !required_args.empty? || !optional_args.empty? ||
|
|
613
611
|
!remaining_arg.nil? || flags_before_args_enforced?
|
|
614
612
|
end
|
|
615
613
|
|
|
@@ -619,7 +617,7 @@ module Toys
|
|
|
619
617
|
#
|
|
620
618
|
def includes_definition?
|
|
621
619
|
includes_arguments? || runnable? || argument_parsing_disabled? ||
|
|
622
|
-
includes_modules? || includes_description?
|
|
620
|
+
includes_modules? || includes_description? || !default_data.empty?
|
|
623
621
|
end
|
|
624
622
|
|
|
625
623
|
##
|
|
@@ -690,7 +688,7 @@ module Toys
|
|
|
690
688
|
# @return [nil] if no acceptor of the given name is found.
|
|
691
689
|
#
|
|
692
690
|
def lookup_acceptor(name)
|
|
693
|
-
@acceptors.fetch(name.to_s) { |k| @parent
|
|
691
|
+
@acceptors.fetch(name.to_s) { |k| @parent&.lookup_acceptor(k) }
|
|
694
692
|
end
|
|
695
693
|
|
|
696
694
|
##
|
|
@@ -701,7 +699,7 @@ module Toys
|
|
|
701
699
|
# @return [nil] if no template of the given name is found.
|
|
702
700
|
#
|
|
703
701
|
def lookup_template(name)
|
|
704
|
-
@templates.fetch(name.to_s) { |k| @parent
|
|
702
|
+
@templates.fetch(name.to_s) { |k| @parent&.lookup_template(k) }
|
|
705
703
|
end
|
|
706
704
|
|
|
707
705
|
##
|
|
@@ -712,7 +710,7 @@ module Toys
|
|
|
712
710
|
# @return [nil] if no mixin of the given name is found.
|
|
713
711
|
#
|
|
714
712
|
def lookup_mixin(name)
|
|
715
|
-
@mixins.fetch(name.to_s) { |k| @parent
|
|
713
|
+
@mixins.fetch(name.to_s) { |k| @parent&.lookup_mixin(k) }
|
|
716
714
|
end
|
|
717
715
|
|
|
718
716
|
##
|
|
@@ -723,7 +721,7 @@ module Toys
|
|
|
723
721
|
# @return [nil] if no completion of the given name is found.
|
|
724
722
|
#
|
|
725
723
|
def lookup_completion(name)
|
|
726
|
-
@completions.fetch(name.to_s) { |k| @parent
|
|
724
|
+
@completions.fetch(name.to_s) { |k| @parent&.lookup_completion(k) }
|
|
727
725
|
end
|
|
728
726
|
|
|
729
727
|
##
|
|
@@ -1014,15 +1012,16 @@ module Toys
|
|
|
1014
1012
|
# @param default [Object] The default value. This is the value that will
|
|
1015
1013
|
# be set in the context if this flag is not provided on the command
|
|
1016
1014
|
# line. Defaults to `nil`.
|
|
1017
|
-
# @param handler [Proc,nil,:set,:push] An optional handler
|
|
1018
|
-
#
|
|
1019
|
-
# arguments
|
|
1020
|
-
#
|
|
1021
|
-
#
|
|
1022
|
-
#
|
|
1023
|
-
#
|
|
1024
|
-
#
|
|
1025
|
-
# `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.
|
|
1026
1025
|
# @param complete_flags [Object] A specifier for shell tab completion
|
|
1027
1026
|
# for flag names associated with this flag. By default, a
|
|
1028
1027
|
# {Toys::Flag::DefaultCompletion} is used, which provides the flag's
|
|
@@ -1347,15 +1346,10 @@ module Toys
|
|
|
1347
1346
|
"Cannot delegate tool #{display_name.inspect} to #{target.join(' ')} because it" \
|
|
1348
1347
|
" already delegates to \"#{@delegate_target.join(' ')}\"."
|
|
1349
1348
|
end
|
|
1350
|
-
if includes_arguments?
|
|
1351
|
-
raise ToolDefinitionError,
|
|
1352
|
-
"Cannot delegate tool #{display_name.inspect} because" \
|
|
1353
|
-
" arguments have already been defined."
|
|
1354
|
-
end
|
|
1355
|
-
if runnable?
|
|
1349
|
+
if includes_arguments? || runnable? || includes_modules? || !default_data.empty?
|
|
1356
1350
|
raise ToolDefinitionError,
|
|
1357
1351
|
"Cannot delegate tool #{display_name.inspect} because" \
|
|
1358
|
-
"
|
|
1352
|
+
" some implementation has already been created for it."
|
|
1359
1353
|
end
|
|
1360
1354
|
disable_argument_parsing
|
|
1361
1355
|
self.run_handler = make_delegation_run_handler(target)
|
|
@@ -1393,7 +1387,7 @@ module Toys
|
|
|
1393
1387
|
def finish_definition(loader)
|
|
1394
1388
|
unless @definition_finished
|
|
1395
1389
|
ContextualError.capture("Error installing tool middleware!", tool_name: full_name) do
|
|
1396
|
-
config_proc = proc {
|
|
1390
|
+
config_proc = proc {}
|
|
1397
1391
|
@built_middleware.reverse_each do |middleware|
|
|
1398
1392
|
config_proc = make_config_proc(middleware, loader, config_proc)
|
|
1399
1393
|
end
|
|
@@ -1493,7 +1487,7 @@ module Toys
|
|
|
1493
1487
|
case signal
|
|
1494
1488
|
when ::String, ::Symbol
|
|
1495
1489
|
sigstr = signal.to_s
|
|
1496
|
-
sigstr = sigstr[3
|
|
1490
|
+
sigstr = sigstr[3..] if sigstr.start_with?("SIG")
|
|
1497
1491
|
signo = ::Signal.list[sigstr]
|
|
1498
1492
|
return signo if signo
|
|
1499
1493
|
when ::Integer
|