toys-core 0.9.2 → 0.10.2

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -1
  3. data/CHANGELOG.md +47 -0
  4. data/LICENSE.md +1 -1
  5. data/README.md +3 -3
  6. data/lib/toys-core.rb +14 -21
  7. data/lib/toys/acceptor.rb +0 -21
  8. data/lib/toys/arg_parser.rb +1 -22
  9. data/lib/toys/cli.rb +102 -70
  10. data/lib/toys/compat.rb +49 -41
  11. data/lib/toys/completion.rb +0 -21
  12. data/lib/toys/context.rb +0 -23
  13. data/lib/toys/core.rb +1 -22
  14. data/lib/toys/dsl/flag.rb +0 -21
  15. data/lib/toys/dsl/flag_group.rb +0 -21
  16. data/lib/toys/dsl/positional_arg.rb +0 -21
  17. data/lib/toys/dsl/tool.rb +136 -51
  18. data/lib/toys/errors.rb +1 -22
  19. data/lib/toys/flag.rb +0 -21
  20. data/lib/toys/flag_group.rb +0 -21
  21. data/lib/toys/input_file.rb +0 -21
  22. data/lib/toys/loader.rb +42 -78
  23. data/lib/toys/middleware.rb +146 -77
  24. data/lib/toys/mixin.rb +0 -21
  25. data/lib/toys/module_lookup.rb +3 -26
  26. data/lib/toys/positional_arg.rb +0 -21
  27. data/lib/toys/source_info.rb +49 -38
  28. data/lib/toys/standard_middleware/add_verbosity_flags.rb +0 -23
  29. data/lib/toys/standard_middleware/apply_config.rb +42 -0
  30. data/lib/toys/standard_middleware/handle_usage_errors.rb +7 -28
  31. data/lib/toys/standard_middleware/set_default_descriptions.rb +0 -23
  32. data/lib/toys/standard_middleware/show_help.rb +0 -23
  33. data/lib/toys/standard_middleware/show_root_version.rb +0 -23
  34. data/lib/toys/standard_mixins/bundler.rb +89 -0
  35. data/lib/toys/standard_mixins/exec.rb +478 -128
  36. data/lib/toys/standard_mixins/fileutils.rb +0 -21
  37. data/lib/toys/standard_mixins/gems.rb +2 -24
  38. data/lib/toys/standard_mixins/highline.rb +0 -21
  39. data/lib/toys/standard_mixins/terminal.rb +0 -21
  40. data/lib/toys/template.rb +0 -21
  41. data/lib/toys/tool.rb +22 -34
  42. data/lib/toys/utils/completion_engine.rb +0 -21
  43. data/lib/toys/utils/exec.rb +142 -71
  44. data/lib/toys/utils/gems.rb +181 -63
  45. data/lib/toys/utils/help_text.rb +0 -21
  46. data/lib/toys/utils/terminal.rb +46 -37
  47. data/lib/toys/wrappable_string.rb +0 -21
  48. metadata +25 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e7a8292d7e4fb6fc31e4f308159f50050c455055a5333d89cebe448f8a2d472
4
- data.tar.gz: 9e96d5f69ca1d072e64f0e2b012479ed1602b73ec94315a13dd6902f9ac5c189
3
+ metadata.gz: c5ae179649a0653db765b024bf25809d9e2cac6eb97aa3f1058be9ac59e9837c
4
+ data.tar.gz: eabbdaf0d81b86ab64674fbda0728ca361d66e9d69cfcd3ce76670f44b828d9b
5
5
  SHA512:
6
- metadata.gz: d8bd19a911524c3a776c947e8dfed37466c877a03f475f472c1082b7287f446e9881cbe666bee87663fcc9d966e76a6426ef9db072c07d47946d5a0a9cd2f7ce
7
- data.tar.gz: 909d606e1bb8864264ee25e175b9422b5754071788bd2dcb590d617dd00aa7041f30a788380717a18d3242230939feff69e7a167d5b45096e22d29d6ae613f2f
6
+ metadata.gz: e1312418f9bd568f880580faed87e55100800def6bb34cf159e26e342949fefceec6a73584af62569775a29921d923618a8cbc05f113d1797103c0133c1b7b2d
7
+ data.tar.gz: 335c0424407723fc242d9ea3ee0153bc9213b1b86e36a347106a96262aeb423c2aaa91cc71dcce72b4179eba5c40df1da112c87982c1543af337276db85bfb86
data/.yardopts CHANGED
@@ -3,7 +3,8 @@
3
3
  --markup=markdown
4
4
  --markup-provider redcarpet
5
5
  --main=README.md
6
- ./lib/**/*.rb
6
+ ./lib/toys/**/*.rb
7
+ ./lib/toys-core.rb
7
8
  -
8
9
  README.md
9
10
  LICENSE.md
@@ -1,5 +1,52 @@
1
1
  # Release History
2
2
 
3
+ ### 0.10.2 / 2020-07-03
4
+
5
+ * FIXED: The load path no longer loses the toys and toys-core directories after a bundle install.
6
+
7
+ ### 0.10.1 / 2020-03-07
8
+
9
+ * FIXED: Setting `:exit_on_nonzero_status` explicitly to false now works as expected.
10
+
11
+ ### 0.10.0 / 2020-02-24
12
+
13
+ Functional changes:
14
+
15
+ * ADDED: `:bundler` mixin that installs and sets up a bundle for the tool
16
+ * ADDED: `bundle` method to `Toys::Utils::Gems` that performs bundler install and setup
17
+ * ADDED: `subtool_apply` directive which applies a block to all subtools.
18
+ * ADDED: Add `.lib` directories to the Ruby load path when executing a tool.
19
+ * ADDED: `toys_version?` and `toys_version!` directives that check against version requirements.
20
+ * ADDED: `exec_separate_tool` and `capture_separate_tool` methods in the `:exec` mixin, to support executing tools in a separate process without forking
21
+ * IMPROVED: `long_desc` directive can now read the description from a text file.
22
+ * IMPROVED: The `tool` directive can take delimited strings as tool names.
23
+ * IMPROVED: Subtool blocks aren't actually executed unless the tool is needed.
24
+ * CHANGED: Added `on_missing` and `on_conflict` arguments to `Toys::Utils::Gems` constructor (which also affects the `:gems` mixin), and deprecated `suppress_confirm` and `default_confirm`.
25
+
26
+ Internal interface changes:
27
+
28
+ * ADDED: `Toys::Tool#subtool_middleware_stack` allowing a tool to modify the middleware stack for its subtools.
29
+ * ADDED: The `Toys::Middleware::Stack` class represents a stack of middleware specs, and distinguishes the default set from those added afterward.
30
+ * ADDED: `Toys.executable_path` attribute allowing an executable to provide the executable for running tools separately.
31
+ * ADDED: `Toys::CLI` now has a `logger_factory` property, to generate separate loggers per tool execution.
32
+ * ADDED: `Toys::CLI` and `Toys::Loader` now let you set `:lib_dir_name`.
33
+ * IMPROVED: Toys-core no longer has a general dependency on rubygems. (Parts that do depend on rubygems, such as the `:gems` mixin, do an explicit `require "rubygems"`.) This makes it possible to write an executable with `ruby --disable=gems` which improves startup time.
34
+ * IMPROVED: Middleware objects no longer have to respond to all middleware methods. If a method is not implemented, it is simply considered a nop.
35
+ * IMPROVED: `Toys::Utils::Terminal` is now thread-safe.
36
+ * CHANGED: `Toys::Utils::Terminal#styled` is no longer mutable.
37
+ * CHANGED: `Toys::Tool#middleware_stack` renamed to `Toys::Tool#built_middleware` to clarify that it is an array of middleware objects rather than specs.
38
+ * CHANGED: `Toys::CLI.default_logger` removed and replaced with `Toys::CLI.default_logger_factory`. In general, global loggers for CLI are now discouraged because they are not thread-safe.
39
+ * CHANGED: `Toys::Loader` uses an internal monitor rather than including `MonitorMixin`.
40
+
41
+ ### 0.9.4 / 2020-01-26
42
+
43
+ * FIXED: Crash in the loader when a non-ruby file appears in a toys directory
44
+
45
+ ### 0.9.3 / 2020-01-05
46
+
47
+ * FIXED: `delegate_to` directive could crash if an overriding tool has already been defined.
48
+ * FIXED: A Ruby 2.7 warning when reporting a Toys file syntax error.
49
+
3
50
  ### 0.9.2 / 2020-01-03
4
51
 
5
52
  * IMPROVED: Mixins can now take real keyword arguments, and will pass them on properly to `on_initialize` and `on_include` blocks.
data/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # License
2
2
 
3
- Copyright 2019 Daniel Azuma
3
+ Copyright 2019-2020 Daniel Azuma and the Toys contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -74,7 +74,7 @@ use to write Toys files. You could point your executable at a directory
74
74
  containing actual Toys files, but the simplest option is to provide the
75
75
  information to the Toys CLI object in a block.
76
76
 
77
- Let's add some functionality.
77
+ Let's add some functionality.
78
78
 
79
79
  #!/usr/bin/env ruby
80
80
 
@@ -150,7 +150,7 @@ available tools.
150
150
  $ ./mycmd
151
151
 
152
152
  Notice that the description set at the "root" of the config block (outside the
153
- tool blocks) shows up here.
153
+ tool blocks) shows up here.
154
154
 
155
155
  ### Configuring the CLI
156
156
 
@@ -338,7 +338,7 @@ templates, and middleware, in the
338
338
 
339
339
  ## License
340
340
 
341
- Copyright 2019 Daniel Azuma
341
+ Copyright 2019-2020 Daniel Azuma and the Toys contributors
342
342
 
343
343
  Permission is hereby granted, free of charge, to any person obtaining a copy
344
344
  of this software and associated documentation files (the "Software"), to deal
@@ -1,26 +1,5 @@
1
1
  # frozen_string_literal: true
2
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
3
  ##
25
4
  # Toys is a configurable command line tool. Write commands in config files
26
5
  # using a simple DSL, and Toys will provide the command line executable and
@@ -68,6 +47,20 @@ module Toys
68
47
  # `require "toys/utils/exec"`.
69
48
  #
70
49
  module Utils; end
50
+
51
+ class << self
52
+ ##
53
+ # Path to the executable. This can, for example, be invoked to run a subtool
54
+ # in a clean environment.
55
+ #
56
+ # @return [String] if there is an executable
57
+ # @return [nil] if there is no such executable
58
+ #
59
+ attr_accessor :executable_path
60
+ end
61
+
62
+ # @private
63
+ CORE_LIB_PATH = __dir__
71
64
  end
72
65
 
73
66
  require "toys/acceptor"
@@ -1,26 +1,5 @@
1
1
  # frozen_string_literal: true
2
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
3
  module Toys
25
4
  ##
26
5
  # An Acceptor validates and converts arguments. It is designed to be
@@ -1,26 +1,5 @@
1
1
  # frozen_string_literal: true
2
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
3
  module Toys
25
4
  ##
26
5
  # An internal class that parses command line arguments for a tool.
@@ -444,7 +423,7 @@ module Toys
444
423
  Context::Key::ARGS => nil,
445
424
  Context::Key::CLI => cli,
446
425
  Context::Key::CONTEXT_DIRECTORY => tool.context_directory,
447
- Context::Key::LOGGER => cli.logger,
426
+ Context::Key::LOGGER => cli.logger_factory.call(tool),
448
427
  Context::Key::TOOL => tool,
449
428
  Context::Key::TOOL_SOURCE => tool.source_info,
450
429
  Context::Key::TOOL_NAME => tool.full_name,
@@ -1,26 +1,6 @@
1
1
  # frozen_string_literal: true
2
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
-
3
+ require "rbconfig"
24
4
  require "logger"
25
5
  require "toys/completion"
26
6
 
@@ -71,7 +51,8 @@ module Toys
71
51
  # roughly into four categories:
72
52
  #
73
53
  # * Options affecting output behavior:
74
- # * `logger`: The logger
54
+ # * `logger`: A global logger for all tools to use
55
+ # * `logger_factory`: A proc that returns a logger to use
75
56
  # * `base_level`: The default log level
76
57
  # * `error_handler`: Callback for handling exceptions
77
58
  # * `executable_name`: The name of the executable
@@ -91,10 +72,17 @@ module Toys
91
72
  # * `preload_dir_name`: Name of preload directories in tool directories
92
73
  # * `data_dir_name`: Name of data directories in tool directories
93
74
  #
94
- # @param logger [Logger] The logger to use.
95
- # Optional. If not provided, will use a default logger that writes
96
- # formatted output to `STDERR`, as defined by
97
- # {Toys::CLI.default_logger}.
75
+ # @param logger [Logger] A global logger to use for all tools. This may be
76
+ # set if the CLI will call at most one tool at a time. However, it will
77
+ # behave incorrectly if CLI might run multiple tools at the same time
78
+ # with different verbosity settings (since the logger cannot have
79
+ # multiple level settings simultaneously). In that case, do not set a
80
+ # global logger, but use the `logger_factory` parameter instead.
81
+ # @param logger_factory [Proc] A proc that takes a {Toys::Tool} as an
82
+ # argument, and returns a `Logger` to use when running that tool.
83
+ # Optional. If not provided (and no global logger is set), CLI will use
84
+ # a default factory that writes generates loggers writing formatted
85
+ # output to `STDERR`, as defined by {Toys::CLI.default_logger_factory}.
98
86
  # @param base_level [Integer] The logger level that should correspond
99
87
  # to zero verbosity.
100
88
  # Optional. If not provided, defaults to the current level of the
@@ -181,13 +169,31 @@ module Toys
181
169
  # path for any tool file in that directory.
182
170
  # Optional. If not provided, data directories are disabled.
183
171
  # Note: the standard toys executable sets this to `".data"`.
184
- #
185
- def initialize(
186
- executable_name: nil, middleware_stack: nil, extra_delimiters: "",
187
- config_dir_name: nil, config_file_name: nil, index_file_name: nil,
188
- preload_file_name: nil, preload_dir_name: nil, data_dir_name: nil,
189
- mixin_lookup: nil, middleware_lookup: nil, template_lookup: nil,
190
- logger: nil, base_level: nil, error_handler: nil, completion: nil
172
+ # @param lib_dir_name [String] A directory with this name that appears in
173
+ # any configuration directory is added to the Ruby load path when
174
+ # executing any tool file in that directory.
175
+ # Optional. If not provided, lib directories are disabled.
176
+ # Note: the standard toys executable sets this to `".lib"`.
177
+ #
178
+ def initialize( # rubocop:disable Metrics/MethodLength
179
+ executable_name: nil,
180
+ middleware_stack: nil,
181
+ extra_delimiters: "",
182
+ config_dir_name: nil,
183
+ config_file_name: nil,
184
+ index_file_name: nil,
185
+ preload_file_name: nil,
186
+ preload_dir_name: nil,
187
+ data_dir_name: nil,
188
+ lib_dir_name: nil,
189
+ mixin_lookup: nil,
190
+ middleware_lookup: nil,
191
+ template_lookup: nil,
192
+ logger_factory: nil,
193
+ logger: nil,
194
+ base_level: nil,
195
+ error_handler: nil,
196
+ completion: nil
191
197
  )
192
198
  @executable_name = executable_name || ::File.basename($PROGRAM_NAME)
193
199
  @middleware_stack = middleware_stack || CLI.default_middleware_stack
@@ -196,8 +202,9 @@ module Toys
196
202
  @template_lookup = template_lookup || CLI.default_template_lookup
197
203
  @error_handler = error_handler || DefaultErrorHandler.new
198
204
  @completion = completion || DefaultCompletion.new
199
- @logger = logger || CLI.default_logger
200
- @base_level = base_level || @logger.level
205
+ @logger = logger
206
+ @logger_factory = logger ? proc { logger } : logger_factory || CLI.default_logger_factory
207
+ @base_level = base_level
201
208
  @extra_delimiters = extra_delimiters
202
209
  @config_dir_name = config_dir_name
203
210
  @config_file_name = config_file_name
@@ -205,12 +212,18 @@ module Toys
205
212
  @preload_file_name = preload_file_name
206
213
  @preload_dir_name = preload_dir_name
207
214
  @data_dir_name = data_dir_name
215
+ @lib_dir_name = lib_dir_name
208
216
  @loader = Loader.new(
209
- index_file_name: @index_file_name, extra_delimiters: @extra_delimiters,
210
- preload_dir_name: @preload_dir_name, preload_file_name: @preload_file_name,
217
+ index_file_name: @index_file_name,
218
+ preload_dir_name: @preload_dir_name,
219
+ preload_file_name: @preload_file_name,
211
220
  data_dir_name: @data_dir_name,
212
- mixin_lookup: @mixin_lookup, template_lookup: @template_lookup,
213
- middleware_lookup: @middleware_lookup, middleware_stack: @middleware_stack
221
+ lib_dir_name: @lib_dir_name,
222
+ middleware_stack: @middleware_stack,
223
+ extra_delimiters: @extra_delimiters,
224
+ mixin_lookup: @mixin_lookup,
225
+ template_lookup: @template_lookup,
226
+ middleware_lookup: @middleware_lookup
214
227
  )
215
228
  end
216
229
 
@@ -235,12 +248,14 @@ module Toys
235
248
  preload_dir_name: @preload_dir_name,
236
249
  preload_file_name: @preload_file_name,
237
250
  data_dir_name: @data_dir_name,
251
+ lib_dir_name: @lib_dir_name,
238
252
  middleware_stack: @middleware_stack,
239
253
  extra_delimiters: @extra_delimiters,
240
254
  mixin_lookup: @mixin_lookup,
241
255
  middleware_lookup: @middleware_lookup,
242
256
  template_lookup: @template_lookup,
243
257
  logger: @logger,
258
+ logger_factory: @logger_factory,
244
259
  base_level: @base_level,
245
260
  error_handler: @error_handler,
246
261
  completion: @completion,
@@ -269,14 +284,21 @@ module Toys
269
284
  attr_reader :extra_delimiters
270
285
 
271
286
  ##
272
- # The logger used by this CLI.
273
- # @return [Logger]
287
+ # The global logger, if any.
288
+ # @return [Logger,nil]
274
289
  #
275
290
  attr_reader :logger
276
291
 
292
+ ##
293
+ # The logger factory.
294
+ # @return [Proc]
295
+ #
296
+ attr_reader :logger_factory
297
+
277
298
  ##
278
299
  # The initial logger level in this CLI, used as the level for verbosity 0.
279
- # @return [Integer]
300
+ # May be `nil`, indicating it will use the initial logger setting.
301
+ # @return [Integer,nil]
280
302
  #
281
303
  attr_reader :base_level
282
304
 
@@ -432,19 +454,22 @@ module Toys
432
454
  require_exact_flag_match: tool.exact_flag_match_required?)
433
455
  arg_parser.parse(args).finish
434
456
  context = tool.tool_class.new(arg_parser.data)
457
+ tool.source_info&.apply_lib_paths
435
458
  tool.run_initializers(context)
436
459
 
437
- cur_logger = logger
438
- original_level = cur_logger.level
439
- cur_logger.level = base_level - context[Context::Key::VERBOSITY]
460
+ cur_logger = context[Context::Key::LOGGER]
461
+ if cur_logger
462
+ original_level = cur_logger.level
463
+ cur_logger.level = (base_level || original_level) - context[Context::Key::VERBOSITY].to_i
464
+ end
440
465
  begin
441
- perform_execution(context, tool)
466
+ execute_tool_in_context(context, tool)
442
467
  ensure
443
- cur_logger.level = original_level
468
+ cur_logger.level = original_level if cur_logger
444
469
  end
445
470
  end
446
471
 
447
- def perform_execution(context, tool)
472
+ def execute_tool_in_context(context, tool)
448
473
  executor = proc do
449
474
  begin
450
475
  if !context[Context::Key::USAGE_ERRORS].empty?
@@ -459,7 +484,7 @@ module Toys
459
484
  handle_interrupt(context, tool.interrupt_handler, e)
460
485
  end
461
486
  end
462
- tool.middleware_stack.reverse_each do |middleware|
487
+ tool.built_middleware.reverse_each do |middleware|
463
488
  executor = make_executor(middleware, context, executor)
464
489
  end
465
490
  catch(:result) do
@@ -493,7 +518,11 @@ module Toys
493
518
  end
494
519
 
495
520
  def make_executor(middleware, context, next_executor)
496
- proc { middleware.run(context, &next_executor) }
521
+ if middleware.respond_to?(:run)
522
+ proc { middleware.run(context, &next_executor) }
523
+ else
524
+ next_executor
525
+ end
497
526
  end
498
527
 
499
528
  ##
@@ -646,30 +675,33 @@ module Toys
646
675
  end
647
676
 
648
677
  ##
649
- # Returns a default logger that writes formatted logs to a given stream.
678
+ # Returns a logger factory that generates loggers that write to stderr.
679
+ # All loggers generated by this factory share a single
680
+ # {Toys::Utils::Terminal}, so log entries may interleave but will not
681
+ # interrupt one another.
650
682
  #
651
- # @param output [IO] The stream to output to (defaults to `$stderr`)
652
- # @return [Logger]
683
+ # @return [Proc]
653
684
  #
654
- def default_logger(output: nil)
685
+ def default_logger_factory
655
686
  require "toys/utils/terminal"
656
- output ||= $stderr
657
- logger = ::Logger.new(output)
658
- terminal = Utils::Terminal.new(output: output)
659
- logger.formatter = proc do |severity, time, _progname, msg|
660
- msg_str =
661
- case msg
662
- when ::String
663
- msg
664
- when ::Exception
665
- "#{msg.message} (#{msg.class})\n" << (msg.backtrace || []).join("\n")
666
- else
667
- msg.inspect
668
- end
669
- format_log(terminal, time, severity, msg_str)
687
+ shared_terminal = Utils::Terminal.new(output: $stderr)
688
+ proc do
689
+ logger = ::Logger.new(shared_terminal)
690
+ logger.formatter = proc do |severity, time, _progname, msg|
691
+ msg_str =
692
+ case msg
693
+ when ::String
694
+ msg
695
+ when ::Exception
696
+ "#{msg.message} (#{msg.class})\n" << (msg.backtrace || []).join("\n")
697
+ else
698
+ msg.inspect
699
+ end
700
+ format_log(shared_terminal, time, severity, msg_str)
701
+ end
702
+ logger.level = ::Logger::WARN
703
+ logger
670
704
  end
671
- logger.level = ::Logger::WARN
672
- logger
673
705
  end
674
706
 
675
707
  private