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
@@ -28,7 +28,7 @@
|
|
28
28
|
;
|
29
29
|
|
30
30
|
require "toys/middleware/base"
|
31
|
-
require "toys/utils/
|
31
|
+
require "toys/utils/terminal"
|
32
32
|
|
33
33
|
module Toys
|
34
34
|
module Middleware
|
@@ -65,16 +65,16 @@ module Toys
|
|
65
65
|
@version_string = version_string
|
66
66
|
@version_flags = version_flags
|
67
67
|
@version_flag_desc = version_flag_desc
|
68
|
-
@
|
68
|
+
@terminal = Utils::Terminal.new(output: stream)
|
69
69
|
end
|
70
70
|
|
71
71
|
##
|
72
72
|
# Adds the version flag if requested.
|
73
73
|
#
|
74
|
-
def config(
|
75
|
-
if @version_string &&
|
76
|
-
|
77
|
-
|
74
|
+
def config(tool_definition, _loader)
|
75
|
+
if @version_string && tool_definition.root?
|
76
|
+
tool_definition.add_flag(:_show_version, @version_flags,
|
77
|
+
report_collisions: false, desc: @version_flag_desc)
|
78
78
|
end
|
79
79
|
yield
|
80
80
|
end
|
@@ -82,9 +82,9 @@ module Toys
|
|
82
82
|
##
|
83
83
|
# This middleware displays the version.
|
84
84
|
#
|
85
|
-
def
|
86
|
-
if
|
87
|
-
@
|
85
|
+
def run(tool)
|
86
|
+
if tool[:_show_version]
|
87
|
+
@terminal.puts(@version_string)
|
88
88
|
else
|
89
89
|
yield
|
90
90
|
end
|
data/lib/toys/runner.rb
ADDED
@@ -0,0 +1,157 @@
|
|
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
|
+
require "optparse"
|
31
|
+
|
32
|
+
module Toys
|
33
|
+
##
|
34
|
+
# An internal class that manages execution of a tool
|
35
|
+
# @private
|
36
|
+
#
|
37
|
+
class Runner
|
38
|
+
def initialize(cli, tool_definition)
|
39
|
+
@cli = cli
|
40
|
+
@tool_definition = tool_definition
|
41
|
+
end
|
42
|
+
|
43
|
+
def run(args, verbosity: 0)
|
44
|
+
data = create_data(args, verbosity)
|
45
|
+
parse_args(args, data)
|
46
|
+
tool = @tool_definition.tool_class.new(@cli, data)
|
47
|
+
|
48
|
+
original_level = @cli.logger.level
|
49
|
+
@cli.logger.level = @cli.base_level - data[Tool::Keys::VERBOSITY]
|
50
|
+
begin
|
51
|
+
perform_execution(tool)
|
52
|
+
ensure
|
53
|
+
@cli.logger.level = original_level
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def create_data(args, base_verbosity)
|
60
|
+
data = @tool_definition.default_data.dup
|
61
|
+
data[Tool::Keys::TOOL_DEFINITION] = @tool_definition
|
62
|
+
data[Tool::Keys::TOOL_NAME] = @tool_definition.full_name
|
63
|
+
data[Tool::Keys::VERBOSITY] = base_verbosity
|
64
|
+
data[Tool::Keys::ARGS] = args
|
65
|
+
data[Tool::Keys::USAGE_ERROR] = nil
|
66
|
+
data
|
67
|
+
end
|
68
|
+
|
69
|
+
def parse_args(args, data)
|
70
|
+
optparse = create_option_parser(data)
|
71
|
+
remaining = optparse.parse(args)
|
72
|
+
remaining = parse_required_args(remaining, args, data)
|
73
|
+
remaining = parse_optional_args(remaining, data)
|
74
|
+
parse_remaining_args(remaining, args, data)
|
75
|
+
rescue ::OptionParser::ParseError => e
|
76
|
+
data[Tool::Keys::USAGE_ERROR] = e.message
|
77
|
+
end
|
78
|
+
|
79
|
+
def create_option_parser(data)
|
80
|
+
optparse = ::OptionParser.new
|
81
|
+
# The following clears out the Officious (hidden default flags).
|
82
|
+
optparse.remove
|
83
|
+
optparse.remove
|
84
|
+
optparse.new
|
85
|
+
optparse.new
|
86
|
+
@tool_definition.flag_definitions.each do |flag|
|
87
|
+
optparse.on(*flag.optparser_info) do |val|
|
88
|
+
data[flag.key] = flag.handler.call(val, data[flag.key])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
@tool_definition.custom_acceptors do |accept|
|
92
|
+
optparse.accept(accept)
|
93
|
+
end
|
94
|
+
optparse
|
95
|
+
end
|
96
|
+
|
97
|
+
def parse_required_args(remaining, args, data)
|
98
|
+
@tool_definition.required_arg_definitions.each do |arg_info|
|
99
|
+
if remaining.empty?
|
100
|
+
reason = "No value given for required argument #{arg_info.display_name}"
|
101
|
+
raise create_parse_error(args, reason)
|
102
|
+
end
|
103
|
+
data[arg_info.key] = arg_info.process_value(remaining.shift)
|
104
|
+
end
|
105
|
+
remaining
|
106
|
+
end
|
107
|
+
|
108
|
+
def parse_optional_args(remaining, data)
|
109
|
+
@tool_definition.optional_arg_definitions.each do |arg_info|
|
110
|
+
break if remaining.empty?
|
111
|
+
data[arg_info.key] = arg_info.process_value(remaining.shift)
|
112
|
+
end
|
113
|
+
remaining
|
114
|
+
end
|
115
|
+
|
116
|
+
def parse_remaining_args(remaining, args, data)
|
117
|
+
return if remaining.empty?
|
118
|
+
unless @tool_definition.remaining_args_definition
|
119
|
+
if @tool_definition.runnable?
|
120
|
+
raise create_parse_error(remaining, "Extra arguments provided")
|
121
|
+
else
|
122
|
+
raise create_parse_error(@tool_definition.full_name + args, "Tool not found")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
data[@tool_definition.remaining_args_definition.key] =
|
126
|
+
remaining.map { |arg| @tool_definition.remaining_args_definition.process_value(arg) }
|
127
|
+
end
|
128
|
+
|
129
|
+
def create_parse_error(path, reason)
|
130
|
+
OptionParser::ParseError.new(*path).tap do |e|
|
131
|
+
e.reason = reason
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def perform_execution(tool)
|
136
|
+
executor = proc do
|
137
|
+
if @tool_definition.runnable?
|
138
|
+
tool.run
|
139
|
+
else
|
140
|
+
@cli.logger.fatal("No implementation for tool #{@tool_definition.display_name.inspect}")
|
141
|
+
tool.exit(-1)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
@tool_definition.middleware_stack.reverse.each do |middleware|
|
145
|
+
executor = make_executor(middleware, tool, executor)
|
146
|
+
end
|
147
|
+
catch(:result) do
|
148
|
+
executor.call
|
149
|
+
0
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def make_executor(middleware, tool, next_executor)
|
154
|
+
proc { middleware.run(tool, &next_executor) }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
data/lib/toys/template.rb
CHANGED
@@ -43,8 +43,8 @@ module Toys
|
|
43
43
|
# The class defines the "configuration" of the template. If your template
|
44
44
|
# has options/parameters, you should provide a constructor, and methods
|
45
45
|
# appropriate to edit those options. The arguments given to the
|
46
|
-
# {Toys::
|
47
|
-
# template object is passed to any block given to {Toys::
|
46
|
+
# {Toys::DSL::Tool#expand} method are passed to your constructor, and your
|
47
|
+
# template object is passed to any block given to {Toys::DSL::Tool#expand}.
|
48
48
|
#
|
49
49
|
# Next, in your template class, call the `to_expand` method, which is defined
|
50
50
|
# in {Toys::Template::ClassMethods#to_expand}. Pass this a block which
|
@@ -77,7 +77,7 @@ module Toys
|
|
77
77
|
# to_expand do |template|
|
78
78
|
# desc "Prints a greeting to #{template.name}"
|
79
79
|
# tool "templated-greeting" do
|
80
|
-
#
|
80
|
+
# run do
|
81
81
|
# puts "Hello, #{template.name}!"
|
82
82
|
# end
|
83
83
|
# end
|
@@ -100,6 +100,7 @@ module Toys
|
|
100
100
|
## @private
|
101
101
|
def self.included(mod)
|
102
102
|
mod.extend(ClassMethods)
|
103
|
+
mod.include(Tool::Keys)
|
103
104
|
end
|
104
105
|
|
105
106
|
##
|
data/lib/toys/templates.rb
CHANGED
@@ -48,8 +48,8 @@ module Toys
|
|
48
48
|
# @param [String,Symbol] name Name of the template class to return
|
49
49
|
# @return [Class,nil] The class, or `nil` if not found
|
50
50
|
#
|
51
|
-
def self.lookup(name)
|
52
|
-
Utils::ModuleLookup.lookup(:templates, name)
|
51
|
+
def self.lookup!(name)
|
52
|
+
Utils::ModuleLookup.lookup!(:templates, name)
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
data/lib/toys/templates/clean.rb
CHANGED
@@ -90,11 +90,11 @@ module Toys
|
|
90
90
|
|
91
91
|
flag :yes, "-y", "--yes", desc: "Do not ask for interactive confirmation"
|
92
92
|
|
93
|
-
|
94
|
-
|
95
|
-
|
93
|
+
include :exec
|
94
|
+
include :fileutils
|
95
|
+
include :terminal
|
96
96
|
|
97
|
-
|
97
|
+
run do
|
98
98
|
configure_exec(exit_on_nonzero_status: true)
|
99
99
|
gemspec = ::Gem::Specification.load "#{template.gem_name}.gemspec"
|
100
100
|
version = gemspec.version
|
@@ -107,7 +107,7 @@ module Toys
|
|
107
107
|
logger.error "Cannot push the gem when there are uncommited changes"
|
108
108
|
exit(1)
|
109
109
|
end
|
110
|
-
exit(1) unless
|
110
|
+
exit(1) unless option(:yes) || confirm("Release #{gemfile}?")
|
111
111
|
sh "gem push pkg/#{gemfile}"
|
112
112
|
if template.tag
|
113
113
|
sh "git tag v#{version}"
|
@@ -84,7 +84,7 @@ module Toys
|
|
84
84
|
tool(template.name) do
|
85
85
|
desc "Run minitest on the current project."
|
86
86
|
|
87
|
-
|
87
|
+
include :exec
|
88
88
|
|
89
89
|
flag :warnings, "-w", "--[no-]warnings",
|
90
90
|
default: template.warnings,
|
@@ -92,7 +92,7 @@ module Toys
|
|
92
92
|
|
93
93
|
remaining_args :tests, desc: "Paths to the tests to run (defaults to all tests)"
|
94
94
|
|
95
|
-
|
95
|
+
run do
|
96
96
|
ruby_args = []
|
97
97
|
unless template.libs.empty?
|
98
98
|
lib_path = template.libs.join(::File::PATH_SEPARATOR)
|
data/lib/toys/tool.rb
CHANGED
@@ -27,712 +27,255 @@
|
|
27
27
|
# POSSIBILITY OF SUCH DAMAGE.
|
28
28
|
;
|
29
29
|
|
30
|
-
require "
|
31
|
-
|
32
|
-
require "toys/utils/wrappable_string"
|
30
|
+
require "logger"
|
33
31
|
|
34
32
|
module Toys
|
35
33
|
##
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
34
|
+
# This class manages the object context in effect during the execution of a
|
35
|
+
# tool. The context is a hash of key-value pairs.
|
36
|
+
#
|
37
|
+
# Flags and arguments defined by your tool normally report their values in
|
38
|
+
# the context, using keys that are strings or symbols.
|
39
|
+
#
|
40
|
+
# Keys that are neither strings nor symbols are by convention used for other
|
41
|
+
# context information, including:
|
42
|
+
# * Common information such as the {Toys::Definition::Tool} object being
|
43
|
+
# executed, the arguments originally passed to it, or the usage error
|
44
|
+
# string. These well-known keys can be accessed via constants in the
|
45
|
+
# {Toys::Tool::Keys} module.
|
46
|
+
# * Common settings such as the verbosity level, and whether to exit
|
47
|
+
# immediately if a subprocess exits with a nonzero result. These keys are
|
48
|
+
# also present as {Toys::Context} constants.
|
49
|
+
# * Private information used internally by middleware and helpers.
|
50
|
+
#
|
51
|
+
# This class provides convenience accessors for common keys and settings, and
|
52
|
+
# you can retrieve argument-set keys using the {#options} hash.
|
41
53
|
#
|
42
54
|
class Tool
|
43
55
|
##
|
44
|
-
#
|
45
|
-
#
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
::
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
::
|
59
|
-
|
60
|
-
|
61
|
-
::
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
@
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
56
|
+
# Well-known context keys.
|
57
|
+
#
|
58
|
+
module Keys
|
59
|
+
##
|
60
|
+
# Context key for the currently running CLI.
|
61
|
+
# @return [Object]
|
62
|
+
#
|
63
|
+
CLI = ::Object.new.freeze
|
64
|
+
|
65
|
+
##
|
66
|
+
# Context key for the verbosity value. Verbosity is an integer defaulting
|
67
|
+
# to 0, with higher values meaning more verbose and lower meaning quieter.
|
68
|
+
# @return [Object]
|
69
|
+
#
|
70
|
+
VERBOSITY = ::Object.new.freeze
|
71
|
+
|
72
|
+
##
|
73
|
+
# Context key for the `Toys::Definition::Tool` object being executed.
|
74
|
+
# @return [Object]
|
75
|
+
#
|
76
|
+
TOOL_DEFINITION = ::Object.new.freeze
|
77
|
+
|
78
|
+
##
|
79
|
+
# Context key for the full name of the tool being executed. Value is an
|
80
|
+
# array of strings.
|
81
|
+
# @return [Object]
|
82
|
+
#
|
83
|
+
TOOL_NAME = ::Object.new.freeze
|
84
|
+
|
85
|
+
##
|
86
|
+
# Context key for the active `Toys::Loader` object.
|
87
|
+
# @return [Object]
|
88
|
+
#
|
89
|
+
LOADER = ::Object.new.freeze
|
90
|
+
|
91
|
+
##
|
92
|
+
# Context key for the active `Logger` object.
|
93
|
+
# @return [Object]
|
94
|
+
#
|
95
|
+
LOGGER = ::Object.new.freeze
|
96
|
+
|
97
|
+
##
|
98
|
+
# Context key for the name of the toys binary. Value is a string.
|
99
|
+
# @return [Object]
|
100
|
+
#
|
101
|
+
BINARY_NAME = ::Object.new.freeze
|
102
|
+
|
103
|
+
##
|
104
|
+
# Context key for the argument list passed to the current tool. Value is
|
105
|
+
# an array of strings.
|
106
|
+
# @return [Object]
|
107
|
+
#
|
108
|
+
ARGS = ::Object.new.freeze
|
109
|
+
|
110
|
+
##
|
111
|
+
# Context key for the usage error raised. Value is a string if there was
|
112
|
+
# an error, or nil if there was no error.
|
113
|
+
# @return [Object]
|
114
|
+
#
|
115
|
+
USAGE_ERROR = ::Object.new.freeze
|
92
116
|
end
|
93
117
|
|
94
118
|
##
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
attr_reader :full_name
|
100
|
-
|
101
|
-
##
|
102
|
-
# Returns the short description string.
|
103
|
-
# @return [Toys::Utils::WrappableString]
|
104
|
-
#
|
105
|
-
attr_reader :desc
|
106
|
-
|
107
|
-
##
|
108
|
-
# Returns the long description strings as an array.
|
109
|
-
# @return [Array<Toys::Utils::WrappableString>]
|
110
|
-
#
|
111
|
-
attr_reader :long_desc
|
112
|
-
|
113
|
-
##
|
114
|
-
# Return a list of all defined flags.
|
115
|
-
# @return [Array<Toys::Tool::FlagDefinition>]
|
116
|
-
#
|
117
|
-
attr_reader :flag_definitions
|
118
|
-
|
119
|
-
##
|
120
|
-
# Return a list of all defined required positional arguments.
|
121
|
-
# @return [Array<Toys::Tool::ArgDefinition>]
|
122
|
-
#
|
123
|
-
attr_reader :required_arg_definitions
|
124
|
-
|
125
|
-
##
|
126
|
-
# Return a list of all defined optional positional arguments.
|
127
|
-
# @return [Array<Toys::Tool::ArgDefinition>]
|
128
|
-
#
|
129
|
-
attr_reader :optional_arg_definitions
|
130
|
-
|
131
|
-
##
|
132
|
-
# Return the remaining arguments specification, or `nil` if remaining
|
133
|
-
# arguments are currently not supported by this tool.
|
134
|
-
# @return [Toys::Tool::ArgDefinition,nil]
|
135
|
-
#
|
136
|
-
attr_reader :remaining_args_definition
|
137
|
-
|
138
|
-
##
|
139
|
-
# Return a list of flags that have been used in the flag definitions.
|
140
|
-
# @return [Array<String>]
|
141
|
-
#
|
142
|
-
attr_reader :used_flags
|
143
|
-
|
144
|
-
##
|
145
|
-
# Return the default argument data.
|
146
|
-
# @return [Hash]
|
147
|
-
#
|
148
|
-
attr_reader :default_data
|
149
|
-
|
150
|
-
##
|
151
|
-
# Return a list of modules that will be available during execution.
|
152
|
-
# @return [Array<Module>]
|
153
|
-
#
|
154
|
-
attr_reader :modules
|
155
|
-
|
156
|
-
##
|
157
|
-
# Return a list of helper methods that will be available during execution.
|
158
|
-
# @return [Hash{Symbol => Proc}]
|
159
|
-
#
|
160
|
-
attr_reader :helpers
|
161
|
-
|
162
|
-
##
|
163
|
-
# Return the script block, or `nil` if not present.
|
164
|
-
# @return [Proc,nil]
|
165
|
-
#
|
166
|
-
attr_reader :script
|
167
|
-
|
168
|
-
##
|
169
|
-
# Returns the middleware stack
|
170
|
-
# @return [Array<Object>]
|
171
|
-
#
|
172
|
-
attr_reader :middleware_stack
|
173
|
-
|
174
|
-
##
|
175
|
-
# Returns the path to the file that contains the definition of this tool.
|
176
|
-
# @return [String]
|
177
|
-
#
|
178
|
-
attr_reader :definition_path
|
179
|
-
|
180
|
-
##
|
181
|
-
# Returns the local name of this tool.
|
182
|
-
# @return [String]
|
183
|
-
#
|
184
|
-
def simple_name
|
185
|
-
full_name.last
|
186
|
-
end
|
187
|
-
|
188
|
-
##
|
189
|
-
# Returns a displayable name of this tool, generally the full name
|
190
|
-
# delimited by spaces.
|
191
|
-
# @return [String]
|
192
|
-
#
|
193
|
-
def display_name
|
194
|
-
full_name.join(" ")
|
195
|
-
end
|
196
|
-
|
197
|
-
##
|
198
|
-
# Returns true if this tool is a root tool.
|
199
|
-
# @return [Boolean]
|
119
|
+
# Create a Context object. Applications generally will not need to create
|
120
|
+
# these objects directly; they are created by the tool when it is preparing
|
121
|
+
# for execution.
|
122
|
+
# @private
|
200
123
|
#
|
201
|
-
|
202
|
-
|
203
|
-
end
|
204
|
-
|
205
|
-
##
|
206
|
-
# Returns true if this tool has an script defined.
|
207
|
-
# @return [Boolean]
|
124
|
+
# @param [Toys::CLI] cli
|
125
|
+
# @param [Hash] data
|
208
126
|
#
|
209
|
-
def
|
210
|
-
|
127
|
+
def initialize(cli, data)
|
128
|
+
@__data = data
|
129
|
+
@__data[Keys::CLI] = cli
|
130
|
+
@__data[Keys::LOADER] = cli.loader
|
131
|
+
@__data[Keys::BINARY_NAME] = cli.binary_name
|
132
|
+
@__data[Keys::LOGGER] = cli.logger
|
211
133
|
end
|
212
134
|
|
213
135
|
##
|
214
|
-
#
|
215
|
-
# @return [
|
136
|
+
# Return the currently running CLI.
|
137
|
+
# @return [Toys::CLI]
|
216
138
|
#
|
217
|
-
def
|
218
|
-
|
139
|
+
def cli
|
140
|
+
@__data[Keys::CLI]
|
219
141
|
end
|
220
142
|
|
221
143
|
##
|
222
|
-
#
|
223
|
-
#
|
224
|
-
# @return [Boolean]
|
144
|
+
# Return the current verbosity setting as an integer.
|
145
|
+
# @return [Integer]
|
225
146
|
#
|
226
|
-
def
|
227
|
-
|
228
|
-
!required_arg_definitions.empty? || !optional_arg_definitions.empty? ||
|
229
|
-
!remaining_args_definition.nil?
|
147
|
+
def verbosity
|
148
|
+
@__data[Keys::VERBOSITY]
|
230
149
|
end
|
231
150
|
|
232
151
|
##
|
233
|
-
#
|
234
|
-
#
|
235
|
-
# @return [Boolean]
|
152
|
+
# Return the tool being executed.
|
153
|
+
# @return [Toys::Definition::Tool]
|
236
154
|
#
|
237
|
-
def
|
238
|
-
|
155
|
+
def tool_definition
|
156
|
+
@__data[Keys::TOOL_DEFINITION]
|
239
157
|
end
|
240
158
|
|
241
159
|
##
|
242
|
-
#
|
243
|
-
# @return [
|
160
|
+
# Return the name of the tool being executed, as an array of strings.
|
161
|
+
# @return [Array[String]]
|
244
162
|
#
|
245
|
-
def
|
246
|
-
|
163
|
+
def tool_name
|
164
|
+
@__data[Keys::TOOL_NAME]
|
247
165
|
end
|
248
166
|
|
249
167
|
##
|
250
|
-
#
|
251
|
-
#
|
168
|
+
# Return the raw arguments passed to the tool, as an array of strings.
|
169
|
+
# This does not include the tool name itself.
|
170
|
+
# @return [Array[String]]
|
252
171
|
#
|
253
|
-
def
|
254
|
-
@
|
172
|
+
def args
|
173
|
+
@__data[Keys::ARGS]
|
255
174
|
end
|
256
175
|
|
257
176
|
##
|
258
|
-
#
|
259
|
-
#
|
177
|
+
# Return any usage error detected during argument parsing, or `nil` if
|
178
|
+
# no error was detected.
|
179
|
+
# @return [String,nil]
|
260
180
|
#
|
261
|
-
def
|
262
|
-
|
263
|
-
result << remaining_args_definition if remaining_args_definition
|
264
|
-
result
|
181
|
+
def usage_error
|
182
|
+
@__data[Keys::USAGE_ERROR]
|
265
183
|
end
|
266
184
|
|
267
185
|
##
|
268
|
-
#
|
269
|
-
# @return [
|
186
|
+
# Return the logger for this execution.
|
187
|
+
# @return [Logger]
|
270
188
|
#
|
271
|
-
def
|
272
|
-
|
273
|
-
flag_definitions.each do |f|
|
274
|
-
result << f.accept if f.accept.is_a?(Acceptor)
|
275
|
-
end
|
276
|
-
arg_definitions.each do |a|
|
277
|
-
result << a.accept if a.accept.is_a?(Acceptor)
|
278
|
-
end
|
279
|
-
result.uniq
|
189
|
+
def logger
|
190
|
+
@__data[Keys::LOGGER]
|
280
191
|
end
|
281
192
|
|
282
193
|
##
|
283
|
-
#
|
284
|
-
#
|
285
|
-
# already set, raises {Toys::ToolDefinitionError}
|
194
|
+
# Return the active loader that can be used to get other tools.
|
195
|
+
# @return [Toys::Loader]
|
286
196
|
#
|
287
|
-
|
288
|
-
|
289
|
-
def lock_definition_path(path)
|
290
|
-
if definition_path && definition_path != path
|
291
|
-
raise ToolDefinitionError,
|
292
|
-
"Cannot redefine tool #{display_name.inspect} in #{path}" \
|
293
|
-
" (already defined in #{definition_path})"
|
294
|
-
end
|
295
|
-
@definition_path = path
|
197
|
+
def loader
|
198
|
+
@__data[Keys::LOADER]
|
296
199
|
end
|
297
200
|
|
298
201
|
##
|
299
|
-
#
|
300
|
-
#
|
301
|
-
# The description may be provided as a {Toys::Utils::WrappableString}, a
|
302
|
-
# single string (which will be wrapped), or an array of strings, which will
|
303
|
-
# be interpreted as string fragments that will be concatenated and wrapped.
|
304
|
-
#
|
305
|
-
# @param [Toys::Utils::WrappableString,String,Array<String>] desc
|
202
|
+
# Return the name of the binary that was executed.
|
203
|
+
# @return [String]
|
306
204
|
#
|
307
|
-
def
|
308
|
-
|
309
|
-
@desc = Utils::WrappableString.make(desc)
|
205
|
+
def binary_name
|
206
|
+
@__data[Keys::BINARY_NAME]
|
310
207
|
end
|
311
208
|
|
312
209
|
##
|
313
|
-
#
|
210
|
+
# Return an option or other piece of data by key.
|
314
211
|
#
|
315
|
-
#
|
316
|
-
#
|
317
|
-
# interpreted as string fragments that will be concatenated and wrapped.
|
212
|
+
# @param [Symbol] key
|
213
|
+
# @return [Object]
|
318
214
|
#
|
319
|
-
|
320
|
-
|
321
|
-
def long_desc=(descs)
|
322
|
-
check_definition_state
|
323
|
-
@long_desc = Utils::WrappableString.make_array(descs)
|
215
|
+
def [](key)
|
216
|
+
@__data[key]
|
324
217
|
end
|
218
|
+
alias get []
|
325
219
|
|
326
220
|
##
|
327
|
-
#
|
328
|
-
# Pass the name of the method in the argument, and provide a block with
|
329
|
-
# the method body. Note the method name may not start with an underscore.
|
221
|
+
# Set an option or other piece of context data by key.
|
330
222
|
#
|
331
|
-
# @param [
|
223
|
+
# @param [Symbol] key
|
224
|
+
# @param [Object] value
|
332
225
|
#
|
333
|
-
def
|
334
|
-
|
335
|
-
name_str = name.to_s
|
336
|
-
unless name_str =~ /^[a-z]\w+$/
|
337
|
-
raise ToolDefinitionError, "Illegal helper name: #{name_str.inspect}"
|
338
|
-
end
|
339
|
-
@helpers[name.to_sym] = block
|
340
|
-
self
|
226
|
+
def []=(key, value)
|
227
|
+
@__data[key] = value
|
341
228
|
end
|
342
229
|
|
343
230
|
##
|
344
|
-
#
|
345
|
-
# itself, or the name of a well-known module under {Toys::Helpers}.
|
231
|
+
# Set an option or other piece of context data by key.
|
346
232
|
#
|
347
|
-
# @param [
|
233
|
+
# @param [Symbol] key
|
234
|
+
# @param [Object] value
|
348
235
|
#
|
349
|
-
def
|
350
|
-
|
351
|
-
|
352
|
-
when ::Module
|
353
|
-
@modules << name
|
354
|
-
when ::Symbol
|
355
|
-
mod = Helpers.lookup(name.to_s)
|
356
|
-
if mod.nil?
|
357
|
-
raise ToolDefinitionError, "Module not found: #{name.inspect}"
|
358
|
-
end
|
359
|
-
@modules << mod
|
236
|
+
def set(key, value = nil)
|
237
|
+
if key.is_a?(::Hash)
|
238
|
+
@__data.merge!(key)
|
360
239
|
else
|
361
|
-
|
240
|
+
@__data[key] = value
|
362
241
|
end
|
363
242
|
self
|
364
243
|
end
|
365
244
|
|
366
245
|
##
|
367
|
-
#
|
368
|
-
#
|
369
|
-
#
|
370
|
-
#
|
246
|
+
# Returns the subset of the context that uses string or symbol keys. By
|
247
|
+
# convention, this includes keys that are set by tool flags and arguments,
|
248
|
+
# but does not include well-known context values such as verbosity or
|
249
|
+
# private context values used by middleware or helpers.
|
371
250
|
#
|
372
|
-
|
373
|
-
@acceptors[acceptor.name] = acceptor
|
374
|
-
self
|
375
|
-
end
|
376
|
-
|
377
|
-
##
|
378
|
-
# Add a flag to the current tool. Each flag must specify a key which
|
379
|
-
# the script may use to obtain the flag value from the context.
|
380
|
-
# You may then provide the flags themselves in `OptionParser` form.
|
381
|
-
#
|
382
|
-
# @param [Symbol] key The key to use to retrieve the value from the
|
383
|
-
# execution context.
|
384
|
-
# @param [Array<String>] flags The flags in OptionParser format.
|
385
|
-
# @param [Object] accept An acceptor that validates and/or converts the
|
386
|
-
# value. You may provide either the name of an acceptor you have
|
387
|
-
# defined, or one of the default acceptors provided by OptionParser.
|
388
|
-
# Optional. If not specified, accepts any value as a string.
|
389
|
-
# @param [Object] default The default value. This is the value that will
|
390
|
-
# be set in the context if this flag is not provided on the command
|
391
|
-
# line. Defaults to `nil`.
|
392
|
-
# @param [Proc,nil] handler An optional handler for setting/updating the
|
393
|
-
# value. If given, it should take two arguments, the new given value
|
394
|
-
# and the previous value, and it should return the new value that
|
395
|
-
# should be set. The default handler simply replaces the previous
|
396
|
-
# value. i.e. the default is effectively `-> (val, _prev) { val }`.
|
397
|
-
# @param [Boolean] report_collisions Raise an exception if a flag is
|
398
|
-
# requested that is already in use or marked as disabled. Default is
|
399
|
-
# true.
|
400
|
-
# @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
|
401
|
-
# description for the flag. See {Toys::Tool#desc=} for a description of
|
402
|
-
# allowed formats. Defaults to the empty string.
|
403
|
-
# @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
|
404
|
-
# Long description for the flag. See {Toys::Tool#long_desc=} for a
|
405
|
-
# description of allowed formats. Defaults to the empty array.
|
406
|
-
#
|
407
|
-
def add_flag(key, flags = [],
|
408
|
-
accept: nil, default: nil, handler: nil,
|
409
|
-
report_collisions: true,
|
410
|
-
desc: nil, long_desc: nil)
|
411
|
-
check_definition_state
|
412
|
-
accept = resolve_acceptor(accept)
|
413
|
-
flag_def = FlagDefinition.new(key, flags, @used_flags, report_collisions,
|
414
|
-
accept, handler, default)
|
415
|
-
flag_def.desc = desc if desc
|
416
|
-
flag_def.long_desc = long_desc if long_desc
|
417
|
-
@flag_definitions << flag_def if flag_def.active?
|
418
|
-
@default_data[key] = default
|
419
|
-
self
|
420
|
-
end
|
421
|
-
|
422
|
-
##
|
423
|
-
# Mark one or more flags as disabled, preventing their use by any
|
424
|
-
# subsequent flag definition. This may be used to prevent middleware from
|
425
|
-
# defining a particular flag.
|
426
|
-
#
|
427
|
-
# @param [String...] flags The flags to disable
|
251
|
+
# @return [Hash]
|
428
252
|
#
|
429
|
-
def
|
430
|
-
|
431
|
-
|
432
|
-
unless intersection.empty?
|
433
|
-
raise ToolDefinitionError, "Cannot disable flags already used: #{intersection.inspect}"
|
253
|
+
def options
|
254
|
+
@__data.select do |k, _v|
|
255
|
+
k.is_a?(::Symbol) || k.is_a?(::String)
|
434
256
|
end
|
435
|
-
@used_flags.concat(flags)
|
436
|
-
self
|
437
257
|
end
|
438
258
|
|
439
259
|
##
|
440
|
-
#
|
441
|
-
#
|
442
|
-
#
|
260
|
+
# Returns the value of the given option. Returns only options with string
|
261
|
+
# or symbol keys; returns `nil` if passed other well-known context keys
|
262
|
+
# such as verbosity.
|
443
263
|
#
|
444
|
-
# @param [Symbol] key
|
445
|
-
#
|
446
|
-
# @param [Object] accept An acceptor that validates and/or converts the
|
447
|
-
# value. You may provide either the name of an acceptor you have
|
448
|
-
# defined, or one of the default acceptors provided by OptionParser.
|
449
|
-
# Optional. If not specified, accepts any value as a string.
|
450
|
-
# @param [String] display_name A name to use for display (in help text and
|
451
|
-
# error reports). Defaults to the key in upper case.
|
452
|
-
# @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
|
453
|
-
# description for the flag. See {Toys::Tool#desc=} for a description of
|
454
|
-
# allowed formats. Defaults to the empty string.
|
455
|
-
# @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
|
456
|
-
# Long description for the flag. See {Toys::Tool#long_desc=} for a
|
457
|
-
# description of allowed formats. Defaults to the empty array.
|
264
|
+
# @param [String,Symbol] key
|
265
|
+
# @return [Object]
|
458
266
|
#
|
459
|
-
def
|
460
|
-
|
461
|
-
accept = resolve_acceptor(accept)
|
462
|
-
arg_def = ArgDefinition.new(key, :required, accept, nil, desc, long_desc, display_name)
|
463
|
-
@required_arg_definitions << arg_def
|
464
|
-
self
|
267
|
+
def option(key)
|
268
|
+
key.is_a?(::Symbol) || key.is_a?(::String) ? @__data[key] : nil
|
465
269
|
end
|
466
270
|
|
467
271
|
##
|
468
|
-
#
|
469
|
-
# a key which the script may use to obtain the argument value from the
|
470
|
-
# context. If an optional argument is not given on the command line, the
|
471
|
-
# value is set to the given default.
|
272
|
+
# Exit immediately with the given status code
|
472
273
|
#
|
473
|
-
# @param [
|
474
|
-
#
|
475
|
-
# @param [Object] default The default value. This is the value that will
|
476
|
-
# be set in the context if this argument is not provided on the command
|
477
|
-
# line. Defaults to `nil`.
|
478
|
-
# @param [Object] accept An acceptor that validates and/or converts the
|
479
|
-
# value. You may provide either the name of an acceptor you have
|
480
|
-
# defined, or one of the default acceptors provided by OptionParser.
|
481
|
-
# Optional. If not specified, accepts any value as a string.
|
482
|
-
# @param [String] display_name A name to use for display (in help text and
|
483
|
-
# error reports). Defaults to the key in upper case.
|
484
|
-
# @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
|
485
|
-
# description for the flag. See {Toys::Tool#desc=} for a description of
|
486
|
-
# allowed formats. Defaults to the empty string.
|
487
|
-
# @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
|
488
|
-
# Long description for the flag. See {Toys::Tool#long_desc=} for a
|
489
|
-
# description of allowed formats. Defaults to the empty array.
|
274
|
+
# @param [Integer] code The status code, which should be 0 for no error,
|
275
|
+
# or nonzero for an error condition.
|
490
276
|
#
|
491
|
-
def
|
492
|
-
|
493
|
-
check_definition_state
|
494
|
-
accept = resolve_acceptor(accept)
|
495
|
-
arg_def = ArgDefinition.new(key, :optional, accept, default, desc, long_desc, display_name)
|
496
|
-
@optional_arg_definitions << arg_def
|
497
|
-
@default_data[key] = default
|
498
|
-
self
|
499
|
-
end
|
500
|
-
|
501
|
-
##
|
502
|
-
# Specify what should be done with unmatched positional arguments. You must
|
503
|
-
# specify a key which the script may use to obtain the remaining args
|
504
|
-
# from the context.
|
505
|
-
#
|
506
|
-
# @param [Symbol] key The key to use to retrieve the value from the
|
507
|
-
# execution context.
|
508
|
-
# @param [Object] default The default value. This is the value that will
|
509
|
-
# be set in the context if no unmatched arguments are provided on the
|
510
|
-
# command line. Defaults to the empty array `[]`.
|
511
|
-
# @param [Object] accept An acceptor that validates and/or converts the
|
512
|
-
# value. You may provide either the name of an acceptor you have
|
513
|
-
# defined, or one of the default acceptors provided by OptionParser.
|
514
|
-
# Optional. If not specified, accepts any value as a string.
|
515
|
-
# @param [String] display_name A name to use for display (in help text and
|
516
|
-
# error reports). Defaults to the key in upper case.
|
517
|
-
# @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
|
518
|
-
# description for the flag. See {Toys::Tool#desc=} for a description of
|
519
|
-
# allowed formats. Defaults to the empty string.
|
520
|
-
# @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
|
521
|
-
# Long description for the flag. See {Toys::Tool#long_desc=} for a
|
522
|
-
# description of allowed formats. Defaults to the empty array.
|
523
|
-
#
|
524
|
-
def set_remaining_args(key, default: [], accept: nil, display_name: nil,
|
525
|
-
desc: nil, long_desc: nil)
|
526
|
-
check_definition_state
|
527
|
-
accept = resolve_acceptor(accept)
|
528
|
-
arg_def = ArgDefinition.new(key, :remaining, accept, default, desc, long_desc, display_name)
|
529
|
-
@remaining_args_definition = arg_def
|
530
|
-
@default_data[key] = default
|
531
|
-
self
|
532
|
-
end
|
533
|
-
|
534
|
-
##
|
535
|
-
# Set the script for this tool. This is a proc that will be called,
|
536
|
-
# with `self` set to a {Toys::Context}.
|
537
|
-
#
|
538
|
-
# @param [Proc] script The script for this tool.
|
539
|
-
#
|
540
|
-
def script=(script)
|
541
|
-
check_definition_state
|
542
|
-
@script = script
|
543
|
-
end
|
544
|
-
|
545
|
-
##
|
546
|
-
# Execute this tool in the given context.
|
547
|
-
#
|
548
|
-
# @param [Toys::CLI] cli The CLI execution context
|
549
|
-
# @param [Array<String>] args The arguments to pass to the tool. Should
|
550
|
-
# not include the tool name.
|
551
|
-
# @param [Integer] verbosity The starting verbosity. Defaults to 0.
|
552
|
-
#
|
553
|
-
# @return [Integer] The result code.
|
554
|
-
#
|
555
|
-
def execute(cli, args, verbosity: 0)
|
556
|
-
ContextualError.capture_path(
|
557
|
-
"Error during tool execution!", definition_path,
|
558
|
-
tool_name: full_name, tool_args: args
|
559
|
-
) do
|
560
|
-
Execution.new(self).execute(cli, args, verbosity: verbosity)
|
561
|
-
end
|
562
|
-
end
|
563
|
-
|
564
|
-
##
|
565
|
-
# Complete definition and run middleware configs
|
566
|
-
# @param [Toys::Loader] loader
|
567
|
-
#
|
568
|
-
# @private
|
569
|
-
#
|
570
|
-
def finish_definition(loader)
|
571
|
-
unless @definition_finished
|
572
|
-
ContextualError.capture("Error installing tool middleware!", tool_name: full_name) do
|
573
|
-
config_proc = proc {}
|
574
|
-
middleware_stack.reverse.each do |middleware|
|
575
|
-
config_proc = make_config_proc(middleware, loader, config_proc)
|
576
|
-
end
|
577
|
-
config_proc.call
|
578
|
-
end
|
579
|
-
@definition_finished = true
|
580
|
-
end
|
581
|
-
self
|
582
|
-
end
|
583
|
-
|
584
|
-
private
|
585
|
-
|
586
|
-
def make_config_proc(middleware, loader, next_config)
|
587
|
-
proc { middleware.config(self, loader, &next_config) }
|
588
|
-
end
|
589
|
-
|
590
|
-
def check_definition_state
|
591
|
-
if @definition_finished
|
592
|
-
raise ToolDefinitionError,
|
593
|
-
"Defintion of tool #{display_name.inspect} is already finished"
|
594
|
-
end
|
595
|
-
end
|
596
|
-
|
597
|
-
def resolve_acceptor(accept)
|
598
|
-
return accept if accept.nil? || accept.is_a?(Acceptor)
|
599
|
-
unless @acceptors.key?(accept)
|
600
|
-
raise ToolDefinitionError, "Unknown acceptor: #{accept.inspect}"
|
601
|
-
end
|
602
|
-
@acceptors[accept]
|
603
|
-
end
|
604
|
-
|
605
|
-
##
|
606
|
-
# An internal class that manages execution of a tool
|
607
|
-
# @private
|
608
|
-
#
|
609
|
-
class Execution
|
610
|
-
def initialize(tool)
|
611
|
-
@tool = tool
|
612
|
-
@data = @tool.default_data.dup
|
613
|
-
@data[Context::TOOL] = tool
|
614
|
-
@data[Context::TOOL_NAME] = tool.full_name
|
615
|
-
end
|
616
|
-
|
617
|
-
def execute(cli, args, verbosity: 0)
|
618
|
-
parse_args(args, verbosity)
|
619
|
-
context = create_child_context(cli)
|
620
|
-
|
621
|
-
original_level = context.logger.level
|
622
|
-
context.logger.level = cli.base_level - @data[Context::VERBOSITY]
|
623
|
-
begin
|
624
|
-
perform_execution(context)
|
625
|
-
ensure
|
626
|
-
context.logger.level = original_level
|
627
|
-
end
|
628
|
-
end
|
629
|
-
|
630
|
-
private
|
631
|
-
|
632
|
-
def parse_args(args, base_verbosity)
|
633
|
-
optparse = create_option_parser
|
634
|
-
@data[Context::VERBOSITY] = base_verbosity
|
635
|
-
@data[Context::ARGS] = args
|
636
|
-
@data[Context::USAGE_ERROR] = nil
|
637
|
-
remaining = optparse.parse(args)
|
638
|
-
remaining = parse_required_args(remaining, args)
|
639
|
-
remaining = parse_optional_args(remaining)
|
640
|
-
parse_remaining_args(remaining, args)
|
641
|
-
rescue ::OptionParser::ParseError => e
|
642
|
-
@data[Context::USAGE_ERROR] = e.message
|
643
|
-
end
|
644
|
-
|
645
|
-
def create_option_parser
|
646
|
-
optparse = ::OptionParser.new
|
647
|
-
# The following clears out the Officious (hidden default flags).
|
648
|
-
optparse.remove
|
649
|
-
optparse.remove
|
650
|
-
optparse.new
|
651
|
-
optparse.new
|
652
|
-
@tool.flag_definitions.each do |flag|
|
653
|
-
optparse.on(*flag.optparser_info) do |val|
|
654
|
-
@data[flag.key] = flag.handler.call(val, @data[flag.key])
|
655
|
-
end
|
656
|
-
end
|
657
|
-
@tool.custom_acceptors do |accept|
|
658
|
-
optparse.accept(accept)
|
659
|
-
end
|
660
|
-
optparse
|
661
|
-
end
|
662
|
-
|
663
|
-
def parse_required_args(remaining, args)
|
664
|
-
@tool.required_arg_definitions.each do |arg_info|
|
665
|
-
if remaining.empty?
|
666
|
-
reason = "No value given for required argument #{arg_info.display_name}"
|
667
|
-
raise create_parse_error(args, reason)
|
668
|
-
end
|
669
|
-
@data[arg_info.key] = arg_info.process_value(remaining.shift)
|
670
|
-
end
|
671
|
-
remaining
|
672
|
-
end
|
673
|
-
|
674
|
-
def parse_optional_args(remaining)
|
675
|
-
@tool.optional_arg_definitions.each do |arg_info|
|
676
|
-
break if remaining.empty?
|
677
|
-
@data[arg_info.key] = arg_info.process_value(remaining.shift)
|
678
|
-
end
|
679
|
-
remaining
|
680
|
-
end
|
681
|
-
|
682
|
-
def parse_remaining_args(remaining, args)
|
683
|
-
return if remaining.empty?
|
684
|
-
unless @tool.remaining_args_definition
|
685
|
-
if @tool.includes_script?
|
686
|
-
raise create_parse_error(remaining, "Extra arguments provided")
|
687
|
-
else
|
688
|
-
raise create_parse_error(@tool.full_name + args, "Tool not found")
|
689
|
-
end
|
690
|
-
end
|
691
|
-
@data[@tool.remaining_args_definition.key] =
|
692
|
-
remaining.map { |arg| @tool.remaining_args_definition.process_value(arg) }
|
693
|
-
end
|
694
|
-
|
695
|
-
def create_parse_error(path, reason)
|
696
|
-
OptionParser::ParseError.new(*path).tap do |e|
|
697
|
-
e.reason = reason
|
698
|
-
end
|
699
|
-
end
|
700
|
-
|
701
|
-
def create_child_context(cli)
|
702
|
-
context = Context.new(cli, @data)
|
703
|
-
modules = @tool.modules
|
704
|
-
context.extend(*modules) unless modules.empty?
|
705
|
-
@tool.helpers.each do |name, block|
|
706
|
-
context.define_singleton_method(name, &block)
|
707
|
-
end
|
708
|
-
context
|
709
|
-
end
|
710
|
-
|
711
|
-
def perform_execution(context)
|
712
|
-
executor = proc do
|
713
|
-
if @tool.includes_script?
|
714
|
-
context.instance_eval(&@tool.script)
|
715
|
-
else
|
716
|
-
context.logger.fatal("No implementation for tool #{@tool.display_name.inspect}")
|
717
|
-
context.exit(-1)
|
718
|
-
end
|
719
|
-
end
|
720
|
-
@tool.middleware_stack.reverse.each do |middleware|
|
721
|
-
executor = make_executor(middleware, context, executor)
|
722
|
-
end
|
723
|
-
catch(:result) do
|
724
|
-
executor.call
|
725
|
-
0
|
726
|
-
end
|
727
|
-
end
|
728
|
-
|
729
|
-
def make_executor(middleware, context, next_executor)
|
730
|
-
proc { middleware.execute(context, &next_executor) }
|
731
|
-
end
|
277
|
+
def exit(code)
|
278
|
+
throw :result, code
|
732
279
|
end
|
733
280
|
end
|
734
281
|
end
|
735
|
-
|
736
|
-
require "toys/tool/acceptor"
|
737
|
-
require "toys/tool/arg_definition"
|
738
|
-
require "toys/tool/flag_definition"
|