toys-core 0.3.6 → 0.3.7
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 +13 -0
- data/lib/toys-core.rb +20 -5
- data/lib/toys/cli.rb +39 -32
- data/lib/toys/core_version.rb +1 -1
- data/lib/toys/{tool → definition}/acceptor.rb +21 -15
- data/lib/toys/{utils/line_output.rb → definition/alias.rb} +47 -59
- data/lib/toys/{tool/arg_definition.rb → definition/arg.rb} +17 -7
- data/lib/toys/{tool/flag_definition.rb → definition/flag.rb} +19 -9
- data/lib/toys/definition/tool.rb +574 -0
- data/lib/toys/dsl/arg.rb +118 -0
- data/lib/toys/dsl/flag.rb +132 -0
- data/lib/toys/dsl/tool.rb +521 -0
- data/lib/toys/errors.rb +2 -2
- data/lib/toys/helpers.rb +3 -3
- data/lib/toys/helpers/exec.rb +31 -25
- data/lib/toys/helpers/fileutils.rb +8 -2
- data/lib/toys/helpers/highline.rb +8 -1
- data/lib/toys/{alias.rb → helpers/terminal.rb} +44 -53
- data/lib/toys/input_file.rb +61 -0
- data/lib/toys/loader.rb +87 -77
- data/lib/toys/middleware.rb +3 -3
- data/lib/toys/middleware/add_verbosity_flags.rb +22 -20
- data/lib/toys/middleware/base.rb +53 -5
- data/lib/toys/middleware/handle_usage_errors.rb +9 -12
- data/lib/toys/middleware/set_default_descriptions.rb +6 -7
- data/lib/toys/middleware/show_help.rb +71 -67
- data/lib/toys/middleware/show_root_version.rb +9 -9
- data/lib/toys/runner.rb +157 -0
- data/lib/toys/template.rb +4 -3
- data/lib/toys/templates.rb +2 -2
- data/lib/toys/templates/clean.rb +2 -2
- data/lib/toys/templates/gem_build.rb +5 -5
- data/lib/toys/templates/minitest.rb +2 -2
- data/lib/toys/templates/rubocop.rb +2 -2
- data/lib/toys/templates/yardoc.rb +2 -2
- data/lib/toys/tool.rb +168 -625
- data/lib/toys/utils/exec.rb +19 -18
- data/lib/toys/utils/gems.rb +140 -0
- data/lib/toys/utils/help_text.rb +25 -20
- data/lib/toys/utils/terminal.rb +412 -0
- data/lib/toys/utils/wrappable_string.rb +3 -1
- metadata +15 -24
- data/lib/toys/config_dsl.rb +0 -699
- data/lib/toys/context.rb +0 -290
- data/lib/toys/helpers/spinner.rb +0 -142
data/lib/toys/errors.rb
CHANGED
@@ -79,7 +79,7 @@ module Toys
|
|
79
79
|
e = ContextualError.new(e, banner, opts)
|
80
80
|
end
|
81
81
|
raise e
|
82
|
-
rescue ::StandardError => e
|
82
|
+
rescue ::ScriptError, ::StandardError => e
|
83
83
|
e = ContextualError.new(e, banner)
|
84
84
|
add_fields_if_missing(e, opts)
|
85
85
|
add_config_path_if_missing(e, path)
|
@@ -92,7 +92,7 @@ module Toys
|
|
92
92
|
rescue ContextualError => e
|
93
93
|
add_fields_if_missing(e, opts)
|
94
94
|
raise e
|
95
|
-
rescue ::StandardError => e
|
95
|
+
rescue ::ScriptError, ::StandardError => e
|
96
96
|
raise ContextualError.new(e, banner, opts)
|
97
97
|
end
|
98
98
|
|
data/lib/toys/helpers.rb
CHANGED
@@ -45,10 +45,10 @@ module Toys
|
|
45
45
|
# * `:spinner` : Displays a spinner on the terminal.
|
46
46
|
#
|
47
47
|
# @param [String,Symbol] name Name of the helper module to return
|
48
|
-
# @return [Module,nil] The module
|
48
|
+
# @return [Module,nil] The module.
|
49
49
|
#
|
50
|
-
def self.lookup(name)
|
51
|
-
Utils::ModuleLookup.lookup(:helpers, name)
|
50
|
+
def self.lookup!(name)
|
51
|
+
Utils::ModuleLookup.lookup!(:helpers, name)
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
data/lib/toys/helpers/exec.rb
CHANGED
@@ -37,24 +37,15 @@ module Toys
|
|
37
37
|
# in a string. Also provides an interface for controlling a spawned
|
38
38
|
# process's streams.
|
39
39
|
#
|
40
|
+
# You may make these methods available to your tool by including the
|
41
|
+
# following directive in your tool configuration:
|
42
|
+
#
|
43
|
+
# include :exec
|
44
|
+
#
|
40
45
|
# This is a frontend for {Toys::Utils::Exec}. More information is
|
41
46
|
# available in that class's documentation.
|
42
47
|
#
|
43
48
|
module Exec
|
44
|
-
## @private
|
45
|
-
def self.extended(context)
|
46
|
-
context[Exec] = Utils::Exec.new do |k|
|
47
|
-
case k
|
48
|
-
when :logger
|
49
|
-
context[Context::LOGGER]
|
50
|
-
when :nonzero_status_handler
|
51
|
-
if context[Context::EXIT_ON_NONZERO_STATUS]
|
52
|
-
proc { |s| context.exit(s.exitstatus) }
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
49
|
##
|
59
50
|
# Set default configuration keys.
|
60
51
|
#
|
@@ -62,7 +53,7 @@ module Toys
|
|
62
53
|
# configuration options in the {Toys::Utils::Exec} docs.
|
63
54
|
#
|
64
55
|
def configure_exec(opts = {})
|
65
|
-
|
56
|
+
Exec._exec(self).configure_defaults(Exec._setup_exec_opts(opts, self))
|
66
57
|
end
|
67
58
|
|
68
59
|
##
|
@@ -82,7 +73,7 @@ module Toys
|
|
82
73
|
# code and any captured output.
|
83
74
|
#
|
84
75
|
def exec(cmd, opts = {}, &block)
|
85
|
-
|
76
|
+
Exec._exec(self).exec(cmd, Exec._setup_exec_opts(opts, self), &block)
|
86
77
|
end
|
87
78
|
|
88
79
|
##
|
@@ -101,7 +92,7 @@ module Toys
|
|
101
92
|
# code and any captured output.
|
102
93
|
#
|
103
94
|
def ruby(args, opts = {}, &block)
|
104
|
-
|
95
|
+
Exec._exec(self).ruby(args, Exec._setup_exec_opts(opts, self), &block)
|
105
96
|
end
|
106
97
|
|
107
98
|
##
|
@@ -110,13 +101,11 @@ module Toys
|
|
110
101
|
# @param [String] cmd The shell command to execute.
|
111
102
|
# @param [Hash] opts The command options. See the section on
|
112
103
|
# configuration options in the {Toys::Utils::Exec} module docs.
|
113
|
-
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller for
|
114
|
-
# the subprocess streams.
|
115
104
|
#
|
116
105
|
# @return [Integer] The exit code
|
117
106
|
#
|
118
107
|
def sh(cmd, opts = {})
|
119
|
-
|
108
|
+
Exec._exec(self).sh(cmd, Exec._setup_exec_opts(opts, self))
|
120
109
|
end
|
121
110
|
|
122
111
|
##
|
@@ -128,21 +117,38 @@ module Toys
|
|
128
117
|
# @param [String,Array<String>] cmd The command to execute.
|
129
118
|
# @param [Hash] opts The command options. See the section on
|
130
119
|
# configuration options in the {Toys::Utils::Exec} module docs.
|
131
|
-
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller for
|
132
|
-
# the subprocess streams.
|
133
120
|
#
|
134
121
|
# @return [String] What was written to standard out.
|
135
122
|
#
|
136
123
|
def capture(cmd, opts = {})
|
137
|
-
|
124
|
+
Exec._exec(self).capture(cmd, Exec._setup_exec_opts(opts, self))
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# Exit if the given status code is nonzero. Otherwise, returns 0.
|
129
|
+
#
|
130
|
+
# @param [Integer,Process::Status,Toys::Utils::Exec::Result] status
|
131
|
+
#
|
132
|
+
def exit_on_nonzero_status(status)
|
133
|
+
status = status.exit_code if status.respond_to?(:exit_code)
|
134
|
+
status = status.exitstatus if status.respond_to?(:exitstatus)
|
135
|
+
exit(status) unless status.zero?
|
136
|
+
0
|
137
|
+
end
|
138
|
+
|
139
|
+
## @private
|
140
|
+
def self._exec(tool)
|
141
|
+
tool[Exec] ||= Utils::Exec.new do |k|
|
142
|
+
k == :logger ? tool[Tool::Keys::LOGGER] : nil
|
143
|
+
end
|
138
144
|
end
|
139
145
|
|
140
146
|
## @private
|
141
|
-
def self._setup_exec_opts(opts,
|
147
|
+
def self._setup_exec_opts(opts, tool)
|
142
148
|
return opts unless opts.key?(:exit_on_nonzero_status)
|
143
149
|
nonzero_status_handler =
|
144
150
|
if opts[:exit_on_nonzero_status]
|
145
|
-
proc { |s|
|
151
|
+
proc { |s| tool.exit(s.exitstatus) }
|
146
152
|
end
|
147
153
|
opts.merge(nonzero_status_handler: nonzero_status_handler)
|
148
154
|
end
|
@@ -34,10 +34,16 @@ module Toys
|
|
34
34
|
##
|
35
35
|
# A module that provides all methods in the "fileutils" standard library.
|
36
36
|
#
|
37
|
+
# You may make the methods in the `FileUtils` standard library module
|
38
|
+
# available to your tool by including the following directive in your tool
|
39
|
+
# configuration:
|
40
|
+
#
|
41
|
+
# include :fileutils
|
42
|
+
#
|
37
43
|
module Fileutils
|
38
44
|
## @private
|
39
|
-
def self.
|
40
|
-
|
45
|
+
def self.included(mod)
|
46
|
+
mod.include(::FileUtils)
|
41
47
|
end
|
42
48
|
end
|
43
49
|
end
|
@@ -27,12 +27,19 @@
|
|
27
27
|
# POSSIBILITY OF SUCH DAMAGE.
|
28
28
|
;
|
29
29
|
|
30
|
+
require "toys/utils/gems"
|
31
|
+
Toys::Utils::Gems.activate("highline", "~> 1.7")
|
30
32
|
require "highline"
|
31
33
|
|
32
34
|
module Toys
|
33
35
|
module Helpers
|
34
36
|
##
|
35
|
-
# A module that provides access to highline.
|
37
|
+
# A module that provides access to the capabilities of the highline gem.
|
38
|
+
#
|
39
|
+
# You may make these methods available to your tool by including the
|
40
|
+
# following directive in your tool configuration:
|
41
|
+
#
|
42
|
+
# include :highline
|
36
43
|
#
|
37
44
|
module Highline
|
38
45
|
##
|
@@ -27,68 +27,59 @@
|
|
27
27
|
# POSSIBILITY OF SUCH DAMAGE.
|
28
28
|
;
|
29
29
|
|
30
|
+
require "toys/utils/terminal"
|
31
|
+
|
30
32
|
module Toys
|
31
|
-
|
32
|
-
# An alias is a name that refers to another name.
|
33
|
-
#
|
34
|
-
class Alias
|
33
|
+
module Helpers
|
35
34
|
##
|
36
|
-
#
|
35
|
+
# A helper that provides a simple terminal. It includes a set of methods
|
36
|
+
# that produce styled output, get user input, and otherwise interact with
|
37
|
+
# the user's terminal.
|
37
38
|
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
# be a local reference (a single string) or a global reference (an
|
41
|
-
# array of strings)
|
39
|
+
# You may make these methods available to your tool by including the
|
40
|
+
# following directive in your tool configuration:
|
42
41
|
#
|
43
|
-
|
44
|
-
@target_name =
|
45
|
-
if target.is_a?(::String)
|
46
|
-
full_name[0..-2] + [target]
|
47
|
-
else
|
48
|
-
target.dup
|
49
|
-
end
|
50
|
-
@target_name.freeze
|
51
|
-
@full_name = full_name.dup.freeze
|
52
|
-
end
|
53
|
-
|
54
|
-
##
|
55
|
-
# Return the name of the tool as an array of strings.
|
56
|
-
# This array may not be modified.
|
57
|
-
# @return [Array<String>]
|
42
|
+
# include :terminal
|
58
43
|
#
|
59
|
-
|
44
|
+
module Terminal
|
45
|
+
##
|
46
|
+
# Returns a global terminal instance
|
47
|
+
# @return [Toys::Utils::Terminal]
|
48
|
+
#
|
49
|
+
def terminal
|
50
|
+
self[Terminal] ||= Utils::Terminal.new
|
51
|
+
end
|
60
52
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
53
|
+
##
|
54
|
+
# @see Toys::Utils::Terminal#puts
|
55
|
+
#
|
56
|
+
def puts(str = "", *styles)
|
57
|
+
terminal.puts(str, *styles)
|
58
|
+
end
|
67
59
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
end
|
60
|
+
##
|
61
|
+
# @see Toys::Utils::Terminal#write
|
62
|
+
#
|
63
|
+
def write(str = "", *styles)
|
64
|
+
terminal.write(str, *styles)
|
65
|
+
end
|
75
66
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
full_name.join(" ")
|
83
|
-
end
|
67
|
+
##
|
68
|
+
# @see Toys::Utils::Terminal#confirm
|
69
|
+
#
|
70
|
+
def confirm(prompt = "Proceed?")
|
71
|
+
terminal.confirm(prompt)
|
72
|
+
end
|
84
73
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
74
|
+
##
|
75
|
+
# @see Toys::Utils::Terminal#spinner
|
76
|
+
#
|
77
|
+
def spinner(leading_text: "", final_text: "",
|
78
|
+
frame_length: nil, frames: nil, style: nil, &block)
|
79
|
+
terminal.spinner(leading_text: leading_text, final_text: final_text,
|
80
|
+
frame_length: frame_length, frames: frames, style: style,
|
81
|
+
&block)
|
82
|
+
end
|
92
83
|
end
|
93
84
|
end
|
94
85
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Copyright 2018 Daniel Azuma
|
2
|
+
#
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# * Neither the name of the copyright holder, nor the names of any other
|
14
|
+
# contributors to this software, may be used to endorse or promote products
|
15
|
+
# derived from this software without specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
18
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
21
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
22
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
23
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
24
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
25
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
26
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
27
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
;
|
29
|
+
|
30
|
+
##
|
31
|
+
# Internal modules providing constant namespaces for config files.
|
32
|
+
#
|
33
|
+
module Toys::InputFile # rubocop:disable Style/ClassAndModuleChildren
|
34
|
+
## @private
|
35
|
+
def self.__binding
|
36
|
+
binding
|
37
|
+
end
|
38
|
+
|
39
|
+
## @private
|
40
|
+
def self.evaluate(tool_class, remaining_words, path)
|
41
|
+
namespace = ::Module.new
|
42
|
+
namespace.module_eval do
|
43
|
+
include ::Toys::Tool::Keys
|
44
|
+
@tool_class = tool_class
|
45
|
+
end
|
46
|
+
name = "M#{namespace.object_id}"
|
47
|
+
const_set(name, namespace)
|
48
|
+
str = <<-STR
|
49
|
+
module #{name}; @tool_class.class_eval do
|
50
|
+
#{::IO.read(path)}
|
51
|
+
end end
|
52
|
+
STR
|
53
|
+
::Toys::DSL::Tool.prepare(tool_class, remaining_words, path) do
|
54
|
+
::Toys::ContextualError.capture_path("Error while loading Toys config!", path) do
|
55
|
+
# rubocop:disable Security/Eval
|
56
|
+
eval(str, __binding, path, 0)
|
57
|
+
# rubocop:enable Security/Eval
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/toys/loader.rb
CHANGED
@@ -33,6 +33,17 @@ module Toys
|
|
33
33
|
# appropriate tool given a set of command line arguments.
|
34
34
|
#
|
35
35
|
class Loader
|
36
|
+
## @private
|
37
|
+
ToolData = ::Struct.new(:definitions, :top_priority, :active_priority) do
|
38
|
+
def top_definition
|
39
|
+
top_priority ? definitions[top_priority] : nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def active_definition
|
43
|
+
active_priority ? definitions[active_priority] : nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
36
47
|
##
|
37
48
|
# Create a Loader
|
38
49
|
#
|
@@ -60,7 +71,7 @@ module Toys
|
|
60
71
|
@preload_file_name = preload_file_name
|
61
72
|
@middleware_stack = middleware_stack
|
62
73
|
@load_worklist = []
|
63
|
-
@
|
74
|
+
@tool_data = {}
|
64
75
|
@max_priority = @min_priority = 0
|
65
76
|
end
|
66
77
|
|
@@ -93,27 +104,26 @@ module Toys
|
|
93
104
|
# that are not part of the tool name and should be passed as tool args.
|
94
105
|
#
|
95
106
|
# @param [String] args Command line arguments
|
96
|
-
# @return [Array(Toys::Tool,Array<String>)]
|
107
|
+
# @return [Array(Toys::Definition::Tool,Array<String>)]
|
97
108
|
#
|
98
109
|
def lookup(args)
|
99
110
|
orig_prefix = args.take_while { |arg| !arg.start_with?("-") }
|
100
|
-
cur_prefix = orig_prefix
|
111
|
+
cur_prefix = orig_prefix
|
101
112
|
loop do
|
102
113
|
load_for_prefix(cur_prefix)
|
103
|
-
p = orig_prefix
|
104
|
-
|
105
|
-
|
106
|
-
if
|
107
|
-
finish_definitions_in_tree(
|
108
|
-
return [
|
114
|
+
p = orig_prefix
|
115
|
+
loop do
|
116
|
+
tool_definition = get_active_tool(p, [])
|
117
|
+
if tool_definition
|
118
|
+
finish_definitions_in_tree(tool_definition.full_name)
|
119
|
+
return [tool_definition, args.slice(p.length..-1)]
|
109
120
|
end
|
110
|
-
p.
|
121
|
+
break if p.empty? || p.length <= cur_prefix.length
|
122
|
+
p = p.slice(0..-2)
|
111
123
|
end
|
112
|
-
|
124
|
+
return nil if cur_prefix.empty?
|
125
|
+
cur_prefix = cur_prefix.slice(0..-2)
|
113
126
|
end
|
114
|
-
tool = get_or_create_tool([])
|
115
|
-
finish_definitions_in_tree([])
|
116
|
-
[tool, args]
|
117
127
|
end
|
118
128
|
|
119
129
|
##
|
@@ -123,20 +133,21 @@ module Toys
|
|
123
133
|
# @param [Array<String>] words The name of the parent tool
|
124
134
|
# @param [Boolean] recursive If true, return all subtools recursively
|
125
135
|
# rather than just the immediate children (the default)
|
126
|
-
# @return [Array<Toys::Tool,Tool::Alias>]
|
136
|
+
# @return [Array<Toys::Definition::Tool,Tool::Definition::Alias>]
|
127
137
|
#
|
128
138
|
def list_subtools(words, recursive: false)
|
129
139
|
load_for_prefix(words)
|
130
140
|
found_tools = []
|
131
141
|
len = words.length
|
132
|
-
@
|
142
|
+
@tool_data.each do |n, td|
|
133
143
|
next if n.empty?
|
134
144
|
if recursive
|
135
145
|
next if n.length <= len || n.slice(0, len) != words
|
136
146
|
else
|
137
147
|
next unless n.slice(0..-2) == words
|
138
148
|
end
|
139
|
-
|
149
|
+
tool = td.active_definition || td.top_definition
|
150
|
+
found_tools << tool unless tool.nil?
|
140
151
|
end
|
141
152
|
sort_tools_by_name(found_tools)
|
142
153
|
end
|
@@ -151,7 +162,7 @@ module Toys
|
|
151
162
|
def has_subtools?(words)
|
152
163
|
load_for_prefix(words)
|
153
164
|
len = words.length
|
154
|
-
@
|
165
|
+
@tool_data.each_key do |n|
|
155
166
|
return true if !n.empty? && n.length > len && n.slice(0, len) == words
|
156
167
|
end
|
157
168
|
false
|
@@ -167,9 +178,10 @@ module Toys
|
|
167
178
|
def finish_definitions_in_tree(words)
|
168
179
|
load_for_prefix(words)
|
169
180
|
len = words.length
|
170
|
-
@
|
181
|
+
@tool_data.each do |n, td|
|
171
182
|
next if n.length < len || n.slice(0, len) != words
|
172
|
-
|
183
|
+
tool = td.active_definition || td.top_definition
|
184
|
+
tool.finish_definition(self) if tool.is_a?(Definition::Tool)
|
173
185
|
end
|
174
186
|
end
|
175
187
|
|
@@ -178,28 +190,18 @@ module Toys
|
|
178
190
|
# Does not do any loading. If the tool is not present, creates it.
|
179
191
|
#
|
180
192
|
# @param [Array<String>] words The name of the tool.
|
181
|
-
# @param [Integer
|
182
|
-
# @return [Toys::Tool,Toys::Alias,nil] The tool or
|
183
|
-
# given priority is insufficient
|
193
|
+
# @param [Integer] priority The priority of the request.
|
194
|
+
# @return [Toys::Definition::Tool,Toys::Definition::Alias,nil] The tool or
|
195
|
+
# alias, or `nil` if the given priority is insufficient
|
184
196
|
#
|
185
197
|
# @private
|
186
198
|
#
|
187
|
-
def
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
end
|
194
|
-
return tool
|
195
|
-
end
|
196
|
-
return nil if tool_priority > priority
|
197
|
-
end
|
198
|
-
get_or_create_tool(words[0..-2]) unless words.empty?
|
199
|
-
tool = Tool.new(words)
|
200
|
-
tool.middleware_stack.concat(Middleware.resolve_stack(@middleware_stack))
|
201
|
-
@tools[words] = [tool, priority]
|
202
|
-
tool
|
199
|
+
def activate_tool_definition(words, priority)
|
200
|
+
tool_data = get_tool_data(words)
|
201
|
+
return tool_data.active_definition if tool_data.active_priority == priority
|
202
|
+
return nil if tool_data.active_priority && tool_data.active_priority > priority
|
203
|
+
tool_data.active_priority = priority
|
204
|
+
get_tool_definition(words, priority)
|
203
205
|
end
|
204
206
|
|
205
207
|
##
|
@@ -209,39 +211,20 @@ module Toys
|
|
209
211
|
# @param [Array<String>] target The alias target name
|
210
212
|
# @param [Integer] priority The priority of the request
|
211
213
|
#
|
212
|
-
# @return [Toys::Alias] The alias created
|
214
|
+
# @return [Toys::Definition::Alias] The alias created
|
213
215
|
#
|
214
216
|
# @private
|
215
217
|
#
|
216
218
|
def make_alias(words, target, priority)
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
raise LoaderError, "Cannot make #{words.inspect} an alias because it is already defined"
|
222
|
-
elsif tool_priority > priority
|
223
|
-
return nil
|
224
|
-
end
|
225
|
-
end
|
219
|
+
tool_data = get_tool_data(words)
|
220
|
+
if tool_data.definitions.key?(priority)
|
221
|
+
raise ToolDefinitionError,
|
222
|
+
"Cannot make #{words.inspect} an alias because it is already defined"
|
226
223
|
end
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
##
|
233
|
-
# Adds a tool directly to the loader.
|
234
|
-
# This should be used only for testing, as it overrides normal priority
|
235
|
-
# checking.
|
236
|
-
#
|
237
|
-
# @param [Toys::Tool] tool Tool to add.
|
238
|
-
# @param [Integer,nil] priority Priority for the tool.
|
239
|
-
#
|
240
|
-
# @private
|
241
|
-
#
|
242
|
-
def put_tool!(tool, priority = nil)
|
243
|
-
@tools[tool.full_name] = [tool, priority]
|
244
|
-
self
|
224
|
+
alias_def = Definition::Alias.new(self, words, target, priority)
|
225
|
+
tool_data.definitions[priority] = alias_def
|
226
|
+
activate_tool_definition(words, priority)
|
227
|
+
alias_def
|
245
228
|
end
|
246
229
|
|
247
230
|
##
|
@@ -254,7 +237,7 @@ module Toys
|
|
254
237
|
# @private
|
255
238
|
#
|
256
239
|
def tool_defined?(words)
|
257
|
-
@
|
240
|
+
@tool_data.key?(words)
|
258
241
|
end
|
259
242
|
|
260
243
|
##
|
@@ -264,25 +247,47 @@ module Toys
|
|
264
247
|
# @param [Array<Array<String>>] looked_up List of names that have already
|
265
248
|
# been traversed during alias resolution. Used to detect circular
|
266
249
|
# alias references.
|
267
|
-
# @return [Toys::Tool,nil] The tool, or `nil` if not found
|
250
|
+
# @return [Toys::Definition::Tool,nil] The tool, or `nil` if not found
|
268
251
|
#
|
269
252
|
# @private
|
270
253
|
#
|
271
|
-
def
|
272
|
-
|
273
|
-
result =
|
274
|
-
|
254
|
+
def get_active_tool(words, looked_up = [])
|
255
|
+
tool_data = get_tool_data(words)
|
256
|
+
result = tool_data.active_definition
|
257
|
+
case result
|
258
|
+
when Definition::Alias
|
275
259
|
words = result.target_name
|
276
260
|
if looked_up.include?(words)
|
277
|
-
raise
|
261
|
+
raise ToolDefinitionError, "Circular alias references: #{looked_up.inspect}"
|
278
262
|
end
|
279
263
|
looked_up << words
|
280
|
-
|
281
|
-
|
264
|
+
get_active_tool(words, looked_up)
|
265
|
+
when Definition::Tool
|
282
266
|
result
|
267
|
+
else
|
268
|
+
tool_data.top_definition
|
283
269
|
end
|
284
270
|
end
|
285
271
|
|
272
|
+
##
|
273
|
+
# Get the tool definition for the given name and priority.
|
274
|
+
#
|
275
|
+
# @private
|
276
|
+
#
|
277
|
+
def get_tool_definition(words, priority)
|
278
|
+
parent = words.empty? ? nil : get_tool_definition(words.slice(0..-2), priority)
|
279
|
+
if parent.is_a?(Definition::Alias)
|
280
|
+
raise ToolDefinitionError,
|
281
|
+
"Cannot create children of #{parent.display_name.inspect} because it is an alias"
|
282
|
+
end
|
283
|
+
tool_data = get_tool_data(words)
|
284
|
+
if tool_data.top_priority.nil? || tool_data.top_priority < priority
|
285
|
+
tool_data.top_priority = priority
|
286
|
+
end
|
287
|
+
tool_data.definitions[priority] ||=
|
288
|
+
Definition::Tool.new(self, parent, words, priority, @middleware_stack)
|
289
|
+
end
|
290
|
+
|
286
291
|
##
|
287
292
|
# Load configuration from the given path.
|
288
293
|
#
|
@@ -309,6 +314,10 @@ module Toys
|
|
309
314
|
|
310
315
|
private
|
311
316
|
|
317
|
+
def get_tool_data(words)
|
318
|
+
@tool_data[words] ||= ToolData.new({}, nil, nil)
|
319
|
+
end
|
320
|
+
|
312
321
|
def load_for_prefix(prefix)
|
313
322
|
cur_worklist = @load_worklist
|
314
323
|
@load_worklist = []
|
@@ -327,7 +336,8 @@ module Toys
|
|
327
336
|
|
328
337
|
def load_path(path, words, remaining_words, priority)
|
329
338
|
if ::File.extname(path) == ".rb"
|
330
|
-
|
339
|
+
tool_class = get_tool_definition(words, priority).tool_class
|
340
|
+
Toys::InputFile.evaluate(tool_class, remaining_words, path)
|
331
341
|
else
|
332
342
|
require_preload_in(path)
|
333
343
|
load_index_in(path, words, remaining_words, priority)
|