toys-core 0.11.5 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/README.md +1 -1
  4. data/lib/toys-core.rb +4 -1
  5. data/lib/toys/acceptor.rb +3 -3
  6. data/lib/toys/arg_parser.rb +6 -7
  7. data/lib/toys/cli.rb +44 -14
  8. data/lib/toys/compat.rb +19 -22
  9. data/lib/toys/completion.rb +3 -1
  10. data/lib/toys/context.rb +2 -2
  11. data/lib/toys/core.rb +1 -1
  12. data/lib/toys/dsl/base.rb +85 -0
  13. data/lib/toys/dsl/flag.rb +3 -3
  14. data/lib/toys/dsl/flag_group.rb +7 -7
  15. data/lib/toys/dsl/internal.rb +206 -0
  16. data/lib/toys/dsl/positional_arg.rb +3 -3
  17. data/lib/toys/dsl/tool.rb +174 -216
  18. data/lib/toys/errors.rb +1 -0
  19. data/lib/toys/flag.rb +15 -18
  20. data/lib/toys/flag_group.rb +5 -4
  21. data/lib/toys/input_file.rb +4 -4
  22. data/lib/toys/loader.rb +189 -50
  23. data/lib/toys/middleware.rb +1 -1
  24. data/lib/toys/mixin.rb +2 -2
  25. data/lib/toys/positional_arg.rb +3 -3
  26. data/lib/toys/settings.rb +900 -0
  27. data/lib/toys/source_info.rb +121 -18
  28. data/lib/toys/standard_middleware/apply_config.rb +5 -4
  29. data/lib/toys/standard_middleware/set_default_descriptions.rb +18 -18
  30. data/lib/toys/standard_middleware/show_help.rb +17 -5
  31. data/lib/toys/standard_mixins/exec.rb +12 -14
  32. data/lib/toys/standard_mixins/git_cache.rb +48 -0
  33. data/lib/toys/standard_mixins/xdg.rb +56 -0
  34. data/lib/toys/template.rb +2 -2
  35. data/lib/toys/{tool.rb → tool_definition.rb} +100 -41
  36. data/lib/toys/utils/exec.rb +4 -5
  37. data/lib/toys/utils/gems.rb +8 -7
  38. data/lib/toys/utils/git_cache.rb +184 -0
  39. data/lib/toys/utils/help_text.rb +90 -34
  40. data/lib/toys/utils/terminal.rb +1 -1
  41. data/lib/toys/utils/xdg.rb +293 -0
  42. metadata +14 -7
data/lib/toys/dsl/flag.rb CHANGED
@@ -9,7 +9,7 @@ module Toys
9
9
  # These directives are available inside a block passed to
10
10
  # {Toys::DSL::Tool#flag}.
11
11
  #
12
- # ## Example
12
+ # ### Example
13
13
  #
14
14
  # tool "mytool" do
15
15
  # flag :value do
@@ -212,7 +212,7 @@ module Toys
212
212
  # across the strings in the array. In this case, whitespace is not
213
213
  # compacted.
214
214
  #
215
- # ## Examples
215
+ # ### Examples
216
216
  #
217
217
  # If you pass in a sentence as a simple string, it may be word wrapped
218
218
  # when displayed:
@@ -243,7 +243,7 @@ module Toys
243
243
  # word-wrapped when displayed. To insert a blank line, include an empty
244
244
  # string as one of the descriptions.
245
245
  #
246
- # ## Example
246
+ # ### Example
247
247
  #
248
248
  # long_desc "This initial paragraph might get word wrapped.",
249
249
  # "This next paragraph is followed by a blank line.",
@@ -10,7 +10,7 @@ module Toys
10
10
  # {Toys::DSL::Tool#at_most_one}, {Toys::DSL::Tool#at_least_one}, or
11
11
  # {Toys::DSL::Tool#exactly_one}.
12
12
  #
13
- # ## Example
13
+ # ### Example
14
14
  #
15
15
  # tool "login" do
16
16
  # all_required do
@@ -43,7 +43,7 @@ module Toys
43
43
  # set in a block passed to this method. If you provide a block, you can
44
44
  # use directives in {Toys::DSL::Flag} within the block.
45
45
  #
46
- # ## Flag syntax
46
+ # ### Flag syntax
47
47
  #
48
48
  # The flags themselves should be provided in OptionParser form. Following
49
49
  # are examples of valid syntax.
@@ -98,7 +98,7 @@ module Toys
98
98
  # or off. This effectively creates two flags, `--abc` which sets the
99
99
  # value to `true`, and `--no-abc` which sets the falue to `false`.
100
100
  #
101
- # ## Default flag syntax
101
+ # ### Default flag syntax
102
102
  #
103
103
  # If no flag syntax strings are provided, a default syntax will be
104
104
  # inferred based on the key and other options.
@@ -123,7 +123,7 @@ module Toys
123
123
  # flag :number, accept: Integer
124
124
  # flag :number, "--number=VAL", accept: Integer
125
125
  #
126
- # ## More examples
126
+ # ### More examples
127
127
  #
128
128
  # A flag that sets its value to the number of times it appears on the
129
129
  # command line:
@@ -200,7 +200,7 @@ module Toys
200
200
  report_collisions, @flag_group, desc, long_desc, display_name)
201
201
  flag_dsl.instance_exec(flag_dsl, &block) if block
202
202
  flag_dsl._add_to(@tool, key)
203
- DSL::Tool.maybe_add_getter(@tool_dsl, key)
203
+ DSL::Internal.maybe_add_getter(@tool_dsl, key)
204
204
  self
205
205
  end
206
206
 
@@ -221,7 +221,7 @@ module Toys
221
221
  # across the strings in the array. In this case, whitespace is not
222
222
  # compacted.
223
223
  #
224
- # ## Examples
224
+ # ### Examples
225
225
  #
226
226
  # If you pass in a sentence as a simple string, it may be word wrapped
227
227
  # when displayed:
@@ -252,7 +252,7 @@ module Toys
252
252
  # word-wrapped when displayed. To insert a blank line, include an empty
253
253
  # string as one of the descriptions.
254
254
  #
255
- # ## Example
255
+ # ### Example
256
256
  #
257
257
  # long_desc "This initial paragraph might get word wrapped.",
258
258
  # "This next paragraph is followed by a blank line.",
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toys
4
+ module DSL
5
+ ##
6
+ # Internal utility calls used by the DSL.
7
+ #
8
+ # @private
9
+ #
10
+ module Internal
11
+ class << self
12
+ ##
13
+ # Called by the Loader and InputFile to prepare a tool class for running
14
+ # the DSL.
15
+ #
16
+ # @private
17
+ #
18
+ def prepare(tool_class, words, priority, remaining_words, source, loader)
19
+ unless tool_class.is_a?(DSL::Tool)
20
+ class << tool_class
21
+ alias_method :super_include, :include
22
+ end
23
+ tool_class.extend(DSL::Tool)
24
+ end
25
+ unless tool_class.instance_variable_defined?(:@__words)
26
+ tool_class.instance_variable_set(:@__words, words)
27
+ tool_class.instance_variable_set(:@__priority, priority)
28
+ tool_class.instance_variable_set(:@__loader, loader)
29
+ tool_class.instance_variable_set(:@__source, [])
30
+ end
31
+ tool_class.instance_variable_set(:@__remaining_words, remaining_words)
32
+ tool_class.instance_variable_get(:@__source).push(source)
33
+ old_source = ::Thread.current[:__toys_current_source]
34
+ begin
35
+ ::Thread.current[:__toys_current_source] = source
36
+ yield
37
+ ensure
38
+ tool_class.instance_variable_get(:@__source).pop
39
+ ::Thread.current[:__toys_current_source] = old_source
40
+ end
41
+ end
42
+
43
+ ##
44
+ # Called by the DSL implementation to get, and optionally activate, the
45
+ # current tool.
46
+ #
47
+ # @private
48
+ #
49
+ def current_tool(tool_class, activate)
50
+ memoize_var = activate ? :@__active_tool : :@__cur_tool
51
+ if tool_class.instance_variable_defined?(memoize_var)
52
+ tool_class.instance_variable_get(memoize_var)
53
+ else
54
+ loader = tool_class.instance_variable_get(:@__loader)
55
+ words = tool_class.instance_variable_get(:@__words)
56
+ priority = tool_class.instance_variable_get(:@__priority)
57
+ cur_tool =
58
+ if activate
59
+ loader.activate_tool(words, priority)
60
+ else
61
+ loader.get_tool(words, priority)
62
+ end
63
+ if cur_tool && activate
64
+ source = tool_class.instance_variable_get(:@__source).last
65
+ cur_tool.lock_source(source)
66
+ end
67
+ tool_class.instance_variable_set(memoize_var, cur_tool)
68
+ end
69
+ end
70
+
71
+ ##
72
+ # Called by the DSL implementation to add a getter to the tool class.
73
+ #
74
+ # @private
75
+ #
76
+ def maybe_add_getter(tool_class, key)
77
+ if key.is_a?(::Symbol) && key.to_s =~ /^[_a-zA-Z]\w*[!?]?$/ && key != :run
78
+ unless tool_class.public_method_defined?(key)
79
+ tool_class.class_eval do
80
+ define_method(key) do
81
+ self[key]
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ ##
89
+ # Called by the DSL implementation to find a named mixin.
90
+ #
91
+ # @private
92
+ #
93
+ def resolve_mixin(mixin, cur_tool, loader)
94
+ mod =
95
+ case mixin
96
+ when ::String
97
+ cur_tool.lookup_mixin(mixin)
98
+ when ::Symbol
99
+ loader.resolve_standard_mixin(mixin.to_s)
100
+ when ::Module
101
+ mixin
102
+ end
103
+ raise ToolDefinitionError, "Mixin not found: #{mixin.inspect}" unless mod
104
+ mod
105
+ end
106
+
107
+ ##
108
+ # Called by the DSL implementation to load a long description from a
109
+ # file.
110
+ #
111
+ # @private
112
+ #
113
+ def load_long_desc_file(path)
114
+ if ::File.extname(path) == ".txt"
115
+ begin
116
+ ::File.readlines(path).map do |line|
117
+ line = line.chomp
118
+ line =~ /^\s/ ? [line] : line
119
+ end
120
+ rescue ::SystemCallError => e
121
+ raise Toys::ToolDefinitionError, e.to_s
122
+ end
123
+ else
124
+ raise Toys::ToolDefinitionError, "Cannot load long desc from file type: #{path}"
125
+ end
126
+ end
127
+
128
+ ##
129
+ # Called by the Tool base class to set config values for a subclass.
130
+ #
131
+ # @private
132
+ #
133
+ def configure_class(tool_class, given_name = nil)
134
+ return if tool_class.name.nil? || tool_class.instance_variable_defined?(:@__loader)
135
+
136
+ mod_names = tool_class.name.split("::")
137
+ class_name = mod_names.pop
138
+ parent = parent_from_mod_name_segments(mod_names)
139
+ loader = parent.instance_variable_get(:@__loader)
140
+ name = given_name ? loader.split_path(given_name) : class_name_to_tool_name(class_name)
141
+
142
+ priority = parent.instance_variable_get(:@__priority)
143
+ words = parent.instance_variable_get(:@__words) + name
144
+ subtool = loader.get_tool(words, priority, tool_class)
145
+
146
+ remaining_words = parent.instance_variable_get(:@__remaining_words)
147
+ next_remaining = name.reduce(remaining_words) do |running_words, word|
148
+ Loader.next_remaining_words(running_words, word)
149
+ end
150
+
151
+ tool_class.instance_variable_set(:@__words, words)
152
+ tool_class.instance_variable_set(:@__priority, priority)
153
+ tool_class.instance_variable_set(:@__loader, loader)
154
+ tool_class.instance_variable_set(:@__source, [current_source_from_context])
155
+ tool_class.instance_variable_set(:@__remaining_words, next_remaining)
156
+ tool_class.instance_variable_set(:@__cur_tool, subtool)
157
+ end
158
+
159
+ ##
160
+ # Called by the Tool base class to add the DSL to a subclass.
161
+ #
162
+ # @private
163
+ #
164
+ def setup_class_dsl(tool_class)
165
+ return if tool_class.name.nil? || tool_class.is_a?(DSL::Tool)
166
+ class << tool_class
167
+ alias_method :super_include, :include
168
+ end
169
+ tool_class.extend(DSL::Tool)
170
+ end
171
+
172
+ private
173
+
174
+ def class_name_to_tool_name(class_name)
175
+ name = class_name.to_s.sub(/^_+/, "").sub(/_+$/, "").gsub(/_+/, "-")
176
+ while name.sub!(/([^-])([A-Z])/, "\\1-\\2") do end
177
+ [name.downcase!]
178
+ end
179
+
180
+ def parent_from_mod_name_segments(mod_names)
181
+ parent = mod_names.reduce(::Object) do |running_mod, seg|
182
+ running_mod.const_get(seg)
183
+ end
184
+ if !parent.is_a?(::Toys::Tool) && parent.instance_variable_defined?(:@__tool_class)
185
+ parent = parent.instance_variable_get(:@__tool_class)
186
+ end
187
+ unless parent.ancestors.include?(::Toys::Context)
188
+ raise ToolDefinitionError, "Toys::Tool can be subclassed only from the Toys DSL"
189
+ end
190
+ parent
191
+ end
192
+
193
+ def current_source_from_context
194
+ source = ::Thread.current[:__toys_current_source]
195
+ if source.nil?
196
+ raise ToolDefinitionError, "Toys::Tool can be subclassed only from a Toys config file"
197
+ end
198
+ unless source.source_type == :file
199
+ raise ToolDefinitionError, "Toys::Tool cannot be subclassed inside a tool block"
200
+ end
201
+ source
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
@@ -10,7 +10,7 @@ module Toys
10
10
  # {Toys::DSL::Tool#required_arg}, {Toys::DSL::Tool#optional_arg}, or
11
11
  # {Toys::DSL::Tool#remaining_args}.
12
12
  #
13
- # ## Example
13
+ # ### Example
14
14
  #
15
15
  # tool "mytool" do
16
16
  # optional_arg :value do
@@ -103,7 +103,7 @@ module Toys
103
103
  # across the strings in the array. In this case, whitespace is not
104
104
  # compacted.
105
105
  #
106
- # ## Examples
106
+ # ### Examples
107
107
  #
108
108
  # If you pass in a sentence as a simple string, it may be word wrapped
109
109
  # when displayed:
@@ -134,7 +134,7 @@ module Toys
134
134
  # word-wrapped when displayed. To insert a blank line, include an empty
135
135
  # string as one of the descriptions.
136
136
  #
137
- # ## Example
137
+ # ### Example
138
138
  #
139
139
  # long_desc "This initial paragraph might get word wrapped.",
140
140
  # "This next paragraph is followed by a blank line.",
data/lib/toys/dsl/tool.rb CHANGED
@@ -10,7 +10,7 @@ module Toys
10
10
  # how to execute the tool, and requesting mixin modules and other services.
11
11
  # It also lets you define subtools, nested arbitrarily deep, using blocks.
12
12
  #
13
- # ## Simple example
13
+ # ### Simple example
14
14
  #
15
15
  # Create a file called `.toys.rb` in the current directory, with the
16
16
  # following contents:
@@ -36,7 +36,8 @@ module Toys
36
36
  module Tool
37
37
  ## @private
38
38
  def method_added(_meth)
39
- DSL::Tool.current_tool(self, true)&.check_definition_state(is_method: true)
39
+ super
40
+ DSL::Internal.current_tool(self, true)&.check_definition_state(is_method: true)
40
41
  end
41
42
 
42
43
  ##
@@ -83,7 +84,7 @@ module Toys
83
84
  # an exception (descended from `StandardError`) to indicate that the
84
85
  # string parameter is invalid.
85
86
  #
86
- # ## Example
87
+ # ### Example
87
88
  #
88
89
  # The following example creates an acceptor named "hex" that is defined
89
90
  # via a regular expression. It then uses it to validate values passed to
@@ -105,7 +106,7 @@ module Toys
105
106
  # @return [self]
106
107
  #
107
108
  def acceptor(name, spec = nil, type_desc: nil, &block)
108
- cur_tool = DSL::Tool.current_tool(self, false)
109
+ cur_tool = DSL::Internal.current_tool(self, false)
109
110
  cur_tool&.add_acceptor(name, spec, type_desc: type_desc || name.to_s, &block)
110
111
  self
111
112
  end
@@ -122,7 +123,7 @@ module Toys
122
123
  # block. Alternatively, you can create a module separately and pass it
123
124
  # directly to this directive.
124
125
  #
125
- # ## Example
126
+ # ### Example
126
127
  #
127
128
  # The following example creates a named mixin and uses it in a tool.
128
129
  #
@@ -149,7 +150,7 @@ module Toys
149
150
  # @return [self]
150
151
  #
151
152
  def mixin(name, mixin_module = nil, &block)
152
- cur_tool = DSL::Tool.current_tool(self, false)
153
+ cur_tool = DSL::Internal.current_tool(self, false)
153
154
  cur_tool&.add_mixin(name, mixin_module, &block)
154
155
  self
155
156
  end
@@ -175,7 +176,7 @@ module Toys
175
176
  # directly. See {Toys::Template} for details on creating a template
176
177
  # class.
177
178
  #
178
- # ## Example
179
+ # ### Example
179
180
  #
180
181
  # The following example creates and uses a simple template.
181
182
  #
@@ -204,7 +205,7 @@ module Toys
204
205
  # @return [self]
205
206
  #
206
207
  def template(name, template_class = nil, &block)
207
- cur_tool = DSL::Tool.current_tool(self, false)
208
+ cur_tool = DSL::Internal.current_tool(self, false)
208
209
  return self if cur_tool.nil?
209
210
  cur_tool.add_template(name, template_class, &block)
210
211
  self
@@ -228,7 +229,7 @@ module Toys
228
229
  # * The symbol `:file_system` which indicates that paths in the file
229
230
  # system should serve as completion candidates.
230
231
  #
231
- # ## Example
232
+ # ### Example
232
233
  #
233
234
  # The following example defines a completion that uses only the immediate
234
235
  # files in the current directory as candidates. (This is different from
@@ -252,7 +253,7 @@ module Toys
252
253
  # @return [self]
253
254
  #
254
255
  def completion(name, spec = nil, **options, &block)
255
- cur_tool = DSL::Tool.current_tool(self, false)
256
+ cur_tool = DSL::Internal.current_tool(self, false)
256
257
  return self if cur_tool.nil?
257
258
  cur_tool.add_completion(name, spec, **options, &block)
258
259
  self
@@ -261,7 +262,7 @@ module Toys
261
262
  ##
262
263
  # Create a subtool. You must provide a block defining the subtool.
263
264
  #
264
- # ## Example
265
+ # ### Example
265
266
  #
266
267
  # The following example defines a tool and two subtools within it.
267
268
  #
@@ -280,7 +281,7 @@ module Toys
280
281
  #
281
282
  # The following example defines a tool that runs one of its subtools.
282
283
  #
283
- # tool "test", runs: ["test", "unit"] do
284
+ # tool "test", delegate_to: ["test", "unit"] do
284
285
  # tool "unit" do
285
286
  # def run
286
287
  # puts "Running unit tests"
@@ -316,7 +317,7 @@ module Toys
316
317
  when :ignore
317
318
  return self
318
319
  when :reset
319
- subtool.reset_definition(@__loader)
320
+ subtool.reset_definition
320
321
  end
321
322
  end
322
323
  if delegate_to
@@ -328,7 +329,6 @@ module Toys
328
329
  end
329
330
  self
330
331
  end
331
- alias name tool
332
332
 
333
333
  ##
334
334
  # Create an alias, representing an "alternate name" for a tool.
@@ -337,7 +337,7 @@ module Toys
337
337
  # `delegate_to` option, except that `alias_tool` takes a _relative_ name
338
338
  # for the delegate.
339
339
  #
340
- # ## Example
340
+ # ### Example
341
341
  #
342
342
  # This example defines a tool and an alias pointing to it. Both the tool
343
343
  # name `test` and the alias `t` will then refer to the same tool.
@@ -364,7 +364,7 @@ module Toys
364
364
  # Causes the current tool to delegate to another tool. When run, it
365
365
  # simply invokes the target tool with the same arguments.
366
366
  #
367
- # ## Example
367
+ # ### Example
368
368
  #
369
369
  # This example defines a tool that runs one of its subtools. Running the
370
370
  # `test` tool will have the same effect (and recognize the same args) as
@@ -386,7 +386,7 @@ module Toys
386
386
  # @return [self]
387
387
  #
388
388
  def delegate_to(target)
389
- cur_tool = DSL::Tool.current_tool(self, true)
389
+ cur_tool = DSL::Internal.current_tool(self, true)
390
390
  return self if cur_tool.nil?
391
391
  cur_tool.delegate_to(@__loader.split_path(target))
392
392
  self
@@ -397,20 +397,64 @@ module Toys
397
397
  # at the current location.
398
398
  #
399
399
  # @param path [String] The file or directory to load.
400
+ # @param as [String] Load into the given tool/namespace. If omitted,
401
+ # configuration will be loaded into the current namespace.
402
+ #
400
403
  # @return [self]
401
404
  #
402
- def load(path)
405
+ def load(path, as: nil)
406
+ if as
407
+ tool(as) do
408
+ load(path)
409
+ end
410
+ return self
411
+ end
403
412
  @__loader.load_path(source_info, path, @__words, @__remaining_words, @__priority)
404
413
  self
405
414
  end
406
415
 
416
+ ##
417
+ # Load configuration from a public git repository, as if its contents
418
+ # were inserted at the current location.
419
+ #
420
+ # @param remote [String] The URL of the git repository. Defaults to the
421
+ # current repository if already loading from git.
422
+ # @param path [String] The path within the repo to the file or directory
423
+ # to load. Defaults to the root of the repo.
424
+ # @param commit [String] The commit branch, tag, or sha. Defaults to the
425
+ # current commit if already loading from git, or to `HEAD`.
426
+ # @param as [String] Load into the given tool/namespace. If omitted,
427
+ # configuration will be loaded into the current namespace.
428
+ # @param update [Boolean] Force-fetch from the remote (unless the commit
429
+ # is a SHA). This will ensure that symbolic commits, such as branch
430
+ # names, are up to date. Default is false.
431
+ #
432
+ # @return [self]
433
+ #
434
+ def load_git(remote: nil, path: nil, commit: nil, as: nil, update: false)
435
+ if as
436
+ tool(as) do
437
+ load_git(remote: remote, path: path, commit: commit)
438
+ end
439
+ return self
440
+ end
441
+ remote ||= source_info.git_remote
442
+ raise ToolDefinitionError, "Git remote not specified" unless remote
443
+ path ||= ""
444
+ commit ||= source_info.git_commit || "HEAD"
445
+ @__loader.load_git(source_info, remote, path, commit,
446
+ @__words, @__remaining_words, @__priority,
447
+ update: update)
448
+ self
449
+ end
450
+
407
451
  ##
408
452
  # Expand the given template in the current location.
409
453
  #
410
454
  # The template may be specified as a class or a well-known template name.
411
455
  # You may also provide arguments to pass to the template.
412
456
  #
413
- # ## Example
457
+ # ### Example
414
458
  #
415
459
  # The following example creates and uses a simple template.
416
460
  #
@@ -437,12 +481,13 @@ module Toys
437
481
  # @return [self]
438
482
  #
439
483
  def expand(template_class, *args, **kwargs)
440
- cur_tool = DSL::Tool.current_tool(self, false)
484
+ cur_tool = DSL::Internal.current_tool(self, false)
441
485
  return self if cur_tool.nil?
442
486
  name = template_class.to_s
443
- if template_class.is_a?(::String)
487
+ case template_class
488
+ when ::String
444
489
  template_class = cur_tool.lookup_template(template_class)
445
- elsif template_class.is_a?(::Symbol)
490
+ when ::Symbol
446
491
  template_class = @__loader.resolve_standard_template(name)
447
492
  end
448
493
  if template_class.nil?
@@ -472,7 +517,7 @@ module Toys
472
517
  # across the strings in the array. In this case, whitespace is not
473
518
  # compacted.
474
519
  #
475
- # ## Examples
520
+ # ### Examples
476
521
  #
477
522
  # If you pass in a sentence as a simple string, it may be word wrapped
478
523
  # when displayed:
@@ -488,7 +533,7 @@ module Toys
488
533
  # @return [self]
489
534
  #
490
535
  def desc(str)
491
- cur_tool = DSL::Tool.current_tool(self, true)
536
+ cur_tool = DSL::Internal.current_tool(self, true)
492
537
  return self if cur_tool.nil?
493
538
  cur_tool.desc = str
494
539
  self
@@ -506,7 +551,7 @@ module Toys
506
551
  # word-wrapped when displayed. To insert a blank line, include an empty
507
552
  # string as one of the descriptions.
508
553
  #
509
- # ## Example
554
+ # ### Example
510
555
  #
511
556
  # long_desc "This initial paragraph might get word wrapped.",
512
557
  # "This next paragraph is followed by a blank line.",
@@ -524,7 +569,7 @@ module Toys
524
569
  # @return [self]
525
570
  #
526
571
  def long_desc(*strs, file: nil, data: nil)
527
- cur_tool = DSL::Tool.current_tool(self, true)
572
+ cur_tool = DSL::Internal.current_tool(self, true)
528
573
  return self if cur_tool.nil?
529
574
  if file
530
575
  unless source_info.source_path
@@ -535,7 +580,7 @@ module Toys
535
580
  elsif data
536
581
  file = source_info.find_data(data, type: :file)
537
582
  end
538
- strs += DSL::Tool.load_long_desc_file(file) if file
583
+ strs += DSL::Internal.load_long_desc_file(file) if file
539
584
  cur_tool.append_long_desc(strs)
540
585
  self
541
586
  end
@@ -545,7 +590,7 @@ module Toys
545
590
  # belong to the group. The flags in the group are listed together in
546
591
  # help screens.
547
592
  #
548
- # ## Example
593
+ # ### Example
549
594
  #
550
595
  # The following example creates a flag group in which all flags are
551
596
  # optional.
@@ -562,11 +607,11 @@ module Toys
562
607
  # `:optional`, `:exactly_one`, `:at_most_one`, `:at_least_one`.
563
608
  # Default is `:optional`.
564
609
  # @param desc [String,Array<String>,Toys::WrappableString] Short
565
- # description for the group. See {Toys::Tool#desc=} for a description
566
- # of allowed formats. Defaults to `"Flags"`.
610
+ # description for the group. See {Toys::DSL::Tool#desc} for a
611
+ # description of allowed formats. Defaults to `"Flags"`.
567
612
  # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
568
613
  # Long description for the flag group. See
569
- # {Toys::Tool#long_desc=} for a description of allowed formats.
614
+ # {Toys::DSL::Tool#long_desc} for a description of allowed formats.
570
615
  # Defaults to the empty array.
571
616
  # @param name [String,Symbol,nil] The name of the group, or nil for no
572
617
  # name.
@@ -581,7 +626,7 @@ module Toys
581
626
  #
582
627
  def flag_group(type: :optional, desc: nil, long_desc: nil, name: nil,
583
628
  report_collisions: true, prepend: false, &block)
584
- cur_tool = DSL::Tool.current_tool(self, true)
629
+ cur_tool = DSL::Internal.current_tool(self, true)
585
630
  return self if cur_tool.nil?
586
631
  cur_tool.add_flag_group(type: type, desc: desc, long_desc: long_desc, name: name,
587
632
  report_collisions: report_collisions, prepend: prepend)
@@ -596,7 +641,7 @@ module Toys
596
641
  # defined in the block belong to the group. All flags in this group are
597
642
  # required.
598
643
  #
599
- # ## Example
644
+ # ### Example
600
645
  #
601
646
  # The following example creates a group of required flags.
602
647
  #
@@ -609,11 +654,11 @@ module Toys
609
654
  # end
610
655
  #
611
656
  # @param desc [String,Array<String>,Toys::WrappableString] Short
612
- # description for the group. See {Toys::Tool#desc=} for a description
613
- # of allowed formats. Defaults to `"Flags"`.
657
+ # description for the group. See {Toys::DSL::Tool#desc} for a
658
+ # description of allowed formats. Defaults to `"Flags"`.
614
659
  # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
615
660
  # Long description for the flag group. See
616
- # {Toys::Tool#long_desc=} for a description of allowed formats.
661
+ # {Toys::DSL::Tool#long_desc} for a description of allowed formats.
617
662
  # Defaults to the empty array.
618
663
  # @param name [String,Symbol,nil] The name of the group, or nil for no
619
664
  # name.
@@ -637,7 +682,7 @@ module Toys
637
682
  # defined in the block belong to the group. At most one flag in this
638
683
  # group must be provided on the command line.
639
684
  #
640
- # ## Example
685
+ # ### Example
641
686
  #
642
687
  # The following example creates a group of flags in which either one or
643
688
  # none may be set, but not more than one.
@@ -652,11 +697,11 @@ module Toys
652
697
  # end
653
698
  #
654
699
  # @param desc [String,Array<String>,Toys::WrappableString] Short
655
- # description for the group. See {Toys::Tool#desc=} for a description
656
- # of allowed formats. Defaults to `"Flags"`.
700
+ # description for the group. See {Toys::DSL::Tool#desc} for a
701
+ # description of allowed formats. Defaults to `"Flags"`.
657
702
  # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
658
703
  # Long description for the flag group. See
659
- # {Toys::Tool#long_desc=} for a description of allowed formats.
704
+ # {Toys::DSL::Tool#long_desc} for a description of allowed formats.
660
705
  # Defaults to the empty array.
661
706
  # @param name [String,Symbol,nil] The name of the group, or nil for no
662
707
  # name.
@@ -681,7 +726,7 @@ module Toys
681
726
  # defined in the block belong to the group. At least one flag in this
682
727
  # group must be provided on the command line.
683
728
  #
684
- # ## Example
729
+ # ### Example
685
730
  #
686
731
  # The following example creates a group of flags in which one or more
687
732
  # may be set.
@@ -696,11 +741,11 @@ module Toys
696
741
  # end
697
742
  #
698
743
  # @param desc [String,Array<String>,Toys::WrappableString] Short
699
- # description for the group. See {Toys::Tool#desc=} for a description
700
- # of allowed formats. Defaults to `"Flags"`.
744
+ # description for the group. See {Toys::DSL::Tool#desc} for a
745
+ # description of allowed formats. Defaults to `"Flags"`.
701
746
  # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
702
747
  # Long description for the flag group. See
703
- # {Toys::Tool#long_desc=} for a description of allowed formats.
748
+ # {Toys::DSL::Tool#long_desc} for a description of allowed formats.
704
749
  # Defaults to the empty array.
705
750
  # @param name [String,Symbol,nil] The name of the group, or nil for no
706
751
  # name.
@@ -725,7 +770,7 @@ module Toys
725
770
  # defined in the block belong to the group. Exactly one flag in this
726
771
  # group must be provided on the command line.
727
772
  #
728
- # ## Example
773
+ # ### Example
729
774
  #
730
775
  # The following example creates a group of flags in which exactly one
731
776
  # must be set.
@@ -740,11 +785,11 @@ module Toys
740
785
  # end
741
786
  #
742
787
  # @param desc [String,Array<String>,Toys::WrappableString] Short
743
- # description for the group. See {Toys::Tool#desc=} for a description
744
- # of allowed formats. Defaults to `"Flags"`.
788
+ # description for the group. See {Toys::DSL::Tool#desc} for a
789
+ # description of allowed formats. Defaults to `"Flags"`.
745
790
  # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
746
791
  # Long description for the flag group. See
747
- # {Toys::Tool#long_desc=} for a description of allowed formats.
792
+ # {Toys::DSL::Tool#long_desc} for a description of allowed formats.
748
793
  # Defaults to the empty array.
749
794
  # @param name [String,Symbol,nil] The name of the group, or nil for no
750
795
  # name.
@@ -778,7 +823,7 @@ module Toys
778
823
  # set in a block passed to this method. If you provide a block, you can
779
824
  # use directives in {Toys::DSL::Flag} within the block.
780
825
  #
781
- # ## Flag syntax
826
+ # ### Flag syntax
782
827
  #
783
828
  # The flags themselves should be provided in OptionParser form. Following
784
829
  # are examples of valid syntax.
@@ -833,7 +878,7 @@ module Toys
833
878
  # or off. This effectively creates two flags, `--abc` which sets the
834
879
  # value to `true`, and `--no-abc` which sets the falue to `false`.
835
880
  #
836
- # ## Default flag syntax
881
+ # ### Default flag syntax
837
882
  #
838
883
  # If no flag syntax strings are provided, a default syntax will be
839
884
  # inferred based on the key and other options.
@@ -858,7 +903,7 @@ module Toys
858
903
  # flag :number, accept: Integer
859
904
  # flag :number, "--number=VAL", accept: Integer
860
905
  #
861
- # ## More examples
906
+ # ### More examples
862
907
  #
863
908
  # A flag that sets its value to the number of times it appears on the
864
909
  # command line:
@@ -937,7 +982,7 @@ module Toys
937
982
  report_collisions: true, group: nil,
938
983
  desc: nil, long_desc: nil, display_name: nil,
939
984
  &block)
940
- cur_tool = DSL::Tool.current_tool(self, true)
985
+ cur_tool = DSL::Internal.current_tool(self, true)
941
986
  return self if cur_tool.nil?
942
987
  flag_dsl = DSL::Flag.new(
943
988
  flags.flatten, accept, default, handler, complete_flags, complete_values,
@@ -945,14 +990,14 @@ module Toys
945
990
  )
946
991
  flag_dsl.instance_exec(flag_dsl, &block) if block
947
992
  flag_dsl._add_to(cur_tool, key)
948
- DSL::Tool.maybe_add_getter(self, key)
993
+ DSL::Internal.maybe_add_getter(self, key)
949
994
  self
950
995
  end
951
996
 
952
997
  ##
953
- # Add a required positional argument to the current tool. You must specify
954
- # a key which the script may use to obtain the argument value from the
955
- # context.
998
+ # Add a required positional argument to the current tool. You must
999
+ # specify a key which the script may use to obtain the argument value
1000
+ # from the context.
956
1001
  #
957
1002
  # If the given key is a symbol representing a valid method name, then a
958
1003
  # helper method is automatically added to retrieve the value. Otherwise,
@@ -963,7 +1008,7 @@ module Toys
963
1008
  # set in a block passed to this method. If you provide a block, you can
964
1009
  # use directives in {Toys::DSL::PositionalArg} within the block.
965
1010
  #
966
- # ## Example
1011
+ # ### Example
967
1012
  #
968
1013
  # This tool "moves" something from a source to destination, and takes two
969
1014
  # required arguments:
@@ -987,8 +1032,8 @@ module Toys
987
1032
  # values of this arg. This is the empty completion by default. To
988
1033
  # customize completion, set this to the name of a previously defined
989
1034
  # completion, or any spec recognized by {Toys::Completion.create}.
990
- # @param display_name [String] A name to use for display (in help text and
991
- # error reports). Defaults to the key in upper case.
1035
+ # @param display_name [String] A name to use for display (in help text
1036
+ # and error reports). Defaults to the key in upper case.
992
1037
  # @param desc [String,Array<String>,Toys::WrappableString] Short
993
1038
  # description for the flag. See {Toys::DSL::Tool#desc} for a
994
1039
  # description of the allowed formats. Defaults to the empty string.
@@ -1006,21 +1051,21 @@ module Toys
1006
1051
  accept: nil, complete: nil, display_name: nil,
1007
1052
  desc: nil, long_desc: nil,
1008
1053
  &block)
1009
- cur_tool = DSL::Tool.current_tool(self, true)
1054
+ cur_tool = DSL::Internal.current_tool(self, true)
1010
1055
  return self if cur_tool.nil?
1011
1056
  arg_dsl = DSL::PositionalArg.new(accept, nil, complete, display_name, desc, long_desc)
1012
1057
  arg_dsl.instance_exec(arg_dsl, &block) if block
1013
1058
  arg_dsl._add_required_to(cur_tool, key)
1014
- DSL::Tool.maybe_add_getter(self, key)
1059
+ DSL::Internal.maybe_add_getter(self, key)
1015
1060
  self
1016
1061
  end
1017
1062
  alias required required_arg
1018
1063
 
1019
1064
  ##
1020
- # Add an optional positional argument to the current tool. You must specify
1021
- # a key which the script may use to obtain the argument value from the
1022
- # context. If an optional argument is not given on the command line, the
1023
- # value is set to the given default.
1065
+ # Add an optional positional argument to the current tool. You must
1066
+ # specify a key which the script may use to obtain the argument value
1067
+ # from the context. If an optional argument is not given on the command
1068
+ # line, the value is set to the given default.
1024
1069
  #
1025
1070
  # If the given key is a symbol representing a valid method name, then a
1026
1071
  # helper method is automatically added to retrieve the value. Otherwise,
@@ -1031,7 +1076,7 @@ module Toys
1031
1076
  # set in a block passed to this method. If you provide a block, you can
1032
1077
  # use directives in {Toys::DSL::PositionalArg} within the block.
1033
1078
  #
1034
- # ## Example
1079
+ # ### Example
1035
1080
  #
1036
1081
  # This tool creates a "link" to a given target. The link location is
1037
1082
  # optional; if it is not given, it is inferred from the target.
@@ -1048,8 +1093,8 @@ module Toys
1048
1093
  # @param key [String,Symbol] The key to use to retrieve the value from
1049
1094
  # the execution context.
1050
1095
  # @param default [Object] The default value. This is the value that will
1051
- # be set in the context if this argument is not provided on the command
1052
- # line. Defaults to `nil`.
1096
+ # be set in the context if this argument is not provided on the
1097
+ # command line. Defaults to `nil`.
1053
1098
  # @param accept [Object] An acceptor that validates and/or converts the
1054
1099
  # value. You may provide either the name of an acceptor you have
1055
1100
  # defined, one of the default acceptors provided by OptionParser, or
@@ -1059,8 +1104,8 @@ module Toys
1059
1104
  # values of this arg. This is the empty completion by default. To
1060
1105
  # customize completion, set this to the name of a previously defined
1061
1106
  # completion, or any spec recognized by {Toys::Completion.create}.
1062
- # @param display_name [String] A name to use for display (in help text and
1063
- # error reports). Defaults to the key in upper case.
1107
+ # @param display_name [String] A name to use for display (in help text
1108
+ # and error reports). Defaults to the key in upper case.
1064
1109
  # @param desc [String,Array<String>,Toys::WrappableString] Short
1065
1110
  # description for the flag. See {Toys::DSL::Tool#desc} for a
1066
1111
  # description of the allowed formats. Defaults to the empty string.
@@ -1078,20 +1123,20 @@ module Toys
1078
1123
  default: nil, accept: nil, complete: nil, display_name: nil,
1079
1124
  desc: nil, long_desc: nil,
1080
1125
  &block)
1081
- cur_tool = DSL::Tool.current_tool(self, true)
1126
+ cur_tool = DSL::Internal.current_tool(self, true)
1082
1127
  return self if cur_tool.nil?
1083
1128
  arg_dsl = DSL::PositionalArg.new(accept, default, complete, display_name, desc, long_desc)
1084
1129
  arg_dsl.instance_exec(arg_dsl, &block) if block
1085
1130
  arg_dsl._add_optional_to(cur_tool, key)
1086
- DSL::Tool.maybe_add_getter(self, key)
1131
+ DSL::Internal.maybe_add_getter(self, key)
1087
1132
  self
1088
1133
  end
1089
1134
  alias optional optional_arg
1090
1135
 
1091
1136
  ##
1092
- # Specify what should be done with unmatched positional arguments. You must
1093
- # specify a key which the script may use to obtain the remaining args from
1094
- # the context.
1137
+ # Specify what should be done with unmatched positional arguments. You
1138
+ # must specify a key which the script may use to obtain the remaining
1139
+ # args from the context.
1095
1140
  #
1096
1141
  # If the given key is a symbol representing a valid method name, then a
1097
1142
  # helper method is automatically added to retrieve the value. Otherwise,
@@ -1102,7 +1147,7 @@ module Toys
1102
1147
  # set in a block passed to this method. If you provide a block, you can
1103
1148
  # use directives in {Toys::DSL::PositionalArg} within the block.
1104
1149
  #
1105
- # ## Example
1150
+ # ### Example
1106
1151
  #
1107
1152
  # This tool displays a "list" of the given directories. If no directories
1108
1153
  # ar given, lists the current directory.
@@ -1131,8 +1176,8 @@ module Toys
1131
1176
  # values of this arg. This is the empty completion by default. To
1132
1177
  # customize completion, set this to the name of a previously defined
1133
1178
  # completion, or any spec recognized by {Toys::Completion.create}.
1134
- # @param display_name [String] A name to use for display (in help text and
1135
- # error reports). Defaults to the key in upper case.
1179
+ # @param display_name [String] A name to use for display (in help text
1180
+ # and error reports). Defaults to the key in upper case.
1136
1181
  # @param desc [String,Array<String>,Toys::WrappableString] Short
1137
1182
  # description for the flag. See {Toys::DSL::Tool#desc} for a
1138
1183
  # description of the allowed formats. Defaults to the empty string.
@@ -1150,12 +1195,12 @@ module Toys
1150
1195
  default: [], accept: nil, complete: nil, display_name: nil,
1151
1196
  desc: nil, long_desc: nil,
1152
1197
  &block)
1153
- cur_tool = DSL::Tool.current_tool(self, true)
1198
+ cur_tool = DSL::Internal.current_tool(self, true)
1154
1199
  return self if cur_tool.nil?
1155
1200
  arg_dsl = DSL::PositionalArg.new(accept, default, complete, display_name, desc, long_desc)
1156
1201
  arg_dsl.instance_exec(arg_dsl, &block) if block
1157
1202
  arg_dsl._set_remaining_on(cur_tool, key)
1158
- DSL::Tool.maybe_add_getter(self, key)
1203
+ DSL::Internal.maybe_add_getter(self, key)
1159
1204
  self
1160
1205
  end
1161
1206
  alias remaining remaining_args
@@ -1168,7 +1213,7 @@ module Toys
1168
1213
  # if the key is a string or does not represent a valid method name, the
1169
1214
  # tool can retrieve the value by calling {Toys::Context#get}.
1170
1215
  #
1171
- # ## Example
1216
+ # ### Example
1172
1217
  #
1173
1218
  # tool "hello" do
1174
1219
  # static :greeting, "Hi there"
@@ -1190,16 +1235,16 @@ module Toys
1190
1235
  # @return [self]
1191
1236
  #
1192
1237
  def static(key, value = nil)
1193
- cur_tool = DSL::Tool.current_tool(self, true)
1238
+ cur_tool = DSL::Internal.current_tool(self, true)
1194
1239
  return self if cur_tool.nil?
1195
1240
  if key.is_a?(::Hash)
1196
1241
  cur_tool.default_data.merge!(key)
1197
1242
  key.each_key do |k|
1198
- DSL::Tool.maybe_add_getter(self, k)
1243
+ DSL::Internal.maybe_add_getter(self, k)
1199
1244
  end
1200
1245
  else
1201
1246
  cur_tool.default_data[key] = value
1202
- DSL::Tool.maybe_add_getter(self, key)
1247
+ DSL::Internal.maybe_add_getter(self, key)
1203
1248
  end
1204
1249
  self
1205
1250
  end
@@ -1207,7 +1252,7 @@ module Toys
1207
1252
  ##
1208
1253
  # Set a option values statically without creating helper methods.
1209
1254
  #
1210
- # ## Example
1255
+ # ### Example
1211
1256
  #
1212
1257
  # tool "hello" do
1213
1258
  # set :greeting, "Hi there"
@@ -1229,7 +1274,7 @@ module Toys
1229
1274
  # @return [self]
1230
1275
  #
1231
1276
  def set(key, value = nil)
1232
- cur_tool = DSL::Tool.current_tool(self, true)
1277
+ cur_tool = DSL::Internal.current_tool(self, true)
1233
1278
  return self if cur_tool.nil?
1234
1279
  if key.is_a?(::Hash)
1235
1280
  cur_tool.default_data.merge!(key)
@@ -1251,7 +1296,7 @@ module Toys
1251
1296
  # @return [self]
1252
1297
  #
1253
1298
  def enforce_flags_before_args(state = true)
1254
- DSL::Tool.current_tool(self, true)&.enforce_flags_before_args(state)
1299
+ DSL::Internal.current_tool(self, true)&.enforce_flags_before_args(state)
1255
1300
  self
1256
1301
  end
1257
1302
 
@@ -1267,7 +1312,7 @@ module Toys
1267
1312
  # @return [self]
1268
1313
  #
1269
1314
  def require_exact_flag_match(state = true)
1270
- DSL::Tool.current_tool(self, true)&.require_exact_flag_match(state)
1315
+ DSL::Internal.current_tool(self, true)&.require_exact_flag_match(state)
1271
1316
  self
1272
1317
  end
1273
1318
 
@@ -1282,7 +1327,7 @@ module Toys
1282
1327
  # @return [self]
1283
1328
  #
1284
1329
  def disable_argument_parsing
1285
- DSL::Tool.current_tool(self, true)&.disable_argument_parsing
1330
+ DSL::Internal.current_tool(self, true)&.disable_argument_parsing
1286
1331
  self
1287
1332
  end
1288
1333
 
@@ -1291,7 +1336,7 @@ module Toys
1291
1336
  # subsequent flag definition. This can be used to prevent middleware from
1292
1337
  # defining a particular flag.
1293
1338
  #
1294
- # ## Example
1339
+ # ### Example
1295
1340
  #
1296
1341
  # This tool does not support the `-v` and `-q` short forms for the two
1297
1342
  # verbosity flags (although it still supports the long forms `--verbose`
@@ -1308,7 +1353,7 @@ module Toys
1308
1353
  # @return [self]
1309
1354
  #
1310
1355
  def disable_flag(*flags)
1311
- DSL::Tool.current_tool(self, true)&.disable_flag(*flags)
1356
+ DSL::Internal.current_tool(self, true)&.disable_flag(*flags)
1312
1357
  self
1313
1358
  end
1314
1359
 
@@ -1319,12 +1364,13 @@ module Toys
1319
1364
  # * The string name of a completion defined in this tool or any of its
1320
1365
  # its ancestors.
1321
1366
  # * A hash of options to pass to the constructor of
1322
- # {Toys::Tool::DefaultCompletion}.
1367
+ # {Toys::ToolDefinition::DefaultCompletion}.
1323
1368
  # * `nil` or `:default` to select the standard completion strategy
1324
- # (which is {Toys::Tool::DefaultCompletion} with no extra options).
1369
+ # (which is {Toys::ToolDefinition::DefaultCompletion} with no extra
1370
+ # options).
1325
1371
  # * Any other specification recognized by {Toys::Completion.create}.
1326
1372
  #
1327
- # ## Example
1373
+ # ### Example
1328
1374
  #
1329
1375
  # The namespace "foo" supports completion only of subtool names. It does
1330
1376
  # not complete the standard flags (like --help).
@@ -1345,7 +1391,7 @@ module Toys
1345
1391
  # @return [self]
1346
1392
  #
1347
1393
  def complete_tool_args(spec = nil, **options, &block)
1348
- cur_tool = DSL::Tool.current_tool(self, true)
1394
+ cur_tool = DSL::Internal.current_tool(self, true)
1349
1395
  return self if cur_tool.nil?
1350
1396
  cur_tool.completion = Completion.scalarize_spec(spec, options, block)
1351
1397
  self
@@ -1360,7 +1406,7 @@ module Toys
1360
1406
  # in the lexical scope. However, it is often more convenient to use
1361
1407
  # {#static} to set the value in the context.)
1362
1408
  #
1363
- # ## Example
1409
+ # ### Example
1364
1410
  #
1365
1411
  # tool "foo" do
1366
1412
  # cur_time = Time.new
@@ -1373,7 +1419,9 @@ module Toys
1373
1419
  # @return [self]
1374
1420
  #
1375
1421
  def to_run(&block)
1376
- define_method(:run, &block)
1422
+ cur_tool = DSL::Internal.current_tool(self, true)
1423
+ return self if cur_tool.nil?
1424
+ cur_tool.run_handler = block
1377
1425
  self
1378
1426
  end
1379
1427
  alias on_run to_run
@@ -1385,7 +1433,7 @@ module Toys
1385
1433
  # either case, the block or method should take one argument, the
1386
1434
  # Interrupt exception that was raised.
1387
1435
  #
1388
- # ## Example
1436
+ # ### Example
1389
1437
  #
1390
1438
  # tool "foo" do
1391
1439
  # def run
@@ -1402,7 +1450,7 @@ module Toys
1402
1450
  # @return [self]
1403
1451
  #
1404
1452
  def on_interrupt(handler = nil, &block)
1405
- cur_tool = DSL::Tool.current_tool(self, true)
1453
+ cur_tool = DSL::Internal.current_tool(self, true)
1406
1454
  return self if cur_tool.nil?
1407
1455
  cur_tool.interrupt_handler = handler || block
1408
1456
  self
@@ -1415,7 +1463,7 @@ module Toys
1415
1463
  # either case, the block or method should take one argument, the array of
1416
1464
  # usage errors reported.
1417
1465
  #
1418
- # ## Example
1466
+ # ### Example
1419
1467
  #
1420
1468
  # This tool runs even if a usage error is encountered. You can find info
1421
1469
  # on the errors from {Toys::Context::Key::USAGE_ERRORS},
@@ -1434,7 +1482,7 @@ module Toys
1434
1482
  # @return [self]
1435
1483
  #
1436
1484
  def on_usage_error(handler = nil, &block)
1437
- cur_tool = DSL::Tool.current_tool(self, true)
1485
+ cur_tool = DSL::Internal.current_tool(self, true)
1438
1486
  return self if cur_tool.nil?
1439
1487
  cur_tool.usage_error_handler = handler || block
1440
1488
  self
@@ -1448,7 +1496,7 @@ module Toys
1448
1496
  # have defined in this tool or one of its ancestors, or the symbol name
1449
1497
  # of a well-known mixin.
1450
1498
  #
1451
- # ## Example
1499
+ # ### Example
1452
1500
  #
1453
1501
  # Include the well-known mixin `:terminal` and perform some terminal
1454
1502
  # magic.
@@ -1463,28 +1511,16 @@ module Toys
1463
1511
  # end
1464
1512
  # end
1465
1513
  #
1466
- # @param mod [Module,Symbol,String] Module or module name.
1514
+ # @param mixin [Module,Symbol,String] Module or module name.
1467
1515
  # @param args [Object...] Arguments to pass to the initializer
1468
1516
  # @param kwargs [keywords] Keyword arguments to pass to the initializer
1469
1517
  # @return [self]
1470
1518
  #
1471
- def include(mod, *args, **kwargs)
1472
- cur_tool = DSL::Tool.current_tool(self, true)
1519
+ def include(mixin, *args, **kwargs)
1520
+ cur_tool = DSL::Internal.current_tool(self, true)
1473
1521
  return self if cur_tool.nil?
1474
- mod = DSL::Tool.resolve_mixin(mod, cur_tool, @__loader)
1475
- if included_modules.include?(mod)
1476
- raise ToolDefinitionError, "Mixin already included: #{mod.name}"
1477
- end
1478
- cur_tool.mark_includes_modules
1479
- super(mod)
1480
- if mod.respond_to?(:initializer)
1481
- callback = mod.initializer
1482
- cur_tool.add_initializer(callback, *args, **kwargs) if callback
1483
- end
1484
- if mod.respond_to?(:inclusion)
1485
- callback = mod.inclusion
1486
- class_exec(*args, **kwargs, &callback) if callback
1487
- end
1522
+ mod = DSL::Internal.resolve_mixin(mixin, cur_tool, @__loader)
1523
+ cur_tool.include_mixin(mod, *args, **kwargs)
1488
1524
  self
1489
1525
  end
1490
1526
 
@@ -1501,9 +1537,9 @@ module Toys
1501
1537
  # @return [nil] if the current tool is not active.
1502
1538
  #
1503
1539
  def include?(mod)
1504
- cur_tool = DSL::Tool.current_tool(self, false)
1540
+ cur_tool = DSL::Internal.current_tool(self, false)
1505
1541
  return if cur_tool.nil?
1506
- super(DSL::Tool.resolve_mixin(mod, cur_tool, @__loader))
1542
+ super(DSL::Internal.resolve_mixin(mod, cur_tool, @__loader))
1507
1543
  end
1508
1544
 
1509
1545
  ##
@@ -1523,7 +1559,7 @@ module Toys
1523
1559
  # in a directory called `.data` inside a Toys directory. This directive
1524
1560
  # locates a data file during tool definition.
1525
1561
  #
1526
- # ## Example
1562
+ # ### Example
1527
1563
  #
1528
1564
  # This tool reads its description from a text file in the `.data`
1529
1565
  # directory.
@@ -1557,17 +1593,17 @@ module Toys
1557
1593
  # @return [nil] if there is no context.
1558
1594
  #
1559
1595
  def context_directory
1560
- DSL::Tool.current_tool(self, false)&.context_directory || source_info.context_directory
1596
+ DSL::Internal.current_tool(self, false)&.context_directory || source_info.context_directory
1561
1597
  end
1562
1598
 
1563
1599
  ##
1564
- # Return the current tool object. This object can be queried to determine
1600
+ # Return the current tool config. This object can be queried to determine
1565
1601
  # such information as the name, but it should not be altered.
1566
1602
  #
1567
- # @return [Toys::Tool]
1603
+ # @return [Toys::ToolDefinition]
1568
1604
  #
1569
1605
  def current_tool
1570
- DSL::Tool.current_tool(self, false)
1606
+ DSL::Internal.current_tool(self, false)
1571
1607
  end
1572
1608
 
1573
1609
  ##
@@ -1577,7 +1613,7 @@ module Toys
1577
1613
  # @return [self]
1578
1614
  #
1579
1615
  def set_context_directory(dir) # rubocop:disable Naming/AccessorMethodName
1580
- cur_tool = DSL::Tool.current_tool(self, false)
1616
+ cur_tool = DSL::Internal.current_tool(self, false)
1581
1617
  return self if cur_tool.nil?
1582
1618
  cur_tool.custom_context_directory = dir
1583
1619
  self
@@ -1591,7 +1627,7 @@ module Toys
1591
1627
  # The block is applied only to subtools defined *after* the block
1592
1628
  # appears. Subtools defined before the block appears are not affected.
1593
1629
  #
1594
- # ## Example
1630
+ # ### Example
1595
1631
  #
1596
1632
  # It is common for tools to use the `:exec` mixin to invoke external
1597
1633
  # programs. This example automatically includes the exec mixin in all
@@ -1616,7 +1652,7 @@ module Toys
1616
1652
  # end
1617
1653
  #
1618
1654
  def subtool_apply(&block)
1619
- cur_tool = DSL::Tool.current_tool(self, false)
1655
+ cur_tool = DSL::Internal.current_tool(self, false)
1620
1656
  return self if cur_tool.nil?
1621
1657
  cur_tool.subtool_middleware_stack.add(:apply_config,
1622
1658
  parent_source: source_info, &block)
@@ -1640,6 +1676,15 @@ module Toys
1640
1676
  end
1641
1677
  end
1642
1678
 
1679
+ ##
1680
+ # Get the settings for this tool.
1681
+ #
1682
+ # @return [Toys::ToolDefinition::Settings] Tool-specific settings.
1683
+ #
1684
+ def settings
1685
+ DSL::Internal.current_tool(self, false)&.settings
1686
+ end
1687
+
1643
1688
  ##
1644
1689
  # Determines whether the current Toys version satisfies the given
1645
1690
  # requirements.
@@ -1672,93 +1717,6 @@ module Toys
1672
1717
  end
1673
1718
  self
1674
1719
  end
1675
-
1676
- ## @private
1677
- def self.new_class(words, priority, loader)
1678
- tool_class = ::Class.new(::Toys::Context)
1679
- tool_class.extend(DSL::Tool)
1680
- tool_class.instance_variable_set(:@__words, words)
1681
- tool_class.instance_variable_set(:@__priority, priority)
1682
- tool_class.instance_variable_set(:@__loader, loader)
1683
- tool_class.instance_variable_set(:@__remaining_words, nil)
1684
- tool_class.instance_variable_set(:@__source, [])
1685
- tool_class
1686
- end
1687
-
1688
- ## @private
1689
- def self.current_tool(tool_class, activate)
1690
- memoize_var = activate ? :@__active_tool : :@__cur_tool
1691
- if tool_class.instance_variable_defined?(memoize_var)
1692
- tool_class.instance_variable_get(memoize_var)
1693
- else
1694
- loader = tool_class.instance_variable_get(:@__loader)
1695
- words = tool_class.instance_variable_get(:@__words)
1696
- priority = tool_class.instance_variable_get(:@__priority)
1697
- cur_tool =
1698
- if activate
1699
- loader.activate_tool(words, priority)
1700
- else
1701
- loader.get_tool(words, priority)
1702
- end
1703
- if cur_tool && activate
1704
- source = tool_class.instance_variable_get(:@__source).last
1705
- cur_tool.lock_source(source)
1706
- end
1707
- tool_class.instance_variable_set(memoize_var, cur_tool)
1708
- end
1709
- end
1710
-
1711
- ## @private
1712
- def self.prepare(tool_class, remaining_words, source)
1713
- tool_class.instance_variable_set(:@__remaining_words, remaining_words)
1714
- tool_class.instance_variable_get(:@__source).push(source)
1715
- yield
1716
- ensure
1717
- tool_class.instance_variable_get(:@__source).pop
1718
- end
1719
-
1720
- ## @private
1721
- def self.maybe_add_getter(tool_class, key)
1722
- if key.is_a?(::Symbol) && key.to_s =~ /^[_a-zA-Z]\w*[!\?]?$/ && key != :run
1723
- unless tool_class.public_method_defined?(key)
1724
- tool_class.class_eval do
1725
- define_method(key) do
1726
- self[key]
1727
- end
1728
- end
1729
- end
1730
- end
1731
- end
1732
-
1733
- ## @private
1734
- def self.resolve_mixin(mod, cur_tool, loader)
1735
- name = mod.to_s
1736
- if mod.is_a?(::String)
1737
- mod = cur_tool.lookup_mixin(mod)
1738
- elsif mod.is_a?(::Symbol)
1739
- mod = loader.resolve_standard_mixin(name)
1740
- end
1741
- unless mod.is_a?(::Module)
1742
- raise ToolDefinitionError, "Module not found: #{name.inspect}"
1743
- end
1744
- mod
1745
- end
1746
-
1747
- ## @private
1748
- def self.load_long_desc_file(path)
1749
- if ::File.extname(path) == ".txt"
1750
- begin
1751
- ::File.readlines(path).map do |line|
1752
- line = line.chomp
1753
- line =~ /^\s/ ? [line] : line
1754
- end
1755
- rescue ::SystemCallError => e
1756
- raise Toys::ToolDefinitionError, e.to_s
1757
- end
1758
- else
1759
- raise Toys::ToolDefinitionError, "Cannot load long desc from file type: #{path}"
1760
- end
1761
- end
1762
1720
  end
1763
1721
  end
1764
1722
  end