toys-core 0.11.5 → 0.13.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 +62 -0
- data/LICENSE.md +1 -1
- data/README.md +5 -2
- data/docs/guide.md +1 -1
- data/lib/toys/acceptor.rb +13 -4
- data/lib/toys/arg_parser.rb +7 -7
- data/lib/toys/cli.rb +170 -120
- data/lib/toys/compat.rb +71 -23
- data/lib/toys/completion.rb +18 -6
- data/lib/toys/context.rb +24 -15
- data/lib/toys/core.rb +6 -2
- data/lib/toys/dsl/base.rb +87 -0
- data/lib/toys/dsl/flag.rb +26 -20
- data/lib/toys/dsl/flag_group.rb +18 -14
- data/lib/toys/dsl/internal.rb +206 -0
- data/lib/toys/dsl/positional_arg.rb +26 -16
- data/lib/toys/dsl/tool.rb +180 -218
- data/lib/toys/errors.rb +64 -8
- data/lib/toys/flag.rb +662 -656
- data/lib/toys/flag_group.rb +24 -10
- data/lib/toys/input_file.rb +13 -7
- data/lib/toys/loader.rb +293 -140
- data/lib/toys/middleware.rb +46 -22
- data/lib/toys/mixin.rb +10 -8
- data/lib/toys/positional_arg.rb +21 -20
- data/lib/toys/settings.rb +914 -0
- data/lib/toys/source_info.rb +147 -35
- data/lib/toys/standard_middleware/add_verbosity_flags.rb +2 -0
- data/lib/toys/standard_middleware/apply_config.rb +6 -4
- data/lib/toys/standard_middleware/handle_usage_errors.rb +1 -0
- data/lib/toys/standard_middleware/set_default_descriptions.rb +19 -18
- data/lib/toys/standard_middleware/show_help.rb +19 -5
- data/lib/toys/standard_middleware/show_root_version.rb +2 -0
- data/lib/toys/standard_mixins/bundler.rb +24 -15
- data/lib/toys/standard_mixins/exec.rb +43 -34
- data/lib/toys/standard_mixins/fileutils.rb +3 -1
- data/lib/toys/standard_mixins/gems.rb +21 -17
- data/lib/toys/standard_mixins/git_cache.rb +46 -0
- data/lib/toys/standard_mixins/highline.rb +8 -8
- data/lib/toys/standard_mixins/terminal.rb +5 -5
- data/lib/toys/standard_mixins/xdg.rb +56 -0
- data/lib/toys/template.rb +11 -9
- data/lib/toys/{tool.rb → tool_definition.rb} +292 -226
- data/lib/toys/utils/completion_engine.rb +7 -2
- data/lib/toys/utils/exec.rb +162 -132
- data/lib/toys/utils/gems.rb +85 -60
- data/lib/toys/utils/git_cache.rb +813 -0
- data/lib/toys/utils/help_text.rb +117 -37
- data/lib/toys/utils/terminal.rb +11 -3
- data/lib/toys/utils/xdg.rb +293 -0
- data/lib/toys/wrappable_string.rb +9 -2
- data/lib/toys-core.rb +18 -6
- metadata +14 -7
data/lib/toys/loader.rb
CHANGED
@@ -8,9 +8,6 @@ module Toys
|
|
8
8
|
# appropriate tool given a set of command line arguments.
|
9
9
|
#
|
10
10
|
class Loader
|
11
|
-
# @private
|
12
|
-
BASE_PRIORITY = -999_999
|
13
|
-
|
14
11
|
##
|
15
12
|
# Create a Loader
|
16
13
|
#
|
@@ -44,18 +41,17 @@ module Toys
|
|
44
41
|
# @param template_lookup [Toys::ModuleLookup] A lookup for
|
45
42
|
# well-known template classes. Defaults to an empty lookup.
|
46
43
|
#
|
47
|
-
def initialize(
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
)
|
44
|
+
def initialize(index_file_name: nil,
|
45
|
+
preload_dir_name: nil,
|
46
|
+
preload_file_name: nil,
|
47
|
+
data_dir_name: nil,
|
48
|
+
lib_dir_name: nil,
|
49
|
+
middleware_stack: [],
|
50
|
+
extra_delimiters: "",
|
51
|
+
mixin_lookup: nil,
|
52
|
+
middleware_lookup: nil,
|
53
|
+
template_lookup: nil,
|
54
|
+
git_cache: nil)
|
59
55
|
if index_file_name && ::File.extname(index_file_name) != ".rb"
|
60
56
|
raise ::ArgumentError, "Illegal index file name #{index_file_name.inspect}"
|
61
57
|
end
|
@@ -71,30 +67,82 @@ module Toys
|
|
71
67
|
@loading_started = false
|
72
68
|
@worklist = []
|
73
69
|
@tool_data = {}
|
70
|
+
@roots_by_priority = {}
|
74
71
|
@max_priority = @min_priority = 0
|
75
|
-
@stop_priority =
|
72
|
+
@stop_priority = -999_999
|
76
73
|
@min_loaded_priority = 999_999
|
77
74
|
@middleware_stack = Middleware.stack(middleware_stack)
|
78
75
|
@delimiter_handler = DelimiterHandler.new(extra_delimiters)
|
79
|
-
|
76
|
+
@git_cache = git_cache
|
77
|
+
get_tool([], -999_999)
|
80
78
|
end
|
81
79
|
|
82
80
|
##
|
83
81
|
# Add a configuration file/directory to the loader.
|
84
82
|
#
|
85
|
-
# @param
|
83
|
+
# @param path [String] A single path to add.
|
86
84
|
# @param high_priority [Boolean] If true, add this path at the top of the
|
87
85
|
# priority list. Defaults to false, indicating the new path should be
|
88
86
|
# at the bottom of the priority list.
|
87
|
+
# @param source_name [String] A custom name for the root source. Optional.
|
88
|
+
# @param context_directory [String,nil,:path,:parent] The context directory
|
89
|
+
# for tools loaded from this path. You can pass a directory path as a
|
90
|
+
# string, `:path` to denote the given path, `:parent` to denote the
|
91
|
+
# given path's parent directory, or `nil` to denote no context.
|
92
|
+
# Defaults to `:parent`.
|
93
|
+
# @return [self]
|
94
|
+
#
|
95
|
+
def add_path(path,
|
96
|
+
high_priority: false,
|
97
|
+
source_name: nil,
|
98
|
+
context_directory: :parent)
|
99
|
+
@mutex.synchronize do
|
100
|
+
raise "Cannot add a path after tool loading has started" if @loading_started
|
101
|
+
priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
|
102
|
+
source = SourceInfo.create_path_root(path, priority,
|
103
|
+
context_directory: context_directory,
|
104
|
+
data_dir_name: @data_dir_name,
|
105
|
+
lib_dir_name: @lib_dir_name,
|
106
|
+
source_name: source_name)
|
107
|
+
@roots_by_priority[priority] = source
|
108
|
+
@worklist << [source, [], priority]
|
109
|
+
end
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# Add a set of configuration files/directories from a common directory to
|
115
|
+
# the loader. The set of paths will be added at the same priority level and
|
116
|
+
# will share a root.
|
117
|
+
#
|
118
|
+
# @param root_path [String] A root path to be seen as the root source. This
|
119
|
+
# should generally be a directory containing the paths to add.
|
120
|
+
# @param relative_paths [String,Array<String>] One or more paths to add, as
|
121
|
+
# relative paths from the common root.
|
122
|
+
# @param high_priority [Boolean] If true, add the paths at the top of the
|
123
|
+
# priority list. Defaults to false, indicating the new paths should be
|
124
|
+
# at the bottom of the priority list.
|
125
|
+
# @param context_directory [String,nil,:path,:parent] The context directory
|
126
|
+
# for tools loaded from this path. You can pass a directory path as a
|
127
|
+
# string, `:path` to denote the given root path, `:parent` to denote
|
128
|
+
# the given root path's parent directory, or `nil` to denote no context.
|
129
|
+
# Defaults to `:path`.
|
89
130
|
# @return [self]
|
90
131
|
#
|
91
|
-
def
|
92
|
-
|
132
|
+
def add_path_set(root_path, relative_paths,
|
133
|
+
high_priority: false,
|
134
|
+
context_directory: :path)
|
135
|
+
relative_paths = Array(relative_paths)
|
93
136
|
@mutex.synchronize do
|
94
137
|
raise "Cannot add a path after tool loading has started" if @loading_started
|
95
138
|
priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
|
96
|
-
|
97
|
-
|
139
|
+
root_source = SourceInfo.create_path_root(root_path, priority,
|
140
|
+
context_directory: context_directory,
|
141
|
+
data_dir_name: @data_dir_name,
|
142
|
+
lib_dir_name: @lib_dir_name)
|
143
|
+
@roots_by_priority[priority] = root_source
|
144
|
+
relative_paths.each do |path, individual_name|
|
145
|
+
source = root_source.relative_child(path, source_name: individual_name)
|
98
146
|
@worklist << [source, [], priority]
|
99
147
|
end
|
100
148
|
end
|
@@ -107,19 +155,65 @@ module Toys
|
|
107
155
|
# @param high_priority [Boolean] If true, add this block at the top of the
|
108
156
|
# priority list. Defaults to false, indicating the block should be at
|
109
157
|
# the bottom of the priority list.
|
110
|
-
# @param
|
111
|
-
# for tools defined in this block. If omitted, a default
|
112
|
-
# will be generated.
|
158
|
+
# @param source_name [String] The source name that will be shown in
|
159
|
+
# documentation for tools defined in this block. If omitted, a default
|
160
|
+
# unique string will be generated.
|
113
161
|
# @param block [Proc] The block of configuration, executed in the context
|
114
162
|
# of the tool DSL {Toys::DSL::Tool}.
|
163
|
+
# @param context_directory [String,nil] The context directory for tools
|
164
|
+
# loaded from this block. You can pass a directory path as a string, or
|
165
|
+
# `nil` to denote no context. Defaults to `nil`.
|
115
166
|
# @return [self]
|
116
167
|
#
|
117
|
-
def add_block(high_priority: false,
|
118
|
-
|
168
|
+
def add_block(high_priority: false,
|
169
|
+
source_name: nil,
|
170
|
+
context_directory: nil,
|
171
|
+
&block)
|
119
172
|
@mutex.synchronize do
|
120
173
|
raise "Cannot add a block after tool loading has started" if @loading_started
|
121
174
|
priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
|
122
|
-
source = SourceInfo.create_proc_root(block,
|
175
|
+
source = SourceInfo.create_proc_root(block, priority,
|
176
|
+
context_directory: context_directory,
|
177
|
+
source_name: source_name,
|
178
|
+
data_dir_name: @data_dir_name,
|
179
|
+
lib_dir_name: @lib_dir_name)
|
180
|
+
@roots_by_priority[priority] = source
|
181
|
+
@worklist << [source, [], priority]
|
182
|
+
end
|
183
|
+
self
|
184
|
+
end
|
185
|
+
|
186
|
+
##
|
187
|
+
# Add a configuration git source to the loader.
|
188
|
+
#
|
189
|
+
# @param git_remote [String] The git repo URL
|
190
|
+
# @param git_path [String] The path to the relevant file or directory in
|
191
|
+
# the repo. Specify the empty string to use the entire repo.
|
192
|
+
# @param git_commit [String] The git ref (i.e. SHA, tag, or branch name)
|
193
|
+
# @param high_priority [Boolean] If true, add this path at the top of the
|
194
|
+
# priority list. Defaults to false, indicating the new path should be
|
195
|
+
# at the bottom of the priority list.
|
196
|
+
# @param update [Boolean] If the commit is not a SHA, pulls any updates
|
197
|
+
# from the remote. Defaults to false, which uses a local cache and does
|
198
|
+
# not update if the commit has been fetched previously.
|
199
|
+
# @param context_directory [String,nil] The context directory for tools
|
200
|
+
# loaded from this source. You can pass a directory path as a string,
|
201
|
+
# or `nil` to denote no context. Defaults to `nil`.
|
202
|
+
# @return [self]
|
203
|
+
#
|
204
|
+
def add_git(git_remote, git_path, git_commit,
|
205
|
+
high_priority: false,
|
206
|
+
update: false,
|
207
|
+
context_directory: nil)
|
208
|
+
@mutex.synchronize do
|
209
|
+
raise "Cannot add a git source after tool loading has started" if @loading_started
|
210
|
+
priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
|
211
|
+
path = git_cache.get(git_remote, path: git_path, commit: git_commit, update: update)
|
212
|
+
source = SourceInfo.create_git_root(git_remote, git_path, git_commit, path, priority,
|
213
|
+
context_directory: context_directory,
|
214
|
+
data_dir_name: @data_dir_name,
|
215
|
+
lib_dir_name: @lib_dir_name)
|
216
|
+
@roots_by_priority[priority] = source
|
123
217
|
@worklist << [source, [], priority]
|
124
218
|
end
|
125
219
|
self
|
@@ -136,7 +230,7 @@ module Toys
|
|
136
230
|
# that are not part of the tool name and should be passed as tool args.
|
137
231
|
#
|
138
232
|
# @param args [Array<String>] Command line arguments
|
139
|
-
# @return [Array(Toys::
|
233
|
+
# @return [Array(Toys::ToolDefinition,Array<String>)]
|
140
234
|
#
|
141
235
|
def lookup(args)
|
142
236
|
orig_prefix, args = @delimiter_handler.find_orig_prefix(args)
|
@@ -157,13 +251,13 @@ module Toys
|
|
157
251
|
# the given name, returns `nil`.
|
158
252
|
#
|
159
253
|
# @param words [Array<String>] The tool name
|
160
|
-
# @return [Toys::
|
254
|
+
# @return [Toys::ToolDefinition] if the tool was found
|
161
255
|
# @return [nil] if no such tool exists
|
162
256
|
#
|
163
257
|
def lookup_specific(words)
|
164
258
|
words = @delimiter_handler.split_path(words.first) if words.size == 1
|
165
259
|
load_for_prefix(words)
|
166
|
-
tool = get_tool_data(words)
|
260
|
+
tool = get_tool_data(words, false)&.cur_definition
|
167
261
|
finish_definitions_in_tree(words) if tool
|
168
262
|
tool
|
169
263
|
end
|
@@ -177,7 +271,7 @@ module Toys
|
|
177
271
|
# rather than just the immediate children (the default)
|
178
272
|
# @param include_hidden [Boolean] If true, include hidden subtools,
|
179
273
|
# e.g. names beginning with underscores.
|
180
|
-
# @return [Array<Toys::
|
274
|
+
# @return [Array<Toys::ToolDefinition>] An array of subtools.
|
181
275
|
#
|
182
276
|
def list_subtools(words, recursive: false, include_hidden: false)
|
183
277
|
load_for_prefix(words)
|
@@ -234,8 +328,8 @@ module Toys
|
|
234
328
|
#
|
235
329
|
# @private
|
236
330
|
#
|
237
|
-
def get_tool(words, priority)
|
238
|
-
get_tool_data(words).get_tool(priority, self)
|
331
|
+
def get_tool(words, priority, tool_class = nil)
|
332
|
+
get_tool_data(words, true).get_tool(priority, self, tool_class)
|
239
333
|
end
|
240
334
|
|
241
335
|
##
|
@@ -248,7 +342,7 @@ module Toys
|
|
248
342
|
# @private
|
249
343
|
#
|
250
344
|
def activate_tool(words, priority)
|
251
|
-
get_tool_data(words).activate_tool(priority, self)
|
345
|
+
get_tool_data(words, true).activate_tool(priority, self)
|
252
346
|
end
|
253
347
|
|
254
348
|
##
|
@@ -267,10 +361,11 @@ module Toys
|
|
267
361
|
#
|
268
362
|
# @private
|
269
363
|
#
|
270
|
-
def build_tool(words, priority)
|
364
|
+
def build_tool(words, priority, tool_class = nil)
|
271
365
|
parent = words.empty? ? nil : get_tool(words.slice(0..-2), priority)
|
272
366
|
middleware_stack = parent ? parent.subtool_middleware_stack : @middleware_stack
|
273
|
-
|
367
|
+
ToolDefinition.new(parent, words, priority, @roots_by_priority[priority],
|
368
|
+
middleware_stack, @middleware_lookup, tool_class)
|
274
369
|
end
|
275
370
|
|
276
371
|
##
|
@@ -335,12 +430,31 @@ module Toys
|
|
335
430
|
# @private
|
336
431
|
#
|
337
432
|
def load_path(parent_source, path, words, remaining_words, priority)
|
433
|
+
if parent_source.git_remote
|
434
|
+
raise LoaderError,
|
435
|
+
"Git source #{parent_source.source_name} tried to load from the local file system"
|
436
|
+
end
|
338
437
|
source = parent_source.absolute_child(path)
|
339
438
|
@mutex.synchronize do
|
340
439
|
load_validated_path(source, words, remaining_words, priority)
|
341
440
|
end
|
342
441
|
end
|
343
442
|
|
443
|
+
##
|
444
|
+
# Load configuration from the given git remote. This is called from the
|
445
|
+
# `load_git` directive in the DSL.
|
446
|
+
#
|
447
|
+
# @private
|
448
|
+
#
|
449
|
+
def load_git(parent_source, git_remote, git_path, git_commit, words, remaining_words, priority,
|
450
|
+
update: false)
|
451
|
+
path = git_cache.get(git_remote, path: git_path, commit: git_commit, update: update)
|
452
|
+
source = parent_source.git_child(git_remote, git_path, git_commit, path)
|
453
|
+
@mutex.synchronize do
|
454
|
+
load_validated_path(source, words, remaining_words, priority)
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
344
458
|
##
|
345
459
|
# Load a subtool block. Called from the `tool` directive in the DSL.
|
346
460
|
#
|
@@ -353,6 +467,18 @@ module Toys
|
|
353
467
|
end
|
354
468
|
end
|
355
469
|
|
470
|
+
##
|
471
|
+
# Get a GitCache.
|
472
|
+
#
|
473
|
+
# @private
|
474
|
+
#
|
475
|
+
def git_cache
|
476
|
+
@git_cache ||= begin
|
477
|
+
require "toys/utils/git_cache"
|
478
|
+
Utils::GitCache.new
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
356
482
|
##
|
357
483
|
# Determine the next setting for remaining_words, given a word.
|
358
484
|
#
|
@@ -368,6 +494,120 @@ module Toys
|
|
368
494
|
end
|
369
495
|
end
|
370
496
|
|
497
|
+
##
|
498
|
+
# Tool data
|
499
|
+
#
|
500
|
+
# @private
|
501
|
+
#
|
502
|
+
class ToolData
|
503
|
+
##
|
504
|
+
# @private
|
505
|
+
#
|
506
|
+
def initialize(words)
|
507
|
+
@words = validate_words(words)
|
508
|
+
@definitions = {}
|
509
|
+
@top_priority = @active_priority = nil
|
510
|
+
@mutex = ::Monitor.new
|
511
|
+
end
|
512
|
+
|
513
|
+
##
|
514
|
+
# @private
|
515
|
+
#
|
516
|
+
def cur_definition
|
517
|
+
@mutex.synchronize { active_definition || top_definition }
|
518
|
+
end
|
519
|
+
|
520
|
+
##
|
521
|
+
# @private
|
522
|
+
#
|
523
|
+
def empty?
|
524
|
+
@definitions.empty?
|
525
|
+
end
|
526
|
+
|
527
|
+
##
|
528
|
+
# @private
|
529
|
+
#
|
530
|
+
def get_tool(priority, loader, tool_class = nil)
|
531
|
+
@mutex.synchronize do
|
532
|
+
if @top_priority.nil? || @top_priority < priority
|
533
|
+
@top_priority = priority
|
534
|
+
end
|
535
|
+
if tool_class && @definitions.include?(priority)
|
536
|
+
raise ToolDefinitionError, "Tool already defined for #{@words.inspect}"
|
537
|
+
end
|
538
|
+
@definitions[priority] ||= loader.build_tool(@words, priority, tool_class)
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
##
|
543
|
+
# @private
|
544
|
+
#
|
545
|
+
def activate_tool(priority, loader)
|
546
|
+
@mutex.synchronize do
|
547
|
+
return active_definition if @active_priority == priority
|
548
|
+
return nil if @active_priority && @active_priority > priority
|
549
|
+
@active_priority = priority
|
550
|
+
get_tool(priority, loader)
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
554
|
+
private
|
555
|
+
|
556
|
+
def validate_words(words)
|
557
|
+
words.each do |word|
|
558
|
+
if /[[:cntrl:] #"$&'()*;<>\[\\\]\^`{|}]/.match(word)
|
559
|
+
raise ToolDefinitionError, "Illegal characters in name #{word.inspect}"
|
560
|
+
end
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
def top_definition
|
565
|
+
@top_priority ? @definitions[@top_priority] : nil
|
566
|
+
end
|
567
|
+
|
568
|
+
def active_definition
|
569
|
+
@active_priority ? @definitions[@active_priority] : nil
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
##
|
574
|
+
# An object that handles name delimiting.
|
575
|
+
#
|
576
|
+
# @private
|
577
|
+
#
|
578
|
+
class DelimiterHandler
|
579
|
+
##
|
580
|
+
# @private
|
581
|
+
#
|
582
|
+
def initialize(extra_delimiters)
|
583
|
+
unless %r{^[[:space:]./:]*$}.match?(extra_delimiters)
|
584
|
+
raise ::ArgumentError, "Illegal delimiters in #{extra_delimiters.inspect}"
|
585
|
+
end
|
586
|
+
chars = ::Regexp.escape(extra_delimiters.chars.uniq.join)
|
587
|
+
@delimiters = ::Regexp.new("[[:space:]#{chars}]")
|
588
|
+
end
|
589
|
+
|
590
|
+
##
|
591
|
+
# @private
|
592
|
+
#
|
593
|
+
def split_path(str)
|
594
|
+
str.split(@delimiters)
|
595
|
+
end
|
596
|
+
|
597
|
+
##
|
598
|
+
# @private
|
599
|
+
#
|
600
|
+
def find_orig_prefix(args)
|
601
|
+
first_split = (args.first || "").split(@delimiters)
|
602
|
+
if first_split.size > 1
|
603
|
+
args = first_split + args.slice(1..-1)
|
604
|
+
return [first_split, args]
|
605
|
+
end
|
606
|
+
orig_prefix = args.take_while { |arg| !arg.start_with?("-") }
|
607
|
+
[orig_prefix, args]
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
371
611
|
private
|
372
612
|
|
373
613
|
def all_cur_definitions
|
@@ -381,8 +621,10 @@ module Toys
|
|
381
621
|
result
|
382
622
|
end
|
383
623
|
|
384
|
-
def get_tool_data(words)
|
385
|
-
@mutex.synchronize
|
624
|
+
def get_tool_data(words, create)
|
625
|
+
@mutex.synchronize do
|
626
|
+
create ? (@tool_data[words] ||= ToolData.new(words)) : @tool_data[words]
|
627
|
+
end
|
386
628
|
end
|
387
629
|
|
388
630
|
##
|
@@ -403,7 +645,7 @@ module Toys
|
|
403
645
|
if remaining_words
|
404
646
|
update_min_loaded_priority(priority)
|
405
647
|
tool_class = get_tool(words, priority).tool_class
|
406
|
-
DSL::
|
648
|
+
DSL::Internal.prepare(tool_class, words, priority, remaining_words, source, self) do
|
407
649
|
ContextualError.capture("Error while loading Toys config!") do
|
408
650
|
tool_class.class_eval(&source.source_proc)
|
409
651
|
end
|
@@ -425,7 +667,7 @@ module Toys
|
|
425
667
|
if source.source_type == :file
|
426
668
|
update_min_loaded_priority(priority)
|
427
669
|
tool_class = get_tool(words, priority).tool_class
|
428
|
-
InputFile.evaluate(tool_class, remaining_words, source)
|
670
|
+
InputFile.evaluate(tool_class, words, priority, remaining_words, source, self)
|
429
671
|
else
|
430
672
|
do_preload(source.source_path)
|
431
673
|
load_index_in(source, words, remaining_words, priority)
|
@@ -467,16 +709,20 @@ module Toys
|
|
467
709
|
if @preload_dir_name
|
468
710
|
preload_dir = ::File.join(path, @preload_dir_name)
|
469
711
|
if ::File.directory?(preload_dir) && ::File.readable?(preload_dir)
|
470
|
-
|
471
|
-
next unless ::File.extname(child) == ".rb"
|
472
|
-
preload_file = ::File.join(preload_dir, child)
|
473
|
-
next if !::File.file?(preload_file) || !::File.readable?(preload_file)
|
474
|
-
require preload_file
|
475
|
-
end
|
712
|
+
preload_dir_contents(preload_dir)
|
476
713
|
end
|
477
714
|
end
|
478
715
|
end
|
479
716
|
|
717
|
+
def preload_dir_contents(preload_dir)
|
718
|
+
::Dir.entries(preload_dir).each do |child|
|
719
|
+
next unless ::File.extname(child) == ".rb"
|
720
|
+
preload_file = ::File.join(preload_dir, child)
|
721
|
+
next if !::File.file?(preload_file) || !::File.readable?(preload_file)
|
722
|
+
require preload_file
|
723
|
+
end
|
724
|
+
end
|
725
|
+
|
480
726
|
def sort_tools_by_name(tools)
|
481
727
|
tools.sort! do |a, b|
|
482
728
|
a = a.full_name
|
@@ -511,98 +757,5 @@ module Toys
|
|
511
757
|
index += 1
|
512
758
|
end
|
513
759
|
end
|
514
|
-
|
515
|
-
##
|
516
|
-
# Tool data
|
517
|
-
#
|
518
|
-
# @private
|
519
|
-
#
|
520
|
-
class ToolData
|
521
|
-
# @private
|
522
|
-
def initialize(words)
|
523
|
-
@words = words
|
524
|
-
@definitions = {}
|
525
|
-
@top_priority = @active_priority = nil
|
526
|
-
@mutex = ::Monitor.new
|
527
|
-
end
|
528
|
-
|
529
|
-
# @private
|
530
|
-
def cur_definition
|
531
|
-
@mutex.synchronize { active_definition || top_definition }
|
532
|
-
end
|
533
|
-
|
534
|
-
# @private
|
535
|
-
def empty?
|
536
|
-
@definitions.empty?
|
537
|
-
end
|
538
|
-
|
539
|
-
# @private
|
540
|
-
def get_tool(priority, loader)
|
541
|
-
@mutex.synchronize do
|
542
|
-
if @top_priority.nil? || @top_priority < priority
|
543
|
-
@top_priority = priority
|
544
|
-
end
|
545
|
-
@definitions[priority] ||= loader.build_tool(@words, priority)
|
546
|
-
end
|
547
|
-
end
|
548
|
-
|
549
|
-
# @private
|
550
|
-
def activate_tool(priority, loader)
|
551
|
-
@mutex.synchronize do
|
552
|
-
return active_definition if @active_priority == priority
|
553
|
-
return nil if @active_priority && @active_priority > priority
|
554
|
-
@active_priority = priority
|
555
|
-
get_tool(priority, loader)
|
556
|
-
end
|
557
|
-
end
|
558
|
-
|
559
|
-
private
|
560
|
-
|
561
|
-
def top_definition
|
562
|
-
@top_priority ? @definitions[@top_priority] : nil
|
563
|
-
end
|
564
|
-
|
565
|
-
def active_definition
|
566
|
-
@active_priority ? @definitions[@active_priority] : nil
|
567
|
-
end
|
568
|
-
end
|
569
|
-
|
570
|
-
##
|
571
|
-
# An object that handles name delimiting.
|
572
|
-
#
|
573
|
-
# @private
|
574
|
-
#
|
575
|
-
class DelimiterHandler
|
576
|
-
## @private
|
577
|
-
ALLOWED_DELIMITERS = %r{^[\./:]*$}.freeze
|
578
|
-
private_constant :ALLOWED_DELIMITERS
|
579
|
-
|
580
|
-
## @private
|
581
|
-
def initialize(extra_delimiters)
|
582
|
-
unless ALLOWED_DELIMITERS =~ extra_delimiters
|
583
|
-
raise ::ArgumentError, "Illegal delimiters in #{extra_delimiters.inspect}"
|
584
|
-
end
|
585
|
-
chars = ::Regexp.escape(extra_delimiters.chars.uniq.join)
|
586
|
-
@extra_delimiters = chars.empty? ? nil : ::Regexp.new("[#{chars}]")
|
587
|
-
end
|
588
|
-
|
589
|
-
## @private
|
590
|
-
def split_path(str)
|
591
|
-
@extra_delimiters ? str.split(@extra_delimiters) : [str]
|
592
|
-
end
|
593
|
-
|
594
|
-
## @private
|
595
|
-
def find_orig_prefix(args)
|
596
|
-
if @extra_delimiters
|
597
|
-
first_split = (args.first || "").split(@extra_delimiters)
|
598
|
-
if first_split.size > 1
|
599
|
-
args = first_split + args.slice(1..-1)
|
600
|
-
return [first_split, args]
|
601
|
-
end
|
602
|
-
end
|
603
|
-
orig_prefix = args.take_while { |arg| !arg.start_with?("-") }
|
604
|
-
[orig_prefix, args]
|
605
|
-
end
|
606
|
-
end
|
607
760
|
end
|
608
761
|
end
|
data/lib/toys/middleware.rb
CHANGED
@@ -37,7 +37,7 @@ module Toys
|
|
37
37
|
# This basic implementation does nothing and simply yields to the next
|
38
38
|
# middleware.
|
39
39
|
#
|
40
|
-
# @param tool [Toys::
|
40
|
+
# @param tool [Toys::ToolDefinition] The tool definition to modify.
|
41
41
|
# @param loader [Toys::Loader] The loader that loaded this tool.
|
42
42
|
# @return [void]
|
43
43
|
#
|
@@ -140,7 +140,9 @@ module Toys
|
|
140
140
|
end
|
141
141
|
end
|
142
142
|
|
143
|
-
##
|
143
|
+
##
|
144
|
+
# @private
|
145
|
+
#
|
144
146
|
def spec_from_array(array)
|
145
147
|
middleware = array.first
|
146
148
|
if !middleware.is_a?(::String) && !middleware.is_a?(::Symbol) && !middleware.is_a?(::Class)
|
@@ -238,16 +240,12 @@ module Toys
|
|
238
240
|
#
|
239
241
|
attr_reader :block
|
240
242
|
|
241
|
-
##
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
@block = block
|
248
|
-
end
|
249
|
-
|
250
|
-
## @private
|
243
|
+
##
|
244
|
+
# Equality check
|
245
|
+
#
|
246
|
+
# @param other [Object]
|
247
|
+
# @return [Boolean]
|
248
|
+
#
|
251
249
|
def ==(other)
|
252
250
|
other.is_a?(Spec) &&
|
253
251
|
object.eql?(other.object) &&
|
@@ -258,10 +256,25 @@ module Toys
|
|
258
256
|
end
|
259
257
|
alias eql? ==
|
260
258
|
|
261
|
-
##
|
259
|
+
##
|
260
|
+
# Return the hash code
|
261
|
+
#
|
262
|
+
# @return [Integer]
|
263
|
+
#
|
262
264
|
def hash
|
263
265
|
[object, name, args, kwargs, block].hash
|
264
266
|
end
|
267
|
+
|
268
|
+
##
|
269
|
+
# @private
|
270
|
+
#
|
271
|
+
def initialize(object, name, args, kwargs, block)
|
272
|
+
@object = object
|
273
|
+
@name = name
|
274
|
+
@args = args
|
275
|
+
@kwargs = kwargs
|
276
|
+
@block = block
|
277
|
+
end
|
265
278
|
end
|
266
279
|
|
267
280
|
##
|
@@ -319,14 +332,12 @@ module Toys
|
|
319
332
|
(@pre_specs + @default_specs + @post_specs).map { |spec| spec.build(middleware_lookup) }
|
320
333
|
end
|
321
334
|
|
322
|
-
##
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
## @private
|
335
|
+
##
|
336
|
+
# Equality check
|
337
|
+
#
|
338
|
+
# @param other [Object]
|
339
|
+
# @return [Boolean]
|
340
|
+
#
|
330
341
|
def ==(other)
|
331
342
|
other.is_a?(Stack) &&
|
332
343
|
pre_specs.eql?(other.pre_specs) &&
|
@@ -335,10 +346,23 @@ module Toys
|
|
335
346
|
end
|
336
347
|
alias eql? ==
|
337
348
|
|
338
|
-
##
|
349
|
+
##
|
350
|
+
# Return the hash code
|
351
|
+
#
|
352
|
+
# @return [Integer]
|
353
|
+
#
|
339
354
|
def hash
|
340
355
|
[@pre_specs, @default_specs, @post_specs].hash
|
341
356
|
end
|
357
|
+
|
358
|
+
##
|
359
|
+
# @private
|
360
|
+
#
|
361
|
+
def initialize(default_specs: nil, pre_specs: nil, post_specs: nil)
|
362
|
+
@pre_specs = pre_specs || []
|
363
|
+
@post_specs = post_specs || []
|
364
|
+
@default_specs = default_specs || []
|
365
|
+
end
|
342
366
|
end
|
343
367
|
end
|
344
368
|
end
|