toys-core 0.9.2 → 0.10.2
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/.yardopts +2 -1
- data/CHANGELOG.md +47 -0
- data/LICENSE.md +1 -1
- data/README.md +3 -3
- data/lib/toys-core.rb +14 -21
- data/lib/toys/acceptor.rb +0 -21
- data/lib/toys/arg_parser.rb +1 -22
- data/lib/toys/cli.rb +102 -70
- data/lib/toys/compat.rb +49 -41
- data/lib/toys/completion.rb +0 -21
- data/lib/toys/context.rb +0 -23
- data/lib/toys/core.rb +1 -22
- data/lib/toys/dsl/flag.rb +0 -21
- data/lib/toys/dsl/flag_group.rb +0 -21
- data/lib/toys/dsl/positional_arg.rb +0 -21
- data/lib/toys/dsl/tool.rb +136 -51
- data/lib/toys/errors.rb +1 -22
- data/lib/toys/flag.rb +0 -21
- data/lib/toys/flag_group.rb +0 -21
- data/lib/toys/input_file.rb +0 -21
- data/lib/toys/loader.rb +42 -78
- data/lib/toys/middleware.rb +146 -77
- data/lib/toys/mixin.rb +0 -21
- data/lib/toys/module_lookup.rb +3 -26
- data/lib/toys/positional_arg.rb +0 -21
- data/lib/toys/source_info.rb +49 -38
- data/lib/toys/standard_middleware/add_verbosity_flags.rb +0 -23
- data/lib/toys/standard_middleware/apply_config.rb +42 -0
- data/lib/toys/standard_middleware/handle_usage_errors.rb +7 -28
- data/lib/toys/standard_middleware/set_default_descriptions.rb +0 -23
- data/lib/toys/standard_middleware/show_help.rb +0 -23
- data/lib/toys/standard_middleware/show_root_version.rb +0 -23
- data/lib/toys/standard_mixins/bundler.rb +89 -0
- data/lib/toys/standard_mixins/exec.rb +478 -128
- data/lib/toys/standard_mixins/fileutils.rb +0 -21
- data/lib/toys/standard_mixins/gems.rb +2 -24
- data/lib/toys/standard_mixins/highline.rb +0 -21
- data/lib/toys/standard_mixins/terminal.rb +0 -21
- data/lib/toys/template.rb +0 -21
- data/lib/toys/tool.rb +22 -34
- data/lib/toys/utils/completion_engine.rb +0 -21
- data/lib/toys/utils/exec.rb +142 -71
- data/lib/toys/utils/gems.rb +181 -63
- data/lib/toys/utils/help_text.rb +0 -21
- data/lib/toys/utils/terminal.rb +46 -37
- data/lib/toys/wrappable_string.rb +0 -21
- metadata +25 -9
data/lib/toys/errors.rb
CHANGED
@@ -1,26 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright 2019 Daniel Azuma
|
4
|
-
#
|
5
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
# of this software and associated documentation files (the "Software"), to deal
|
7
|
-
# in the Software without restriction, including without limitation the rights
|
8
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
# copies of the Software, and to permit persons to whom the Software is
|
10
|
-
# furnished to do so, subject to the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be included in
|
13
|
-
# all copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
-
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
-
# IN THE SOFTWARE.
|
22
|
-
;
|
23
|
-
|
24
3
|
module Toys
|
25
4
|
##
|
26
5
|
# An exception indicating an error in a tool definition.
|
@@ -96,7 +75,7 @@ module Toys
|
|
96
75
|
rescue ::SyntaxError => e
|
97
76
|
if (match = /#{::Regexp.escape(path)}:(\d+)/.match(e.message))
|
98
77
|
opts = opts.merge(config_path: path, config_line: match[1].to_i)
|
99
|
-
e = ContextualError.new(e, banner, opts)
|
78
|
+
e = ContextualError.new(e, banner, **opts)
|
100
79
|
end
|
101
80
|
raise e
|
102
81
|
rescue ::ScriptError, ::StandardError => e
|
data/lib/toys/flag.rb
CHANGED
@@ -1,26 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright 2019 Daniel Azuma
|
4
|
-
#
|
5
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
# of this software and associated documentation files (the "Software"), to deal
|
7
|
-
# in the Software without restriction, including without limitation the rights
|
8
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
# copies of the Software, and to permit persons to whom the Software is
|
10
|
-
# furnished to do so, subject to the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be included in
|
13
|
-
# all copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
-
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
-
# IN THE SOFTWARE.
|
22
|
-
;
|
23
|
-
|
24
3
|
module Toys
|
25
4
|
##
|
26
5
|
# Representation of a formal set of flags that set a particular context
|
data/lib/toys/flag_group.rb
CHANGED
@@ -1,26 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright 2019 Daniel Azuma
|
4
|
-
#
|
5
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
# of this software and associated documentation files (the "Software"), to deal
|
7
|
-
# in the Software without restriction, including without limitation the rights
|
8
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
# copies of the Software, and to permit persons to whom the Software is
|
10
|
-
# furnished to do so, subject to the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be included in
|
13
|
-
# all copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
-
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
-
# IN THE SOFTWARE.
|
22
|
-
;
|
23
|
-
|
24
3
|
module Toys
|
25
4
|
##
|
26
5
|
# A FlagGroup is a group of flags with the same requirement settings.
|
data/lib/toys/input_file.rb
CHANGED
@@ -1,26 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright 2019 Daniel Azuma
|
4
|
-
#
|
5
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
# of this software and associated documentation files (the "Software"), to deal
|
7
|
-
# in the Software without restriction, including without limitation the rights
|
8
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
# copies of the Software, and to permit persons to whom the Software is
|
10
|
-
# furnished to do so, subject to the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be included in
|
13
|
-
# all copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
-
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
-
# IN THE SOFTWARE.
|
22
|
-
;
|
23
|
-
|
24
3
|
##
|
25
4
|
# This module is a namespace for constant scopes. Whenever a configuration file
|
26
5
|
# is parsed, a module is created under this parent for that file's constants.
|
data/lib/toys/loader.rb
CHANGED
@@ -1,26 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright 2019 Daniel Azuma
|
4
|
-
#
|
5
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
# of this software and associated documentation files (the "Software"), to deal
|
7
|
-
# in the Software without restriction, including without limitation the rights
|
8
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
# copies of the Software, and to permit persons to whom the Software is
|
10
|
-
# furnished to do so, subject to the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be included in
|
13
|
-
# all copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
-
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
-
# IN THE SOFTWARE.
|
22
|
-
;
|
23
|
-
|
24
3
|
require "monitor"
|
25
4
|
|
26
5
|
module Toys
|
@@ -28,11 +7,7 @@ module Toys
|
|
28
7
|
# The Loader service loads tools from configuration files, and finds the
|
29
8
|
# appropriate tool given a set of command line arguments.
|
30
9
|
#
|
31
|
-
# This class is not thread-safe.
|
32
|
-
#
|
33
10
|
class Loader
|
34
|
-
include ::MonitorMixin
|
35
|
-
|
36
11
|
##
|
37
12
|
# Create a Loader
|
38
13
|
#
|
@@ -50,6 +25,9 @@ module Toys
|
|
50
25
|
# @param data_dir_name [String,nil] A directory with this name that appears
|
51
26
|
# in any configuration directory is added to the data directory search
|
52
27
|
# path for any tool file in that directory.
|
28
|
+
# @param lib_dir_name [String,nil] A directory with this name that appears
|
29
|
+
# in any configuration directory is added to the Ruby load path for any
|
30
|
+
# tool file in that directory.
|
53
31
|
# @param middleware_stack [Array<Toys::Middleware::Spec>] An array of
|
54
32
|
# middleware that will be used by default for all tools loaded by this
|
55
33
|
# loader.
|
@@ -63,13 +41,22 @@ module Toys
|
|
63
41
|
# @param template_lookup [Toys::ModuleLookup] A lookup for
|
64
42
|
# well-known template classes. Defaults to an empty lookup.
|
65
43
|
#
|
66
|
-
def initialize(
|
67
|
-
|
68
|
-
|
69
|
-
|
44
|
+
def initialize(
|
45
|
+
index_file_name: nil,
|
46
|
+
preload_dir_name: nil,
|
47
|
+
preload_file_name: nil,
|
48
|
+
data_dir_name: nil,
|
49
|
+
lib_dir_name: nil,
|
50
|
+
middleware_stack: [],
|
51
|
+
extra_delimiters: "",
|
52
|
+
mixin_lookup: nil,
|
53
|
+
middleware_lookup: nil,
|
54
|
+
template_lookup: nil
|
55
|
+
)
|
70
56
|
if index_file_name && ::File.extname(index_file_name) != ".rb"
|
71
57
|
raise ::ArgumentError, "Illegal index file name #{index_file_name.inspect}"
|
72
58
|
end
|
59
|
+
@mutex = ::Monitor.new
|
73
60
|
@mixin_lookup = mixin_lookup || ModuleLookup.new
|
74
61
|
@template_lookup = template_lookup || ModuleLookup.new
|
75
62
|
@middleware_lookup = middleware_lookup || ModuleLookup.new
|
@@ -77,11 +64,12 @@ module Toys
|
|
77
64
|
@preload_file_name = preload_file_name
|
78
65
|
@preload_dir_name = preload_dir_name
|
79
66
|
@data_dir_name = data_dir_name
|
67
|
+
@lib_dir_name = lib_dir_name
|
80
68
|
@loading_started = false
|
81
69
|
@worklist = []
|
82
70
|
@tool_data = {}
|
83
71
|
@max_priority = @min_priority = 0
|
84
|
-
@middleware_stack = Middleware.
|
72
|
+
@middleware_stack = Middleware.stack(middleware_stack)
|
85
73
|
@delimiter_handler = DelimiterHandler.new(extra_delimiters)
|
86
74
|
get_tool([], -999_999)
|
87
75
|
end
|
@@ -97,7 +85,7 @@ module Toys
|
|
97
85
|
#
|
98
86
|
def add_path(paths, high_priority: false)
|
99
87
|
paths = Array(paths)
|
100
|
-
synchronize do
|
88
|
+
@mutex.synchronize do
|
101
89
|
raise "Cannot add a path after tool loading has started" if @loading_started
|
102
90
|
priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
|
103
91
|
paths.each do |path|
|
@@ -123,7 +111,7 @@ module Toys
|
|
123
111
|
#
|
124
112
|
def add_block(high_priority: false, name: nil, &block)
|
125
113
|
name ||= "(Code block #{block.object_id})"
|
126
|
-
synchronize do
|
114
|
+
@mutex.synchronize do
|
127
115
|
raise "Cannot add a block after tool loading has started" if @loading_started
|
128
116
|
priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
|
129
117
|
source = SourceInfo.create_proc_root(block, name)
|
@@ -238,8 +226,6 @@ module Toys
|
|
238
226
|
##
|
239
227
|
# Get or create the tool definition for the given name and priority.
|
240
228
|
#
|
241
|
-
# @return [Toys::Tool]
|
242
|
-
#
|
243
229
|
# @private
|
244
230
|
#
|
245
231
|
def get_tool(words, priority)
|
@@ -253,12 +239,6 @@ module Toys
|
|
253
239
|
# the active priority, returns `nil`. If the given priority is higher than
|
254
240
|
# the active priority, returns and activates a new tool.
|
255
241
|
#
|
256
|
-
# @param words [Array<String>] The name of the tool.
|
257
|
-
# @param priority [Integer] The priority of the request.
|
258
|
-
#
|
259
|
-
# @return [Toys::Tool] The tool found.
|
260
|
-
# @return [nil] if the given priority is insufficient.
|
261
|
-
#
|
262
242
|
# @private
|
263
243
|
#
|
264
244
|
def activate_tool(words, priority)
|
@@ -269,9 +249,6 @@ module Toys
|
|
269
249
|
# Returns true if the given tool name currently exists in the loader.
|
270
250
|
# Does not load the tool if not found.
|
271
251
|
#
|
272
|
-
# @param words [Array<String>] The name of the tool.
|
273
|
-
# @return [Boolean]
|
274
|
-
#
|
275
252
|
# @private
|
276
253
|
#
|
277
254
|
def tool_defined?(words)
|
@@ -282,29 +259,21 @@ module Toys
|
|
282
259
|
# Build a new tool.
|
283
260
|
# Called only from ToolData.
|
284
261
|
#
|
285
|
-
# @param words [Array<String>] The name of the tool.
|
286
|
-
# @param priority [Integer] The priority of the request.
|
287
|
-
#
|
288
|
-
# @return [Toys::Tool] A new tool object.
|
289
|
-
#
|
290
262
|
# @private
|
291
263
|
#
|
292
264
|
def build_tool(words, priority)
|
293
265
|
parent = words.empty? ? nil : get_tool(words.slice(0..-2), priority)
|
294
|
-
|
295
|
-
Tool.new(self, parent, words, priority,
|
266
|
+
middleware_stack = parent ? parent.subtool_middleware_stack : @middleware_stack
|
267
|
+
Tool.new(self, parent, words, priority, middleware_stack, @middleware_lookup)
|
296
268
|
end
|
297
269
|
|
298
270
|
##
|
299
271
|
# Loads the subtree under the given prefix.
|
300
272
|
#
|
301
|
-
# @param prefix [Array<String>] The name prefix.
|
302
|
-
# @return [self]
|
303
|
-
#
|
304
273
|
# @private
|
305
274
|
#
|
306
275
|
def load_for_prefix(prefix)
|
307
|
-
synchronize do
|
276
|
+
@mutex.synchronize do
|
308
277
|
@loading_started = true
|
309
278
|
cur_worklist = @worklist
|
310
279
|
@worklist = []
|
@@ -323,10 +292,6 @@ module Toys
|
|
323
292
|
##
|
324
293
|
# Attempt to get a well-known mixin module for the given symbolic name.
|
325
294
|
#
|
326
|
-
# @param name [Symbol] Mixin name
|
327
|
-
# @return [Module] The mixin
|
328
|
-
# @return [nil] if not found.
|
329
|
-
#
|
330
295
|
# @private
|
331
296
|
#
|
332
297
|
def resolve_standard_mixin(name)
|
@@ -336,10 +301,6 @@ module Toys
|
|
336
301
|
##
|
337
302
|
# Attempt to get a well-known template class for the given symbolic name.
|
338
303
|
#
|
339
|
-
# @param name [Symbol] Template name
|
340
|
-
# @return [Class] The template.
|
341
|
-
# @return [nil] if not found.
|
342
|
-
#
|
343
304
|
# @private
|
344
305
|
#
|
345
306
|
def resolve_standard_template(name)
|
@@ -350,27 +311,29 @@ module Toys
|
|
350
311
|
# Load configuration from the given path. This is called from the `load`
|
351
312
|
# directive in the DSL.
|
352
313
|
#
|
353
|
-
# @param parent_source [Toys::SourceInfo] The source of the caller.
|
354
|
-
# @param path [String] The file or directory to load.
|
355
|
-
# @param words [Array<String>] The name of the caller, i.e. the context in
|
356
|
-
# which to load.
|
357
|
-
# @param remaining_words [Array<String>] The remaining words.
|
358
|
-
# @param priority [Integer] The priority.
|
359
|
-
#
|
360
314
|
# @private
|
361
315
|
#
|
362
316
|
def load_path(parent_source, path, words, remaining_words, priority)
|
363
317
|
source = parent_source.absolute_child(path)
|
364
|
-
synchronize do
|
318
|
+
@mutex.synchronize do
|
365
319
|
load_validated_path(source, words, remaining_words, priority)
|
366
320
|
end
|
367
321
|
end
|
368
322
|
|
369
323
|
##
|
370
|
-
#
|
324
|
+
# Load a subtool block. Called from the `tool` directive in the DSL.
|
325
|
+
#
|
326
|
+
# @private
|
371
327
|
#
|
372
|
-
|
373
|
-
|
328
|
+
def load_block(parent_source, block, words, remaining_words, priority)
|
329
|
+
source = parent_source.proc_child(block)
|
330
|
+
@mutex.synchronize do
|
331
|
+
load_proc(source, words, remaining_words, priority)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
##
|
336
|
+
# Determine the next setting for remaining_words, given a word.
|
374
337
|
#
|
375
338
|
# @private
|
376
339
|
#
|
@@ -387,11 +350,11 @@ module Toys
|
|
387
350
|
private
|
388
351
|
|
389
352
|
def tool_data_snapshot
|
390
|
-
synchronize { @tool_data.dup }
|
353
|
+
@mutex.synchronize { @tool_data.dup }
|
391
354
|
end
|
392
355
|
|
393
356
|
def get_tool_data(words)
|
394
|
-
synchronize { @tool_data[words] ||= ToolData.new(words) }
|
357
|
+
@mutex.synchronize { @tool_data[words] ||= ToolData.new(words) }
|
395
358
|
end
|
396
359
|
|
397
360
|
##
|
@@ -443,15 +406,16 @@ module Toys
|
|
443
406
|
|
444
407
|
def load_index_in(source, words, remaining_words, priority)
|
445
408
|
return unless @index_file_name
|
446
|
-
index_source = source.relative_child(@index_file_name, @data_dir_name)
|
409
|
+
index_source = source.relative_child(@index_file_name, @data_dir_name, @lib_dir_name)
|
447
410
|
load_relevant_path(index_source, words, remaining_words, priority) if index_source
|
448
411
|
end
|
449
412
|
|
450
413
|
def load_child_in(source, child, words, remaining_words, priority)
|
451
414
|
return if child.start_with?(".") || child == @index_file_name ||
|
452
415
|
child == @preload_file_name || child == @preload_dir_name ||
|
453
|
-
child == @data_dir_name
|
454
|
-
child_source = source.relative_child(child, @data_dir_name)
|
416
|
+
child == @data_dir_name || child == @lib_dir_name
|
417
|
+
child_source = source.relative_child(child, @data_dir_name, @lib_dir_name)
|
418
|
+
return unless child_source
|
455
419
|
child_word = ::File.basename(child, ".rb")
|
456
420
|
next_words = words + [child_word]
|
457
421
|
next_remaining = Loader.next_remaining_words(remaining_words, child_word)
|
data/lib/toys/middleware.rb
CHANGED
@@ -1,26 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright 2019 Daniel Azuma
|
4
|
-
#
|
5
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
# of this software and associated documentation files (the "Software"), to deal
|
7
|
-
# in the Software without restriction, including without limitation the rights
|
8
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
# copies of the Software, and to permit persons to whom the Software is
|
10
|
-
# furnished to do so, subject to the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be included in
|
13
|
-
# all copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
-
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
-
# IN THE SOFTWARE.
|
22
|
-
;
|
23
|
-
|
24
3
|
module Toys
|
25
4
|
##
|
26
5
|
# A middleware is an object that has the opportunity to alter the
|
@@ -39,15 +18,15 @@ module Toys
|
|
39
18
|
# middleware, a Toys middleware can wrap execution with its own code,
|
40
19
|
# replace it outright, or leave it unmodified.
|
41
20
|
#
|
42
|
-
# Generally, a middleware is a class that implements
|
43
|
-
# in this module: {Toys::Middleware#config} and
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
21
|
+
# Generally, a middleware is a class that implements one or more of the
|
22
|
+
# methods defined in this module: {Toys::Middleware#config}, and
|
23
|
+
# {Toys::Middleware#run}. This module provides default implementations that
|
24
|
+
# do nothing, but using them is not required. Middleware objects need respond
|
25
|
+
# only to methods they care about.
|
47
26
|
#
|
48
27
|
module Middleware
|
49
28
|
##
|
50
|
-
# This method is called after a tool has been defined, and gives this
|
29
|
+
# This method is called *after* a tool has been defined, and gives this
|
51
30
|
# middleware the opportunity to modify the tool definition. It is passed
|
52
31
|
# the tool definition object and the loader, and can make any changes to
|
53
32
|
# the tool definition. In most cases, this method should also call
|
@@ -95,12 +74,6 @@ module Toys
|
|
95
74
|
##
|
96
75
|
# Create a middleware spec.
|
97
76
|
#
|
98
|
-
# @overload spec(middleware_object)
|
99
|
-
# Create a spec wrapping an existing middleware object
|
100
|
-
#
|
101
|
-
# @param middleware_object [Toys::Middleware] The middleware object
|
102
|
-
# @return [Toys::Middleware::Spec] A spec
|
103
|
-
#
|
104
77
|
# @overload spec(name, *args, **kwargs, &block)
|
105
78
|
# Create a spec indicating a given middleware name should be
|
106
79
|
# instantiated with the given arguments.
|
@@ -111,27 +84,63 @@ module Toys
|
|
111
84
|
# @param block [Proc,nil] The block to pass to the constructor
|
112
85
|
# @return [Toys::Middleware::Spec] A spec
|
113
86
|
#
|
87
|
+
# @overload spec(array)
|
88
|
+
# Create a middleware spec from an array specification.
|
89
|
+
#
|
90
|
+
# The array must be 1-4 elements long. The first element must be the
|
91
|
+
# middleware name or class. The other three arguments may include any
|
92
|
+
# or all of the following optional elements, in any order:
|
93
|
+
# * An array for the positional arguments to pass to the constructor
|
94
|
+
# * A hash for the keyword arguments to pass to the constructor
|
95
|
+
# * A proc for the block to pass to the constructor
|
96
|
+
#
|
97
|
+
# @param array [Array] The array input
|
98
|
+
# @return [Toys::Middleware::Spec] A spec
|
99
|
+
#
|
100
|
+
# @overload spec(middleware_object)
|
101
|
+
# Create a spec wrapping an existing middleware object
|
102
|
+
#
|
103
|
+
# @param middleware_object [Toys::Middleware] The middleware object
|
104
|
+
# @return [Toys::Middleware::Spec] A spec
|
105
|
+
#
|
114
106
|
def spec(middleware, *args, **kwargs, &block)
|
115
|
-
|
107
|
+
case middleware
|
108
|
+
when ::Array
|
109
|
+
spec_from_array(middleware)
|
110
|
+
when ::String, ::Symbol, ::Class
|
116
111
|
Spec.new(nil, middleware, args, kwargs, block)
|
112
|
+
when Spec
|
113
|
+
middleware
|
117
114
|
else
|
118
115
|
Spec.new(middleware, nil, nil, nil, nil)
|
119
116
|
end
|
120
117
|
end
|
121
118
|
|
122
119
|
##
|
123
|
-
# Create a
|
120
|
+
# Create a {Toys::Middleware::Stack} from an array of middleware specs.
|
121
|
+
# Each element may be one of the following:
|
124
122
|
#
|
125
|
-
#
|
126
|
-
#
|
127
|
-
#
|
128
|
-
#
|
129
|
-
#
|
130
|
-
# * A proc for the block to pass to the constructor
|
123
|
+
# * A {Toys::Middleware} object
|
124
|
+
# * A {Toys::Middleware::Spec}
|
125
|
+
# * An array whose first element is a middleware name or class, and the
|
126
|
+
# subsequent elements are params that define what to pass to the class
|
127
|
+
# constructor (see {Toys::Middleware.spec})
|
131
128
|
#
|
132
|
-
# @param
|
133
|
-
# @return [Toys::Middleware::
|
129
|
+
# @param input [Array<Toys::Middleware,Toys::Middleware::Spec,Array>]
|
130
|
+
# @return [Toys::Middleware::Stack]
|
134
131
|
#
|
132
|
+
def stack(input)
|
133
|
+
case input
|
134
|
+
when Stack
|
135
|
+
input
|
136
|
+
when ::Array
|
137
|
+
Stack.new(default_specs: input.map { |spec| spec(spec) })
|
138
|
+
else
|
139
|
+
raise ::ArgumentError, "Illegal middleware stack: #{input.inspect}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
## @private
|
135
144
|
def spec_from_array(array)
|
136
145
|
middleware = array.first
|
137
146
|
if !middleware.is_a?(::String) && !middleware.is_a?(::Symbol) && !middleware.is_a?(::Class)
|
@@ -154,32 +163,6 @@ module Toys
|
|
154
163
|
end
|
155
164
|
Spec.new(nil, middleware, args, kwargs, block)
|
156
165
|
end
|
157
|
-
|
158
|
-
##
|
159
|
-
# Resolve all arguments into an array of middleware specs. Each argument
|
160
|
-
# may be one of the following:
|
161
|
-
#
|
162
|
-
# * A {Toys::Middleware} object
|
163
|
-
# * A {Toys::Middleware::Spec}
|
164
|
-
# * An array whose first element is a middleware name or class, and the
|
165
|
-
# subsequent elements are params that define what to pass to the class
|
166
|
-
# constructor (see {Toys::Middleware.spec_from_array})
|
167
|
-
#
|
168
|
-
# @param items [Array<Toys::Middleware,Toys::Middleware::Spec,Array>]
|
169
|
-
# @return [Array<Toys::Middleware::Spec>]
|
170
|
-
#
|
171
|
-
def resolve_specs(*items)
|
172
|
-
items.map do |item|
|
173
|
-
case item
|
174
|
-
when ::Array
|
175
|
-
spec_from_array(item)
|
176
|
-
when Spec
|
177
|
-
item
|
178
|
-
else
|
179
|
-
spec(item)
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
166
|
end
|
184
167
|
|
185
168
|
##
|
@@ -216,14 +199,7 @@ module Toys
|
|
216
199
|
else
|
217
200
|
klass = @name
|
218
201
|
end
|
219
|
-
|
220
|
-
# initialize will fail if there are no formal keyword args.
|
221
|
-
formals = klass.instance_method(:initialize).parameters
|
222
|
-
if @kwargs.empty? && formals.all? { |arg| arg.first != :key && arg.first != :keyrest }
|
223
|
-
klass.new(*@args, &@block)
|
224
|
-
else
|
225
|
-
klass.new(*@args, **@kwargs, &@block)
|
226
|
-
end
|
202
|
+
Compat.instantiate(klass, @args, @kwargs, @block)
|
227
203
|
end
|
228
204
|
|
229
205
|
##
|
@@ -270,6 +246,99 @@ module Toys
|
|
270
246
|
@kwargs = kwargs
|
271
247
|
@block = block
|
272
248
|
end
|
249
|
+
|
250
|
+
## @private
|
251
|
+
def ==(other)
|
252
|
+
other.is_a?(Spec) &&
|
253
|
+
object.eql?(other.object) &&
|
254
|
+
name.eql?(other.name) &&
|
255
|
+
args.eql?(other.args) &&
|
256
|
+
kwargs.eql?(other.kwargs) &&
|
257
|
+
block.eql?(other.block)
|
258
|
+
end
|
259
|
+
alias eql? ==
|
260
|
+
|
261
|
+
## @private
|
262
|
+
def hash
|
263
|
+
[object, name, args, kwargs, block].hash
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
##
|
268
|
+
# A stack of middleware specs.
|
269
|
+
#
|
270
|
+
class Stack
|
271
|
+
##
|
272
|
+
# The middleware specs that precede the default set.
|
273
|
+
# @return [Array<Toys::Middleware:Spec>]
|
274
|
+
#
|
275
|
+
attr_reader :pre_specs
|
276
|
+
|
277
|
+
##
|
278
|
+
# The default set of middleware specs.
|
279
|
+
# @return [Array<Toys::Middleware:Spec>]
|
280
|
+
#
|
281
|
+
attr_reader :default_specs
|
282
|
+
|
283
|
+
##
|
284
|
+
# The middleware specs that follow the default set.
|
285
|
+
# @return [Array<Toys::Middleware:Spec>]
|
286
|
+
#
|
287
|
+
attr_reader :post_specs
|
288
|
+
|
289
|
+
##
|
290
|
+
# Add a middleware spec to the stack, in the default location, which is
|
291
|
+
# at the end of pre_specs). See {Toys::Middleware.spec} for a description
|
292
|
+
# of the arguments you can pass.
|
293
|
+
#
|
294
|
+
# @overload add(name, *args, **kwargs, &block)
|
295
|
+
# @overload add(array)
|
296
|
+
# @overload add(middleware_object)
|
297
|
+
#
|
298
|
+
def add(middleware, *args, **kwargs, &block)
|
299
|
+
pre_specs.push(Middleware.spec(middleware, *args, **kwargs, &block))
|
300
|
+
end
|
301
|
+
|
302
|
+
##
|
303
|
+
# Duplicate this stack.
|
304
|
+
#
|
305
|
+
# @return [Toys::Middleware::Stack]
|
306
|
+
#
|
307
|
+
def dup
|
308
|
+
Stack.new(pre_specs: pre_specs.dup,
|
309
|
+
post_specs: post_specs.dup,
|
310
|
+
default_specs: default_specs.dup)
|
311
|
+
end
|
312
|
+
|
313
|
+
##
|
314
|
+
# Build the middleware in this stack.
|
315
|
+
#
|
316
|
+
# @return [Array<Toys::Middleware>]
|
317
|
+
#
|
318
|
+
def build(middleware_lookup)
|
319
|
+
(@pre_specs + @default_specs + @post_specs).map { |spec| spec.build(middleware_lookup) }
|
320
|
+
end
|
321
|
+
|
322
|
+
## @private
|
323
|
+
def initialize(default_specs: nil, pre_specs: nil, post_specs: nil)
|
324
|
+
@pre_specs = pre_specs || []
|
325
|
+
@post_specs = post_specs || []
|
326
|
+
@default_specs = default_specs || []
|
327
|
+
end
|
328
|
+
|
329
|
+
## @private
|
330
|
+
def ==(other)
|
331
|
+
other.is_a?(Stack) &&
|
332
|
+
pre_specs.eql?(other.pre_specs) &&
|
333
|
+
default_specs.eql?(other.default_specs) &&
|
334
|
+
post_specs.eql?(other.post_specs)
|
335
|
+
end
|
336
|
+
alias eql? ==
|
337
|
+
|
338
|
+
## @private
|
339
|
+
def hash
|
340
|
+
[@pre_specs, @default_specs, @post_specs].hash
|
341
|
+
end
|
273
342
|
end
|
274
343
|
end
|
275
344
|
end
|