toys-core 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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