toys-core 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9ea4eb80e6cbef15a8fafda59f0150f53aeb3a1a196b4749b5abe357d8fc47c
4
- data.tar.gz: 374d30463b4eb8388674c5764a3fbcc050986ae927f3b2cb576d671bf47b3bc5
3
+ metadata.gz: 125540448a7c5431f6c788058f7b5b02d0d089b28def144fa673b579bd2b1bff
4
+ data.tar.gz: 878c996695f10022f756b2a17459e556f29a06b1e96b8b9e2d636624e8bfc65c
5
5
  SHA512:
6
- metadata.gz: 061e82b410a5640bf233bfed13f6ca15b08b16699c2c8ebee1bece2cb48a0e8af0afb5c96ad28fcbd1ab09b484a679c018a0ea26f40cedb8263fca75cf4fdba6
7
- data.tar.gz: 123e78e35b94cb1b3e56107f379f9e63f3fe4b4151e3d790a6828457c208cbcbd5c8ac9f4ac111d0176499b4e728c600760833dc6159ad00548601d7c3837305
6
+ metadata.gz: 5a3e9c3fe05a4ced439a842c53c6c2cb73026417f5c6707a3a1ac1e8394a003e546d8aa50a73d94749ac79bd2720148ee86a18c53b60fe52a080a8368f133098
7
+ data.tar.gz: 98f82815c36fcca6b534248e98ab642762a513410f22467de12a819d32bd589d6e8a8ca1fd187b55bada01987021c9a3f8922ab0e8dec329cade1239db1f464c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Release History
2
2
 
3
+ ### 0.9.0 / 2019-12-02
4
+
5
+ Functional changes:
6
+
7
+ * ADDED: The `delegate_to` directive causes the tool to delegate execution to another tool. This means it takes the same arguments and has the same execution behavior.
8
+ * ADDED: The `delegate_to` argument to the `tool` directive causes the tool to delegate to another tool. (Note: the `alias_tool` directive is now just shorthand for creating a tool with a delegate, and as such is mildly deprecated.)
9
+ * ADDED: The `current_tool` function can be called from the DSL to get the current `Toys::Tool` object.
10
+ * ADDED: The `:e` option is now an alias for `:exit_on_nonzero_status`.
11
+ * IMPROVED: `alias_tool` is now just shorthand for delegating. This means, aliases can now point to namespaces and will resolve subtools of their targets, and they now support tab completion and online help.
12
+ * IMPROVED: This release of Toys is now compatible with Ruby 2.7.0-preview3. It fixes some Ruby 2.7 specific bugs, and sanitizes keyword argument usage to eliminate Ruby 2.7 warnings.
13
+ * IMPROVED: JRuby is now supported for most operations. However, JRuby is generally not recommended because of JVM boot latency, lack of Kernel#fork support, and other issues.
14
+ * FIXED: The the `tool` directive no longer crashes if not passed a block.
15
+
16
+ Internal interface changes:
17
+
18
+ * REMOVED: The `Toys::Alias` class has been removed, along with relevant functionality in `Toys::Loader` including `Toys::Loader#make_alias`. Use tool delegation instead.
19
+ * CHANGED: Positional arguments to middleware specs must now be wrapped in an array.
20
+ * CHANGED: The `Toys::ArgParser` constructor takes a `default_data` argument instead of `verbosity`.
21
+ * CHANGED: Version constant is now `Toys::Core::VERSION`.
22
+ * CHNAGED: The `flag` argument to `Toys::Flag::DefaultCompletion#initialize` is now a required keyword argument.
23
+ * ADDED: `Toys::Tool#delegate_to` causes the tool to delegate to another tool.
24
+ * ADDED: The `Toys::Context::Key::DELEGATED_FROM` key provides the delegating context, if any.
25
+
3
26
  ### 0.8.1 / 2019-11-19
4
27
 
5
28
  * FIXED: Listing subtools would crash if a broken alias was present.
@@ -12,6 +35,7 @@ This is a major update with significant new features and a bunch of fixes. It al
12
35
  Major changes and features:
13
36
 
14
37
  * CHANGED: Relicensed under the MIT License.
38
+ * CHANGED: Requires Ruby 2.3 or later.
15
39
  * ADDED: Tab completion for bash. Added APIs and DSL constructs for tools to customize completions.
16
40
  * ADDED: The usage error screen displays suggestions when an argument is misspelled. (Requires Ruby 2.4 or later.)
17
41
  * ADDED: Tools can provide an interrupt handler and a custom usage error handler. Added appropriate APIs and DSL methods.
data/README.md CHANGED
@@ -9,10 +9,10 @@ to write command line executables using the Toys DSL and the power of the Toys
9
9
  classes.
10
10
 
11
11
  For more detailed information about Toys-Core, see the
12
- [Toys-Core User's Guide](https://www.rubydoc.info/gems/toys-core/file/docs/guide.md).
12
+ [Toys-Core User's Guide](https://dazuma.github.io/toys/gems/toys-core/latest/file.guide.html).
13
13
  For background information about Toys itself, see the
14
- [Toys README](https://www.rubydoc.info/gems/toys) and the
15
- [Toys User Guide](https://www.rubydoc.info/gems/toys/file/docs/guide.md).
14
+ [Toys README](https://dazuma.github.io/toys/gems/toys/latest) and the
15
+ [Toys User Guide](https://dazuma.github.io/toys/gems/toys/latest/file.guide.html).
16
16
 
17
17
  ## Introductory tutorial
18
18
 
@@ -21,14 +21,12 @@ executable using Toys-Core.
21
21
 
22
22
  It assumes basic familiarity with Toys, so, if you have not done so, I
23
23
  recommend first walking through the tutorial in the
24
- [Toys README](https://www.rubydoc.info/gems/toys). It also assumes you are
25
- running a unix-like system such as Linux or macOS. Some commands might need to
26
- be modified if you're running on Windows.
24
+ [Toys README](https://dazuma.github.io/toys/gems/toys/latest). It also assumes
25
+ you are running a unix-like system such as Linux or macOS. Some commands might
26
+ need to be modified if you're running on Windows.
27
27
 
28
28
  ### Install Toys
29
29
 
30
- Toys requires Ruby 2.3 or later. (JRuby is not currently supported.)
31
-
32
30
  Install the **toys-core** gem using:
33
31
 
34
32
  $ gem install toys-core
@@ -36,6 +34,11 @@ Install the **toys-core** gem using:
36
34
  You may also install the **toys** gem, which brings in **toys-core** as a
37
35
  dependency.
38
36
 
37
+ Toys-Core requires Ruby 2.3 or later.
38
+
39
+ Most parts of Toys-Core work on JRuby. However, JRuby is not recommended
40
+ because of JVM boot latency, lack of support for Kernel#fork, and other issues.
41
+
39
42
  ### Create a new executable
40
43
 
41
44
  We'll start by creating an executable Ruby script. Using your favorite text
@@ -327,11 +330,11 @@ deep framework with many more features. Learn about how to write tools using
327
330
  the Toys DSL, including validating and interpreting command line arguments,
328
331
  using templates and mixins, controlling subprocesses, and producing nice styled
329
332
  output, in the
330
- [Toys User Guide](https://www.rubydoc.info/gems/toys/file/docs/guide.md).
333
+ [Toys User Guide](https://dazuma.github.io/toys/gems/toys/latest/file.guide.html).
331
334
  Learn more about how to customize and package your own executable, including
332
335
  handling errors, controlling log output, and providing your own mixins,
333
336
  templates, and middleware, in the
334
- [Toys-Core User Guide](https://www.rubydoc.info/gems/toys-core/file/docs/guide.md).
337
+ [Toys-Core User Guide](https://dazuma.github.io/toys/gems/toys-core/latest/file.guide.html).
335
338
 
336
339
  ## License
337
340
 
data/docs/guide.md CHANGED
@@ -14,7 +14,7 @@ line executables in Ruby using the Toys-Core framework.
14
14
  This guide assumes you are already familiar with Toys itself, including how to
15
15
  define tools by writing Toys files, parsing arguments and flags, and how tools
16
16
  are executed. For background, please see the
17
- [Toys User's Guide](https://www.rubydoc.info/gems/toys/file/docs/guide.md).
17
+ [Toys User's Guide](https://dazuma.github.io/toys/gems/toys/latest/file.guide.html).
18
18
 
19
19
  **(This user's guide is still under construction.)**
20
20
 
data/lib/toys-core.rb CHANGED
@@ -71,13 +71,12 @@ module Toys
71
71
  end
72
72
 
73
73
  require "toys/acceptor"
74
- require "toys/alias"
75
74
  require "toys/arg_parser"
76
75
  require "toys/cli"
77
76
  require "toys/compat"
78
77
  require "toys/completion"
79
78
  require "toys/context"
80
- require "toys/core_version"
79
+ require "toys/core"
81
80
  require "toys/dsl/flag"
82
81
  require "toys/dsl/flag_group"
83
82
  require "toys/dsl/positional_arg"
data/lib/toys/acceptor.rb CHANGED
@@ -512,36 +512,61 @@ module Toys
512
512
  # default pass-through acceptor {Toys::Acceptor::DEFAULT}. Any type
513
513
  # description you provide is ignored.
514
514
  #
515
- # @param spec [Object] See the description for recognized values.
516
- # @param type_desc [String] The type description for interpolating into
515
+ # Additional options:
516
+ #
517
+ # * `:type_desc` (String) The type description for interpolating into
517
518
  # help text. Ignored if the spec indicates the default acceptor or a
518
519
  # well-known acceptor.
520
+ #
521
+ # @param spec [Object] See the description for recognized values.
522
+ # @param options [Hash] Additional options to pass to the completion.
519
523
  # @param block [Proc] See the description for recognized forms.
520
524
  # @return [Toys::Acceptor::Base,Proc]
521
525
  #
522
- def create(spec = nil, type_desc: nil, &block)
526
+ def create(spec = nil, **options, &block)
523
527
  well_known = lookup_well_known(spec)
524
528
  return well_known if well_known
529
+ if spec.is_a?(::Hash)
530
+ options = options.merge(spec)
531
+ spec = nil
532
+ end
533
+ spec ||= options.delete(:"")
534
+ internal_create(spec, options, block)
535
+ end
536
+
537
+ ## @private
538
+ def scalarize_spec(spec, options, block)
539
+ spec ||= block
540
+ if options.empty?
541
+ spec
542
+ elsif spec
543
+ options.merge({"": spec})
544
+ else
545
+ options
546
+ end
547
+ end
548
+
549
+ private
550
+
551
+ def internal_create(spec, options, block)
525
552
  case spec
526
553
  when Base
527
554
  spec
528
555
  when ::Regexp
529
- Pattern.new(spec, type_desc: type_desc, &block)
556
+ Pattern.new(spec, **options, &block)
530
557
  when ::Array
531
- Enum.new(spec, type_desc: type_desc)
558
+ Enum.new(spec, **options)
532
559
  when ::Proc
533
- Simple.new(spec, type_desc: type_desc)
560
+ Simple.new(spec, **options)
534
561
  when ::Range
535
- Range.new(spec, type_desc: type_desc, &block)
562
+ Range.new(spec, **options, &block)
536
563
  when nil, :default
537
- block ? Simple.new(type_desc: type_desc, &block) : DEFAULT
564
+ block ? Simple.new(**options, &block) : DEFAULT
538
565
  else
539
566
  raise ToolDefinitionError, "Illegal acceptor spec: #{spec.inspect}"
540
567
  end
541
568
  end
542
569
 
543
- private
544
-
545
570
  def standard_well_knowns
546
571
  @standard_well_knowns ||= {
547
572
  ::Object => DEFAULT,
@@ -266,7 +266,7 @@ module Toys
266
266
  # to the user. Optional.
267
267
  #
268
268
  def initialize(message = nil, value: nil, values: nil, suggestions: nil)
269
- super(message || "Tool not found: \"#{Array(values).join(' ')}\".",
269
+ super(message || "Tool not found: \"#{Array(values).join(' ')}\"",
270
270
  value: value, suggestions: suggestions)
271
271
  @name = name
272
272
  end
@@ -291,14 +291,14 @@ module Toys
291
291
  #
292
292
  # @param cli [Toys::CLI] The CLI in effect.
293
293
  # @param tool [Toys::Tool] The tool defining the argument format.
294
- # @param verbosity [Integer] The initial verbosity level (default is 0).
294
+ # @param default_data [Hash] Additional initial data (such as verbosity).
295
295
  # @param require_exact_flag_match [Boolean] Whether to require flag matches
296
296
  # be exact (not partial). Default is false.
297
297
  #
298
- def initialize(cli, tool, verbosity: 0, require_exact_flag_match: false)
298
+ def initialize(cli, tool, default_data: {}, require_exact_flag_match: false)
299
299
  @require_exact_flag_match = require_exact_flag_match
300
300
  @loader = cli.loader
301
- @data = initial_data(cli, tool, verbosity)
301
+ @data = initial_data(cli, tool, default_data)
302
302
  @tool = tool
303
303
  @seen_flag_keys = []
304
304
  @errors = []
@@ -439,7 +439,7 @@ module Toys
439
439
  REMAINING_HANDLER = ->(val, prev) { prev.is_a?(::Array) ? prev << val : [val] }
440
440
  ARG_HANDLER = ->(val, _prev) { val }
441
441
 
442
- def initial_data(cli, tool, verbosity)
442
+ def initial_data(cli, tool, default_data)
443
443
  data = {
444
444
  Context::Key::ARGS => nil,
445
445
  Context::Key::CLI => cli,
@@ -451,7 +451,7 @@ module Toys
451
451
  Context::Key::USAGE_ERRORS => [],
452
452
  }
453
453
  Compat.merge_clones(data, tool.default_data)
454
- data[Context::Key::VERBOSITY] ||= verbosity
454
+ default_data.each { |k, v| data[k] ||= v }
455
455
  data
456
456
  end
457
457
 
data/lib/toys/cli.rb CHANGED
@@ -224,7 +224,7 @@ module Toys
224
224
  # @yieldparam cli [Toys::CLI] If you pass a block, the new CLI is yielded
225
225
  # to it so you can add paths and make other modifications.
226
226
  #
227
- def child(_opts = {})
227
+ def child(**_opts)
228
228
  cli = CLI.new(executable_name: @executable_name,
229
229
  config_dir_name: @config_dir_name,
230
230
  config_file_name: @config_file_name,
@@ -392,7 +392,7 @@ module Toys
392
392
  #
393
393
  # @return [Integer] The resulting process status code (i.e. 0 for success).
394
394
  #
395
- def run(*args, verbosity: 0)
395
+ def run(*args, verbosity: 0, delegated_from: nil)
396
396
  tool, remaining = ContextualError.capture("Error finding tool definition") do
397
397
  @loader.lookup(args.flatten)
398
398
  end
@@ -400,7 +400,11 @@ module Toys
400
400
  "Error during tool execution!", tool.source_info&.source_path,
401
401
  tool_name: tool.full_name, tool_args: remaining
402
402
  ) do
403
- run_tool(tool, remaining, verbosity: verbosity)
403
+ default_data = {
404
+ Context::Key::VERBOSITY => verbosity,
405
+ Context::Key::DELEGATED_FROM => delegated_from,
406
+ }
407
+ run_tool(tool, remaining, default_data)
404
408
  end
405
409
  rescue ContextualError, ::Interrupt => e
406
410
  @error_handler.call(e).to_i
@@ -414,12 +418,12 @@ module Toys
414
418
  #
415
419
  # @param tool [Toys::Tool] The tool to run.
416
420
  # @param args [Array<String>] Command line arguments passed to the tool.
417
- # @param verbosity [Integer] Initial verbosity. Default is 0.
421
+ # @param default_data [Hash] Initial tool context data.
418
422
  # @return [Integer] The resulting status code
419
423
  #
420
- def run_tool(tool, args, verbosity: 0)
424
+ def run_tool(tool, args, default_data)
421
425
  arg_parser = ArgParser.new(self, tool,
422
- verbosity: verbosity,
426
+ default_data: default_data,
423
427
  require_exact_flag_match: tool.exact_flag_match_required?)
424
428
  arg_parser.parse(args).finish
425
429
  context = tool.tool_class.new(arg_parser.data)
data/lib/toys/compat.rb CHANGED
@@ -30,6 +30,14 @@ module Toys
30
30
  ## @private
31
31
  CURRENT_VERSION = ::Gem::Version.new(::RUBY_VERSION)
32
32
 
33
+ ## @private
34
+ IS_JRUBY = ::RUBY_PLATFORM == "java"
35
+
36
+ ## @private
37
+ def self.allow_fork?
38
+ !IS_JRUBY && RbConfig::CONFIG["host_os"] !~ /mswin/
39
+ end
40
+
33
41
  ## @private
34
42
  def self.check_minimum_version(version)
35
43
  CURRENT_VERSION >= ::Gem::Version.new(version)
@@ -70,7 +70,7 @@ module Toys
70
70
  # @return [Toys::Completion::Context]
71
71
  #
72
72
  def with(**delta_params)
73
- Context.new(@params.merge(delta_params))
73
+ Context.new(**@params.merge(delta_params))
74
74
  end
75
75
 
76
76
  ##
@@ -208,7 +208,7 @@ module Toys
208
208
 
209
209
  ## @private
210
210
  def hash
211
- string.hash
211
+ string.hash ^ (partial? ? 1 : 0)
212
212
  end
213
213
 
214
214
  ##
@@ -306,7 +306,7 @@ module Toys
306
306
  dir = ::File.expand_path(prefix, @cwd)
307
307
  return [] unless ::File.directory?(dir)
308
308
  prefix = nil if [".", ""].include?(prefix)
309
- omits = [".", ".."]
309
+ omits = [".", "..", ""]
310
310
  children = Compat.glob_in_dir(name, dir).find_all do |child|
311
311
  !omits.include?(child)
312
312
  end
@@ -420,16 +420,22 @@ module Toys
420
420
  # @return [Toys::Completion::Base,Proc]
421
421
  #
422
422
  def self.create(spec = nil, **options, &block)
423
- spec ||= block
423
+ if spec.is_a?(::Hash)
424
+ options = options.merge(spec)
425
+ spec = nil
426
+ end
427
+ spec ||= options.delete(:"") || block
424
428
  case spec
425
429
  when nil, :empty, :default
426
430
  EMPTY
427
431
  when ::Proc, Base
428
432
  spec
429
433
  when ::Array
430
- Enum.new(spec, options)
434
+ Enum.new(spec, **options)
431
435
  when :file_system
432
- FileSystem.new(options)
436
+ FileSystem.new(**options)
437
+ when ::Class
438
+ spec.new(**options)
433
439
  else
434
440
  if spec.respond_to?(:call)
435
441
  spec
@@ -438,5 +444,17 @@ module Toys
438
444
  end
439
445
  end
440
446
  end
447
+
448
+ ## @private
449
+ def self.scalarize_spec(spec, options, block)
450
+ spec ||= block
451
+ if options.empty?
452
+ spec
453
+ elsif spec
454
+ options.merge({"": spec})
455
+ else
456
+ options
457
+ end
458
+ end
441
459
  end
442
460
  end
data/lib/toys/context.rb CHANGED
@@ -86,6 +86,14 @@ module Toys
86
86
  #
87
87
  CONTEXT_DIRECTORY = ::Object.new.freeze
88
88
 
89
+ ##
90
+ # Context key for the context from which the current call was delegated.
91
+ # The value is either another context object, or `nil` if the current
92
+ # call is not delegated.
93
+ # @return [Object]
94
+ #
95
+ DELEGATED_FROM = ::Object.new.freeze
96
+
89
97
  ##
90
98
  # Context key for the active `Logger` object.
91
99
  # @return [Object]
@@ -23,8 +23,16 @@
23
23
 
24
24
  module Toys
25
25
  ##
26
- # Current version of Toys core.
27
- # @return [String]
26
+ # The core Toys classes.
28
27
  #
29
- CORE_VERSION = "0.8.1"
28
+ module Core
29
+ ##
30
+ # Current version of Toys core.
31
+ # @return [String]
32
+ #
33
+ VERSION = "0.9.0"
34
+ end
35
+
36
+ ## @private deprecated
37
+ CORE_VERSION = Core::VERSION
30
38
  end
data/lib/toys/dsl/flag.rb CHANGED
@@ -55,8 +55,8 @@ module Toys
55
55
  @long_desc = long_desc || []
56
56
  @display_name = display_name
57
57
  accept(acceptor)
58
- complete_flags(flag_completion)
59
- complete_values(value_completion)
58
+ complete_flags(flag_completion, **{})
59
+ complete_values(value_completion, **{})
60
60
  end
61
61
 
62
62
  ##
@@ -135,9 +135,7 @@ module Toys
135
135
  # @return [self]
136
136
  #
137
137
  def accept(spec = nil, **options, &block)
138
- @acceptor_spec = spec
139
- @acceptor_options = options
140
- @acceptor_block = block
138
+ @acceptor = Acceptor.scalarize_spec(spec, options, block)
141
139
  self
142
140
  end
143
141
 
@@ -186,9 +184,7 @@ module Toys
186
184
  # @return [self]
187
185
  #
188
186
  def complete_flags(spec = nil, **options, &block)
189
- @flag_completion_spec = spec
190
- @flag_completion_options = options
191
- @flag_completion_block = block
187
+ @flag_completion = Completion.scalarize_spec(spec, options, block)
192
188
  self
193
189
  end
194
190
 
@@ -204,9 +200,7 @@ module Toys
204
200
  # @return [self]
205
201
  #
206
202
  def complete_values(spec = nil, **options, &block)
207
- @value_completion_spec = spec
208
- @value_completion_options = options
209
- @value_completion_block = block
203
+ @value_completion = Completion.scalarize_spec(spec, options, block)
210
204
  self
211
205
  end
212
206
 
@@ -313,16 +307,9 @@ module Toys
313
307
 
314
308
  ## @private
315
309
  def _add_to(tool, key)
316
- acceptor = tool.scalar_acceptor(@acceptor_spec, @acceptor_options, &@acceptor_block)
317
- flag_completion = tool.scalar_completion(
318
- @flag_completion_spec, @flag_completion_options, &@flag_completion_block
319
- )
320
- value_completion = tool.scalar_completion(
321
- @value_completion_spec, @value_completion_options, &@value_completion_block
322
- )
323
310
  tool.add_flag(key, @flags,
324
- accept: acceptor, default: @default, handler: @handler,
325
- complete_flags: flag_completion, complete_values: value_completion,
311
+ accept: @acceptor, default: @default, handler: @handler,
312
+ complete_flags: @flag_completion, complete_values: @value_completion,
326
313
  report_collisions: @report_collisions, group: @group,
327
314
  desc: @desc, long_desc: @long_desc, display_name: @display_name)
328
315
  end