toys-core 0.11.3 → 0.12.1
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 +47 -2
- data/README.md +1 -1
- data/lib/toys-core.rb +4 -1
- data/lib/toys/acceptor.rb +3 -3
- data/lib/toys/arg_parser.rb +6 -7
- data/lib/toys/cli.rb +44 -14
- data/lib/toys/compat.rb +19 -22
- data/lib/toys/completion.rb +3 -1
- data/lib/toys/context.rb +2 -2
- data/lib/toys/core.rb +1 -1
- data/lib/toys/dsl/base.rb +85 -0
- data/lib/toys/dsl/flag.rb +3 -3
- data/lib/toys/dsl/flag_group.rb +7 -7
- data/lib/toys/dsl/internal.rb +206 -0
- data/lib/toys/dsl/positional_arg.rb +3 -3
- data/lib/toys/dsl/tool.rb +174 -216
- data/lib/toys/errors.rb +1 -0
- data/lib/toys/flag.rb +15 -18
- data/lib/toys/flag_group.rb +5 -4
- data/lib/toys/input_file.rb +4 -4
- data/lib/toys/loader.rb +189 -50
- data/lib/toys/middleware.rb +1 -1
- data/lib/toys/mixin.rb +2 -2
- data/lib/toys/positional_arg.rb +3 -3
- data/lib/toys/settings.rb +900 -0
- data/lib/toys/source_info.rb +121 -18
- data/lib/toys/standard_middleware/apply_config.rb +5 -4
- data/lib/toys/standard_middleware/set_default_descriptions.rb +18 -18
- data/lib/toys/standard_middleware/show_help.rb +17 -5
- data/lib/toys/standard_mixins/bundler.rb +7 -2
- data/lib/toys/standard_mixins/exec.rb +22 -15
- data/lib/toys/standard_mixins/git_cache.rb +48 -0
- data/lib/toys/standard_mixins/xdg.rb +56 -0
- data/lib/toys/template.rb +2 -2
- data/lib/toys/{tool.rb → tool_definition.rb} +100 -41
- data/lib/toys/utils/exec.rb +12 -10
- data/lib/toys/utils/gems.rb +48 -14
- data/lib/toys/utils/git_cache.rb +184 -0
- data/lib/toys/utils/help_text.rb +90 -34
- data/lib/toys/utils/terminal.rb +1 -1
- data/lib/toys/utils/xdg.rb +293 -0
- metadata +15 -8
data/lib/toys/errors.rb
CHANGED
data/lib/toys/flag.rb
CHANGED
@@ -85,11 +85,11 @@ module Toys
|
|
85
85
|
# true.
|
86
86
|
# @param group [Toys::FlagGroup] Group containing this flag.
|
87
87
|
# @param desc [String,Array<String>,Toys::WrappableString] Short
|
88
|
-
# description for the flag. See {Toys::
|
89
|
-
# allowed formats. Defaults to the empty string.
|
88
|
+
# description for the flag. See {Toys::ToolDefinition#desc} for a
|
89
|
+
# description of allowed formats. Defaults to the empty string.
|
90
90
|
# @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
|
91
|
-
# Long description for the flag. See {Toys::
|
92
|
-
# description of allowed formats. Defaults to the empty array.
|
91
|
+
# Long description for the flag. See {Toys::ToolDefinition#long_desc}
|
92
|
+
# for a description of allowed formats. Defaults to the empty array.
|
93
93
|
# @param display_name [String] A display name for this flag, used in help
|
94
94
|
# text and error messages.
|
95
95
|
# @param used_flags [Array<String>] An array of flags already in use.
|
@@ -368,7 +368,7 @@ module Toys
|
|
368
368
|
key_str = key.to_s
|
369
369
|
flag_str =
|
370
370
|
if key_str.length == 1
|
371
|
-
"-#{key_str}" if key_str =~ /[a-zA-Z0-9
|
371
|
+
"-#{key_str}" if key_str =~ /[a-zA-Z0-9?]/
|
372
372
|
elsif key_str.length > 1
|
373
373
|
key_str = key_str.downcase.tr("_", "-").gsub(/[^a-z0-9-]/, "").sub(/^-+/, "")
|
374
374
|
"--#{key_str}" unless key_str.empty?
|
@@ -455,23 +455,19 @@ module Toys
|
|
455
455
|
#
|
456
456
|
def initialize(str)
|
457
457
|
case str
|
458
|
-
when /\A(-([
|
458
|
+
when /\A(-([?\w]))\z/
|
459
459
|
setup(str, $1, nil, $1, $2, :short, nil, nil, nil, nil)
|
460
|
-
when /\A(-([
|
461
|
-
setup(str, $1, nil, $1, $2, :short, :value, :optional, $3, $
|
462
|
-
when /\A(-([
|
463
|
-
setup(str, $1, nil, $1, $2, :short, :value, :optional, $3, $4)
|
464
|
-
when /\A(-([\?\w]))( ?)(\w+)\z/
|
460
|
+
when /\A(-([?\w]))(?:( ?)\[|\[( ))(\w+)\]\z/
|
461
|
+
setup(str, $1, nil, $1, $2, :short, :value, :optional, $3 || $4, $5)
|
462
|
+
when /\A(-([?\w]))( ?)(\w+)\z/
|
465
463
|
setup(str, $1, nil, $1, $2, :short, :value, :required, $3, $4)
|
466
|
-
when /\A--\[no-\](\w[
|
464
|
+
when /\A--\[no-\](\w[?\w-]*)\z/
|
467
465
|
setup(str, "--#{$1}", "--no-#{$1}", str, $1, :long, :boolean, nil, nil, nil)
|
468
|
-
when /\A(--(\w[
|
466
|
+
when /\A(--(\w[?\w-]*))\z/
|
469
467
|
setup(str, $1, nil, $1, $2, :long, nil, nil, nil, nil)
|
470
|
-
when /\A(--(\w[
|
471
|
-
setup(str, $1, nil, $1, $2, :long, :value, :optional, $3, $
|
472
|
-
when /\A(--(\w[
|
473
|
-
setup(str, $1, nil, $1, $2, :long, :value, :optional, $3, $4)
|
474
|
-
when /\A(--(\w[\?\w-]*))([= ])(\w+)\z/
|
468
|
+
when /\A(--(\w[?\w-]*))(?:([= ])\[|\[([= ]))(\w+)\]\z/
|
469
|
+
setup(str, $1, nil, $1, $2, :long, :value, :optional, $3 || $4, $5)
|
470
|
+
when /\A(--(\w[?\w-]*))([= ])(\w+)\z/
|
475
471
|
setup(str, $1, nil, $1, $2, :long, :value, :required, $3, $4)
|
476
472
|
else
|
477
473
|
raise ToolDefinitionError, "Illegal flag: #{str.inspect}"
|
@@ -731,6 +727,7 @@ module Toys
|
|
731
727
|
# @param include_negative [Boolean] Whether to include `--no-*` forms.
|
732
728
|
#
|
733
729
|
def initialize(flag:, include_short: true, include_long: true, include_negative: true)
|
730
|
+
super()
|
734
731
|
@flag = flag
|
735
732
|
@include_short = include_short
|
736
733
|
@include_long = include_long
|
data/lib/toys/flag_group.rb
CHANGED
@@ -17,11 +17,12 @@ module Toys
|
|
17
17
|
#
|
18
18
|
# @param type [Symbol] The type of group. Default is `:optional`.
|
19
19
|
# @param desc [String,Array<String>,Toys::WrappableString] Short
|
20
|
-
# description for the group. See {Toys::
|
21
|
-
# of allowed formats. Defaults to `"Flags"`.
|
20
|
+
# description for the group. See {Toys::ToolDefinition#desc} for a
|
21
|
+
# description of allowed formats. Defaults to `"Flags"`.
|
22
22
|
# @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
|
23
|
-
# Long description for the flag group. See
|
24
|
-
# a description of allowed
|
23
|
+
# Long description for the flag group. See
|
24
|
+
# {Toys::ToolDefinition#long_desc} for a description of allowed
|
25
|
+
# formats. Defaults to the empty array.
|
25
26
|
# @param name [String,Symbol,nil] The name of the group, or nil for no
|
26
27
|
# name.
|
27
28
|
# @return [Toys::FlagGroup::Base] A flag group of the correct subclass.
|
data/lib/toys/input_file.rb
CHANGED
@@ -11,11 +11,11 @@ module Toys::InputFile # rubocop:disable Style/ClassAndModuleChildren
|
|
11
11
|
end
|
12
12
|
|
13
13
|
## @private
|
14
|
-
def self.evaluate(tool_class, remaining_words, source)
|
14
|
+
def self.evaluate(tool_class, words, priority, remaining_words, source, loader)
|
15
15
|
namespace = ::Module.new
|
16
16
|
namespace.module_eval do
|
17
17
|
include ::Toys::Context::Key
|
18
|
-
@
|
18
|
+
@__tool_class = tool_class
|
19
19
|
end
|
20
20
|
path = source.source_path
|
21
21
|
basename = ::File.basename(path).tr(".-", "_").gsub(/\W/, "")
|
@@ -23,7 +23,7 @@ module Toys::InputFile # rubocop:disable Style/ClassAndModuleChildren
|
|
23
23
|
str = build_eval_string(name, ::IO.read(path))
|
24
24
|
if str
|
25
25
|
const_set(name, namespace)
|
26
|
-
::Toys::DSL::
|
26
|
+
::Toys::DSL::Internal.prepare(tool_class, words, priority, remaining_words, source, loader) do
|
27
27
|
::Toys::ContextualError.capture_path("Error while loading Toys config!", path) do
|
28
28
|
# rubocop:disable Security/Eval
|
29
29
|
eval(str, __binding, path, -2)
|
@@ -39,7 +39,7 @@ module Toys::InputFile # rubocop:disable Style/ClassAndModuleChildren
|
|
39
39
|
return nil if index.nil?
|
40
40
|
"#{string[0, index]}\n" \
|
41
41
|
"module #{module_name}\n" \
|
42
|
-
"@
|
42
|
+
"@__tool_class.class_eval do\n" \
|
43
43
|
"#{string[index..-1]}\n" \
|
44
44
|
"end\n" \
|
45
45
|
"end\n"
|
data/lib/toys/loader.rb
CHANGED
@@ -54,7 +54,8 @@ module Toys
|
|
54
54
|
extra_delimiters: "",
|
55
55
|
mixin_lookup: nil,
|
56
56
|
middleware_lookup: nil,
|
57
|
-
template_lookup: nil
|
57
|
+
template_lookup: nil,
|
58
|
+
git_cache: nil
|
58
59
|
)
|
59
60
|
if index_file_name && ::File.extname(index_file_name) != ".rb"
|
60
61
|
raise ::ArgumentError, "Illegal index file name #{index_file_name.inspect}"
|
@@ -71,30 +72,82 @@ module Toys
|
|
71
72
|
@loading_started = false
|
72
73
|
@worklist = []
|
73
74
|
@tool_data = {}
|
75
|
+
@roots_by_priority = {}
|
74
76
|
@max_priority = @min_priority = 0
|
75
77
|
@stop_priority = BASE_PRIORITY
|
76
78
|
@min_loaded_priority = 999_999
|
77
79
|
@middleware_stack = Middleware.stack(middleware_stack)
|
78
80
|
@delimiter_handler = DelimiterHandler.new(extra_delimiters)
|
81
|
+
@git_cache = git_cache
|
79
82
|
get_tool([], BASE_PRIORITY)
|
80
83
|
end
|
81
84
|
|
82
85
|
##
|
83
86
|
# Add a configuration file/directory to the loader.
|
84
87
|
#
|
85
|
-
# @param
|
88
|
+
# @param path [String] A single path to add.
|
86
89
|
# @param high_priority [Boolean] If true, add this path at the top of the
|
87
90
|
# priority list. Defaults to false, indicating the new path should be
|
88
91
|
# at the bottom of the priority list.
|
92
|
+
# @param source_name [String] A custom name for the root source. Optional.
|
93
|
+
# @param context_directory [String,nil,:path,:parent] The context directory
|
94
|
+
# for tools loaded from this path. You can pass a directory path as a
|
95
|
+
# string, `:path` to denote the given path, `:parent` to denote the
|
96
|
+
# given path's parent directory, or `nil` to denote no context.
|
97
|
+
# Defaults to `:parent`.
|
89
98
|
# @return [self]
|
90
99
|
#
|
91
|
-
def add_path(
|
92
|
-
|
100
|
+
def add_path(path,
|
101
|
+
high_priority: false,
|
102
|
+
source_name: nil,
|
103
|
+
context_directory: :parent)
|
93
104
|
@mutex.synchronize do
|
94
105
|
raise "Cannot add a path after tool loading has started" if @loading_started
|
95
106
|
priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
|
96
|
-
|
97
|
-
|
107
|
+
source = SourceInfo.create_path_root(path, priority,
|
108
|
+
context_directory: context_directory,
|
109
|
+
data_dir_name: @data_dir_name,
|
110
|
+
lib_dir_name: @lib_dir_name,
|
111
|
+
source_name: source_name)
|
112
|
+
@roots_by_priority[priority] = source
|
113
|
+
@worklist << [source, [], priority]
|
114
|
+
end
|
115
|
+
self
|
116
|
+
end
|
117
|
+
|
118
|
+
##
|
119
|
+
# Add a set of configuration files/directories from a common directory to
|
120
|
+
# the loader. The set of paths will be added at the same priority level and
|
121
|
+
# will share a root.
|
122
|
+
#
|
123
|
+
# @param root_path [String] A root path to be seen as the root source. This
|
124
|
+
# should generally be a directory containing the paths to add.
|
125
|
+
# @param relative_paths [String,Array<String>] One or more paths to add, as
|
126
|
+
# relative paths from the common root.
|
127
|
+
# @param high_priority [Boolean] If true, add the paths at the top of the
|
128
|
+
# priority list. Defaults to false, indicating the new paths should be
|
129
|
+
# at the bottom of the priority list.
|
130
|
+
# @param context_directory [String,nil,:path,:parent] The context directory
|
131
|
+
# for tools loaded from this path. You can pass a directory path as a
|
132
|
+
# string, `:path` to denote the given root path, `:parent` to denote
|
133
|
+
# the given root path's parent directory, or `nil` to denote no context.
|
134
|
+
# Defaults to `:path`.
|
135
|
+
# @return [self]
|
136
|
+
#
|
137
|
+
def add_path_set(root_path, relative_paths,
|
138
|
+
high_priority: false,
|
139
|
+
context_directory: :path)
|
140
|
+
relative_paths = Array(relative_paths)
|
141
|
+
@mutex.synchronize do
|
142
|
+
raise "Cannot add a path after tool loading has started" if @loading_started
|
143
|
+
priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
|
144
|
+
root_source = SourceInfo.create_path_root(root_path, priority,
|
145
|
+
context_directory: context_directory,
|
146
|
+
data_dir_name: @data_dir_name,
|
147
|
+
lib_dir_name: @lib_dir_name)
|
148
|
+
@roots_by_priority[priority] = root_source
|
149
|
+
relative_paths.each do |path, individual_name|
|
150
|
+
source = root_source.relative_child(path, source_name: individual_name)
|
98
151
|
@worklist << [source, [], priority]
|
99
152
|
end
|
100
153
|
end
|
@@ -107,19 +160,65 @@ module Toys
|
|
107
160
|
# @param high_priority [Boolean] If true, add this block at the top of the
|
108
161
|
# priority list. Defaults to false, indicating the block should be at
|
109
162
|
# the bottom of the priority list.
|
110
|
-
# @param
|
111
|
-
# for tools defined in this block. If omitted, a default
|
112
|
-
# will be generated.
|
163
|
+
# @param source_name [String] The source name that will be shown in
|
164
|
+
# documentation for tools defined in this block. If omitted, a default
|
165
|
+
# unique string will be generated.
|
113
166
|
# @param block [Proc] The block of configuration, executed in the context
|
114
167
|
# of the tool DSL {Toys::DSL::Tool}.
|
168
|
+
# @param context_directory [String,nil] The context directory for tools
|
169
|
+
# loaded from this block. You can pass a directory path as a string, or
|
170
|
+
# `nil` to denote no context. Defaults to `nil`.
|
115
171
|
# @return [self]
|
116
172
|
#
|
117
|
-
def add_block(high_priority: false,
|
118
|
-
|
173
|
+
def add_block(high_priority: false,
|
174
|
+
source_name: nil,
|
175
|
+
context_directory: nil,
|
176
|
+
&block)
|
119
177
|
@mutex.synchronize do
|
120
178
|
raise "Cannot add a block after tool loading has started" if @loading_started
|
121
179
|
priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
|
122
|
-
source = SourceInfo.create_proc_root(block,
|
180
|
+
source = SourceInfo.create_proc_root(block, priority,
|
181
|
+
context_directory: context_directory,
|
182
|
+
source_name: source_name,
|
183
|
+
data_dir_name: @data_dir_name,
|
184
|
+
lib_dir_name: @lib_dir_name)
|
185
|
+
@roots_by_priority[priority] = source
|
186
|
+
@worklist << [source, [], priority]
|
187
|
+
end
|
188
|
+
self
|
189
|
+
end
|
190
|
+
|
191
|
+
##
|
192
|
+
# Add a configuration git source to the loader.
|
193
|
+
#
|
194
|
+
# @param git_remote [String] The git repo URL
|
195
|
+
# @param git_path [String] The path to the relevant file or directory in
|
196
|
+
# the repo. Specify the empty string to use the entire repo.
|
197
|
+
# @param git_commit [String] The git ref (i.e. SHA, tag, or branch name)
|
198
|
+
# @param high_priority [Boolean] If true, add this path at the top of the
|
199
|
+
# priority list. Defaults to false, indicating the new path should be
|
200
|
+
# at the bottom of the priority list.
|
201
|
+
# @param update [Boolean] If the commit is not a SHA, pulls any updates
|
202
|
+
# from the remote. Defaults to false, which uses a local cache and does
|
203
|
+
# not update if the commit has been fetched previously.
|
204
|
+
# @param context_directory [String,nil] The context directory for tools
|
205
|
+
# loaded from this source. You can pass a directory path as a string,
|
206
|
+
# or `nil` to denote no context. Defaults to `nil`.
|
207
|
+
# @return [self]
|
208
|
+
#
|
209
|
+
def add_git(git_remote, git_path, git_commit,
|
210
|
+
high_priority: false,
|
211
|
+
update: false,
|
212
|
+
context_directory: nil)
|
213
|
+
@mutex.synchronize do
|
214
|
+
raise "Cannot add a git source after tool loading has started" if @loading_started
|
215
|
+
priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
|
216
|
+
path = git_cache.find(git_remote, path: git_path, commit: git_commit, update: update)
|
217
|
+
source = SourceInfo.create_git_root(git_remote, git_path, git_commit, path, priority,
|
218
|
+
context_directory: context_directory,
|
219
|
+
data_dir_name: @data_dir_name,
|
220
|
+
lib_dir_name: @lib_dir_name)
|
221
|
+
@roots_by_priority[priority] = source
|
123
222
|
@worklist << [source, [], priority]
|
124
223
|
end
|
125
224
|
self
|
@@ -136,7 +235,7 @@ module Toys
|
|
136
235
|
# that are not part of the tool name and should be passed as tool args.
|
137
236
|
#
|
138
237
|
# @param args [Array<String>] Command line arguments
|
139
|
-
# @return [Array(Toys::
|
238
|
+
# @return [Array(Toys::ToolDefinition,Array<String>)]
|
140
239
|
#
|
141
240
|
def lookup(args)
|
142
241
|
orig_prefix, args = @delimiter_handler.find_orig_prefix(args)
|
@@ -157,13 +256,13 @@ module Toys
|
|
157
256
|
# the given name, returns `nil`.
|
158
257
|
#
|
159
258
|
# @param words [Array<String>] The tool name
|
160
|
-
# @return [Toys::
|
259
|
+
# @return [Toys::ToolDefinition] if the tool was found
|
161
260
|
# @return [nil] if no such tool exists
|
162
261
|
#
|
163
262
|
def lookup_specific(words)
|
164
263
|
words = @delimiter_handler.split_path(words.first) if words.size == 1
|
165
264
|
load_for_prefix(words)
|
166
|
-
tool = get_tool_data(words)
|
265
|
+
tool = get_tool_data(words, false)&.cur_definition
|
167
266
|
finish_definitions_in_tree(words) if tool
|
168
267
|
tool
|
169
268
|
end
|
@@ -177,7 +276,7 @@ module Toys
|
|
177
276
|
# rather than just the immediate children (the default)
|
178
277
|
# @param include_hidden [Boolean] If true, include hidden subtools,
|
179
278
|
# e.g. names beginning with underscores.
|
180
|
-
# @return [Array<Toys::
|
279
|
+
# @return [Array<Toys::ToolDefinition>] An array of subtools.
|
181
280
|
#
|
182
281
|
def list_subtools(words, recursive: false, include_hidden: false)
|
183
282
|
load_for_prefix(words)
|
@@ -234,8 +333,8 @@ module Toys
|
|
234
333
|
#
|
235
334
|
# @private
|
236
335
|
#
|
237
|
-
def get_tool(words, priority)
|
238
|
-
get_tool_data(words).get_tool(priority, self)
|
336
|
+
def get_tool(words, priority, tool_class = nil)
|
337
|
+
get_tool_data(words, true).get_tool(priority, self, tool_class)
|
239
338
|
end
|
240
339
|
|
241
340
|
##
|
@@ -248,7 +347,7 @@ module Toys
|
|
248
347
|
# @private
|
249
348
|
#
|
250
349
|
def activate_tool(words, priority)
|
251
|
-
get_tool_data(words).activate_tool(priority, self)
|
350
|
+
get_tool_data(words, true).activate_tool(priority, self)
|
252
351
|
end
|
253
352
|
|
254
353
|
##
|
@@ -267,10 +366,11 @@ module Toys
|
|
267
366
|
#
|
268
367
|
# @private
|
269
368
|
#
|
270
|
-
def build_tool(words, priority)
|
369
|
+
def build_tool(words, priority, tool_class = nil)
|
271
370
|
parent = words.empty? ? nil : get_tool(words.slice(0..-2), priority)
|
272
371
|
middleware_stack = parent ? parent.subtool_middleware_stack : @middleware_stack
|
273
|
-
|
372
|
+
ToolDefinition.new(parent, words, priority, @roots_by_priority[priority],
|
373
|
+
middleware_stack, @middleware_lookup, tool_class)
|
274
374
|
end
|
275
375
|
|
276
376
|
##
|
@@ -335,12 +435,31 @@ module Toys
|
|
335
435
|
# @private
|
336
436
|
#
|
337
437
|
def load_path(parent_source, path, words, remaining_words, priority)
|
438
|
+
if parent_source.git_remote
|
439
|
+
raise LoaderError,
|
440
|
+
"Git source #{parent_source.source_name} tried to load from the local file system"
|
441
|
+
end
|
338
442
|
source = parent_source.absolute_child(path)
|
339
443
|
@mutex.synchronize do
|
340
444
|
load_validated_path(source, words, remaining_words, priority)
|
341
445
|
end
|
342
446
|
end
|
343
447
|
|
448
|
+
##
|
449
|
+
# Load configuration from the given git remote. This is called from the
|
450
|
+
# `load_git` directive in the DSL.
|
451
|
+
#
|
452
|
+
# @private
|
453
|
+
#
|
454
|
+
def load_git(parent_source, git_remote, git_path, git_commit, words, remaining_words, priority,
|
455
|
+
update: false)
|
456
|
+
path = git_cache.find(git_remote, path: git_path, commit: git_commit, update: update)
|
457
|
+
source = parent_source.git_child(git_remote, git_path, git_commit, path)
|
458
|
+
@mutex.synchronize do
|
459
|
+
load_validated_path(source, words, remaining_words, priority)
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
344
463
|
##
|
345
464
|
# Load a subtool block. Called from the `tool` directive in the DSL.
|
346
465
|
#
|
@@ -353,6 +472,18 @@ module Toys
|
|
353
472
|
end
|
354
473
|
end
|
355
474
|
|
475
|
+
##
|
476
|
+
# Get a GitCache.
|
477
|
+
#
|
478
|
+
# @private
|
479
|
+
#
|
480
|
+
def git_cache
|
481
|
+
@git_cache ||= begin
|
482
|
+
require "toys/utils/git_cache"
|
483
|
+
Utils::GitCache.new
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
356
487
|
##
|
357
488
|
# Determine the next setting for remaining_words, given a word.
|
358
489
|
#
|
@@ -381,8 +512,10 @@ module Toys
|
|
381
512
|
result
|
382
513
|
end
|
383
514
|
|
384
|
-
def get_tool_data(words)
|
385
|
-
@mutex.synchronize
|
515
|
+
def get_tool_data(words, create)
|
516
|
+
@mutex.synchronize do
|
517
|
+
create ? (@tool_data[words] ||= ToolData.new(words)) : @tool_data[words]
|
518
|
+
end
|
386
519
|
end
|
387
520
|
|
388
521
|
##
|
@@ -403,7 +536,7 @@ module Toys
|
|
403
536
|
if remaining_words
|
404
537
|
update_min_loaded_priority(priority)
|
405
538
|
tool_class = get_tool(words, priority).tool_class
|
406
|
-
DSL::
|
539
|
+
DSL::Internal.prepare(tool_class, words, priority, remaining_words, source, self) do
|
407
540
|
ContextualError.capture("Error while loading Toys config!") do
|
408
541
|
tool_class.class_eval(&source.source_proc)
|
409
542
|
end
|
@@ -425,7 +558,7 @@ module Toys
|
|
425
558
|
if source.source_type == :file
|
426
559
|
update_min_loaded_priority(priority)
|
427
560
|
tool_class = get_tool(words, priority).tool_class
|
428
|
-
InputFile.evaluate(tool_class, remaining_words, source)
|
561
|
+
InputFile.evaluate(tool_class, words, priority, remaining_words, source, self)
|
429
562
|
else
|
430
563
|
do_preload(source.source_path)
|
431
564
|
load_index_in(source, words, remaining_words, priority)
|
@@ -467,16 +600,20 @@ module Toys
|
|
467
600
|
if @preload_dir_name
|
468
601
|
preload_dir = ::File.join(path, @preload_dir_name)
|
469
602
|
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
|
603
|
+
preload_dir_contents(preload_dir)
|
476
604
|
end
|
477
605
|
end
|
478
606
|
end
|
479
607
|
|
608
|
+
def preload_dir_contents(preload_dir)
|
609
|
+
::Dir.entries(preload_dir).each do |child|
|
610
|
+
next unless ::File.extname(child) == ".rb"
|
611
|
+
preload_file = ::File.join(preload_dir, child)
|
612
|
+
next if !::File.file?(preload_file) || !::File.readable?(preload_file)
|
613
|
+
require preload_file
|
614
|
+
end
|
615
|
+
end
|
616
|
+
|
480
617
|
def sort_tools_by_name(tools)
|
481
618
|
tools.sort! do |a, b|
|
482
619
|
a = a.full_name
|
@@ -520,7 +657,7 @@ module Toys
|
|
520
657
|
class ToolData
|
521
658
|
# @private
|
522
659
|
def initialize(words)
|
523
|
-
@words = words
|
660
|
+
@words = validate_words(words)
|
524
661
|
@definitions = {}
|
525
662
|
@top_priority = @active_priority = nil
|
526
663
|
@mutex = ::Monitor.new
|
@@ -537,12 +674,15 @@ module Toys
|
|
537
674
|
end
|
538
675
|
|
539
676
|
# @private
|
540
|
-
def get_tool(priority, loader)
|
677
|
+
def get_tool(priority, loader, tool_class = nil)
|
541
678
|
@mutex.synchronize do
|
542
679
|
if @top_priority.nil? || @top_priority < priority
|
543
680
|
@top_priority = priority
|
544
681
|
end
|
545
|
-
@definitions
|
682
|
+
if tool_class && @definitions.include?(priority)
|
683
|
+
raise ToolDefinitionError, "Tool already defined for #{@words.inspect}"
|
684
|
+
end
|
685
|
+
@definitions[priority] ||= loader.build_tool(@words, priority, tool_class)
|
546
686
|
end
|
547
687
|
end
|
548
688
|
|
@@ -558,6 +698,14 @@ module Toys
|
|
558
698
|
|
559
699
|
private
|
560
700
|
|
701
|
+
def validate_words(words)
|
702
|
+
words.each do |word|
|
703
|
+
if /[[:cntrl:] #"$&'()*;<>\[\\\]\^`{|}]/.match(word)
|
704
|
+
raise ToolDefinitionError, "Illegal characters in name #{word.inspect}"
|
705
|
+
end
|
706
|
+
end
|
707
|
+
end
|
708
|
+
|
561
709
|
def top_definition
|
562
710
|
@top_priority ? @definitions[@top_priority] : nil
|
563
711
|
end
|
@@ -573,32 +721,23 @@ module Toys
|
|
573
721
|
# @private
|
574
722
|
#
|
575
723
|
class DelimiterHandler
|
576
|
-
## @private
|
577
|
-
ALLOWED_DELIMITERS = %r{^[\./:]*$}.freeze
|
578
|
-
private_constant :ALLOWED_DELIMITERS
|
579
|
-
|
580
|
-
## @private
|
581
724
|
def initialize(extra_delimiters)
|
582
|
-
unless
|
725
|
+
unless %r{^[[:space:]./:]*$}.match?(extra_delimiters)
|
583
726
|
raise ::ArgumentError, "Illegal delimiters in #{extra_delimiters.inspect}"
|
584
727
|
end
|
585
728
|
chars = ::Regexp.escape(extra_delimiters.chars.uniq.join)
|
586
|
-
@
|
729
|
+
@delimiters = ::Regexp.new("[[:space:]#{chars}]")
|
587
730
|
end
|
588
731
|
|
589
|
-
## @private
|
590
732
|
def split_path(str)
|
591
|
-
|
733
|
+
str.split(@delimiters)
|
592
734
|
end
|
593
735
|
|
594
|
-
## @private
|
595
736
|
def find_orig_prefix(args)
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
return [first_split, args]
|
601
|
-
end
|
737
|
+
first_split = (args.first || "").split(@delimiters)
|
738
|
+
if first_split.size > 1
|
739
|
+
args = first_split + args.slice(1..-1)
|
740
|
+
return [first_split, args]
|
602
741
|
end
|
603
742
|
orig_prefix = args.take_while { |arg| !arg.start_with?("-") }
|
604
743
|
[orig_prefix, args]
|