toys-core 0.18.0 → 0.19.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4bd5a5658f6cecd3721768123c8125b9f8d0459f9f6812aede164c3207c30da3
4
- data.tar.gz: 2576dc86d5ea42e516bc2b812809ad653b3c7302a3d30a9bc28be3490de749e8
3
+ metadata.gz: 815f546de5a0da65099cf1fdce70527fb42aafd3793bf8a99e35f63d77c4e66b
4
+ data.tar.gz: 79bd650fb1bdc5d04a2b1e1d235864e39313b4320518b9ac6296a8e1892aa8b1
5
5
  SHA512:
6
- metadata.gz: 6795ecfbda46c2259164fcef70089882c07c087e40cb378ee2726691fde2c3e89888bb13323dc974260ce79211f0ad603a67bb73de09319eca8c57a9fb3006cd
7
- data.tar.gz: 9d2f4b8da60d7470d4bc3a0759841ed705d194025556bde61efa084ec7d5a8f400acece16ef289c41c3cc0c8e98229a34035cc075d59798626347c53621d90a4
6
+ metadata.gz: e696b9c3921aec0c56796bdd7b93a09d056f6d46347a5936bbf6a0ce9bc7b2811f90d4b9553c54bdf69b62d74289e4608da98473e9b62e34f155accbf665f7a1
7
+ data.tar.gz: 675e01b73df268ceda5cfd5bd62f4a21b164c2fce0b37e88f3397bc12f76ba8baa4b2969df5d82901ffbb78440dd1156374cc6642792d81a4ec9c149a800148a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Release History
2
2
 
3
+ ### v0.19.1 / 2026-01-06
4
+
5
+ * DOCS: Some formatting fixes in the user guide
6
+
7
+ ### v0.19.0 / 2025-12-22
8
+
9
+ Compatibility update for Ruby 4.0, including:
10
+
11
+ * The logger gem is now an explicit dependency
12
+ * Calling a tool via exec no longer disables rubygems
13
+ * Bundler integration does a better job of cleaning up temporary lockfiles under bundler 4
14
+
15
+ Additionally, this release includes updates to readmes and users guides
16
+
3
17
  ### v0.18.0 / 2025-12-05
4
18
 
5
19
  * ADDED: The load_gem directive can now take version requirements as positional arguments
data/README.md CHANGED
@@ -99,9 +99,9 @@ look familiar. Let's run it now, and experiment with passing flags to it.
99
99
  Notice that we did not create a `tool` block, but instead set up description,
100
100
  flags, and functionality directly in the configuration block. This configures
101
101
  the "root tool", i.e. what happens when you run the executable without passing
102
- a tool name to it. (In fact, it's legal to do this in Toys as well, by setting
103
- functionality at the "top level" of a `.toys.rb` file without including any
104
- `tool` block.)
102
+ a tool name to it. (In fact, it's technically legal to do this in Toys as well,
103
+ by setting functionality at the "top level" of a `.toys.rb` file without any
104
+ `tool` block, although you probably won't actually want to do so.)
105
105
 
106
106
  ### Tool-based executables
107
107
 
data/docs/guide.md CHANGED
@@ -59,8 +59,8 @@ An executable can customize many aspects of its behavior, such as the
59
59
  **logging output**, **error handling**, and even shell **tab completion**.
60
60
 
61
61
  Finally, Toys-Core can also be used to publish **Toys extensions**, collections
62
- of mixins, templates, and predefined tools that can be distributed as gems to
63
- enhance Toys for other users.
62
+ of mixins, templates, and/or predefined tools that can be distributed as gems
63
+ to enhance Toys for other users.
64
64
 
65
65
  ## Using the CLI object
66
66
 
@@ -80,27 +80,29 @@ to ensure that the `toys-core` gem is loaded, and `require "toys-core"`.
80
80
 
81
81
  Following is a simple "hello world" example using the CLI:
82
82
 
83
- #!/usr/bin/env ruby
83
+ ```ruby
84
+ #!/usr/bin/env ruby
84
85
 
85
- require "toys-core"
86
+ require "toys-core"
86
87
 
87
- # Instantiate a CLI with the default options
88
- cli = Toys::CLI.new
88
+ # Instantiate a CLI with the default options
89
+ cli = Toys::CLI.new
89
90
 
90
- # Define the functionality
91
- cli.add_config_block do
92
- desc "My first executable!"
93
- flag :whom, default: "world"
94
- def run
95
- puts "Hello, #{whom}!"
96
- end
97
- end
91
+ # Define the functionality
92
+ cli.add_config_block do
93
+ desc "My first executable!"
94
+ flag :whom, default: "world"
95
+ def run
96
+ puts "Hello, #{whom}!"
97
+ end
98
+ end
98
99
 
99
- # Run the CLI, passing the command line arguments
100
- result = cli.run(*ARGV)
100
+ # Run the CLI, passing the command line arguments
101
+ result = cli.run(*ARGV)
101
102
 
102
- # Handle the result code.
103
- exit(result)
103
+ # Handle the result code.
104
+ exit(result)
105
+ ```
104
106
 
105
107
  ### CLI execution
106
108
 
@@ -123,10 +125,10 @@ When you call {Toys::CLI#run}, the CLI runs through three phases:
123
125
  When the CLI needs the definition of a tool, it queries the {Toys::Loader}. The
124
126
  loader object is configured with a set of tool _sources_ representing ways to
125
127
  define a tool. These sources may be blocks passed directly to the CLI, or
126
- directories and files loaded from the file system or even remote git
127
- repositories. When a tool is requested by name, the loader is responsible for
128
- locating the tool definition in those sources, and constructing the tool
129
- definition object, represented by {Toys::ToolDefinition}.
128
+ directories and files loaded from the file system, from gems, or even from
129
+ remote git repositories. When a tool is requested by name, the loader is
130
+ responsible for locating the tool definition in those sources, and constructing
131
+ the tool definition object, represented by {Toys::ToolDefinition}.
130
132
 
131
133
  One important property of the loader is that it is _lazy_. It queries tool
132
134
  sources only when it has reason to believe that a tool it is looking for may be
@@ -141,19 +143,21 @@ when a tool is requested does the block actually execute. Furthermore, if you
141
143
  have `tool` blocks inside the block, the loader will execute only those that
142
144
  are relevant to a tool it wants. Hence:
143
145
 
144
- cli.add_config_block do
145
- tool "foo" do
146
- def run
147
- puts "foo called"
148
- end
149
- end
146
+ ```ruby
147
+ cli.add_config_block do
148
+ tool "foo" do
149
+ def run
150
+ puts "foo called"
151
+ end
152
+ end
150
153
 
151
- tool "bar" do
152
- def run
153
- puts "bar called"
154
- end
155
- end
154
+ tool "bar" do
155
+ def run
156
+ puts "bar called"
156
157
  end
158
+ end
159
+ end
160
+ ```
157
161
 
158
162
  If only `foo` is requested, the loader will execute the `tool "foo" do` block
159
163
  to get that tool definition, but will not execute the `tool "bar" do` block.
@@ -184,7 +188,7 @@ class, but it implements a few extra features and cleans up a few ambiguities.
184
188
 
185
189
  The execution phase involves:
186
190
 
187
- * Running the tool's initializers, if any, in order.
191
+ * Running the tool's initializers (if any) in order.
188
192
  * Running the tool's middleware. Each middleware "wraps" the execution of
189
193
  subsequent middleware and the final tool execution, and has the opportunity
190
194
  to inject functionality before and after the main execution, or even to
@@ -245,28 +249,30 @@ If you are writing your own command line executable using Toys-Core, often the
245
249
  easiest way to define your tools is to use a block. The "hello world" example
246
250
  at the start of this guide uses this technique:
247
251
 
248
- #!/usr/bin/env ruby
252
+ ```ruby
253
+ #!/usr/bin/env ruby
249
254
 
250
- require "toys-core"
255
+ require "toys-core"
251
256
 
252
- cli = Toys::CLI.new
257
+ cli = Toys::CLI.new
253
258
 
254
- # Define the functionality by passing a block to the CLI
255
- cli.add_config_block do
256
- desc "My first executable!"
257
- flag :whom, default: "world"
258
- def run
259
- puts "Hello, #{whom}!"
260
- end
261
- end
259
+ # Define the functionality by passing a block to the CLI
260
+ cli.add_config_block do
261
+ desc "My first executable!"
262
+ flag :whom, default: "world"
263
+ def run
264
+ puts "Hello, #{whom}!"
265
+ end
266
+ end
262
267
 
263
- result = cli.run(*ARGV)
264
- exit(result)
268
+ result = cli.run(*ARGV)
269
+ exit(result)
270
+ ```
265
271
 
266
272
  The block simply contains Toys DSL syntax. The above example configures the
267
273
  "root tool", that is, the functionality of the program if you do not pass a
268
274
  tool name on the command line. You can also include "tool" blocks to define
269
- named tools, just as you would in a normal Toys file.
275
+ named tools and subtools, just as you would in a normal Toys file.
270
276
 
271
277
  The reference documentation for {Toys::CLI#add_config_block} lists several
272
278
  options that can be passed in. `:context_directory` lets you select a context
@@ -282,25 +288,29 @@ messages and documentation, can also be set explicitly.
282
288
  If you want to define tools in separate files, you can do so and pass the file
283
289
  paths to the CLI using {Toys::CLI#add_config_path}.
284
290
 
285
- #!/usr/bin/env ruby
291
+ ```ruby
292
+ #!/usr/bin/env ruby
286
293
 
287
- require "toys-core"
294
+ require "toys-core"
288
295
 
289
- cli = Toys::CLI.new
296
+ cli = Toys::CLI.new
290
297
 
291
- # Load a file defining the functionality
292
- cli.add_config_path("/usr/local/share/my_tool.rb)
298
+ # Load a file defining the functionality
299
+ cli.add_config_path("/usr/local/share/my_tool.rb")
293
300
 
294
- result = cli.run(*ARGV)
295
- exit(result)
301
+ result = cli.run(*ARGV)
302
+ exit(result)
303
+ ```
296
304
 
297
305
  The contents of `/usr/local/share/my_tool.rb` could then be:
298
306
 
299
- desc "My first executable!"
300
- flag :whom, default: "world"
301
- def run
302
- puts "Hello, #{whom}!"
303
- end
307
+ ```ruby
308
+ desc "My first executable!"
309
+ flag :whom, default: "world"
310
+ def run
311
+ puts "Hello, #{whom}!"
312
+ end
313
+ ```
304
314
 
305
315
  You can point to a specific file to load, or to a Toys directory, whose
306
316
  contents will be loaded similarly to how a `.toys` directory is loaded.
@@ -329,45 +339,49 @@ priority level than previously added sources. Thus, any tools defined in the
329
339
  new source would be overridden by tools of the same name defined in previously
330
340
  added sources.
331
341
 
332
- #!/usr/bin/env ruby
342
+ ```ruby
343
+ #!/usr/bin/env ruby
333
344
 
334
- require "toys-core"
345
+ require "toys-core"
335
346
 
336
- cli = Toys::CLI.new
347
+ cli = Toys::CLI.new
337
348
 
338
- # Add a block defining a tool called "hello"
339
- cli.add_config_block do
340
- tool "hello" do
341
- def run
342
- puts "Hello from the first config block!"
343
- end
344
- end
349
+ # Add a block defining a tool called "hello"
350
+ cli.add_config_block do
351
+ tool "hello" do
352
+ def run
353
+ puts "Hello from the first config block!"
345
354
  end
355
+ end
356
+ end
346
357
 
347
- # Add a lower-priority block defining a tool with the same name
348
- cli.add_config_block do
349
- tool "hello" do
350
- def run
351
- puts "Hello from the second config block!"
352
- end
353
- end
358
+ # Add a lower-priority block defining a tool with the same name
359
+ cli.add_config_block do
360
+ tool "hello" do
361
+ def run
362
+ puts "Hello from the second config block!"
354
363
  end
364
+ end
365
+ end
355
366
 
356
- # Runs the tool defined in the first block
357
- result = cli.run("hello")
358
- exit(result)
367
+ # Runs the tool defined in the first block
368
+ result = cli.run("hello")
369
+ exit(result)
370
+ ```
359
371
 
360
372
  When defining tool blocks or loading tools from files, you can also add the new
361
373
  source at the *front* of the priority list by passing an argument:
362
374
 
363
- # Add tools with the highest priority
364
- cli.add_config_block high_priority: true do
365
- tool "hello" do
366
- def run
367
- puts "Hello from the second config block!"
368
- end
369
- end
375
+ ```ruby
376
+ # Add tools with the highest priority
377
+ cli.add_config_block high_priority: true do
378
+ tool "hello" do
379
+ def run
380
+ puts "Hello from the second config block!"
370
381
  end
382
+ end
383
+ end
384
+ ```
371
385
 
372
386
  Priorities are used by the `toys` gem when loading tools from different
373
387
  directories. Any `.toys.rb` file or `.toys` directory is added to the CLI at
@@ -375,9 +389,86 @@ the front of the list, with the highest priority. Parent directories are added
375
389
  at subsequently lower priorities, and common directories such as the home
376
390
  directory are loaded at the lowest priority.
377
391
 
378
- ### Changing built-in mixins and templates
392
+ ### Customizing built-in mixins and templates
379
393
 
380
- (TODO)
394
+ Mixins and templates are two of the most useful mechanisms for sharing code and
395
+ generating code for tools. In the main Toys gem, a certain set of mixins are
396
+ built-in and can be referenced via symbols. For example, the *exec* mixin that
397
+ provides facilities for running and controlling external processes, can be
398
+ included using `include :exec`. In this section, we see how to define your own
399
+ "built-in" mixins and templates that can be referenced via symbol.
400
+
401
+ "Built-in" mixins and templates (and middleware, which we shall cover later)
402
+ are provided via the {Toys::ModuleLookup} mechanism. ModuleLookup lets you
403
+ select a directory for "standard" instances. By default, Toys-Core establishes
404
+ the `toys/standard_mixins` directory in the gem as the standard directory for
405
+ mixins, and whenever you reference a mixin by symbol, it is used to determine
406
+ the name of a file to open and the name of a module to load. You can, however,
407
+ change this directory and provide a different ModuleLookup when constructing a
408
+ CLI object.
409
+
410
+ Suppose, for example, you are writing a gem `my_tools` that uses Toys-Core, and
411
+ you have a directory in your gem's `lib` called `my_tools/mixins` where you
412
+ want your standard mixins to live. You could define mixins there:
413
+
414
+ ```ruby
415
+ # This file is my_tools/mixins/foo_mixin.rb
416
+
417
+ require "toys-core"
418
+
419
+ module MyTools
420
+ module Mixins
421
+ module FooMixin
422
+ include Toys::Mixin
423
+
424
+ def foo
425
+ puts "Foo was called"
426
+ end
427
+ end
428
+ end
429
+ end
430
+ ```
431
+
432
+ Here is how you could configure a CLI to load standard mixins from that
433
+ directory, and then use the above mixin.
434
+
435
+ ```ruby
436
+ # This file is my_tools.rb
437
+
438
+ require "toys-core"
439
+
440
+ my_mixin_lookup = Toys::ModuleLookup.new.add_path("my_tools/mixins")
441
+ cli = Toys::CLI.new(mixin_lookup: my_mixin_lookup)
442
+
443
+ cli.add_config_block do
444
+ def run
445
+ include :foo_mixin
446
+ foo
447
+ end
448
+ end
449
+ ```
450
+
451
+ When you configure a ModuleLookup, you provide one or more paths, which are
452
+ path prefixes that are used in a `require` statement. In the above example,
453
+ we used the path `my_tools/mixins` for the ModuleLookup. Now when the CLI uses
454
+ this ModuleLookup to find a mixin called `:foo_mixin`, it will attempt to
455
+ `require "my_tools/mixins/foo_mixin"`, which matches the file where we defined
456
+ our mixin.
457
+
458
+ Notice also that `foo_mixin.rb` above defines FooMixin within a specific module
459
+ hierarchy, corresponding to the file name `my_tools/mixins/foo_mixin.rb`
460
+ according to standard Ruby naming conventions. The fully-qualified module name
461
+ for the mixin must match this expected name, constructed from the path provided
462
+ to the ModuleLookup and the name of the mixin. You can change the way this name
463
+ mapping occurs, by providing the `:module_base` argument to the ModuleLookup
464
+ constructor.
465
+
466
+ Template lookup happens similarly. Toys-Core does not provide a set of default
467
+ templates, but the Toys gem does; the `StandardCLI` class used by Toys sets the
468
+ `:template_lookup` to point to the `toys/templates` directory in that gem's
469
+ library. If you want to customize the default template lookup for your
470
+ Toys-based library, you can similarly provide your own ModuleLookup. This will
471
+ let you control how templates are resolved when specified by symbol.
381
472
 
382
473
  ## Customizing diagnostic output
383
474
 
@@ -395,22 +486,24 @@ Toys provides a Logger for each tool execution. Tools can access this Logger by
395
486
  calling the `logger` method, or by getting the `Toys::Context::Key::LOGGER`
396
487
  context object.
397
488
 
398
- #!/usr/bin/env ruby
489
+ ```ruby
490
+ #!/usr/bin/env ruby
399
491
 
400
- require "toys-core"
492
+ require "toys-core"
401
493
 
402
- cli = Toys::CLI.new
494
+ cli = Toys::CLI.new
403
495
 
404
- cli.add_config_block do
405
- tool "hello" do
406
- def run
407
- logger.info "This log entry is displayed in verbose mode."
408
- end
409
- end
496
+ cli.add_config_block do
497
+ tool "hello" do
498
+ def run
499
+ logger.info "This log entry is displayed in verbose mode."
410
500
  end
501
+ end
502
+ end
411
503
 
412
- result = cli.run(*ARGV)
413
- exit(result)
504
+ result = cli.run(*ARGV)
505
+ exit(result)
506
+ ```
414
507
 
415
508
  #### Log level and verbosity
416
509
 
@@ -433,15 +526,19 @@ Passing `verbosity: 1` will set the starting verbosity to 1, meaning
433
526
  the invoker then provides an extra `--verbose` flag, the verbosity will further
434
527
  increase to 2, allowing `Logger::DEBUG` entries to appear.
435
528
 
436
- # ...
437
- result = cli.run(*ARGV, verbosity: 1)
438
- exit(result)
529
+ ```ruby
530
+ # ...
531
+ result = cli.run(*ARGV, verbosity: 1)
532
+ exit(result)
533
+ ```
439
534
 
440
535
  You can also modify the log level that verbosity 0 maps to by passing the
441
536
  `base_level` argument to the CLI constructor. The following causes verbosity 0
442
537
  to map to `Logger::INFO` rather than `Logger::WARN`.
443
538
 
444
- cli = Toys::CLI.new(base_level: Logger::INFO)
539
+ ```ruby
540
+ cli = Toys::CLI.new(base_level: Logger::INFO)
541
+ ```
445
542
 
446
543
  #### Customizing the logger
447
544
 
@@ -450,8 +547,10 @@ configures it to log to STDERR. If you want to change any of these settings,
450
547
  you can provide your own logger by passing a `logger` to the CLI constructor
451
548
  constructor.
452
549
 
453
- my_logger = Logger.new("my_logfile.log")
454
- cli = Toys::CLI.new(logger: my_logger)
550
+ ```ruby
551
+ my_logger = Logger.new("my_logfile.log")
552
+ cli = Toys::CLI.new(logger: my_logger)
553
+ ```
455
554
 
456
555
  A logger passed directly to the CLI is *global*. The CLI will attempt to use it
457
556
  for every execution, even if multiple executions are happening concurrently. In
@@ -461,10 +560,12 @@ your CLI might be run multiple times concurrently, we recommend instead passing
461
560
  a `logger_factory` to the CLI constructor. This is a Proc that will be invoked
462
561
  to create a new logger for each execution.
463
562
 
464
- my_logger_factory = Proc.new do
465
- Logger.new("my_logfile.log")
466
- end
467
- cli = Toys::CLI.new(logger_factory: my_logger_factory)
563
+ ```ruby
564
+ my_logger_factory = Proc.new do
565
+ Logger.new("my_logfile.log")
566
+ end
567
+ cli = Toys::CLI.new(logger_factory: my_logger_factory)
568
+ ```
468
569
 
469
570
  #### StandardUI logging
470
571
 
@@ -474,8 +575,10 @@ formats log entries with the severity and timestamp using ANSI coloring.
474
575
  You can use this logger by passing {Toys::Utils::StandardUI#logger_factory} to
475
576
  the CLI constructor:
476
577
 
477
- standard_ui = Toys::Utils::StandardUI.new
478
- cli = Toys::CLI.new(logger_factory: standard_ui.logger_factory)
578
+ ```ruby
579
+ standard_ui = Toys::Utils::StandardUI.new
580
+ cli = Toys::CLI.new(logger_factory: standard_ui.logger_factory)
581
+ ```
479
582
 
480
583
  You can also customize the logger by subclassing StandardUI and overriding its
481
584
  methods or adjusting its parameters. In particular, you can alter the
@@ -501,15 +604,17 @@ final handling of an unhandled exception, such as displaying the error to the
501
604
  terminal, or reraising the exception. The handler should then return the
502
605
  desired result code for the execution.
503
606
 
504
- my_error_handler = Proc.new |wrapped_error| do
505
- # Propagate signals out and let the Ruby VM handle them.
506
- raise wrapped_error.cause if wrapped_error.cause.is_a?(SignalException)
507
- # Handle any other exception types by printing a message.
508
- $stderr.puts "An error occurred. Please contact your administrator."
509
- # Return the result code
510
- 255
511
- end
512
- cli = Toys::CLI.new(error_handler: my_error_handler)
607
+ ```ruby
608
+ my_error_handler = Proc.new |wrapped_error| do
609
+ # Propagate signals out and let the Ruby VM handle them.
610
+ raise wrapped_error.cause if wrapped_error.cause.is_a?(SignalException)
611
+ # Handle any other exception types by printing a message.
612
+ $stderr.puts "An error occurred. Please contact your administrator."
613
+ # Return the result code
614
+ 255
615
+ end
616
+ cli = Toys::CLI.new(error_handler: my_error_handler)
617
+ ```
513
618
 
514
619
  If you do not set an error handler, the exception is raised out of the
515
620
  {Toys::CLI#run} call. In the case of signals, the *cause*, represented by a
@@ -530,8 +635,10 @@ conventional result code of `128 + signo` (e.g. 130 for interrupts).
530
635
  You can use this error handler by passing
531
636
  {Toys::Utils::StandardUI#error_handler} to the CLI constructor:
532
637
 
533
- standard_ui = Toys::Utils::StandardUI.new
534
- cli = Toys::CLI.new(error_handler: standard_ui.error_handler)
638
+ ```ruby
639
+ standard_ui = Toys::Utils::StandardUI.new
640
+ cli = Toys::CLI.new(error_handler: standard_ui.error_handler)
641
+ ```
535
642
 
536
643
  You can also customize the error handler by subclassing StandardUI and
537
644
  overriding its methods. In particular, you can alter what is displayed in
@@ -588,7 +695,7 @@ replace normal tool execution.
588
695
 
589
696
  Middleware is normally configured as part of the CLI object. Each CLI includes
590
697
  an ordered list, a _stack_, of middleware specifications, each represented by
591
- {Toys::Middleware::Spec}. A middleware spec can be a specific middleware
698
+ {Toys::Middleware::Spec}. A middleware spec can reference a specific middleware
592
699
  object, a class to instantiate, or a name that can be looked up from a
593
700
  directory of middleware class files. You can pass an array of these specs to a
594
701
  CLI object when you instantiate it.
@@ -597,12 +704,14 @@ A useful example can be seen in the default Toys CLI behavior. If you do not
597
704
  provide a middleware stack when instantiating {Toys::CLI}, the class uses a
598
705
  default stack that looks approximately like this:
599
706
 
600
- [
601
- Toys::Middleware.spec(:set_default_descriptions),
602
- Toys::Middleware.spec(:show_help, help_flags: true, fallback_execution: true),
603
- Toys::Middleware.spec(:handle_usage_errors),
604
- Toys::Middleware.spec(:add_verbosity_flags),
605
- ]
707
+ ```ruby
708
+ [
709
+ Toys::Middleware.spec(:set_default_descriptions),
710
+ Toys::Middleware.spec(:show_help, help_flags: true, fallback_execution: true),
711
+ Toys::Middleware.spec(:handle_usage_errors),
712
+ Toys::Middleware.spec(:add_verbosity_flags),
713
+ ]
714
+ ```
606
715
 
607
716
  Each of the names, e.g. `:set_default_descriptions`, is the name of a Ruby
608
717
  file in the `toys-core` gem under `toys/standard_middleware`. You can configure
@@ -659,51 +768,53 @@ following is a simple middleware that adds the `--show-timing` flag to every
659
768
  tool. When the flag is set, the middleware displays how long the tool took to
660
769
  execute.
661
770
 
662
- class TimingMiddleware
663
- # This is a context key that will be used to store the "--show-timing"
664
- # flag state. We can use `Object.new` to ensure that the key is unique
665
- # across other middlewares and tool definitions.
666
- KEY = Object.new.freeze
667
-
668
- # This method intercepts tool configuration. We use it to add a flag that
669
- # enables timing display.
670
- def config(tool, _loader)
671
- # Add a flag to control this functionality. Suppress collisions, i.e.
672
- # just silently do nothing if the tool has already added a flag called
673
- # "--show-timing".
674
- tool.add_flag(KEY, "--show-timing", report_collisions: false)
675
-
676
- # Calling yield passes control to the rest of the middleware stack.
677
- # Normally you should call yield, to ensure that the remaining
678
- # middleware can run. If you omit this, no additional middleware will
679
- # be able to run tool configuration. Note you can also perform
680
- # additional processing after the yield call, i.e. after the rest of
681
- # the middleware stack has run.
682
- yield
683
- end
684
-
685
- # This method intercepts tool execution. We use it to collect timing
686
- # information, and display it if the flag has been provided in the
687
- # command line arguments.
688
- def run(context)
689
- # Read monotonic time at the start of execution.
690
- start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
691
-
692
- # Call yield to run the rest of the middleware stack, including the
693
- # actual tool execution. If you omit this, you will prevent the rest of
694
- # the middleware stack, AND the actual tool execution, from running.
695
- # So you could omit the yield call if your goal is to replace tool
696
- # execution with your own code.
697
- yield
698
-
699
- # Read monotonic time again after execution.
700
- end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
701
-
702
- # Display the elapsed time, if the tool was passed the "--show-timing"
703
- # flag.
704
- puts "Tool took #{end_time - start_time} secs" if context[KEY]
705
- end
706
- end
771
+ ```ruby
772
+ class TimingMiddleware
773
+ # This is a context key that will be used to store the "--show-timing"
774
+ # flag state. We can use `Object.new` to ensure that the key is unique
775
+ # across other middlewares and tool definitions.
776
+ KEY = Object.new.freeze
777
+
778
+ # This method intercepts tool configuration. We use it to add a flag that
779
+ # enables timing display.
780
+ def config(tool, _loader)
781
+ # Add a flag to control this functionality. Suppress collisions, i.e.
782
+ # just silently do nothing if the tool has already added a flag called
783
+ # "--show-timing".
784
+ tool.add_flag(KEY, "--show-timing", report_collisions: false)
785
+
786
+ # Calling yield passes control to the rest of the middleware stack.
787
+ # Normally you should call yield, to ensure that the remaining
788
+ # middleware can run. If you omit this, no additional middleware will
789
+ # be able to run tool configuration. Note you can also perform
790
+ # additional processing after the yield call, i.e. after the rest of
791
+ # the middleware stack has run.
792
+ yield
793
+ end
794
+
795
+ # This method intercepts tool execution. We use it to collect timing
796
+ # information, and display it if the flag has been provided in the
797
+ # command line arguments.
798
+ def run(context)
799
+ # Read monotonic time at the start of execution.
800
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
801
+
802
+ # Call yield to run the rest of the middleware stack, including the
803
+ # actual tool execution. If you omit this, you will prevent the rest of
804
+ # the middleware stack, AND the actual tool execution, from running.
805
+ # So you could omit the yield call if your goal is to replace tool
806
+ # execution with your own code.
807
+ yield
808
+
809
+ # Read monotonic time again after execution.
810
+ end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
811
+
812
+ # Display the elapsed time, if the tool was passed the "--show-timing"
813
+ # flag.
814
+ puts "Tool took #{end_time - start_time} secs" if context[KEY]
815
+ end
816
+ end
817
+ ```
707
818
 
708
819
  We can now insert our middleware into the stack when we create a CLI. Here
709
820
  we'll take that "default" stack we saw earlier and add our timing middleware at
@@ -712,22 +823,20 @@ other middleware, and thus its timing measurement includes the latency incurred
712
823
  by other middleware (including middleware that replaces execution such as
713
824
  `:show_help`).
714
825
 
715
- my_middleware_stack = [
716
- Toys::Middleware.spec(TimingMiddleware),
717
- Toys::Middleware.spec(:set_default_descriptions),
718
- Toys::Middleware.spec(:show_help, help_flags: true, fallback_execution: true),
719
- Toys::Middleware.spec(:handle_usage_errors),
720
- Toys::Middleware.spec(:add_verbosity_flags),
721
- ]
722
- cli = Toys::CLI.new(middleware_stack: my_middleware_stack)
826
+ ```ruby
827
+ my_middleware_stack = [
828
+ Toys::Middleware.spec(TimingMiddleware),
829
+ Toys::Middleware.spec(:set_default_descriptions),
830
+ Toys::Middleware.spec(:show_help, help_flags: true, fallback_execution: true),
831
+ Toys::Middleware.spec(:handle_usage_errors),
832
+ Toys::Middleware.spec(:add_verbosity_flags),
833
+ ]
834
+ cli = Toys::CLI.new(middleware_stack: my_middleware_stack)
835
+ ```
723
836
 
724
837
  Now, every tool run by this CLI wil have the `--show-timing` flag and
725
838
  associated functionality.
726
839
 
727
- #### Changing built-in middleware
728
-
729
- (TODO)
730
-
731
840
  ## Shell and command line integration
732
841
 
733
842
  (TODO)
data/lib/toys/cli.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "logger"
4
+
3
5
  module Toys
4
6
  ##
5
7
  # A Toys-based CLI.
@@ -125,7 +127,7 @@ module Toys
125
127
  # Optional. If not provided, defaults to the set of standard template
126
128
  # classes provided by toys core, as defined by
127
129
  # {Toys::CLI.default_template_lookup}. If you explicitly want no
128
- # standard tenokates, pass an empty instance of {Toys::ModuleLookup}.
130
+ # standard templates, pass an empty instance of {Toys::ModuleLookup}.
129
131
  #
130
132
  # @param config_dir_name [String] A directory with this name that appears
131
133
  # in the loader path, is treated as a configuration directory whose
@@ -562,7 +564,6 @@ module Toys
562
564
  # @return [Proc]
563
565
  #
564
566
  def default_logger_factory
565
- require "logger"
566
567
  proc do
567
568
  logger = ::Logger.new($stderr)
568
569
  logger.level = ::Logger::WARN
data/lib/toys/core.rb CHANGED
@@ -9,7 +9,7 @@ module Toys
9
9
  # Current version of Toys core.
10
10
  # @return [String]
11
11
  #
12
- VERSION = "0.18.0"
12
+ VERSION = "0.19.1"
13
13
  end
14
14
 
15
15
  ##
@@ -817,7 +817,7 @@ module Toys
817
817
  def self._setup_clean_process(cmd)
818
818
  raise ::ArgumentError, "Toys process is unknown" unless ::Toys.executable_path
819
819
  cmd = ::Shellwords.split(cmd) if cmd.is_a?(::String)
820
- cmd = [::RbConfig.ruby, "--disable=gems", ::Toys.executable_path] + cmd
820
+ cmd = [::RbConfig.ruby, ::Toys.executable_path] + cmd
821
821
  if defined?(::Bundler)
822
822
  if ::Bundler.respond_to?(:with_unbundled_env)
823
823
  ::Bundler.with_unbundled_env { yield(cmd) }
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "logger"
4
+
3
5
  module Toys
4
6
  module Utils
5
7
  ##
@@ -263,7 +265,6 @@ module Toys
263
265
  #
264
266
  def initialize(**opts, &block)
265
267
  require "rbconfig"
266
- require "logger"
267
268
  require "stringio"
268
269
  @default_opts = Opts.new(&block).add(opts)
269
270
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "fileutils"
3
4
  require "monitor"
4
5
 
5
6
  module Toys
@@ -193,9 +194,7 @@ module Toys
193
194
  end
194
195
  end
195
196
 
196
- ##
197
197
  # @private
198
- #
199
198
  def self.find_gemfile(search_dir, gemfile_names: nil)
200
199
  gemfile_names ||= DEFAULT_GEMFILE_NAMES
201
200
  Array(gemfile_names).each do |file|
@@ -207,13 +206,18 @@ module Toys
207
206
 
208
207
  @global_mutex = ::Monitor.new
209
208
 
210
- ##
211
209
  # @private
212
- #
213
210
  def self.synchronize(&block)
214
211
  @global_mutex.synchronize(&block)
215
212
  end
216
213
 
214
+ # @private
215
+ def self.delete_at_exit(path)
216
+ # Call this from a class method so it doesn't hold onto the instance
217
+ # for the duration of the Ruby process
218
+ at_exit { ::FileUtils.rm_f(path) }
219
+ end
220
+
217
221
  private
218
222
 
219
223
  def terminal
@@ -344,7 +348,7 @@ module Toys
344
348
  elsif ::RUBY_VERSION < "3"
345
349
  [">= 2.2", "< 2.5"]
346
350
  else
347
- ["~> 2.2"]
351
+ [">= 2.2", "< 5"]
348
352
  end
349
353
  end
350
354
 
@@ -421,11 +425,10 @@ module Toys
421
425
  end
422
426
 
423
427
  def delete_modified_gemfile(modified_gemfile_path)
424
- # rubocop:disable Lint/NonAtomicFileOperation
425
- ::File.delete(modified_gemfile_path) if ::File.exist?(modified_gemfile_path)
428
+ ::FileUtils.rm_f(modified_gemfile_path)
426
429
  modified_lockfile_path = find_lockfile_path(modified_gemfile_path)
427
- ::File.delete(modified_lockfile_path) if ::File.exist?(modified_lockfile_path)
428
- # rubocop:enable Lint/NonAtomicFileOperation
430
+ ::FileUtils.rm_f(modified_lockfile_path)
431
+ Gems.delete_at_exit(modified_lockfile_path)
429
432
  end
430
433
 
431
434
  def restore_toys_libs
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "logger"
4
+
3
5
  module Toys
4
6
  module Utils
5
7
  ##
@@ -28,7 +30,6 @@ module Toys
28
30
  # terminal output. Default is `$stderr`.
29
31
  #
30
32
  def initialize(output: nil)
31
- require "logger"
32
33
  require "toys/utils/terminal"
33
34
  @terminal = output || $stderr
34
35
  @terminal = Terminal.new(output: @terminal) unless @terminal.is_a?(Terminal)
metadata CHANGED
@@ -1,14 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toys-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.0
4
+ version: 0.19.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Azuma
8
8
  bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies: []
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: logger
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
12
26
  description: Toys-Core is the command line tool framework underlying Toys. It can
13
27
  be used to create command line executables using the Toys DSL and classes.
14
28
  email:
@@ -78,10 +92,10 @@ homepage: https://github.com/dazuma/toys
78
92
  licenses:
79
93
  - MIT
80
94
  metadata:
81
- changelog_uri: https://dazuma.github.io/toys/gems/toys-core/v0.18.0/file.CHANGELOG.html
95
+ changelog_uri: https://dazuma.github.io/toys/gems/toys-core/v0.19.1/file.CHANGELOG.html
82
96
  source_code_uri: https://github.com/dazuma/toys/tree/main/toys-core
83
97
  bug_tracker_uri: https://github.com/dazuma/toys/issues
84
- documentation_uri: https://dazuma.github.io/toys/gems/toys-core/v0.18.0
98
+ documentation_uri: https://dazuma.github.io/toys/gems/toys-core/v0.19.1
85
99
  rdoc_options: []
86
100
  require_paths:
87
101
  - lib
@@ -96,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
96
110
  - !ruby/object:Gem::Version
97
111
  version: '0'
98
112
  requirements: []
99
- rubygems_version: 3.6.9
113
+ rubygems_version: 4.0.3
100
114
  specification_version: 4
101
115
  summary: Framework for creating command line executables
102
116
  test_files: []