toys-core 0.11.5 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|