toys-core 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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