toys-core 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.yardopts +9 -0
- data/CHANGELOG.md +21 -0
- data/LICENSE.md +29 -0
- data/README.md +30 -0
- data/lib/toys-core.rb +54 -0
- data/lib/toys/alias.rb +94 -0
- data/lib/toys/cli.rb +268 -0
- data/lib/toys/config_dsl.rb +356 -0
- data/lib/toys/context.rb +278 -0
- data/lib/toys/core_version.rb +36 -0
- data/lib/toys/errors.rb +42 -0
- data/lib/toys/helpers.rb +52 -0
- data/lib/toys/helpers/exec.rb +469 -0
- data/lib/toys/helpers/file_utils.rb +39 -0
- data/lib/toys/loader.rb +381 -0
- data/lib/toys/middleware.rb +124 -0
- data/lib/toys/middleware/add_verbosity_switches.rb +99 -0
- data/lib/toys/middleware/base.rb +51 -0
- data/lib/toys/middleware/handle_usage_errors.rb +67 -0
- data/lib/toys/middleware/set_default_descriptions.rb +131 -0
- data/lib/toys/middleware/show_usage.rb +170 -0
- data/lib/toys/middleware/show_version.rb +99 -0
- data/lib/toys/template.rb +123 -0
- data/lib/toys/templates.rb +55 -0
- data/lib/toys/templates/clean.rb +82 -0
- data/lib/toys/templates/gem_build.rb +121 -0
- data/lib/toys/templates/minitest.rb +126 -0
- data/lib/toys/templates/rubocop.rb +86 -0
- data/lib/toys/templates/yardoc.rb +101 -0
- data/lib/toys/tool.rb +749 -0
- data/lib/toys/utils/module_lookup.rb +101 -0
- data/lib/toys/utils/usage.rb +196 -0
- metadata +146 -0
@@ -0,0 +1,99 @@
|
|
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 "toys/middleware/base"
|
31
|
+
|
32
|
+
module Toys
|
33
|
+
module Middleware
|
34
|
+
##
|
35
|
+
# A middleware that provides switches for editing the verbosity.
|
36
|
+
#
|
37
|
+
# This middleware adds `-v`, `--verbose`, `-q`, and `--quiet` switches, if
|
38
|
+
# not already defined by the tool. These switches affect the setting of
|
39
|
+
# {Toys::Context::VERBOSITY}, and, thus, the logger level.
|
40
|
+
#
|
41
|
+
class AddVerbositySwitches < Base
|
42
|
+
##
|
43
|
+
# Default verbose switches
|
44
|
+
# @return [Array<String>]
|
45
|
+
#
|
46
|
+
DEFAULT_VERBOSE_SWITCHES = ["-v", "--verbose"].freeze
|
47
|
+
|
48
|
+
##
|
49
|
+
# Default quiet switches
|
50
|
+
# @return [Array<String>]
|
51
|
+
#
|
52
|
+
DEFAULT_QUIET_SWITCHES = ["-q", "--quiet"].freeze
|
53
|
+
|
54
|
+
##
|
55
|
+
# Create a AddVerbositySwitches middleware.
|
56
|
+
#
|
57
|
+
# @param [Boolean,Array<String>,Proc] verbose_switches Specify switches
|
58
|
+
# to increase verbosity. The value may be any of the following:
|
59
|
+
# * An array of switches that increase verbosity.
|
60
|
+
# * The `true` value to use {DEFAULT_VERBOSE_SWITCHES}. (Default)
|
61
|
+
# * The `false` value to disable verbose switches.
|
62
|
+
# * A proc that takes a tool and returns any of the above.
|
63
|
+
# @param [Boolean,Array<String>,Proc] quiet_switches Specify switches
|
64
|
+
# to decrease verbosity. The value may be any of the following:
|
65
|
+
# * An array of switches that decrease verbosity.
|
66
|
+
# * The `true` value to use {DEFAULT_QUIET_SWITCHES}. (Default)
|
67
|
+
# * The `false` value to disable quiet switches.
|
68
|
+
# * A proc that takes a tool and returns any of the above.
|
69
|
+
#
|
70
|
+
def initialize(verbose_switches: true, quiet_switches: true)
|
71
|
+
@verbose_switches = verbose_switches
|
72
|
+
@quiet_switches = quiet_switches
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# Configure the tool switches.
|
77
|
+
#
|
78
|
+
def config(tool)
|
79
|
+
verbose_switches = Middleware.resolve_switches_spec(@verbose_switches, tool,
|
80
|
+
DEFAULT_VERBOSE_SWITCHES)
|
81
|
+
unless verbose_switches.empty?
|
82
|
+
tool.add_switch(Context::VERBOSITY, *verbose_switches,
|
83
|
+
doc: "Increase verbosity",
|
84
|
+
handler: ->(_val, cur) { cur + 1 },
|
85
|
+
only_unique: true)
|
86
|
+
end
|
87
|
+
quiet_switches = Middleware.resolve_switches_spec(@quiet_switches, tool,
|
88
|
+
DEFAULT_QUIET_SWITCHES)
|
89
|
+
unless quiet_switches.empty?
|
90
|
+
tool.add_switch(Context::VERBOSITY, *quiet_switches,
|
91
|
+
doc: "Decrease verbosity",
|
92
|
+
handler: ->(_val, cur) { cur - 1 },
|
93
|
+
only_unique: true)
|
94
|
+
end
|
95
|
+
yield
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,51 @@
|
|
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
|
+
module Toys
|
31
|
+
module Middleware
|
32
|
+
##
|
33
|
+
# A base middleware with a no-op implementation.
|
34
|
+
#
|
35
|
+
class Base
|
36
|
+
##
|
37
|
+
# The base middleware does not affect tool configuration.
|
38
|
+
#
|
39
|
+
def config(_tool)
|
40
|
+
yield
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# The base middleware does not affect tool execution.
|
45
|
+
#
|
46
|
+
def execute(_context)
|
47
|
+
yield
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,67 @@
|
|
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 "toys/middleware/base"
|
31
|
+
require "toys/utils/usage"
|
32
|
+
|
33
|
+
module Toys
|
34
|
+
module Middleware
|
35
|
+
##
|
36
|
+
# This middleware handles the case of a usage error. If a usage error, such
|
37
|
+
# as an unrecognized switch or an unfulfilled required argument, is
|
38
|
+
# detected, this middleware intercepts execution and displays the error
|
39
|
+
# along with the usage string, and terminates execution with an error code.
|
40
|
+
#
|
41
|
+
class HandleUsageErrors < Base
|
42
|
+
##
|
43
|
+
# Create a HandleUsageErrors middleware.
|
44
|
+
#
|
45
|
+
# @param [Intgeer] exit_code The exit code to return if a usage error
|
46
|
+
# occurs. Default is -1.
|
47
|
+
#
|
48
|
+
def initialize(exit_code: -1)
|
49
|
+
@exit_code = exit_code
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Intercept and handle usage errors during execution.
|
54
|
+
#
|
55
|
+
def execute(context)
|
56
|
+
if context[Context::USAGE_ERROR]
|
57
|
+
puts(context[Context::USAGE_ERROR])
|
58
|
+
puts("")
|
59
|
+
puts(Utils::Usage.from_context(context).string(show_path: true))
|
60
|
+
context.exit(@exit_code)
|
61
|
+
else
|
62
|
+
yield
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,131 @@
|
|
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 "toys/middleware/base"
|
31
|
+
|
32
|
+
module Toys
|
33
|
+
module Middleware
|
34
|
+
##
|
35
|
+
# This middleware sets default description fields for tools that do not
|
36
|
+
# have them set otherwise. You can set separate descriptions for tools,
|
37
|
+
# groups, and the root.
|
38
|
+
#
|
39
|
+
class SetDefaultDescriptions < Base
|
40
|
+
##
|
41
|
+
# The default description for tools.
|
42
|
+
# @return [String]
|
43
|
+
#
|
44
|
+
DEFAULT_TOOL_DESC = "(No description available)".freeze
|
45
|
+
|
46
|
+
##
|
47
|
+
# The default description for groups.
|
48
|
+
# @return [String]
|
49
|
+
#
|
50
|
+
DEFAULT_GROUP_DESC = "(A group of commands)".freeze
|
51
|
+
|
52
|
+
##
|
53
|
+
# The default description for the root tool.
|
54
|
+
# @return [String]
|
55
|
+
#
|
56
|
+
DEFAULT_ROOT_DESC =
|
57
|
+
"This command line tool was built using the toys-core gem." \
|
58
|
+
" See https://www.rubydoc.info/gems/toys-core for more info." \
|
59
|
+
" To replace this message, configure the SetDefaultDescriptions" \
|
60
|
+
" middleware.".freeze
|
61
|
+
|
62
|
+
##
|
63
|
+
# Create a SetDefaultDescriptions middleware given default descriptions.
|
64
|
+
#
|
65
|
+
# @param [String] default_tool_desc The default short description for
|
66
|
+
# tools with an eecutor. Defaults to {DEFAULT_TOOL_DESC}.
|
67
|
+
# @param [String,nil] default_tool_long_desc The default long description
|
68
|
+
# for tools with an eecutor. If `nil` (the default), falls back to
|
69
|
+
# the value of the `default_tool_desc` parameter.
|
70
|
+
# @param [String] default_group_desc The default short description for
|
71
|
+
# tools with no eecutor. Defaults to {DEFAULT_GROUP_DESC}.
|
72
|
+
# @param [String,nil] default_group_long_desc The default long
|
73
|
+
# description for tools with no eecutor. If `nil` (the default),
|
74
|
+
# falls back to the value of the `default_group_desc` parameter.
|
75
|
+
# @param [String] default_root_desc The default long description for the
|
76
|
+
# root tool. Defaults to {DEFAULT_ROOT_DESC}.
|
77
|
+
#
|
78
|
+
def initialize(default_tool_desc: DEFAULT_TOOL_DESC,
|
79
|
+
default_tool_long_desc: nil,
|
80
|
+
default_group_desc: DEFAULT_GROUP_DESC,
|
81
|
+
default_group_long_desc: nil,
|
82
|
+
default_root_desc: DEFAULT_ROOT_DESC)
|
83
|
+
@default_tool_desc = default_tool_desc
|
84
|
+
@default_tool_long_desc = default_tool_long_desc
|
85
|
+
@default_group_desc = default_group_desc
|
86
|
+
@default_group_long_desc = default_group_long_desc
|
87
|
+
@default_root_desc = default_root_desc
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Add default description text to tools.
|
92
|
+
#
|
93
|
+
def config(tool)
|
94
|
+
if tool.root?
|
95
|
+
config_root_desc(tool)
|
96
|
+
elsif tool.includes_executor?
|
97
|
+
config_tool_desc(tool)
|
98
|
+
else
|
99
|
+
config_group_desc(tool)
|
100
|
+
end
|
101
|
+
yield
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def config_root_desc(tool)
|
107
|
+
if @default_root_desc && tool.effective_long_desc.empty?
|
108
|
+
tool.long_desc = @default_root_desc
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def config_tool_desc(tool)
|
113
|
+
if @default_tool_long_desc && tool.effective_long_desc.empty?
|
114
|
+
tool.long_desc = @default_tool_long_desc
|
115
|
+
end
|
116
|
+
if @default_tool_desc && tool.effective_desc.empty?
|
117
|
+
tool.desc = @default_tool_desc
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def config_group_desc(tool)
|
122
|
+
if @default_group_long_desc && tool.effective_long_desc.empty?
|
123
|
+
tool.long_desc = @default_group_long_desc
|
124
|
+
end
|
125
|
+
if @default_group_desc && tool.effective_desc.empty?
|
126
|
+
tool.desc = @default_group_desc
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,170 @@
|
|
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 "toys/middleware/base"
|
31
|
+
require "toys/utils/usage"
|
32
|
+
|
33
|
+
module Toys
|
34
|
+
module Middleware
|
35
|
+
##
|
36
|
+
# A middleware that shows usage documentation.
|
37
|
+
#
|
38
|
+
# This can be configured to display usage text when a switch (typically
|
39
|
+
# `--help`) is provided. It can also be configured to display usage text
|
40
|
+
# automatically for tools that do not have an executor.
|
41
|
+
#
|
42
|
+
# If a tool has no executor, this middleware can also add a
|
43
|
+
# `--[no-]recursive` flag, which, when set to `true` (the default), shows
|
44
|
+
# all subcommands recursively rather than only immediate subcommands.
|
45
|
+
#
|
46
|
+
class ShowUsage < Base
|
47
|
+
##
|
48
|
+
# Default help switches
|
49
|
+
# @return [Array<String>]
|
50
|
+
#
|
51
|
+
DEFAULT_HELP_SWITCHES = ["-?", "--help"].freeze
|
52
|
+
|
53
|
+
##
|
54
|
+
# Default recursive switches
|
55
|
+
# @return [Array<String>]
|
56
|
+
#
|
57
|
+
DEFAULT_RECURSIVE_SWITCHES = ["-r", "--[no-]recursive"].freeze
|
58
|
+
|
59
|
+
##
|
60
|
+
# Default search switches
|
61
|
+
# @return [Array<String>]
|
62
|
+
#
|
63
|
+
DEFAULT_SEARCH_SWITCHES = ["-s WORD", "--search=WORD"].freeze
|
64
|
+
|
65
|
+
##
|
66
|
+
# Create a ShowUsage middleware.
|
67
|
+
#
|
68
|
+
# @param [Boolean,Array<String>,Proc] help_switches Specify switches to
|
69
|
+
# activate help. The value may be any of the following:
|
70
|
+
# * An array of switches.
|
71
|
+
# * The `true` value to use {DEFAULT_HELP_SWITCHES}. (Default)
|
72
|
+
# * The `false` value for no switches.
|
73
|
+
# * A proc that takes a tool and returns any of the above.
|
74
|
+
# @param [Boolean,Array<String>,Proc] recursive_switches Specify switches
|
75
|
+
# to control recursive subcommand search. The value may be any of the
|
76
|
+
# following:
|
77
|
+
# * An array of switches.
|
78
|
+
# * The `true` value to use {DEFAULT_RECURSIVE_SWITCHES}. (Default)
|
79
|
+
# * The `false` value for no switches.
|
80
|
+
# * A proc that takes a tool and returns any of the above.
|
81
|
+
# @param [Boolean,Array<String>,Proc] search_switches Specify switches
|
82
|
+
# to search subcommands for a search term. The value may be any of
|
83
|
+
# the following:
|
84
|
+
# * An array of switches.
|
85
|
+
# * The `true` value to use {DEFAULT_SEARCH_SWITCHES}. (Default)
|
86
|
+
# * The `false` value for no switches.
|
87
|
+
# * A proc that takes a tool and returns any of the above.
|
88
|
+
# @param [Boolean] default_recursive Whether to search recursively for
|
89
|
+
# subcommands by default. Default is `true`.
|
90
|
+
# @param [Boolean] fallback_execution Cause the tool to display its own
|
91
|
+
# usage text if it does not otherwise have an executor. This is
|
92
|
+
# mostly useful for groups, which have children but no executor.
|
93
|
+
# Default is `true`.
|
94
|
+
#
|
95
|
+
def initialize(help_switches: true,
|
96
|
+
recursive_switches: true,
|
97
|
+
search_switches: true,
|
98
|
+
default_recursive: true,
|
99
|
+
fallback_execution: true)
|
100
|
+
@help_switches = help_switches
|
101
|
+
@recursive_switches = recursive_switches
|
102
|
+
@search_switches = search_switches
|
103
|
+
@default_recursive = default_recursive ? true : false
|
104
|
+
@fallback_execution = fallback_execution
|
105
|
+
end
|
106
|
+
|
107
|
+
##
|
108
|
+
# Configure switches and default data.
|
109
|
+
#
|
110
|
+
def config(tool)
|
111
|
+
help_switches = Middleware.resolve_switches_spec(@help_switches, tool,
|
112
|
+
DEFAULT_HELP_SWITCHES)
|
113
|
+
is_default = !tool.includes_executor? && @fallback_execution
|
114
|
+
if !help_switches.empty?
|
115
|
+
doc = "Show help message"
|
116
|
+
doc << " (default for groups)" if is_default
|
117
|
+
tool.add_switch(:_help, *help_switches,
|
118
|
+
doc: doc,
|
119
|
+
default: is_default,
|
120
|
+
only_unique: true)
|
121
|
+
elsif is_default
|
122
|
+
tool.default_data[:_help] = true
|
123
|
+
end
|
124
|
+
if !tool.includes_executor? && (!help_switches.empty? || @fallback_execution)
|
125
|
+
add_recursive_switches(tool)
|
126
|
+
add_search_switches(tool)
|
127
|
+
end
|
128
|
+
yield
|
129
|
+
end
|
130
|
+
|
131
|
+
##
|
132
|
+
# Display usage text if requested.
|
133
|
+
#
|
134
|
+
def execute(context)
|
135
|
+
if context[:_help]
|
136
|
+
usage = Utils::Usage.from_context(context)
|
137
|
+
puts(usage.string(recursive: context[:_recursive_subcommands],
|
138
|
+
search: context[:_search_subcommands],
|
139
|
+
show_path: context.verbosity > 0))
|
140
|
+
else
|
141
|
+
yield
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def add_recursive_switches(tool)
|
148
|
+
recursive_switches = Middleware.resolve_switches_spec(@recursive_switches, tool,
|
149
|
+
DEFAULT_RECURSIVE_SWITCHES)
|
150
|
+
unless recursive_switches.empty?
|
151
|
+
tool.add_switch(:_recursive_subcommands, *recursive_switches,
|
152
|
+
default: @default_recursive,
|
153
|
+
doc: "Show all subcommands recursively" \
|
154
|
+
" (default is #{@default_recursive})",
|
155
|
+
only_unique: true)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def add_search_switches(tool)
|
160
|
+
search_switches = Middleware.resolve_switches_spec(@search_switches, tool,
|
161
|
+
DEFAULT_SEARCH_SWITCHES)
|
162
|
+
unless search_switches.empty?
|
163
|
+
tool.add_switch(:_search_subcommands, *search_switches,
|
164
|
+
doc: "Search subcommands for the given term",
|
165
|
+
only_unique: true)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|