toys-core 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +98 -0
  3. data/LICENSE.md +16 -24
  4. data/README.md +307 -59
  5. data/docs/guide.md +44 -4
  6. data/lib/toys-core.rb +58 -49
  7. data/lib/toys/acceptor.rb +672 -0
  8. data/lib/toys/alias.rb +106 -0
  9. data/lib/toys/arg_parser.rb +624 -0
  10. data/lib/toys/cli.rb +422 -181
  11. data/lib/toys/compat.rb +83 -0
  12. data/lib/toys/completion.rb +442 -0
  13. data/lib/toys/context.rb +354 -0
  14. data/lib/toys/core_version.rb +18 -26
  15. data/lib/toys/dsl/flag.rb +213 -56
  16. data/lib/toys/dsl/flag_group.rb +237 -51
  17. data/lib/toys/dsl/positional_arg.rb +210 -0
  18. data/lib/toys/dsl/tool.rb +968 -317
  19. data/lib/toys/errors.rb +46 -28
  20. data/lib/toys/flag.rb +821 -0
  21. data/lib/toys/flag_group.rb +282 -0
  22. data/lib/toys/input_file.rb +18 -26
  23. data/lib/toys/loader.rb +110 -100
  24. data/lib/toys/middleware.rb +24 -31
  25. data/lib/toys/mixin.rb +90 -59
  26. data/lib/toys/module_lookup.rb +125 -0
  27. data/lib/toys/positional_arg.rb +184 -0
  28. data/lib/toys/source_info.rb +192 -0
  29. data/lib/toys/standard_middleware/add_verbosity_flags.rb +38 -43
  30. data/lib/toys/standard_middleware/handle_usage_errors.rb +39 -40
  31. data/lib/toys/standard_middleware/set_default_descriptions.rb +111 -89
  32. data/lib/toys/standard_middleware/show_help.rb +130 -113
  33. data/lib/toys/standard_middleware/show_root_version.rb +29 -35
  34. data/lib/toys/standard_mixins/exec.rb +116 -78
  35. data/lib/toys/standard_mixins/fileutils.rb +16 -24
  36. data/lib/toys/standard_mixins/gems.rb +29 -30
  37. data/lib/toys/standard_mixins/highline.rb +34 -41
  38. data/lib/toys/standard_mixins/terminal.rb +72 -26
  39. data/lib/toys/template.rb +51 -35
  40. data/lib/toys/tool.rb +1161 -206
  41. data/lib/toys/utils/completion_engine.rb +171 -0
  42. data/lib/toys/utils/exec.rb +279 -182
  43. data/lib/toys/utils/gems.rb +58 -49
  44. data/lib/toys/utils/help_text.rb +117 -111
  45. data/lib/toys/utils/terminal.rb +69 -62
  46. data/lib/toys/wrappable_string.rb +162 -0
  47. metadata +24 -22
  48. data/lib/toys/definition/acceptor.rb +0 -191
  49. data/lib/toys/definition/alias.rb +0 -112
  50. data/lib/toys/definition/arg.rb +0 -140
  51. data/lib/toys/definition/flag.rb +0 -370
  52. data/lib/toys/definition/flag_group.rb +0 -205
  53. data/lib/toys/definition/source_info.rb +0 -190
  54. data/lib/toys/definition/tool.rb +0 -842
  55. data/lib/toys/dsl/arg.rb +0 -132
  56. data/lib/toys/runner.rb +0 -188
  57. data/lib/toys/standard_middleware.rb +0 -47
  58. data/lib/toys/utils/module_lookup.rb +0 -135
  59. data/lib/toys/utils/wrappable_string.rb +0 -165
@@ -0,0 +1,192 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2019 Daniel Azuma
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
+ # IN THE SOFTWARE.
22
+ ;
23
+
24
+ module Toys
25
+ ##
26
+ # Information about source toys directories and files.
27
+ #
28
+ class SourceInfo
29
+ ##
30
+ # Create a SourceInfo.
31
+ # @private
32
+ #
33
+ def initialize(parent, context_directory, source, source_type, source_name, data_dir_name)
34
+ @parent = parent
35
+ @context_directory = context_directory
36
+ @source = source
37
+ @source_type = source_type
38
+ @source_path = source if source.is_a?(::String)
39
+ @source_proc = source if source.is_a?(::Proc)
40
+ @source_name = source_name
41
+ @data_dir =
42
+ if data_dir_name && @source_path
43
+ dir = ::File.join(::File.dirname(@source_path), data_dir_name)
44
+ dir if ::File.directory?(dir) && ::File.readable?(dir)
45
+ end
46
+ end
47
+
48
+ ##
49
+ # The parent of this SourceInfo.
50
+ #
51
+ # @return [Toys::SourceInfo] The parent.
52
+ # @return [nil] if this SourceInfo is the root.
53
+ #
54
+ attr_reader :parent
55
+
56
+ ##
57
+ # The context directory path (normally the directory containing the
58
+ # toplevel toys file or directory).
59
+ #
60
+ # @return [String] The context directory path.
61
+ # @return [nil] if there is no context directory (perhaps because the tool
62
+ # is being defined from a block)
63
+ #
64
+ attr_reader :context_directory
65
+
66
+ ##
67
+ # The source, which may be a path or a proc.
68
+ #
69
+ # @return [String] Path to the source file or directory.
70
+ # @return [Proc] The block serving as the source.
71
+ #
72
+ attr_reader :source
73
+
74
+ ##
75
+ # Return the type of source.
76
+ #
77
+ # @return [:file,:directory,:proc]
78
+ #
79
+ attr_reader :source_type
80
+
81
+ ##
82
+ # The path of the current source file or directory.
83
+ #
84
+ # @return [String] The source path
85
+ # @return [nil] if this source is not a file system path.
86
+ #
87
+ attr_reader :source_path
88
+
89
+ ##
90
+ # The source proc.
91
+ #
92
+ # @return [Proc] The source proc
93
+ # @return [nil] if this source is not a proc.
94
+ #
95
+ attr_reader :source_proc
96
+
97
+ ##
98
+ # The user-visible name of this source.
99
+ #
100
+ # @return [String]
101
+ #
102
+ attr_reader :source_name
103
+ alias to_s source_name
104
+
105
+ ##
106
+ # Locate the given data file or directory and return an absolute path.
107
+ #
108
+ # @param path [String] The relative path to find
109
+ # @param type [nil,:file,:directory] Type of file system object to find,
110
+ # or nil (the default) to return any type.
111
+ # @return [String] Absolute path of the resulting data.
112
+ # @return [nil] if the data was not found.
113
+ #
114
+ def find_data(path, type: nil)
115
+ if @data_dir
116
+ full_path = ::File.join(@data_dir, path)
117
+ case type
118
+ when :file
119
+ return full_path if ::File.file?(full_path)
120
+ when :directory
121
+ return full_path if ::File.directory?(full_path)
122
+ else
123
+ return full_path if ::File.readable?(full_path)
124
+ end
125
+ end
126
+ parent&.find_data(path, type: type)
127
+ end
128
+
129
+ ##
130
+ # Create a child SourceInfo relative to the parent path.
131
+ # @private
132
+ #
133
+ def relative_child(filename, data_dir_name)
134
+ raise "Cannot create relative child of a proc" unless source_path
135
+ child_path = ::File.join(source_path, filename)
136
+ child_path, type = SourceInfo.check_path(child_path, true)
137
+ return nil unless child_path
138
+ SourceInfo.new(self, context_directory, child_path, type, child_path, data_dir_name)
139
+ end
140
+
141
+ ##
142
+ # Create a child SourceInfo with an absolute path.
143
+ # @private
144
+ #
145
+ def absolute_child(child_path)
146
+ child_path, type = SourceInfo.check_path(child_path, false)
147
+ SourceInfo.new(self, context_directory, child_path, type, child_path, nil)
148
+ end
149
+
150
+ ##
151
+ # Create a root source info for a file path.
152
+ # @private
153
+ #
154
+ def self.create_path_root(source_path)
155
+ source_path, type = check_path(source_path, false)
156
+ context_directory = ::File.dirname(source_path)
157
+ new(nil, context_directory, source_path, type, source_path, nil)
158
+ end
159
+
160
+ ##
161
+ # Create a root source info for a proc.
162
+ # @private
163
+ #
164
+ def self.create_proc_root(source_proc, source_name)
165
+ new(nil, nil, source_proc, :proc, source_name, nil)
166
+ end
167
+
168
+ ##
169
+ # Check a path and determine the canonical path and type.
170
+ # @private
171
+ #
172
+ def self.check_path(path, lenient)
173
+ path = ::File.expand_path(path)
174
+ unless ::File.readable?(path)
175
+ raise LoaderError, "Cannot read: #{path}" unless lenient
176
+ return [nil, nil]
177
+ end
178
+ if ::File.file?(path)
179
+ unless ::File.extname(path) == ".rb"
180
+ raise LoaderError, "File is not a ruby file: #{path}" unless lenient
181
+ return [nil, nil]
182
+ end
183
+ [path, :file]
184
+ elsif ::File.directory?(path)
185
+ [path, :directory]
186
+ else
187
+ raise LoaderError, "Unknown type: #{path}" unless lenient
188
+ [nil, nil]
189
+ end
190
+ end
191
+ end
192
+ end
@@ -1,32 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2018 Daniel Azuma
3
+ # Copyright 2019 Daniel Azuma
4
4
  #
5
- # All rights reserved.
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
6
11
  #
7
- # Redistribution and use in source and binary forms, with or without
8
- # modification, are permitted provided that the following conditions are met:
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
9
14
  #
10
- # * Redistributions of source code must retain the above copyright notice,
11
- # this list of conditions and the following disclaimer.
12
- # * Redistributions in binary form must reproduce the above copyright notice,
13
- # this list of conditions and the following disclaimer in the documentation
14
- # and/or other materials provided with the distribution.
15
- # * Neither the name of the copyright holder, nor the names of any other
16
- # contributors to this software, may be used to endorse or promote products
17
- # derived from this software without specific prior written permission.
18
- #
19
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
- # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
- # POSSIBILITY OF SUCH DAMAGE.
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
+ # IN THE SOFTWARE.
30
22
  ;
31
23
 
32
24
  module Toys
@@ -36,7 +28,7 @@ module Toys
36
28
  #
37
29
  # This middleware adds `-v`, `--verbose`, `-q`, and `--quiet` flags, if
38
30
  # not already defined by the tool. These flags affect the setting of
39
- # {Toys::Tool::Keys::VERBOSITY}, and, thus, the logger level.
31
+ # {Toys::Context::Key::VERBOSITY}, and, thus, the logger level.
40
32
  #
41
33
  class AddVerbosityFlags
42
34
  include Middleware
@@ -56,7 +48,7 @@ module Toys
56
48
  ##
57
49
  # Create a AddVerbosityFlags middleware.
58
50
  #
59
- # @param [Boolean,Array<String>,Proc] verbose_flags Specify flags
51
+ # @param verbose_flags [Boolean,Array<String>,Proc] Specify flags
60
52
  # to increase verbosity. The value may be any of the following:
61
53
  #
62
54
  # * An array of flags that increase verbosity.
@@ -64,7 +56,7 @@ module Toys
64
56
  # * The `false` value to disable verbose flags.
65
57
  # * A proc that takes a tool and returns any of the above.
66
58
  #
67
- # @param [Boolean,Array<String>,Proc] quiet_flags Specify flags
59
+ # @param quiet_flags [Boolean,Array<String>,Proc] Specify flags
68
60
  # to decrease verbosity. The value may be any of the following:
69
61
  #
70
62
  # * An array of flags that decrease verbosity.
@@ -79,26 +71,29 @@ module Toys
79
71
 
80
72
  ##
81
73
  # Configure the tool flags.
74
+ # @private
82
75
  #
83
- def config(tool_definition, _loader)
84
- unless tool_definition.argument_parsing_disabled?
85
- StandardMiddleware.append_common_flag_group(tool_definition)
86
- add_verbose_flags(tool_definition)
87
- add_quiet_flags(tool_definition)
76
+ def config(tool, _loader)
77
+ unless tool.argument_parsing_disabled?
78
+ StandardMiddleware.append_common_flag_group(tool)
79
+ add_verbose_flags(tool)
80
+ add_quiet_flags(tool)
88
81
  end
89
82
  yield
90
83
  end
91
84
 
92
85
  private
93
86
 
94
- def add_verbose_flags(tool_definition)
95
- verbose_flags = resolve_flags_spec(@verbose_flags, tool_definition,
96
- DEFAULT_VERBOSE_FLAGS)
87
+ INCREMENT_HANDLER = ->(_val, cur) { cur.to_i + 1 }
88
+ DECREMENT_HANDLER = ->(_val, cur) { cur.to_i - 1 }
89
+
90
+ def add_verbose_flags(tool)
91
+ verbose_flags = resolve_flags_spec(@verbose_flags, tool, DEFAULT_VERBOSE_FLAGS)
97
92
  unless verbose_flags.empty?
98
- tool_definition.add_flag(
99
- Tool::Keys::VERBOSITY, verbose_flags,
93
+ tool.add_flag(
94
+ Context::Key::VERBOSITY, verbose_flags,
100
95
  report_collisions: false,
101
- handler: ->(_val, cur) { cur + 1 },
96
+ handler: INCREMENT_HANDLER,
102
97
  desc: "Increase verbosity",
103
98
  long_desc: "Increase verbosity, causing additional logging levels to display.",
104
99
  group: StandardMiddleware::COMMON_FLAG_GROUP
@@ -106,13 +101,13 @@ module Toys
106
101
  end
107
102
  end
108
103
 
109
- def add_quiet_flags(tool_definition)
110
- quiet_flags = resolve_flags_spec(@quiet_flags, tool_definition, DEFAULT_QUIET_FLAGS)
104
+ def add_quiet_flags(tool)
105
+ quiet_flags = resolve_flags_spec(@quiet_flags, tool, DEFAULT_QUIET_FLAGS)
111
106
  unless quiet_flags.empty?
112
- tool_definition.add_flag(
113
- Tool::Keys::VERBOSITY, quiet_flags,
107
+ tool.add_flag(
108
+ Context::Key::VERBOSITY, quiet_flags,
114
109
  report_collisions: false,
115
- handler: ->(_val, cur) { cur - 1 },
110
+ handler: DECREMENT_HANDLER,
116
111
  desc: "Decrease verbosity",
117
112
  long_desc: "Decrease verbosity, causing fewer logging levels to display.",
118
113
  group: StandardMiddleware::COMMON_FLAG_GROUP
@@ -1,32 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2018 Daniel Azuma
3
+ # Copyright 2019 Daniel Azuma
4
4
  #
5
- # All rights reserved.
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
6
11
  #
7
- # Redistribution and use in source and binary forms, with or without
8
- # modification, are permitted provided that the following conditions are met:
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
9
14
  #
10
- # * Redistributions of source code must retain the above copyright notice,
11
- # this list of conditions and the following disclaimer.
12
- # * Redistributions in binary form must reproduce the above copyright notice,
13
- # this list of conditions and the following disclaimer in the documentation
14
- # and/or other materials provided with the distribution.
15
- # * Neither the name of the copyright holder, nor the names of any other
16
- # contributors to this software, may be used to endorse or promote products
17
- # derived from this software without specific prior written permission.
18
- #
19
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
- # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
- # POSSIBILITY OF SUCH DAMAGE.
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
+ # IN THE SOFTWARE.
30
22
  ;
31
23
 
32
24
  module Toys
@@ -40,34 +32,41 @@ module Toys
40
32
  class HandleUsageErrors
41
33
  include Middleware
42
34
 
35
+ ##
36
+ # Exit code for usage error. (2 by convention)
37
+ # @return [Integer]
38
+ #
39
+ USAGE_ERROR_EXIT_CODE = 2
40
+
43
41
  ##
44
42
  # Create a HandleUsageErrors middleware.
45
43
  #
46
- # @param [Intgeer] exit_code The exit code to return if a usage error
47
- # occurs. Default is -1.
48
- # @param [IO] stream Output stream to write to. Default is stderr.
49
- # @param [Boolean,nil] styled_output Cause the tool to display help text
44
+ # @param exit_code [Integer] The exit code to return if a usage error
45
+ # occurs. Default is {USAGE_ERROR_EXIT_CODE}.
46
+ # @param stream [IO] Output stream to write to. Default is stderr.
47
+ # @param styled_output [Boolean,nil] Cause the tool to display help text
50
48
  # with ansi styles. If `nil`, display styles if the output stream is
51
49
  # a tty. Default is `nil`.
52
50
  #
53
- def initialize(exit_code: -1, stream: $stderr, styled_output: nil)
54
- @exit_code = exit_code
51
+ def initialize(exit_code: nil, stream: $stderr, styled_output: nil)
52
+ require "toys/utils/terminal"
53
+ @exit_code = exit_code || USAGE_ERROR_EXIT_CODE
55
54
  @terminal = Utils::Terminal.new(output: stream, styled: styled_output)
56
55
  end
57
56
 
58
57
  ##
59
58
  # Intercept and handle usage errors during execution.
59
+ # @private
60
60
  #
61
- def run(tool)
62
- if tool[Tool::Keys::USAGE_ERROR]
63
- help_text = Utils::HelpText.from_tool(tool)
64
- @terminal.puts(tool[Tool::Keys::USAGE_ERROR], :bright_red, :bold)
65
- @terminal.puts("")
66
- @terminal.puts(help_text.usage_string(wrap_width: @terminal.width))
67
- Tool.exit(@exit_code)
68
- else
69
- yield
70
- end
61
+ def run(context)
62
+ yield
63
+ rescue ArgParsingError => e
64
+ require "toys/utils/help_text"
65
+ help_text = Utils::HelpText.from_context(context)
66
+ @terminal.puts(e.usage_errors.join("\n"), :bright_red, :bold)
67
+ @terminal.puts("")
68
+ @terminal.puts(help_text.usage_string(wrap_width: @terminal.width))
69
+ Context.exit(@exit_code)
71
70
  end
72
71
  end
73
72
  end
@@ -1,32 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2018 Daniel Azuma
3
+ # Copyright 2019 Daniel Azuma
4
4
  #
5
- # All rights reserved.
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
6
11
  #
7
- # Redistribution and use in source and binary forms, with or without
8
- # modification, are permitted provided that the following conditions are met:
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
9
14
  #
10
- # * Redistributions of source code must retain the above copyright notice,
11
- # this list of conditions and the following disclaimer.
12
- # * Redistributions in binary form must reproduce the above copyright notice,
13
- # this list of conditions and the following disclaimer in the documentation
14
- # and/or other materials provided with the distribution.
15
- # * Neither the name of the copyright holder, nor the names of any other
16
- # contributors to this software, may be used to endorse or promote products
17
- # derived from this software without specific prior written permission.
18
- #
19
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
- # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
- # POSSIBILITY OF SUCH DAMAGE.
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
+ # IN THE SOFTWARE.
30
22
  ;
31
23
 
32
24
  module Toys
@@ -69,41 +61,27 @@ module Toys
69
61
  " https://www.rubydoc.info/gems/toys-core for more info.",
70
62
  "To replace this message, set the description and long description" \
71
63
  " of the root tool, or configure the SetDefaultDescriptions" \
72
- " middleware."
64
+ " middleware.",
73
65
  ].freeze
74
66
 
75
- ##
76
- # A mapping of names for acceptable types
77
- # @return [Hash]
78
- #
79
- ACCEPTABLE_NAMES = {
80
- nil => "string",
81
- ::String => "nonempty string",
82
- ::TrueClass => "boolean",
83
- ::FalseClass => "boolean",
84
- ::OptionParser::DecimalInteger => "decimal integer",
85
- ::OptionParser::OctalInteger => "octal integer",
86
- ::OptionParser::DecimalNumeric => "decimal numeric"
87
- }.freeze
88
-
89
67
  ##
90
68
  # Create a SetDefaultDescriptions middleware given default descriptions.
91
69
  #
92
- # @param [String,nil] default_tool_desc The default short description for
70
+ # @param default_tool_desc [String,nil] The default short description for
93
71
  # runnable tools, or `nil` not to set one. Defaults to
94
72
  # {DEFAULT_TOOL_DESC}.
95
- # @param [String,nil] default_tool_long_desc The default long description
73
+ # @param default_tool_long_desc [String,nil] The default long description
96
74
  # for runnable tools, or `nil` not to set one. Defaults to `nil`.
97
- # @param [String,nil] default_namespace_desc The default short
75
+ # @param default_namespace_desc [String,nil] The default short
98
76
  # description for non-runnable tools, or `nil` not to set one.
99
77
  # Defaults to {DEFAULT_TOOL_DESC}.
100
- # @param [String,nil] default_namespace_long_desc The default long
78
+ # @param default_namespace_long_desc [String,nil] The default long
101
79
  # description for non-runnable tools, or `nil` not to set one.
102
80
  # Defaults to `nil`.
103
- # @param [String,nil] default_root_desc The default short description for
81
+ # @param default_root_desc [String,nil] The default short description for
104
82
  # the root tool, or `nil` not to set one. Defaults to
105
83
  # {DEFAULT_ROOT_DESC}.
106
- # @param [String,nil] default_root_long_desc The default long description
84
+ # @param default_root_long_desc [String,nil] The default long description
107
85
  # for the root tool, or `nil` not to set one. Defaults to
108
86
  # {DEFAULT_ROOT_LONG_DESC}.
109
87
  #
@@ -123,15 +101,20 @@ module Toys
123
101
 
124
102
  ##
125
103
  # Add default description text to tools.
104
+ # @private
126
105
  #
127
106
  def config(tool, loader)
128
107
  data = {tool: tool, loader: loader}
129
- tool.flag_definitions.each do |flag|
108
+ tool.flags.each do |flag|
130
109
  config_desc(flag, generate_flag_desc(flag, data), generate_flag_long_desc(flag, data))
131
110
  end
132
- tool.arg_definitions.each do |arg|
111
+ tool.positional_args.each do |arg|
133
112
  config_desc(arg, generate_arg_desc(arg, data), generate_arg_long_desc(arg, data))
134
113
  end
114
+ tool.flag_groups.each do |flag_group|
115
+ config_desc(flag_group, generate_flag_group_desc(flag_group, data),
116
+ generate_flag_group_long_desc(flag_group, data))
117
+ end
135
118
  config_desc(tool, generate_tool_desc(tool, data), generate_tool_long_desc(tool, data))
136
119
  yield
137
120
  end
@@ -143,13 +126,13 @@ module Toys
143
126
  # By default, it uses the parameters given to the middleware object.
144
127
  # Override this method to provide different logic.
145
128
  #
146
- # @param [Toys::Tool] tool The tool to document.
147
- # @param [Hash] data Additional data that might be useful. Currently,
129
+ # @param tool [Toys::Tool] The tool to document.
130
+ # @param data [Hash] Additional data that might be useful. Currently,
148
131
  # the {Toys::Loader} is passed with key `:loader`. Future versions
149
132
  # of Toys may provide additional information.
150
- # @return [String,Array<String>,Toys::Utils::WrappableString,nil] The
151
- # default description, or `nil` not to set a default. See
152
- # {Toys::Tool#desc=} for info on the format.
133
+ # @return [String,Array<String>,Toys::WrappableString] The default
134
+ # description. See {Toys::Tool#desc=} for info on the format.
135
+ # @return [nil] if this middleware should not set the description.
153
136
  #
154
137
  def generate_tool_desc(tool, data)
155
138
  if tool.root?
@@ -166,13 +149,14 @@ module Toys
166
149
  # By default, it uses the parameters given to the middleware object.
167
150
  # Override this method to provide different logic.
168
151
  #
169
- # @param [Toys::Tool] tool The tool to document
170
- # @param [Hash] data Additional data that might be useful. Currently,
171
- # the {Toys::Loader} is passed with key `:loader`. Future versions
172
- # of Toys may provide additional information.
173
- # @return [Array<Toys::Utils::WrappableString,String,Array<String>>,nil]
174
- # The default long description, or `nil` not to set a default. See
175
- # {Toys::Tool#long_desc=} for info on the format.
152
+ # @param tool [Toys::Tool] The tool to document
153
+ # @param data [Hash] Additional data that might be useful. Currently,
154
+ # the {Toys::Loader} is passed with key `:loader`. Future versions of
155
+ # Toys may provide additional information.
156
+ # @return [Array<Toys::WrappableString,String,Array<String>>] The default
157
+ # long description. See {Toys::Tool#long_desc=} for info on the
158
+ # format.
159
+ # @return [nil] if this middleware should not set the long description.
176
160
  #
177
161
  def generate_tool_long_desc(tool, data)
178
162
  if tool.root?
@@ -188,17 +172,17 @@ module Toys
188
172
  # This method implements the logic for generating a flag description.
189
173
  # Override this method to provide different logic.
190
174
  #
191
- # @param [Toys::Tool::FlagDefinition] flag The flag to document
192
- # @param [Hash] data Additional data that might be useful. Currently,
193
- # the {Toys::Tool} is passed with key `:tool`. Future versions of
194
- # Toys may provide additional information.
195
- # @return [String,Array<String>,Toys::Utils::WrappableString,nil] The
196
- # default description, or `nil` not to set a default. See
197
- # {Toys::Tool#desc=} for info on the format.
175
+ # @param flag [Toys::Flag] The flag to document
176
+ # @param data [Hash] Additional data that might be useful. Currently,
177
+ # the {Toys::Tool} is passed with key `:tool`. Future
178
+ # versions of Toys may provide additional information.
179
+ # @return [String,Array<String>,Toys::WrappableString] The default
180
+ # description. See {Toys::Tool#desc=} for info on the format.
181
+ # @return [nil] if this middleware should not set the description.
198
182
  #
199
183
  def generate_flag_desc(flag, data) # rubocop:disable Lint/UnusedMethodArgument
200
184
  name = flag.key.to_s.tr("_", "-").gsub(/[^\w-]/, "").downcase.inspect
201
- acceptable = flag.flag_type == :value ? acceptable_name(flag.accept) : "boolean flag"
185
+ acceptable = flag.flag_type == :value ? flag.acceptor.type_desc : "boolean flag"
202
186
  default_clause = flag.default ? " (default is #{flag.default.inspect})" : ""
203
187
  "Sets the #{name} option as type #{acceptable}#{default_clause}."
204
188
  end
@@ -207,13 +191,14 @@ module Toys
207
191
  # This method implements logic for generating a flag long description.
208
192
  # Override this method to provide different logic.
209
193
  #
210
- # @param [Toys::Tool::FlagDefinition] flag The flag to document
211
- # @param [Hash] data Additional data that might be useful. Currently,
194
+ # @param flag [Toys::Flag] The flag to document
195
+ # @param data [Hash] Additional data that might be useful. Currently,
212
196
  # the {Toys::Tool} is passed with key `:tool`. Future versions of
213
- # Toys may provide additional information.
214
- # @return [Array<Toys::Utils::WrappableString,String,Array<String>>,nil]
215
- # The default long description, or `nil` not to set a default. See
216
- # {Toys::Tool#long_desc=} for info on the format.
197
+ # versions of Toys may provide additional information.
198
+ # @return [Array<Toys::WrappableString,String,Array<String>>] The default
199
+ # long description. See {Toys::Tool#long_desc=} for info on the
200
+ # format.
201
+ # @return [nil] if this middleware should not set the long description.
217
202
  #
218
203
  def generate_flag_long_desc(flag, data) # rubocop:disable Lint/UnusedMethodArgument
219
204
  nil
@@ -223,16 +208,16 @@ module Toys
223
208
  # This method implements the logic for generating an arg description.
224
209
  # Override this method to provide different logic.
225
210
  #
226
- # @param [Toys::Tool::ArgDefinition] arg The arg to document
227
- # @param [Hash] data Additional data that might be useful. Currently,
211
+ # @param arg [Toys::PositionalArg] The arg to document
212
+ # @param data [Hash] Additional data that might be useful. Currently,
228
213
  # the {Toys::Tool} is passed with key `:tool`. Future versions of
229
214
  # Toys may provide additional information.
230
- # @return [String,Array<String>,Toys::Utils::WrappableString,nil] The
231
- # default description, or `nil` not to set a default. See
232
- # {Toys::Tool#desc=} for info on the format.
215
+ # @return [String,Array<String>,Toys::WrappableString] The default
216
+ # description. See {Toys::Tool#desc=} for info on the format.
217
+ # @return [nil] if this middleware should not set the description.
233
218
  #
234
219
  def generate_arg_desc(arg, data) # rubocop:disable Lint/UnusedMethodArgument
235
- acceptable = acceptable_name(arg.accept)
220
+ acceptable = arg.acceptor.type_desc
236
221
  default_clause = arg.default ? " (default is #{arg.default.inspect})" : ""
237
222
  case arg.type
238
223
  when :required
@@ -248,26 +233,63 @@ module Toys
248
233
  # This method implements logic for generating an arg long description.
249
234
  # Override this method to provide different logic.
250
235
  #
251
- # @param [Toys::Tool::ArgDefinition] arg The arg to document
252
- # @param [Hash] data Additional data that might be useful. Currently,
236
+ # @param arg [Toys::PositionalArg] The arg to document
237
+ # @param data [Hash] Additional data that might be useful. Currently,
253
238
  # the {Toys::Tool} is passed with key `:tool`. Future versions of
254
239
  # Toys may provide additional information.
255
- # @return [Array<Toys::Utils::WrappableString,String,Array<String>>,nil]
256
- # The default long description, or `nil` not to set a default. See
257
- # {Toys::Tool#long_desc=} for info on the format.
240
+ # @return [Array<Toys::WrappableString,String,Array<String>>] The default
241
+ # long description. See {Toys::Tool#long_desc=} for info on the
242
+ # format.
243
+ # @return [nil] if this middleware should not set the long description.
258
244
  #
259
245
  def generate_arg_long_desc(arg, data) # rubocop:disable Lint/UnusedMethodArgument
260
246
  nil
261
247
  end
262
248
 
263
249
  ##
264
- # Return a reasonable name for an acceptor
250
+ # This method implements the logic for generating a flag group
251
+ # description. Override this method to provide different logic.
265
252
  #
266
- # @param [Object] accept An acceptor to name
267
- # @return [String]
253
+ # @param group [Toys::FlagGroup] The flag group to document
254
+ # @param data [Hash] Additional data that might be useful. Currently,
255
+ # the {Toys::Tool} is passed with key `:tool`. Future
256
+ # versions of Toys may provide additional information.
257
+ # @return [String,Array<String>,Toys::WrappableString] The default
258
+ # description. See {Toys::Tool#desc=} for info on the format.
259
+ # @return [nil] if this middleware should not set the description.
260
+ #
261
+ def generate_flag_group_desc(group, data) # rubocop:disable Lint/UnusedMethodArgument
262
+ if group.is_a?(FlagGroup::Required)
263
+ "Required Flags"
264
+ else
265
+ "Flags"
266
+ end
267
+ end
268
+
269
+ ##
270
+ # This method implements the logic for generating a flag group long
271
+ # description. Override this method to provide different logic.
268
272
  #
269
- def acceptable_name(accept)
270
- ACCEPTABLE_NAMES[accept] || accept.to_s.downcase
273
+ # @param group [Toys::FlagGroup] The flag group to document
274
+ # @param data [Hash] Additional data that might be useful. Currently,
275
+ # the {Toys::Tool} is passed with key `:tool`. Future
276
+ # versions of Toys may provide additional information.
277
+ # @return [Array<Toys::WrappableString,String,Array<String>>] The default
278
+ # long description. See {Toys::Tool#long_desc=} for info on the
279
+ # format.
280
+ # @return [nil] if this middleware should not set the long description.
281
+ #
282
+ def generate_flag_group_long_desc(group, data) # rubocop:disable Lint/UnusedMethodArgument
283
+ case group
284
+ when FlagGroup::Required
285
+ ["These flags are required."]
286
+ when FlagGroup::ExactlyOne
287
+ ["Exactly one of these flags must be set."]
288
+ when FlagGroup::AtMostOne
289
+ ["At most one of these flags must be set."]
290
+ when FlagGroup::AtLeastOne
291
+ ["At least one of these flags must be set."]
292
+ end
271
293
  end
272
294
 
273
295
  private