toys-core 0.15.6 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7afb8f468a42a9d5eb626646212238c656e9be93d7a29601631f4e3059975eaf
4
- data.tar.gz: d1b66149b911a8f4a9eab94c459f15cc7400ee871f8f3db1f473d7eddbd2ba61
3
+ metadata.gz: c3713d359e1b03fc22968ed9e3254c82c92e157c92146c6d5728d514eb88dc1c
4
+ data.tar.gz: d96baedc494f78a534a9f0a00ca64409bdbd25e1cf33ec48d2a16f59824c72b9
5
5
  SHA512:
6
- metadata.gz: ec09f204706004b5d6a914c1b2b1eabaaa0bd3d7accbbde8fe9e5d00aae3fba389676400b54abff33fb7cb79f5cb530577d099d0095838523ce9165b6659c52a
7
- data.tar.gz: f47170aecd0ffe9072a6c022a9def134e88114d52c8d8f57fdc4b11aeae68971bb7ee0ad7234ddc7df8f41d2c788e2f723a241f4fb0f0dd67c62281e3a2a6da4
6
+ metadata.gz: 4ca769a0b88654941c7369f9fdcf22c532ae53358c554e38817beea217d9d544d20324cd2288c8d961cd37ab2527a22e82289a3726e6d5113a7c6d42f6502a56
7
+ data.tar.gz: c50ff7f52713c42eed61ea3e2e81e7b53d1a75ccc89b8ceaff6f4114d71d647018ecf80c529d4254234501108dd5e9f95406e297a64d5064d3f7fa88e8297b47
data/CHANGELOG.md CHANGED
@@ -1,8 +1,13 @@
1
1
  # Release History
2
2
 
3
+ ### v0.16.0 / 2025-10-31
4
+
5
+ * ADDED: Updated minimum Ruby version to 2.7
6
+ * FIXED: ToolDefinition#includes_arguments no longer returns true if only default data is set
7
+
3
8
  ### v0.15.6 / 2024-05-15
4
9
 
5
- * FIXED: Fixed argument parsing to allow a flag value with a newline delimited by =
10
+ * FIXED: Fixed argument parsing so flags with value delimited by "=" will support values containing newlines
6
11
 
7
12
  ### v0.15.5 / 2024-01-31
8
13
 
data/README.md CHANGED
@@ -338,7 +338,7 @@ Detailed usage information can be found in the
338
338
 
339
339
  ## System requirements
340
340
 
341
- Toys-Core requires Ruby 2.4 or later.
341
+ Toys-Core requires Ruby 2.7 or later.
342
342
 
343
343
  Most parts of Toys-Core work on JRuby. However, JRuby is not recommended
344
344
  because of JVM boot latency, lack of support for Kernel#fork, and other issues.
@@ -348,7 +348,7 @@ recommended because it has a few known bugs that affect Toys.
348
348
 
349
349
  ## License
350
350
 
351
- Copyright 2019-2023 Daniel Azuma and the Toys contributors
351
+ Copyright 2019-2025 Daniel Azuma and the Toys contributors
352
352
 
353
353
  Permission is hereby granted, free of charge, to any person obtaining a copy
354
354
  of this software and associated documentation files (the "Software"), to deal
@@ -465,7 +465,7 @@ module Toys
465
465
 
466
466
  def handle_single_flags(str)
467
467
  until str.empty?
468
- str = handle_plain_flag("-#{str[0]}", str[1..-1])
468
+ str = handle_plain_flag("-#{str[0]}", str[1..])
469
469
  end
470
470
  end
471
471
 
@@ -521,7 +521,7 @@ module Toys
521
521
 
522
522
  def find_flag(name)
523
523
  flag_result = @tool.resolve_flag(name)
524
- if flag_result.not_found? || @require_exact_flag_match && !flag_result.found_exact?
524
+ if flag_result.not_found? || (@require_exact_flag_match && !flag_result.found_exact?)
525
525
  @errors << FlagUnrecognizedError.new(
526
526
  value: name, suggestions: Compat.suggestions(name, @tool.used_flags)
527
527
  )
data/lib/toys/cli.rb CHANGED
@@ -628,17 +628,15 @@ module Toys
628
628
 
629
629
  def build_executor(tool, context)
630
630
  executor = proc do
631
- begin
632
- if !context[Context::Key::USAGE_ERRORS].empty?
633
- handle_usage_errors(context, tool)
634
- elsif !tool.runnable?
635
- raise NotRunnableError, "No implementation for tool #{tool.display_name.inspect}"
636
- else
637
- yield context
638
- end
639
- rescue ::SignalException => e
640
- handle_signal_by_tool(context, tool, e)
631
+ if !context[Context::Key::USAGE_ERRORS].empty?
632
+ handle_usage_errors(context, tool)
633
+ elsif !tool.runnable?
634
+ raise NotRunnableError, "No implementation for tool #{tool.display_name.inspect}"
635
+ else
636
+ yield context
641
637
  end
638
+ rescue ::SignalException => e
639
+ handle_signal_by_tool(context, tool, e)
642
640
  end
643
641
  tool.built_middleware.reverse_each do |middleware|
644
642
  executor = make_executor(middleware, context, executor)
data/lib/toys/compat.rb CHANGED
@@ -4,13 +4,17 @@ require "rbconfig"
4
4
 
5
5
  module Toys
6
6
  ##
7
- # Compatibility wrappers for older Ruby versions.
7
+ # Compatibility wrappers for certain Ruby implementations and versions, and
8
+ # other environment differences.
8
9
  #
9
10
  # @private
10
11
  #
11
12
  module Compat
12
13
  parts = ::RUBY_VERSION.split(".")
13
- ruby_version = parts[0].to_i * 10000 + parts[1].to_i * 100 + parts[2].to_i
14
+ ruby_version = (parts[0].to_i * 10000) + (parts[1].to_i * 100) + parts[2].to_i
15
+
16
+ # @private
17
+ RUBY_VERSION_CODE = ruby_version
14
18
 
15
19
  # @private
16
20
  def self.jruby?
@@ -27,6 +31,11 @@ module Toys
27
31
  ::RbConfig::CONFIG["host_os"] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
28
32
  end
29
33
 
34
+ # @private
35
+ def self.macos?
36
+ ::RbConfig::CONFIG["host_os"] =~ /darwin/
37
+ end
38
+
30
39
  # @private
31
40
  def self.allow_fork?
32
41
  !jruby? && !truffleruby? && !windows?
@@ -58,86 +67,5 @@ module Toys
58
67
  []
59
68
  end
60
69
  end
61
-
62
- # The :base argument to Dir.glob requires Ruby 2.5 or later.
63
- if ruby_version >= 20500
64
- # @private
65
- def self.glob_in_dir(glob, dir)
66
- ::Dir.glob(glob, base: dir)
67
- end
68
- else
69
- # @private
70
- def self.glob_in_dir(glob, dir)
71
- ::Dir.chdir(dir) { ::Dir.glob(glob) }
72
- end
73
- end
74
-
75
- # Dir.children requires Ruby 2.5 or later.
76
- if ruby_version >= 20500
77
- # @private
78
- def self.dir_children(dir)
79
- ::Dir.children(dir)
80
- end
81
- else
82
- # @private
83
- def self.dir_children(dir)
84
- ::Dir.entries(dir) - [".", ".."]
85
- end
86
- end
87
-
88
- # Due to a bug in Ruby < 2.7, passing an empty **kwargs splat to
89
- # initialize will fail if there are no formal keyword args.
90
- # This also hits TruffleRuby
91
- # (see https://github.com/oracle/truffleruby/issues/2567)
92
- if ruby_version >= 20700 && !truffleruby?
93
- # @private
94
- def self.instantiate(klass, args, kwargs, block)
95
- klass.new(*args, **kwargs, &block)
96
- end
97
- else
98
- # @private
99
- def self.instantiate(klass, args, kwargs, block)
100
- formals = klass.instance_method(:initialize).parameters
101
- if kwargs.empty? && formals.all? { |arg| arg.first != :key && arg.first != :keyrest }
102
- klass.new(*args, &block)
103
- else
104
- klass.new(*args, **kwargs, &block)
105
- end
106
- end
107
- end
108
-
109
- # File.absolute_path? requires Ruby 2.7 or later. For earlier Rubies, use
110
- # an ad-hoc mechanism.
111
- if ruby_version >= 20700
112
- # @private
113
- def self.absolute_path?(path)
114
- ::File.absolute_path?(path)
115
- end
116
- elsif ::Dir.getwd =~ /^[a-zA-Z]:/
117
- # @private
118
- def self.absolute_path?(path)
119
- /^[a-zA-Z]:/.match?(path)
120
- end
121
- else
122
- # @private
123
- def self.absolute_path?(path)
124
- path.start_with?("/")
125
- end
126
- end
127
-
128
- # The second argument to method_defined? and private_method_defined?
129
- # requires Ruby 2.6 or later.
130
- if ruby_version >= 20600
131
- # @private
132
- def self.method_defined_without_ancestors?(klass, name)
133
- klass.method_defined?(name, false) || klass.private_method_defined?(name, false)
134
- end
135
- else
136
- # @private
137
- def self.method_defined_without_ancestors?(klass, name)
138
- klass.instance_methods(false).include?(name) ||
139
- klass.private_instance_methods(false).include?(name)
140
- end
141
- end
142
70
  end
143
71
  end
@@ -49,7 +49,7 @@ module Toys
49
49
  # @return [Toys::Completion::Context]
50
50
  #
51
51
  def with(**delta_params)
52
- Context.new(**@params.merge(delta_params))
52
+ Context.new(**@params, **delta_params)
53
53
  end
54
54
 
55
55
  ##
@@ -195,7 +195,7 @@ module Toys
195
195
  # @private
196
196
  #
197
197
  def hash
198
- string.hash ^ (partial? ? 1 : 0)
198
+ [@string, @partial].hash
199
199
  end
200
200
 
201
201
  ##
@@ -295,7 +295,7 @@ module Toys
295
295
  return [] unless ::File.directory?(dir)
296
296
  prefix = nil if [".", ""].include?(prefix)
297
297
  omits = [".", "..", ""]
298
- children = Compat.glob_in_dir(name, dir).find_all do |child|
298
+ children = ::Dir.glob(name, base: dir).find_all do |child|
299
299
  !omits.include?(child)
300
300
  end
301
301
  children += ::Dir.entries(dir).find_all do |child|
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.15.6"
12
+ VERSION = "0.16.0"
13
13
  end
14
14
 
15
15
  ##
@@ -12,8 +12,8 @@ module Toys
12
12
  # @private A list of method names to avoid using as getters
13
13
  #
14
14
  AVOID_GETTERS = (::Object.instance_methods + [:run, :initialize])
15
- .find_all { |name| /^[a-z]\w*$/.match?(name) }
16
- .map { |name| [name, true] }.to_h
15
+ .grep(/^[a-z]\w*$/)
16
+ .to_h { |name| [name, true] }
17
17
  .freeze
18
18
 
19
19
  class << self
@@ -109,7 +109,8 @@ module Toys
109
109
  when nil
110
110
  return if !/^[a-zA-Z]\w*[!?]?$/.match?(key.to_s) ||
111
111
  AVOID_GETTERS.key?(key) ||
112
- Compat.method_defined_without_ancestors?(tool_class, key)
112
+ tool_class.method_defined?(key, false) ||
113
+ tool_class.private_method_defined?(key, false)
113
114
  end
114
115
  tool_class.class_eval do
115
116
  define_method(key) do
data/lib/toys/dsl/tool.rb CHANGED
@@ -504,7 +504,7 @@ module Toys
504
504
  if template_class.nil?
505
505
  raise ToolDefinitionError, "Template not found: #{name.inspect}"
506
506
  end
507
- template = Compat.instantiate(template_class, args, kwargs, nil)
507
+ template = template_class.new(*args, **kwargs)
508
508
  yield template if block_given?
509
509
  class_exec(template, &template_class.expansion)
510
510
  self
@@ -14,7 +14,7 @@ module Toys::InputFile # rubocop:disable Style/ClassAndModuleChildren
14
14
  def self.evaluate(tool_class, words, priority, remaining_words, source, loader)
15
15
  namespace = ::Module.new
16
16
  namespace.module_eval do
17
- include ::Toys::Context::Key
17
+ include ::Toys::Context::Key # rubocop:disable Layout/EmptyLinesAfterModuleInclusion
18
18
  @__tool_class = tool_class
19
19
  end
20
20
  path = source.source_path
@@ -25,9 +25,7 @@ module Toys::InputFile # rubocop:disable Style/ClassAndModuleChildren
25
25
  const_set(name, namespace)
26
26
  ::Toys::DSL::Internal.prepare(tool_class, words, priority, remaining_words, source, loader) do
27
27
  ::Toys::ContextualError.capture_path("Error while loading Toys config!", path) do
28
- # rubocop:disable Security/Eval
29
- eval(str, __binding, path)
30
- # rubocop:enable Security/Eval
28
+ eval(str, __binding, path) # rubocop:disable Security/Eval
31
29
  end
32
30
  end
33
31
  end
@@ -48,7 +46,7 @@ module Toys::InputFile # rubocop:disable Style/ClassAndModuleChildren
48
46
  return nil if index.nil?
49
47
  "#{string[0, index]}" \
50
48
  "module #{module_name}; @__tool_class.class_eval do; " \
51
- "#{string[index..-1]}\n" \
49
+ "#{string[index..]}\n" \
52
50
  "end; end\n"
53
51
  end
54
52
  end
data/lib/toys/loader.rb CHANGED
@@ -288,7 +288,7 @@ module Toys
288
288
  found_tools = all_cur_definitions.find_all do |tool|
289
289
  name = tool.full_name
290
290
  name.length > len && name.slice(0, len) == words &&
291
- (include_hidden || name[len..-1].none? { |word| word.start_with?("_") })
291
+ (include_hidden || name[len..].none? { |word| word.start_with?("_") })
292
292
  end
293
293
  found_tools.sort_by!(&:full_name)
294
294
  found_tools = filter_non_runnable_tools(found_tools, include_namespaces, include_non_runnable)
@@ -303,7 +303,7 @@ module Toys
303
303
  # @param words [Array<String>] The name of the parent tool
304
304
  # @return [Boolean]
305
305
  #
306
- def has_subtools?(words) # rubocop:disable Naming/PredicateName
306
+ def has_subtools?(words) # rubocop:disable Naming/PredicatePrefix
307
307
  load_for_prefix(words)
308
308
  len = words.length
309
309
  all_cur_definitions.any? do |tool|
@@ -204,7 +204,7 @@ module Toys
204
204
  else
205
205
  klass = @name
206
206
  end
207
- Compat.instantiate(klass, @args, @kwargs, @block)
207
+ klass.new(*@args, **@kwargs, &@block)
208
208
  end
209
209
 
210
210
  ##
data/lib/toys/settings.rb CHANGED
@@ -769,10 +769,19 @@ module Toys
769
769
  attr_reader :default
770
770
  attr_reader :group_class
771
771
 
772
+ ##
773
+ # @return [boolean] Whether the field is a group
774
+ #
772
775
  def group?
773
776
  !@group_class.nil?
774
777
  end
775
778
 
779
+ ##
780
+ # Validate the given value.
781
+ #
782
+ # @return [Object] The validated value
783
+ # @raise [FieldError] If the value cannot be validated
784
+ #
776
785
  def validate(value)
777
786
  validated_value = @type.call(value)
778
787
  if validated_value == ILLEGAL_VALUE
@@ -877,8 +886,8 @@ module Toys
877
886
  raise ::ArgumentError, "Illegal settings field name: #{name}"
878
887
  end
879
888
  existing = public_instance_methods(false)
880
- if existing.include?(name.to_sym) || existing.include?("#{name}=".to_sym) ||
881
- existing.include?("#{name}_set?".to_sym) || existing.include?("#{name}_unset!".to_sym)
889
+ if existing.include?(name.to_sym) || existing.include?(:"#{name}=") ||
890
+ existing.include?(:"#{name}_set?") || existing.include?(:"#{name}_unset!")
882
891
  raise ::ArgumentError, "Settings field already exists: #{name}"
883
892
  end
884
893
  name.to_sym
@@ -48,43 +48,43 @@ module Toys
48
48
  ##
49
49
  # Calls [HighLine#agree](https://www.rubydoc.info/gems/highline/HighLine:agree)
50
50
  #
51
- def agree(*args, &block)
52
- self[KEY].agree(*args, &block)
51
+ def agree(...)
52
+ self[KEY].agree(...)
53
53
  end
54
54
 
55
55
  ##
56
56
  # Calls [HighLine#ask](https://www.rubydoc.info/gems/highline/HighLine:ask)
57
57
  #
58
- def ask(*args, &block)
59
- self[KEY].ask(*args, &block)
58
+ def ask(...)
59
+ self[KEY].ask(...)
60
60
  end
61
61
 
62
62
  ##
63
63
  # Calls [HighLine#choose](https://www.rubydoc.info/gems/highline/HighLine:choose)
64
64
  #
65
- def choose(*args, &block)
66
- self[KEY].choose(*args, &block)
65
+ def choose(...)
66
+ self[KEY].choose(...)
67
67
  end
68
68
 
69
69
  ##
70
70
  # Calls [HighLine#list](https://www.rubydoc.info/gems/highline/HighLine:list)
71
71
  #
72
- def list(*args, &block)
73
- self[KEY].list(*args, &block)
72
+ def list(...)
73
+ self[KEY].list(...)
74
74
  end
75
75
 
76
76
  ##
77
77
  # Calls [HighLine#say](https://www.rubydoc.info/gems/highline/HighLine:say)
78
78
  #
79
- def say(*args, &block)
80
- self[KEY].say(*args, &block)
79
+ def say(...)
80
+ self[KEY].say(...)
81
81
  end
82
82
 
83
83
  ##
84
84
  # Calls [HighLine#indent](https://www.rubydoc.info/gems/highline/HighLine:indent)
85
85
  #
86
- def indent(*args, &block)
87
- self[KEY].indent(*args, &block)
86
+ def indent(...)
87
+ self[KEY].indent(...)
88
88
  end
89
89
 
90
90
  ##
@@ -552,9 +552,8 @@ module Toys
552
552
  # @return [true,false]
553
553
  #
554
554
  def runnable?
555
- @run_handler.is_a?(::Symbol) &&
556
- tool_class.public_instance_methods(false).include?(@run_handler) ||
557
- @run_handler.is_a?(::Proc)
555
+ @run_handler.is_a?(::Proc) ||
556
+ (@run_handler.is_a?(::Symbol) && tool_class.public_instance_methods(false).include?(@run_handler))
558
557
  end
559
558
 
560
559
  ##
@@ -608,8 +607,7 @@ module Toys
608
607
  # @return [true,false]
609
608
  #
610
609
  def includes_arguments?
611
- !default_data.empty? || !flags.empty? ||
612
- !required_args.empty? || !optional_args.empty? ||
610
+ !flags.empty? || !required_args.empty? || !optional_args.empty? ||
613
611
  !remaining_arg.nil? || flags_before_args_enforced?
614
612
  end
615
613
 
@@ -619,7 +617,7 @@ module Toys
619
617
  #
620
618
  def includes_definition?
621
619
  includes_arguments? || runnable? || argument_parsing_disabled? ||
622
- includes_modules? || includes_description?
620
+ includes_modules? || includes_description? || !default_data.empty?
623
621
  end
624
622
 
625
623
  ##
@@ -690,7 +688,7 @@ module Toys
690
688
  # @return [nil] if no acceptor of the given name is found.
691
689
  #
692
690
  def lookup_acceptor(name)
693
- @acceptors.fetch(name.to_s) { |k| @parent ? @parent.lookup_acceptor(k) : nil }
691
+ @acceptors.fetch(name.to_s) { |k| @parent&.lookup_acceptor(k) }
694
692
  end
695
693
 
696
694
  ##
@@ -701,7 +699,7 @@ module Toys
701
699
  # @return [nil] if no template of the given name is found.
702
700
  #
703
701
  def lookup_template(name)
704
- @templates.fetch(name.to_s) { |k| @parent ? @parent.lookup_template(k) : nil }
702
+ @templates.fetch(name.to_s) { |k| @parent&.lookup_template(k) }
705
703
  end
706
704
 
707
705
  ##
@@ -712,7 +710,7 @@ module Toys
712
710
  # @return [nil] if no mixin of the given name is found.
713
711
  #
714
712
  def lookup_mixin(name)
715
- @mixins.fetch(name.to_s) { |k| @parent ? @parent.lookup_mixin(k) : nil }
713
+ @mixins.fetch(name.to_s) { |k| @parent&.lookup_mixin(k) }
716
714
  end
717
715
 
718
716
  ##
@@ -723,7 +721,7 @@ module Toys
723
721
  # @return [nil] if no completion of the given name is found.
724
722
  #
725
723
  def lookup_completion(name)
726
- @completions.fetch(name.to_s) { |k| @parent ? @parent.lookup_completion(k) : nil }
724
+ @completions.fetch(name.to_s) { |k| @parent&.lookup_completion(k) }
727
725
  end
728
726
 
729
727
  ##
@@ -1347,15 +1345,10 @@ module Toys
1347
1345
  "Cannot delegate tool #{display_name.inspect} to #{target.join(' ')} because it" \
1348
1346
  " already delegates to \"#{@delegate_target.join(' ')}\"."
1349
1347
  end
1350
- if includes_arguments?
1351
- raise ToolDefinitionError,
1352
- "Cannot delegate tool #{display_name.inspect} because" \
1353
- " arguments have already been defined."
1354
- end
1355
- if runnable?
1348
+ if includes_arguments? || runnable? || includes_modules? || !default_data.empty?
1356
1349
  raise ToolDefinitionError,
1357
1350
  "Cannot delegate tool #{display_name.inspect} because" \
1358
- " the run method has already been defined."
1351
+ " some implementation has already been created for it."
1359
1352
  end
1360
1353
  disable_argument_parsing
1361
1354
  self.run_handler = make_delegation_run_handler(target)
@@ -1393,7 +1386,7 @@ module Toys
1393
1386
  def finish_definition(loader)
1394
1387
  unless @definition_finished
1395
1388
  ContextualError.capture("Error installing tool middleware!", tool_name: full_name) do
1396
- config_proc = proc { nil }
1389
+ config_proc = proc {}
1397
1390
  @built_middleware.reverse_each do |middleware|
1398
1391
  config_proc = make_config_proc(middleware, loader, config_proc)
1399
1392
  end
@@ -1493,7 +1486,7 @@ module Toys
1493
1486
  case signal
1494
1487
  when ::String, ::Symbol
1495
1488
  sigstr = signal.to_s
1496
- sigstr = sigstr[3..-1] if sigstr.start_with?("SIG")
1489
+ sigstr = sigstr[3..] if sigstr.start_with?("SIG")
1497
1490
  signo = ::Signal.list[sigstr]
1498
1491
  return signo if signo
1499
1492
  when ::Integer
@@ -304,7 +304,7 @@ module Toys
304
304
  if cmd.is_a?(::Array)
305
305
  if cmd.size > 1
306
306
  binary = canonical_binary_spec(cmd.first, exec_opts)
307
- [binary] + cmd[1..-1].map(&:to_s)
307
+ [binary] + cmd[1..].map(&:to_s)
308
308
  else
309
309
  [canonical_binary_spec(Array(cmd.first), exec_opts)]
310
310
  end
@@ -521,14 +521,12 @@ module Toys
521
521
  def capture(which)
522
522
  stream = stream_for(which)
523
523
  @join_threads << ::Thread.new do
524
- begin
525
- data = stream.read
526
- @mutex.synchronize do
527
- @captures[which] = data
528
- end
529
- ensure
530
- stream.close
524
+ data = stream.read
525
+ @mutex.synchronize do
526
+ @captures[which] = data
531
527
  end
528
+ ensure
529
+ stream.close
532
530
  end
533
531
  self
534
532
  end
@@ -577,16 +575,14 @@ module Toys
577
575
  end
578
576
  stream = stream_for(which, allow_in: true)
579
577
  @join_threads << ::Thread.new do
580
- begin
581
- if which == :in
582
- ::IO.copy_stream(io, stream)
583
- else
584
- ::IO.copy_stream(stream, io)
585
- end
586
- ensure
587
- stream.close
588
- io.close
578
+ if which == :in
579
+ ::IO.copy_stream(io, stream)
580
+ else
581
+ ::IO.copy_stream(stream, io)
589
582
  end
583
+ ensure
584
+ stream.close
585
+ io.close
590
586
  end
591
587
  self
592
588
  end
@@ -1065,7 +1061,7 @@ module Toys
1065
1061
  else
1066
1062
  cmd_binary = @spawn_cmd.first
1067
1063
  cmd_binary = cmd_binary.first if cmd_binary.is_a?(::Array)
1068
- "exec: #{([cmd_binary] + @spawn_cmd[1..-1]).inspect}"
1064
+ "exec: #{([cmd_binary] + @spawn_cmd[1..]).inspect}"
1069
1065
  end
1070
1066
  end
1071
1067
  end
@@ -1218,7 +1214,7 @@ module Toys
1218
1214
 
1219
1215
  def interpret_in_array(setting)
1220
1216
  if setting.first.is_a?(::Symbol)
1221
- setup_in_stream_of_type(setting.first, setting[1..-1])
1217
+ setup_in_stream_of_type(setting.first, setting[1..])
1222
1218
  elsif setting.first.is_a?(::String)
1223
1219
  setup_in_stream_of_type(:file, setting)
1224
1220
  elsif setting.size == 2 && setting.first.is_a?(::IO) && setting.last.is_a?(::IO)
@@ -1292,7 +1288,7 @@ module Toys
1292
1288
 
1293
1289
  def interpret_out_array(key, setting)
1294
1290
  if setting.first.is_a?(::Symbol)
1295
- setup_out_stream_of_type(key, setting.first, setting[1..-1])
1291
+ setup_out_stream_of_type(key, setting.first, setting[1..])
1296
1292
  elsif setting.first.is_a?(::String)
1297
1293
  setup_out_stream_of_type(key, :file, setting)
1298
1294
  elsif setting.size == 2 && setting.first.is_a?(::IO) && setting.last.is_a?(::IO)
@@ -1381,7 +1377,7 @@ module Toys
1381
1377
  (arg.first == :autoclose || arg.first.is_a?(::IO))
1382
1378
  [arg.last, :close]
1383
1379
  else
1384
- arg = arg[1..-1] if arg.first == :file
1380
+ arg = arg[1..] if arg.first == :file
1385
1381
  if arg.empty? || !arg.first.is_a?(::String)
1386
1382
  raise "Expected file name for #{key} tee argument"
1387
1383
  end
@@ -1498,49 +1494,41 @@ module Toys
1498
1494
  def write_string_thread(string)
1499
1495
  stream = make_in_pipe
1500
1496
  @join_threads << ::Thread.new do
1501
- begin
1502
- stream.write string
1503
- ensure
1504
- stream.close
1505
- end
1497
+ stream.write string
1498
+ ensure
1499
+ stream.close
1506
1500
  end
1507
1501
  end
1508
1502
 
1509
1503
  def copy_to_in_thread(io)
1510
1504
  stream = make_in_pipe
1511
1505
  @join_threads << ::Thread.new do
1512
- begin
1513
- ::IO.copy_stream(io, stream)
1514
- ensure
1515
- stream.close
1516
- io.close
1517
- end
1506
+ ::IO.copy_stream(io, stream)
1507
+ ensure
1508
+ stream.close
1509
+ io.close
1518
1510
  end
1519
1511
  end
1520
1512
 
1521
1513
  def copy_from_out_thread(key, io)
1522
1514
  stream = make_out_pipe(key)
1523
1515
  @join_threads << ::Thread.new do
1524
- begin
1525
- ::IO.copy_stream(stream, io)
1526
- ensure
1527
- stream.close
1528
- io.close
1529
- end
1516
+ ::IO.copy_stream(stream, io)
1517
+ ensure
1518
+ stream.close
1519
+ io.close
1530
1520
  end
1531
1521
  end
1532
1522
 
1533
1523
  def capture_stream_thread(key)
1534
1524
  stream = make_out_pipe(key)
1535
1525
  @join_threads << ::Thread.new do
1536
- begin
1537
- data = stream.read
1538
- @mutex.synchronize do
1539
- @captures[key] = data
1540
- end
1541
- ensure
1542
- stream.close
1526
+ data = stream.read
1527
+ @mutex.synchronize do
1528
+ @captures[key] = data
1543
1529
  end
1530
+ ensure
1531
+ stream.close
1544
1532
  end
1545
1533
  end
1546
1534
  end
@@ -146,11 +146,9 @@ module Toys
146
146
  #
147
147
  def activate(name, *requirements)
148
148
  Gems.synchronize do
149
- begin
150
- gem(name, *requirements)
151
- rescue ::Gem::LoadError => e
152
- handle_activation_error(e, name, requirements)
153
- end
149
+ gem(name, *requirements)
150
+ rescue ::Gem::LoadError => e
151
+ handle_activation_error(e, name, requirements)
154
152
  end
155
153
  end
156
154
 
@@ -395,7 +393,7 @@ module Toys
395
393
  modified_lockfile_path = find_lockfile_path(modified_gemfile_path)
396
394
  if ::File.readable?(lockfile_path)
397
395
  lockfile_content = ::File.read(lockfile_path)
398
- ::File.open(modified_lockfile_path, "w") { |file| file.write(lockfile_content) }
396
+ ::File.write(modified_lockfile_path, lockfile_content)
399
397
  end
400
398
  modified_gemfile_path
401
399
  end
@@ -423,9 +421,11 @@ module Toys
423
421
  end
424
422
 
425
423
  def delete_modified_gemfile(modified_gemfile_path)
424
+ # rubocop:disable Lint/NonAtomicFileOperation
426
425
  ::File.delete(modified_gemfile_path) if ::File.exist?(modified_gemfile_path)
427
426
  modified_lockfile_path = find_lockfile_path(modified_gemfile_path)
428
427
  ::File.delete(modified_lockfile_path) if ::File.exist?(modified_lockfile_path)
428
+ # rubocop:enable Lint/NonAtomicFileOperation
429
429
  end
430
430
 
431
431
  def restore_toys_libs
@@ -465,7 +465,7 @@ module Toys
465
465
  return if result.success?
466
466
  terminal.puts("Failed to update. Trying update with clean lockfile...")
467
467
  lockfile_path = find_lockfile_path(gemfile_path)
468
- ::File.delete(lockfile_path) if ::File.exist?(lockfile_path)
468
+ ::File.delete(lockfile_path) if ::File.exist?(lockfile_path) # rubocop:disable Lint/NonAtomicFileOperation
469
469
  result = exec_util.exec_ruby([bundler_bin, "update"] + args)
470
470
  return if result.success?
471
471
  raise ::Bundler::InstallError, "Failed to install or update bundle: #{gemfile_path}"
@@ -545,7 +545,7 @@ module Toys
545
545
  repo_dir = ::File.join(dir, REPO_DIR_NAME)
546
546
  is_sha = commit =~ /^[0-9a-f]{40}$/
547
547
  update = repo_lock.ref_stale?(commit, update) unless is_sha
548
- if update && !is_sha || !commit_exists?(repo_dir, local_commit)
548
+ if (update && !is_sha) || !commit_exists?(repo_dir, local_commit)
549
549
  git(repo_dir, ["fetch", "--depth=1", "--force", "origin", "#{commit}:#{local_commit}"],
550
550
  error_message: "Unable to fetch commit: #{commit}")
551
551
  repo_lock.update_ref!(commit)
@@ -579,7 +579,7 @@ module Toys
579
579
  repo_path = ::File.join(dir, REPO_DIR_NAME)
580
580
  ::FileUtils.mkdir_p(into)
581
581
  ::FileUtils.chmod_R("u+w", into, force: true)
582
- Compat.dir_children(into).each { |child| ::FileUtils.rm_rf(::File.join(into, child)) }
582
+ ::Dir.children(into).each { |child| ::FileUtils.rm_rf(::File.join(into, child)) }
583
583
  result = copy_from_repo(repo_path, into, sha, path)
584
584
  repo_lock.access_repo!
585
585
  result
@@ -588,7 +588,7 @@ module Toys
588
588
  def copy_from_repo(repo_dir, into, sha, path)
589
589
  git(repo_dir, ["checkout", sha])
590
590
  if path == "."
591
- Compat.dir_children(repo_dir).each do |entry|
591
+ ::Dir.children(repo_dir).each do |entry|
592
592
  next if entry == ".git"
593
593
  to_path = ::File.join(into, entry)
594
594
  unless ::File.exist?(to_path)
@@ -746,6 +746,9 @@ module Toys
746
746
  @data["sources"][sha]&.fetch(path, nil)
747
747
  end
748
748
 
749
+ ##
750
+ # @private
751
+ #
749
752
  def find_sources(paths: nil, shas: nil)
750
753
  results = []
751
754
  @data["sources"].each do |sha, sha_data|
@@ -312,7 +312,7 @@ module Toys
312
312
  remaining_doc = desc
313
313
  if initial.size <= @indent + @left_column_width
314
314
  @lines << "#{initial} #{desc.first}"
315
- remaining_doc = desc[1..-1] || []
315
+ remaining_doc = desc[1..] || []
316
316
  else
317
317
  @lines << initial
318
318
  end
@@ -398,7 +398,7 @@ module Toys
398
398
  else
399
399
  desc = wrap_indent_indent2(WrappableString.new(["#{prefix} -"] + desc.fragments))
400
400
  @lines << indent_str(desc[0])
401
- desc[1..-1].each do |line|
401
+ desc[1..].each do |line|
402
402
  @lines << indent2_str(line)
403
403
  end
404
404
  end
@@ -523,7 +523,7 @@ module Toys
523
523
  desc += delegate.long_desc
524
524
  end
525
525
  end
526
- desc = desc[1..-1] if desc.first == ""
526
+ desc = desc[1..] if desc.first == ""
527
527
  desc = wrap_indent(desc)
528
528
  return if desc.empty?
529
529
  @lines << ""
@@ -728,7 +728,7 @@ module Toys
728
728
  else
729
729
  desc = wrap_indent(WrappableString.new(["#{prefix} -"] + desc.fragments))
730
730
  @lines << desc[0]
731
- desc[1..-1].each do |line|
731
+ desc[1..].each do |line|
732
732
  @lines << indent_str(line)
733
733
  end
734
734
  end
@@ -64,11 +64,9 @@ module Toys
64
64
  def start
65
65
  if @command
66
66
  result = @exec_service.exec(@command, in: :controller) do |controller|
67
- begin
68
- yield controller.in if controller.pid
69
- rescue ::Errno::EPIPE => e
70
- raise e unless @rescue_broken_pipes
71
- end
67
+ yield controller.in if controller.pid
68
+ rescue ::Errno::EPIPE => e
69
+ raise e unless @rescue_broken_pipes
72
70
  end
73
71
  return result.exit_code unless result.failed?
74
72
  end
@@ -155,12 +155,10 @@ module Toys
155
155
  #
156
156
  def write(str = "", *styles)
157
157
  @output_mutex.synchronize do
158
- begin
159
- output&.write(apply_styles(str.to_s, *styles))
160
- output&.flush
161
- rescue ::IOError
162
- nil
163
- end
158
+ output&.write(apply_styles(str.to_s, *styles))
159
+ output&.flush
160
+ rescue ::IOError
161
+ nil
164
162
  end
165
163
  self
166
164
  end
@@ -173,11 +171,9 @@ module Toys
173
171
  #
174
172
  def readline
175
173
  @input_mutex.synchronize do
176
- begin
177
- input&.gets
178
- rescue ::IOError
179
- nil
180
- end
174
+ input&.gets
175
+ rescue ::IOError
176
+ nil
181
177
  end
182
178
  end
183
179
 
@@ -470,7 +466,7 @@ module Toys
470
466
  @terminal.write(@frames[@cur_frame][0])
471
467
  @cond.wait(@frame_length)
472
468
  size = @frames[@cur_frame][1]
473
- @terminal.write("\b" * size + " " * size + "\b" * size)
469
+ @terminal.write(("\b" * size) + (" " * size) + ("\b" * size))
474
470
  @cur_frame += 1
475
471
  @cur_frame = 0 if @cur_frame >= @frames.size
476
472
  end
@@ -260,7 +260,7 @@ module Toys
260
260
 
261
261
  def validate_dir_env(name)
262
262
  path = @env[name].to_s
263
- !path.empty? && Compat.absolute_path?(path) ? path : nil
263
+ !path.empty? && ::File.absolute_path?(path) ? path : nil
264
264
  end
265
265
 
266
266
  def validate_dirs_env(name)
@@ -268,7 +268,7 @@ module Toys
268
268
  end
269
269
 
270
270
  def validate_dirs(paths)
271
- paths = paths.find_all { |path| Compat.absolute_path?(path) }
271
+ paths = paths.find_all { |path| ::File.absolute_path?(path) }
272
272
  paths.empty? ? nil : paths
273
273
  end
274
274
 
data/lib/toys-core.rb CHANGED
@@ -120,10 +120,11 @@ module Toys
120
120
  CORE_LIB_PATH = __dir__
121
121
  end
122
122
 
123
+ require "toys/compat"
124
+
123
125
  require "toys/acceptor"
124
126
  require "toys/arg_parser"
125
127
  require "toys/cli"
126
- require "toys/compat"
127
128
  require "toys/completion"
128
129
  require "toys/context"
129
130
  require "toys/core"
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toys-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.6
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Azuma
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-05-15 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies: []
13
12
  description: Toys-Core is the command line tool framework underlying Toys. It can
14
13
  be used to create command line executables using the Toys DSL and classes.
@@ -79,11 +78,10 @@ homepage: https://github.com/dazuma/toys
79
78
  licenses:
80
79
  - MIT
81
80
  metadata:
82
- changelog_uri: https://dazuma.github.io/toys/gems/toys-core/v0.15.6/file.CHANGELOG.html
81
+ changelog_uri: https://dazuma.github.io/toys/gems/toys-core/v0.16.0/file.CHANGELOG.html
83
82
  source_code_uri: https://github.com/dazuma/toys/tree/main/toys-core
84
83
  bug_tracker_uri: https://github.com/dazuma/toys/issues
85
- documentation_uri: https://dazuma.github.io/toys/gems/toys-core/v0.15.6
86
- post_install_message:
84
+ documentation_uri: https://dazuma.github.io/toys/gems/toys-core/v0.16.0
87
85
  rdoc_options: []
88
86
  require_paths:
89
87
  - lib
@@ -91,15 +89,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
91
89
  requirements:
92
90
  - - ">="
93
91
  - !ruby/object:Gem::Version
94
- version: 2.4.0
92
+ version: 2.7.0
95
93
  required_rubygems_version: !ruby/object:Gem::Requirement
96
94
  requirements:
97
95
  - - ">="
98
96
  - !ruby/object:Gem::Version
99
97
  version: '0'
100
98
  requirements: []
101
- rubygems_version: 3.5.9
102
- signing_key:
99
+ rubygems_version: 3.6.9
103
100
  specification_version: 4
104
101
  summary: Framework for creating command line executables
105
102
  test_files: []