squared 0.8.3 → 0.8.4

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: 037121270d4c460552bc1b0c24580e95dc6d70c678ab2d82f3339567ab652c84
4
- data.tar.gz: 7d7bdbb4164fe03ddfdc64ae0d5b0640cd6d688fa8660f0193399d816f418de1
3
+ metadata.gz: 8864164b082f5ec20e4e23ec0cea81a0d2ad0cde6977a0d484b113e72058421a
4
+ data.tar.gz: 2b46eca82d45c97c5a5e3ee5881e40429967916e2d569a6cf16271a0d0851a94
5
5
  SHA512:
6
- metadata.gz: f96c91ee8cc22e2b81159529e6037b72c07047d7b5fe86834f7fbcf4e89ad884141002d2e47bc121f04c1041a3eb8abc10514f79509e4ee83a1d561dad6d5155
7
- data.tar.gz: 5984989ccad4013691a522196eaee92d047a1a2ab0636731952143fde8ac20b96ae9789fcde670586f46c474217443840bee1e22b54b8b49991cd3032717ef0d
6
+ metadata.gz: 0d03eca41b747dc5fe16d9767870b83733cf475e60df90358631aed94fe4fa1dbc07b00fe6bfea7e845d9b6cf361e028504c3e6302e17564fcbad5e5c3ea7a80
7
+ data.tar.gz: 20e7c2931780199d66ac670a4fa5b74e6a3a2b71215b1edb32cd48d0c1ca0c0622ee7bdabca21291d10add12231540092936f1e9aa3ffd8f065170ad3c65630f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,68 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.4] - 2026-09-04
4
+
5
+ ### Added
6
+
7
+ - Common module Host for capabilities was created.
8
+ - OptionPartition can parse option values with a Regexp.
9
+ - OptionPartition can parse option values with a set group.
10
+ - Docker property registry was converted into an accessor.
11
+ - Docker banner arguments [context|registry|tag] were implemented.
12
+ - OptionPartition can parse single flags with a quoted merged value.
13
+ - Docker command image action push is interactive.
14
+ - Docker accessor tag can be initialized through ENV with TAG_${NAME}.
15
+ - OptionPartition can parse option values with an explicit boolean.
16
+
17
+ ### Changed
18
+
19
+ - Git command log action view with interactive supports exclusions.
20
+ - OptionPartition static method union returns a copy of original target.
21
+
22
+ ## [0.7.11]
23
+
24
+ ### Added
25
+
26
+ - Node command package action [approve|deny] is interactive.
27
+
28
+ ### Fixed
29
+
30
+ - Docker container commands [run|exec] did not sanitize input prompt.
31
+
32
+ ## [0.6.18]
33
+
34
+ ### Added
35
+
36
+ - Docker command bake action buildx with first arg "-" inherits base args.
37
+
38
+ ### Fixed
39
+
40
+ - Git action grep did not recognize format and max-count.
41
+ - Node command nvm did not quote executable path.
42
+
43
+ ## [0.5.26]
44
+
45
+ ### Fixed
46
+
47
+ - Node command nvm did not quote executable path.
48
+
49
+ ## [0.4.41]
50
+
51
+ ### Added
52
+
53
+ - Common base argument SUCCESS for command confirmation was created.
54
+
55
+ ### Changed
56
+
57
+ - Program options prefer using quotes instead of escape.
58
+ - Docker container run arguments with a leading dash are concatenated.
59
+
60
+ ### Fixed
61
+
62
+ - Common prompt method choice did not grep list items.
63
+ - OptionPartition static method strip did not parse flags with dashes.
64
+ - Project base method choice_index did not support multiple selections.
65
+
3
66
  ## [0.8.3] - 2026-08-18
4
67
 
5
68
  ### Added
@@ -28,10 +91,6 @@
28
91
 
29
92
  ## [0.6.17]
30
93
 
31
- ### Added
32
-
33
- - NPM command install can bypass engines with ENV and engine-strict=false.
34
-
35
94
  ### Fixed
36
95
 
37
96
  - Ruby command gem action push interactive file selector did nothing.
@@ -2028,10 +2087,12 @@
2028
2087
 
2029
2088
  - Changelog was created.
2030
2089
 
2090
+ [0.8.4]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.4
2031
2091
  [0.8.3]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.3
2032
2092
  [0.8.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.2
2033
2093
  [0.8.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.1
2034
2094
  [0.8.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.0
2095
+ [0.7.11]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.11
2035
2096
  [0.7.10]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.10
2036
2097
  [0.7.9]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.9
2037
2098
  [0.7.8]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.8
@@ -2043,6 +2104,7 @@
2043
2104
  [0.7.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.2
2044
2105
  [0.7.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.1
2045
2106
  [0.7.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.0
2107
+ [0.6.18]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.18
2046
2108
  [0.6.17]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.17
2047
2109
  [0.6.16]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.16
2048
2110
  [0.6.15]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.15
@@ -2061,6 +2123,7 @@
2061
2123
  [0.6.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.2
2062
2124
  [0.6.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.1
2063
2125
  [0.6.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.0
2126
+ [0.5.26]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.26
2064
2127
  [0.5.25]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.25
2065
2128
  [0.5.24]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.24
2066
2129
  [0.5.23]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.23
@@ -2087,6 +2150,7 @@
2087
2150
  [0.5.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.2-ruby
2088
2151
  [0.5.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.1-ruby
2089
2152
  [0.5.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.0-ruby
2153
+ [0.4.41]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.41
2090
2154
  [0.4.40]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.40
2091
2155
  [0.4.39]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.39
2092
2156
  [0.4.38]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.38
data/README.md CHANGED
@@ -693,11 +693,9 @@ GIT_COUNT=50 # list display limit
693
693
  | :--------- | :---------------- | :------------------------------------------------------------------ |
694
694
  | branch | create | TRACK=0,1,s F|FORCE |
695
695
  | branch | move copy | F|FORCE |
696
- | branch | delete | COUNT=n |
697
696
  | branch | global | SYNC |
698
- | checkout | branch | DETACH TRACK=s COUNT=n |
697
+ | checkout | branch | D|DETACH TRACK=s |
699
698
  | checkout | detach | REFLOG=1 |
700
- | checkout | track | COUNT=n |
701
699
  | checkout | global path | HEAD=s PATHSPEC=s |
702
700
  | checkout | * | F|FORCE MERGE |
703
701
  | clone | * | DEPTH=n ORIGIN=s BRANCH=s REVISION=s BARE=1 LOCAL=0,1 |
@@ -721,10 +719,10 @@ GIT_COUNT=50 # list display limit
721
719
  | rebase | onto | INTERACTIVE I HEAD=s |
722
720
  | reset | mode (mixed) | N REFRESH=0 |
723
721
  | reset | index | PATHSPEC=s |
724
- | reset | commit | COUNT=n REFLOG=1 |
722
+ | reset | commit | REFLOG=1 |
725
723
  | reset | -commit | HEAD=s |
726
724
  | restore | * | PATHSPEC=s |
727
- | revbuild | global | UNTRACKED_FILES=s IGNORE_SUBMODULES=s IGNORED=s (status) |
725
+ | revbuild | global (status) | UNTRACKED_FILES=s IGNORE_SUBMODULES=s IGNORED=s |
728
726
  | stash | push | PATHSPEC=s |
729
727
  | stash | global | ALL=0,1 KEEP_INDEX=0,1 INCLUDE_UNTRACKED=0,1 STAGED=0,1 M|MESSAGE=s |
730
728
  | status | global | BRANCH LONG IGNORE_SUBMODULES=s,0-3 PATHSPEC=s |
@@ -732,9 +730,7 @@ GIT_COUNT=50 # list display limit
732
730
  | switch | detach | REFLOG=1 |
733
731
  | switch | -detach | HEAD=s |
734
732
  | switch | * | F|FORCE |
735
- | tag | add | SIGN FORCE HEAD=s M|MESSAGE=s TRAILER=s |
736
- | tag | sign | F|FORCE HEAD=s M|MESSAGE=s TRAILER=s |
737
- | tag | delete | COUNT=n |
733
+ | tag | add sign | F|FORCE HEAD=s M|MESSAGE=s TRAILER=s |
738
734
  | rev | commit branch | HEAD=s |
739
735
 
740
736
  ### Docker
@@ -749,6 +745,8 @@ DOCKER_TAG_${NAME}=v0.1.0 # project only (override)
749
745
  DOCKER_ALL=1 # list every image/container
750
746
  DOCKER_Y=1 # confirm all
751
747
 
748
+ TAG_${NAME}=1.0.0 # initialize
749
+
752
750
  BUILD_SQUARED_OPTS="NODE_TAG=24 RUBY_VERSION=4.0.0" DOCKER_SQUARED_OPTS="--no-cache --label=v1" rake squared:build
753
751
  docker build --no-cache --label=v1 --build-arg="NODE_TAG=24" --build-arg="RUBY_VERSION=4.0.0" .
754
752
  ```
@@ -15,6 +15,7 @@ module Squared
15
15
  CHOICE: 25,
16
16
  QUOTE: "'",
17
17
  SPACE: ' => ',
18
+ SUCCESS: 'Success',
18
19
  GRAPH: %w[| - | \\ -].freeze,
19
20
  BORDER: %w[| - - - - - | | - -].freeze,
20
21
  VIEW: 'view',
@@ -89,7 +89,8 @@ module Squared
89
89
  end
90
90
  else
91
91
  t = type.to_sym
92
- if (c = __get__(:colors)[t] || __get__(:colors)[t.to_s.sub('bright_', '').to_sym])
92
+ c = __get__(:colors)[t] || __get__(:colors)[t.to_s.sub('bright_', '').to_sym]
93
+ if c
93
94
  if index == -1
94
95
  s = wrap.call(s, [c])
95
96
  else
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake'
4
+
5
+ module Squared
6
+ module Common
7
+ module Host
8
+ module_function
9
+
10
+ def windows?
11
+ !!Rake::Win32.windows?
12
+ end
13
+
14
+ def mri?
15
+ RUBY_ENGINE == 'ruby'
16
+ end
17
+
18
+ def docker?
19
+ !Dir['/.dockerenv', '/docker-*.{sh,d}'].empty?
20
+ end
21
+
22
+ def powershell?
23
+ return true if ENV['SHELL']&.end_with?(File.join('', 'pwsh'))
24
+ return false unless windows?
25
+
26
+ case ENV['TERM_PROGRAM']
27
+ when 'powershell.exe', 'vscode'
28
+ true
29
+ else
30
+ ENV.fetch('PSModulePath', '').split(';', 2).size > 1
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -57,6 +57,7 @@ module Squared
57
57
  def choice(msg, list = nil, min: 1, max: 1, multiple: false, index: false, grep: nil, border: nil, auto: true,
58
58
  force: true, attempts: 3, timeout: 0, empty: nil)
59
59
  require 'timeout'
60
+ multiple = Array(multiple) if multiple.is_a?(::Numeric)
60
61
  if list
61
62
  grep &&= Array(grep).map { |val| Regexp.new(val) }
62
63
  items = list.each_with_object([]) do |val, out|
@@ -81,7 +82,7 @@ module Squared
81
82
  puts print_footer(border: border)
82
83
  end
83
84
  msg = "#{msg + (force ? ':' : '?')} [#{min}-#{max}#{if (n = multiple)
84
- "|,#{n.is_a?(::Numeric) ? "{#{n}}" : '*'}"
85
+ "|,#{n.is_a?(::Array) ? "{#{n.join(',')}}" : '*'}"
85
86
  end}] "
86
87
  end
87
88
  end
@@ -110,7 +111,7 @@ module Squared
110
111
  unless k.include?(nil)
111
112
  k.uniq!
112
113
  k.sort!
113
- unless multiple.is_a?(::Numeric) && multiple != k.size
114
+ unless multiple.is_a?(::Array) && !multiple.include?(k.size)
114
115
  return index || !items ? k : k.map { |i| items[i.pred] }
115
116
  end
116
117
  end
@@ -41,12 +41,12 @@ module Squared
41
41
 
42
42
  "#{$1}=%s" % if $2.include?(' ')
43
43
  shell_quote($2, option: false)
44
- elsif Rake::Win32.windows?
44
+ elsif windows?
45
45
  $2
46
46
  else
47
47
  Shellwords.escape($2)
48
48
  end
49
- elsif Rake::Win32.windows?
49
+ elsif windows?
50
50
  quote ? shell_quote(val, force: force, double: double) : val
51
51
  else
52
52
  val.empty? ? '' : Shellwords.escape(val)
@@ -61,7 +61,7 @@ module Squared
61
61
  if val =~ QUOTE_VALUE
62
62
  if pass == '"' || pass == "'"
63
63
  return val if pass == $1
64
- elsif pass || ($1 == '"' && Rake::Win32.windows? && val.match?(/([#{File::SEPARATOR} ]|\\")/o))
64
+ elsif pass || ($1 == '"' && windows? && val.match?(/([#{File::SEPARATOR} ]|\\")/o))
65
65
  return val
66
66
  end
67
67
  case preserve
@@ -74,7 +74,7 @@ module Squared
74
74
  end
75
75
  end
76
76
  q = -> { (base || val).gsub("'\\\\''", "'") }
77
- if double || Rake::Win32.windows? || (ARG[:QUOTE] == '"' && !override)
77
+ if double || windows? || (ARG[:QUOTE] == '"' && !override)
78
78
  "\"#{q.call.gsub(/(?<!\\)"/, '\\"')}\""
79
79
  else
80
80
  "'#{q.call.gsub("'", "'\\\\''")}'"
@@ -82,7 +82,7 @@ module Squared
82
82
  end
83
83
 
84
84
  def shell_option(flag, val = nil, sep: '=', escape: true, quote: true, force: true, double: false, merge: false,
85
- switch: nil, override: false)
85
+ switch: nil, explicit: false, override: false)
86
86
  flag = flag.to_s
87
87
  if flag =~ QUOTE_VALUE
88
88
  double = $1 == '"'
@@ -91,18 +91,15 @@ module Squared
91
91
  override = true
92
92
  end
93
93
  sep = unless flag.empty?
94
+ single = merge ? '' : explicit.is_a?(::String) ? explicit : ' '
94
95
  if flag[0] == '-'
95
- if flag[1] == '-'
96
- sep
97
- else
98
- merge ? '' : ' '
99
- end
96
+ flag[1] == '-' ? sep : single
100
97
  elsif switch
101
98
  pre = switch unless flag.start_with?(switch)
102
99
  merge ? '' : sep
103
100
  elsif flag.size == 1
104
101
  pre = '-'
105
- merge ? '' : ' '
102
+ single
106
103
  else
107
104
  pre = '--'
108
105
  sep
@@ -164,7 +161,7 @@ module Squared
164
161
  def shell_bin(name, env: true)
165
162
  path = defined?(PATH) ? PATH : {}
166
163
  key = name.to_s.upcase
167
- key = File.basename(key, '.*') if Rake::Win32.windows?
164
+ key = File.basename(key, '.*') if windows?
168
165
  shell_quote((env && ENV["PATH_#{key}"]) || path[key] || path[key.to_sym] || path[key = key.downcase] ||
169
166
  path[key.to_sym] || name, option: false, force: false, double: true)
170
167
  end
@@ -187,6 +184,10 @@ module Squared
187
184
  def basic_option(flag, val, **kwargs)
188
185
  shell_option(flag, val, escape: false, force: false, **kwargs)
189
186
  end
187
+
188
+ def windows?
189
+ !!Rake::Win32.windows?
190
+ end
190
191
  end
191
192
  end
192
193
  end
@@ -105,18 +105,21 @@ module Squared
105
105
  time << "#{d}d"
106
106
  epoch -= d * dd
107
107
  end
108
- if (h = epoch / hh) > 0
108
+ h = epoch / hh
109
+ if h > 0
109
110
  time << (clock ? h.to_s : "#{h}h")
110
111
  epoch -= h * hh
111
112
  end
112
- if (m = epoch / mm) > 0
113
+ m = epoch / mm
114
+ if m > 0
113
115
  time << (clock ? m.to_s.rjust(2, '0') : "#{m}m")
114
116
  epoch -= m * mm
115
117
  elsif clock
116
118
  time << '00'
117
119
  end
118
120
  unless hm
119
- if (s = epoch / ss) > 0
121
+ s = epoch / ss
122
+ if s > 0
120
123
  time << (clock ? s.to_s.rjust(2, '0') : "#{s}s")
121
124
  epoch -= s * ss
122
125
  elsif clock
@@ -144,7 +147,7 @@ module Squared
144
147
  end
145
148
 
146
149
  def env_key(*val)
147
- val.join('_').gsub(/\W+/, '_').upcase
150
+ val.join('_').sub(/\A\d+\W*/, '__').gsub(/\W+/, '_').upcase
148
151
  end
149
152
 
150
153
  def env_bool(key, default = false, index: false, **kwargs)
@@ -4,6 +4,7 @@ require 'set'
4
4
 
5
5
  require_relative 'common/base'
6
6
  require_relative 'common/format'
7
+ require_relative 'common/host'
7
8
  require_relative 'common/prompt'
8
9
  require_relative 'common/shell'
9
10
  require_relative 'common/system'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- VERSION = '0.8.3'
4
+ VERSION = '0.8.4'
5
5
  end
@@ -5,6 +5,7 @@ module Squared
5
5
  class Application
6
6
  include Enumerable
7
7
  include Common::Format
8
+ include Host
8
9
  include Utils
9
10
  include Rake::DSL
10
11
 
@@ -13,8 +14,8 @@ module Squared
13
14
 
14
15
  class << self
15
16
  def implement(*objs, base: nil)
16
- objs.each_with_index do |obj, index|
17
- if base && index == 0
17
+ objs.each_with_index do |obj, i|
18
+ if base && i == 0
18
19
  case base
19
20
  when 0, true
20
21
  @kind_extend[0] = obj
@@ -215,7 +216,7 @@ module Squared
215
216
  end
216
217
  raise 'method not found'
217
218
  rescue => e
218
- warn log_message(Logger::WARN, e, subject: main, hint: hint || args)
219
+ warn log_warn(e, subject: main, hint: hint || args)
219
220
  end
220
221
  end
221
222
  @group = nil
@@ -740,30 +741,6 @@ module Squared
740
741
  !!find(home)&.enabled?
741
742
  end
742
743
 
743
- def windows?
744
- Rake::Win32.windows?
745
- end
746
-
747
- def mri?
748
- RUBY_ENGINE == 'ruby'
749
- end
750
-
751
- def docker?
752
- !Dir['/.dockerenv', '/docker-*.{sh,d}'].empty?
753
- end
754
-
755
- def powershell?
756
- return true if ENV['SHELL']&.end_with?(File.join('', 'pwsh'))
757
- return false unless windows?
758
-
759
- case ENV['TERM_PROGRAM']
760
- when 'powershell.exe', 'vscode'
761
- true
762
- else
763
- ENV.fetch('PSModulePath', '').split(';', 2).size > 1
764
- end
765
- end
766
-
767
744
  def rootpath(*args)
768
745
  root.join(*args)
769
746
  end
@@ -797,6 +774,7 @@ module Squared
797
774
  end
798
775
 
799
776
  public :task_join
777
+ public(*Host.private_instance_methods(false))
800
778
 
801
779
  private
802
780
 
@@ -12,6 +12,7 @@ module Squared
12
12
  include Enumerable
13
13
  include Comparable
14
14
  include Common::Format
15
+ include Host
15
16
  include System
16
17
  include Shell
17
18
  include Prompt
@@ -338,6 +339,7 @@ module Squared
338
339
  ext = flag == :ext ? param_guard(action, flag, args: args, key: :ext) : flag.to_s
339
340
  tag = param_guard(action, flag, args: args, key: :tag)
340
341
  dir = param_guard(action, flag, args: args, key: :dir)
342
+ digest = args.digest
341
343
  unless tag.match?(URI_SCHEME)
342
344
  tag = unpack_get tag, ext
343
345
  tag ||= if @release
@@ -346,7 +348,7 @@ module Squared
346
348
  raise ArgumentError, message('no base uri', tag, hint: ext)
347
349
  end
348
350
  end
349
- force = case (digest = args.digest)
351
+ force = case digest
350
352
  when 'f', 'force'
351
353
  digest = nil
352
354
  true
@@ -362,7 +364,8 @@ module Squared
362
364
  when :set
363
365
  format_desc action, flag, 'version,dir?=u/home|p/arent'
364
366
  task flag, [:version] do |_, args|
365
- args = if (version = args.version)
367
+ version = args.version
368
+ args = if version
366
369
  args.extras
367
370
  else
368
371
  version, opts = choice_index('Select a version',
@@ -377,7 +380,9 @@ module Squared
377
380
  asdf(flag, args, version: version)
378
381
  end
379
382
  else
380
- format_desc(action, flag, case flag when :exec, :env then 'command' end)
383
+ format_desc(action, flag, case flag
384
+ when :exec, :env then 'command'
385
+ end)
381
386
  task flag do |_, args|
382
387
  args = args.to_a
383
388
  if args.empty? && (flag == :exec || flag == :env)
@@ -533,7 +538,8 @@ module Squared
533
538
  graph_deps.flatten(1).sort.each do |proj|
534
539
  next if @@graph[:_].include?(proj)
535
540
 
536
- if (val = ENV["PREREQS_#{proj.envname_get}"] || ENV["PREREQS_#{proj.ref.upcase}"])
541
+ val = ENV["PREREQS_#{proj.envname_get}"] || ENV["PREREQS_#{proj.ref.upcase}"]
542
+ if val
537
543
  split_escape(val) do |meth|
538
544
  if proj.has?(meth)
539
545
  begin
@@ -581,7 +587,8 @@ module Squared
581
587
  on :first, :clean unless pass
582
588
  case @clean
583
589
  when Struct
584
- if (val = instance_eval(&@clean.block) || @clean.run)
590
+ val = instance_eval(&@clean.block) || @clean.run
591
+ if val
585
592
  cur = @clean
586
593
  @clean = val
587
594
  clean(*args, sync: sync, pass: true, **kwargs)
@@ -1366,9 +1373,8 @@ module Squared
1366
1373
  def run_b(obj, **kwargs)
1367
1374
  case obj
1368
1375
  when Struct
1369
- if (val = instance_eval(&obj.block) || obj.run)
1370
- run_b(val, **kwargs)
1371
- end
1376
+ val = instance_eval(&obj.block) || obj.run
1377
+ run_b(val, **kwargs) if val
1372
1378
  when Proc
1373
1379
  instance_eval(&obj)
1374
1380
  when Method
@@ -1726,7 +1732,7 @@ module Squared
1726
1732
  end
1727
1733
 
1728
1734
  def print_success
1729
- puts 'Success'
1735
+ puts ARG[:SUCCESS]
1730
1736
  end
1731
1737
 
1732
1738
  def print_error(*args, loglevel: Logger::WARN, **kwargs)
@@ -1921,7 +1927,8 @@ module Squared
1921
1927
 
1922
1928
  def append_hash(data, target: @session || [], build: false)
1923
1929
  if build && (type = env('BUILD', suffix: 'TYPE', ignore: false))
1924
- if (extra = data[type = "__#{type}__"] || data[type.to_sym]).is_a?(Hash)
1930
+ extra = data[type = "__#{type}__"] || data[type.to_sym]
1931
+ if extra.is_a?(Hash)
1925
1932
  data = data.merge(extra)
1926
1933
  else
1927
1934
  extra = nil
@@ -2139,7 +2146,10 @@ module Squared
2139
2146
  if accept
2140
2147
  hint = Array(ret).map { |val| sub_style(val.to_s, theme[:inline]) }.join(', ')
2141
2148
  accept = Array(accept).map { |val| Array(val) }
2142
- ret = Array(ret) if accept.any? { |val| val[1] == true }
2149
+ if accept.any? { |val| val[1] == true }
2150
+ ret = [ret]
2151
+ multiple = -1
2152
+ end
2143
2153
  begin
2144
2154
  item = accept.shift
2145
2155
  c = confirm("#{item[0]}#{" [#{hint}]" if hint}", item[2] ? 'Y' : 'N')
@@ -2152,7 +2162,7 @@ module Squared
2152
2162
  end until accept.empty?
2153
2163
  end
2154
2164
  if values
2155
- ret = Array(ret)
2165
+ ret = [ret] unless accept && multiple == -1
2156
2166
  Array(values).each do |val|
2157
2167
  if val.is_a?(Array)
2158
2168
  val, force = val
@@ -2198,7 +2208,7 @@ module Squared
2198
2208
  cmd.reverse! if File::NULL == 'NUL'
2199
2209
  cmd.unshift('').join(' ')
2200
2210
  end
2201
- if workspace.powershell?
2211
+ if powershell?
2202
2212
  "#{shell_bin('powershell.exe')} -Command \"& {#{args.join(' ; ')}}\"#{out}"
2203
2213
  else
2204
2214
  args.map { |val| "#{val}#{out}" }.join(' && ')
@@ -2335,7 +2345,7 @@ module Squared
2335
2345
  end
2336
2346
 
2337
2347
  def indexchar
2338
- workspace.windows? ? '=' : '^'
2348
+ windows? ? '=' : '^'
2339
2349
  end
2340
2350
 
2341
2351
  def shortname(*args, suffix: '?', delim: ',', pass: false)
@@ -2432,7 +2442,7 @@ module Squared
2432
2442
  return if from == false
2433
2443
 
2434
2444
  require 'timeout'
2435
- unless (path.to_s == Dir.pwd || pass) && (workspace.mri? || !workspace.windows?)
2445
+ unless (path.to_s == Dir.pwd || pass) && (mri? || !windows?)
2436
2446
  pwd = Dir.pwd
2437
2447
  Dir.chdir(path)
2438
2448
  end
@@ -2775,6 +2785,20 @@ module Squared
2775
2785
  false
2776
2786
  end
2777
2787
 
2788
+ def choice!(args, char: ':', first: true, empty: false, only: false)
2789
+ return true if (empty && args.empty?) || (only && args.size == 1 && args.first == char)
2790
+
2791
+ if first
2792
+ if args.first == char
2793
+ args.shift
2794
+ return true
2795
+ end
2796
+ elsif args.delete(char)
2797
+ return true
2798
+ end
2799
+ false
2800
+ end
2801
+
2778
2802
  def variables
2779
2803
  VAR_SET
2780
2804
  end