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
@@ -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
@@ -65,11 +57,11 @@ module Toys
65
57
  # This basic implementation does nothing and simply yields to the next
66
58
  # middleware.
67
59
  #
68
- # @param [Toys::Definition::Tool] _tool_definition The tool definition
69
- # to modify.
70
- # @param [Toys::Loader] _loader The loader that loaded this tool.
60
+ # @param tool [Toys::Tool] The tool definition to modify.
61
+ # @param loader [Toys::Loader] The loader that loaded this tool.
62
+ # @return [void]
71
63
  #
72
- def config(_tool_definition, _loader)
64
+ def config(tool, loader) # rubocop:disable Lint/UnusedMethodArgument
73
65
  yield
74
66
  end
75
67
 
@@ -86,14 +78,15 @@ module Toys
86
78
  #
87
79
  # Like a tool's `run` method, this method's return value is unused. If
88
80
  # you want to output from a tool, write to stdout or stderr. If you want
89
- # to set the exit status code, call {Toys::Tool#exit} on the tool object.
81
+ # to set the exit status code, call {Toys::Context#exit} on the context.
90
82
  #
91
83
  # This basic implementation does nothing and simply yields to the next
92
84
  # middleware.
93
85
  #
94
- # @param [Toys::Tool] _tool The tool execution instance.
86
+ # @param context [Toys::Context] The tool execution context.
87
+ # @return [void]
95
88
  #
96
- def run(_tool)
89
+ def run(context) # rubocop:disable Lint/UnusedMethodArgument
97
90
  yield
98
91
  end
99
92
  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
  # A mixin is a collection of methods that are available to be called from a
37
29
  # tool implementation (i.e. its run method). The mixin is added to the tool
38
30
  # class, so it has access to the same methods that can be called by the tool,
39
- # such as {Toys::Tool#option}.
31
+ # such as {Toys::Context#get}.
40
32
  #
41
33
  # ## Usage
42
34
  #
@@ -44,19 +36,24 @@ module Toys
44
36
  # the methods you want to be available.
45
37
  #
46
38
  # If you want to perform some initialization specific to the mixin, you can
47
- # provide a `to_initialize` block and/or a `to_include` block.
48
- #
49
- # The `to_initialize` block is called when the tool itself is instantiated.
50
- # It has access to tool methods such as {Toys::Tool#option}, and can perform
51
- # setup for the tool execution itself, often involving initializing some
52
- # persistent state and storing it in the tool using {Toys::Tool#set}. The
53
- # `to_initialize` block is passed any extra arguments that were provided to
54
- # the `include` directive.
55
- #
56
- # The `to_include` block is called in the context of your tool class when
57
- # your mixin is included. It is also passed any extra arguments that were
58
- # provided to the `include` directive. It can be used to issue directives
59
- # or define methods on the DSL, specific to the mixin.
39
+ # provide an *initializer* block and/or an *inclusion* block. These can be
40
+ # specified by calling the module methods defined in
41
+ # {Toys::Mixin::ModuleMethods}.
42
+ #
43
+ # The initializer block is called when the tool context is instantiated
44
+ # in preparation for execution. It has access to context methods such as
45
+ # {Toys::Context#get}, and can perform setup for the tool execution itself,
46
+ # such as initializing some persistent state and storing it in the tool using
47
+ # {Toys::Context#set}. The initializer block is passed any extra arguments
48
+ # that were provided to the `include` directive. Define the initializer by
49
+ # calling {Toys::Mixin::ModuleMethods#on_initialize}.
50
+ #
51
+ # The inclusion block is called in the context of your tool class when your
52
+ # mixin is included. It is also passed any extra arguments that were provided
53
+ # to the `include` directive. It can be used to issue directives to define
54
+ # tools or other objects in the DSL, or even enhance the DSL by defining DSL
55
+ # methods specific to the mixin. Define the inclusion block by calling
56
+ # {Toys::Mixin::ModuleMethods#on_include}.
60
57
  #
61
58
  # ## Example
62
59
  #
@@ -68,14 +65,16 @@ module Toys
68
65
  # module MyCounterMixin
69
66
  # include Toys::Mixin
70
67
  #
71
- # # Initialize the counter. Called with self set to the tool so it can
72
- # # affect the tool state.
73
- # to_initialize do |start = 0|
68
+ # # Initialize the counter. Notice that the initializer is evaluated
69
+ # # in the context of the runtime context, so has access to the runtime
70
+ # # context state.
71
+ # on_initialize do |start = 0|
74
72
  # set(:counter_value, start)
75
73
  # end
76
74
  #
77
- # # Mixin methods are called with self set to the tool and can affect
78
- # # the tool state.
75
+ # # Mixin methods are evaluated in the runtime context and so have
76
+ # # access to the runtime context state, just as if you had defined
77
+ # # them in your tool.
79
78
  # def counter_value
80
79
  # get(:counter_value)
81
80
  # end
@@ -100,9 +99,23 @@ module Toys
100
99
  # end
101
100
  #
102
101
  module Mixin
102
+ ##
103
+ # Create a mixin module with the given block.
104
+ #
105
+ # @param block [Proc] Defines the mixin module.
106
+ # @return [Class]
107
+ #
108
+ def self.create(&block)
109
+ mixin_mod = ::Module.new do
110
+ include ::Toys::Mixin
111
+ end
112
+ mixin_mod.module_eval(&block) if block
113
+ mixin_mod
114
+ end
115
+
103
116
  ## @private
104
117
  def self.included(mod)
105
- return if mod.respond_to?(:to_initialize)
118
+ return if mod.respond_to?(:on_initialize)
106
119
  mod.extend(ModuleMethods)
107
120
  end
108
121
 
@@ -111,32 +124,50 @@ module Toys
111
124
  #
112
125
  module ModuleMethods
113
126
  ##
114
- # Provide a block that initializes this mixin when the tool is
115
- # constructed.
127
+ # Set the initializer for this mixin. This block is evaluated in the
128
+ # runtime context before execution, and is passed any arguments provided
129
+ # to the `include` directive. It can perform any runtime initialization
130
+ # needed by the mixin.
131
+ #
132
+ # @param block [Proc] Sets the initializer proc.
133
+ # @return [self]
116
134
  #
117
- def to_initialize(&block)
118
- self.initialization_callback = block
135
+ def on_initialize(&block)
136
+ self.initializer = block
137
+ self
119
138
  end
120
139
 
121
140
  ##
122
- # Provide a block that modifies the tool class when the mixin is
123
- # included.
141
+ # The initializer proc for this mixin. This proc is evaluated in the
142
+ # runtime context before execution, and is passed any arguments provided
143
+ # to the `include` directive. It can perform any runtime initialization
144
+ # needed by the mixin.
124
145
  #
125
- def to_include(&block)
126
- self.inclusion_callback = block
127
- end
146
+ # @return [Proc] The iniitiliazer for this mixin.
147
+ #
148
+ attr_accessor :initializer
128
149
 
129
150
  ##
130
- # You may alternately set the initializer block using this accessor.
131
- # @return [Proc]
151
+ # Set an inclusion proc for this mixin. This block is evaluated in the
152
+ # tool class immediately after the mixin is included, and is passed any
153
+ # arguments provided to the `include` directive.
132
154
  #
133
- attr_accessor :initialization_callback
155
+ # @param block [Proc] Sets the inclusion proc.
156
+ # @return [self]
157
+ #
158
+ def on_include(&block)
159
+ self.inclusion = block
160
+ self
161
+ end
134
162
 
135
163
  ##
136
- # You may alternately set the inclusion block using this accessor.
137
- # @return [Proc]
164
+ # The inclusion proc for this mixin. This block is evaluated in the tool
165
+ # class immediately after the mixin is included, and is passed any
166
+ # arguments provided to the `include` directive.
167
+ #
168
+ # @return [Proc] The inclusion procedure for this mixin.
138
169
  #
139
- attr_accessor :inclusion_callback
170
+ attr_accessor :inclusion
140
171
  end
141
172
  end
142
173
  end
@@ -0,0 +1,125 @@
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
+ # A helper module that provides methods to do module lookups. This is
27
+ # used to obtain named helpers, middleware, and templates from the
28
+ # respective modules.
29
+ #
30
+ class ModuleLookup
31
+ class << self
32
+ ##
33
+ # Convert the given string to a path element. Specifically, converts
34
+ # to `lower_snake_case`.
35
+ #
36
+ # @param str [String,Symbol] String to convert.
37
+ # @return [String] Converted string
38
+ #
39
+ def to_path_name(str)
40
+ str = str.to_s.sub(/^_/, "").sub(/_$/, "").gsub(/_+/, "_")
41
+ while str.sub!(/([^_])([A-Z])/, "\\1_\\2") do end
42
+ str.downcase
43
+ end
44
+
45
+ ##
46
+ # Convert the given string to a module name. Specifically, converts
47
+ # to `UpperCamelCase`, and then to a symbol.
48
+ #
49
+ # @param str [String,Symbol] String to convert.
50
+ # @return [Symbol] Converted name
51
+ #
52
+ def to_module_name(str)
53
+ str = str.to_s.sub(/^_/, "").sub(/_$/, "").gsub(/_+/, "_")
54
+ str.to_s.gsub(/(?:^|_)([a-zA-Z])/) { ::Regexp.last_match(1).upcase }.to_sym
55
+ end
56
+
57
+ ##
58
+ # Given a require path, return the module expected to be defined.
59
+ #
60
+ # @param path [String] File path, delimited by forward slash
61
+ # @return [Module] The module loaded from that path
62
+ #
63
+ def path_to_module(path)
64
+ path.split("/").reduce(::Object) do |running_mod, seg|
65
+ mod_name = to_module_name(seg)
66
+ unless running_mod.constants.include?(mod_name)
67
+ raise ::NameError, "Module #{running_mod.name}::#{mod_name} not found"
68
+ end
69
+ running_mod.const_get(mod_name)
70
+ end
71
+ end
72
+ end
73
+
74
+ ##
75
+ # Create an empty ModuleLookup
76
+ #
77
+ def initialize
78
+ @paths = []
79
+ end
80
+
81
+ ##
82
+ # Add a lookup path for modules.
83
+ #
84
+ # @param path_base [String] The base require path
85
+ # @param module_base [Module] The base module, or `nil` (the default) to
86
+ # infer a default from the path base.
87
+ # @param high_priority [Boolean] If true, add to the head of the lookup
88
+ # path, otherwise add to the end.
89
+ # @return [self]
90
+ #
91
+ def add_path(path_base, module_base: nil, high_priority: false)
92
+ module_base ||= ModuleLookup.path_to_module(path_base)
93
+ if high_priority
94
+ @paths.unshift([path_base, module_base])
95
+ else
96
+ @paths << [path_base, module_base]
97
+ end
98
+ self
99
+ end
100
+
101
+ ##
102
+ # Obtain a named module. Returns `nil` if the name is not present.
103
+ #
104
+ # @param name [String,Symbol] The name of the module to return.
105
+ # @return [Module] The specified module
106
+ #
107
+ def lookup(name)
108
+ @paths.each do |path_base, module_base|
109
+ path = "#{path_base}/#{ModuleLookup.to_path_name(name)}"
110
+ begin
111
+ require path
112
+ rescue ::LoadError
113
+ next
114
+ end
115
+ mod_name = ModuleLookup.to_module_name(name)
116
+ unless module_base.constants.include?(mod_name)
117
+ raise ::NameError,
118
+ "File #{path.inspect} did not define #{module_base.name}::#{mod_name}"
119
+ end
120
+ return module_base.const_get(mod_name)
121
+ end
122
+ nil
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,184 @@
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
+ # Representation of a formal positional argument
27
+ #
28
+ class PositionalArg
29
+ ##
30
+ # Create a PositionalArg definition.
31
+ # This argument list is subject to change. Use {Toys::PositionalArg.create}
32
+ # instead for a more stable interface.
33
+ # @private
34
+ #
35
+ def initialize(key, type, acceptor, default, completion, desc, long_desc, display_name)
36
+ @key = key
37
+ @type = type
38
+ @acceptor = Acceptor.create(acceptor)
39
+ @default = default
40
+ @completion = Completion.create(completion)
41
+ @desc = WrappableString.make(desc)
42
+ @long_desc = WrappableString.make_array(long_desc)
43
+ @display_name = display_name || key.to_s.tr("-", "_").gsub(/\W/, "").upcase
44
+ end
45
+
46
+ ##
47
+ # Create a PositionalArg definition.
48
+ #
49
+ # @param key [String,Symbol] The key to use to retrieve the value from
50
+ # the execution context.
51
+ # @param type [Symbol] The type of arg. Valid values are `:required`,
52
+ # `:optional`, and `:remaining`.
53
+ # @param accept [Object] An acceptor that validates and/or converts the
54
+ # value. See {Toys::Acceptor.create} for recognized formats. Optional.
55
+ # If not specified, defaults to {Toys::Acceptor::DEFAULT}.
56
+ # @param complete [Object] A specifier for shell tab completion. See
57
+ # {Toys::Completion.create} for recognized formats.
58
+ # @param display_name [String] A name to use for display (in help text and
59
+ # error reports). Defaults to the key in upper case.
60
+ # @param desc [String,Array<String>,Toys::WrappableString] Short
61
+ # description for the flag. See {Toys::DSL::Tool#desc} for a
62
+ # description of the allowed formats. Defaults to the empty string.
63
+ # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
64
+ # Long description for the flag. See {Toys::DSL::Tool#long_desc} for
65
+ # a description of the allowed formats. (But note that this param
66
+ # takes an Array of description lines, rather than a series of
67
+ # arguments.) Defaults to the empty array.
68
+ # @return [Toys::PositionalArg]
69
+ #
70
+ def self.create(key, type,
71
+ accept: nil, default: nil, complete: nil, desc: nil,
72
+ long_desc: nil, display_name: nil)
73
+ new(key, type, accept, default, complete, desc, long_desc, display_name)
74
+ end
75
+
76
+ ##
77
+ # The key for this arg.
78
+ # @return [Symbol]
79
+ #
80
+ attr_reader :key
81
+
82
+ ##
83
+ # Type of this argument.
84
+ # @return [:required,:optional,:remaining]
85
+ #
86
+ attr_reader :type
87
+
88
+ ##
89
+ # The effective acceptor.
90
+ # @return [Toys::Acceptor::Base]
91
+ #
92
+ attr_accessor :acceptor
93
+
94
+ ##
95
+ # The default value, which may be `nil`.
96
+ # @return [Object]
97
+ #
98
+ attr_reader :default
99
+
100
+ ##
101
+ # The proc that determines shell completions for the value.
102
+ # @return [Proc,Toys::Completion::Base]
103
+ #
104
+ attr_reader :completion
105
+
106
+ ##
107
+ # The short description string.
108
+ #
109
+ # When reading, this is always returned as a {Toys::WrappableString}.
110
+ #
111
+ # When setting, the description may be provided as any of the following:
112
+ # * A {Toys::WrappableString}.
113
+ # * A normal String, which will be transformed into a
114
+ # {Toys::WrappableString} using spaces as word delimiters.
115
+ # * An Array of String, which will be transformed into a
116
+ # {Toys::WrappableString} where each array element represents an
117
+ # individual word for wrapping.
118
+ #
119
+ # @return [Toys::WrappableString]
120
+ #
121
+ attr_reader :desc
122
+
123
+ ##
124
+ # The long description strings.
125
+ #
126
+ # When reading, this is returned as an Array of {Toys::WrappableString}
127
+ # representing the lines in the description.
128
+ #
129
+ # When setting, the description must be provided as an Array where *each
130
+ # element* may be any of the following:
131
+ # * A {Toys::WrappableString} representing one line.
132
+ # * A normal String representing a line. This will be transformed into a
133
+ # {Toys::WrappableString} using spaces as word delimiters.
134
+ # * An Array of String representing a line. This will be transformed into
135
+ # a {Toys::WrappableString} where each array element represents an
136
+ # individual word for wrapping.
137
+ #
138
+ # @return [Array<Toys::WrappableString>]
139
+ #
140
+ attr_reader :long_desc
141
+
142
+ ##
143
+ # The displayable name.
144
+ # @return [String]
145
+ #
146
+ attr_accessor :display_name
147
+
148
+ ##
149
+ # Set the short description string.
150
+ #
151
+ # See {#desc} for details.
152
+ #
153
+ # @param desc [Toys::WrappableString,String,Array<String>]
154
+ #
155
+ def desc=(desc)
156
+ @desc = WrappableString.make(desc)
157
+ end
158
+
159
+ ##
160
+ # Set the long description strings.
161
+ #
162
+ # See {#long_desc} for details.
163
+ #
164
+ # @param long_desc [Array<Toys::WrappableString,String,Array<String>>]
165
+ #
166
+ def long_desc=(long_desc)
167
+ @long_desc = WrappableString.make_array(long_desc)
168
+ end
169
+
170
+ ##
171
+ # Append long description strings.
172
+ #
173
+ # You must pass an array of lines in the long description. See {#long_desc}
174
+ # for details on how each line may be represented.
175
+ #
176
+ # @param long_desc [Array<Toys::WrappableString,String,Array<String>>]
177
+ # @return [self]
178
+ #
179
+ def append_long_desc(long_desc)
180
+ @long_desc.concat(WrappableString.make_array(long_desc))
181
+ self
182
+ end
183
+ end
184
+ end