squared 0.4.6 → 0.4.8

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: c948cc37c50fe20628bd7ca5f1c7d39125b571199f76688a7b58e34a08c8ce2d
4
- data.tar.gz: 02abbb0dbf14cca7c0b5dc4ca6af5b08270e5d6a9bab95c23972bbc201b6fca9
3
+ metadata.gz: 483002a469a23461dcee07ded3610011d831f2c737142cd4b464899fbdf70cde
4
+ data.tar.gz: 4601b03b61f6270e8b3841777effd420b47c752ddbe96924ca329ee92c98ee4c
5
5
  SHA512:
6
- metadata.gz: e5590666ce8061dc02a8d2f3c809ee2ccff87f3abe62bccc6d2510adf418a8ab41e323d75e6781b52aab091d8677ec7cd4e3c55b49177f2baf3a814920daad69
7
- data.tar.gz: aeb0fd0ba9095f2a97de0977dab5a0f13da4e79ac6972fbda56ab9e022d07d44211bd552624e953aab158745145d054c620972f5f64829d97b58fb47a21e9517
6
+ metadata.gz: 5767519e7f0150a753c0cbf97aa59292ad59a29b4f78c6e7596e5bd369b035258339d062b896c59d37496ccdcd4224179a745065f1dc41a35f876a507f30e171
7
+ data.tar.gz: c457cbd60b4865aea1e218ce9609c4094f618cc4b404eac309c8235b3bc116599487d1c641151cd99bceab6d28a03b48abd1774e447017afb2d28439aca40298
data/CHANGELOG.md CHANGED
@@ -1,5 +1,77 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.8] - 2025-04-21
4
+
5
+ ### Added
6
+
7
+ - Workspace project tasks can be executed with a block.
8
+ - Gem command outdated supports semantic version upgrades.
9
+ - Project task unpack supports "gem" extension.
10
+ - Ruby command version was implemented.
11
+ - Ruby command file and script were implemented.
12
+ - Git global command autostash was created.
13
+ - Workspace static method exclude for global tasks was created.
14
+ - Git command checkout action branch and track are interactive.
15
+ - Git command branch action set and delete are interactive.
16
+ - Git command tag action delete is interactive.
17
+ - Docker command image run and container exec are interactive.
18
+ - Docker command network connect and disconnect are interactive.
19
+
20
+ ### Changed
21
+
22
+ - Project methods with conflicting names were given "!" suffix.
23
+ - Docker exec args are run without escape protection.
24
+ - Format method enable_aixterm no longer accepts a block.
25
+
26
+ ### Fixed
27
+
28
+ - Git command log action view did not parse options.
29
+ - Project task unpack did not use custom extensions.
30
+ - Docker did not append secrets when given a string.
31
+
32
+ ## [0.4.7] - 2025-04-17
33
+
34
+ ### Added
35
+
36
+ - Project support class OptionPartition was created.
37
+ - Git command tag action sign was implemented.
38
+ - Gem command uninstall was implemented.
39
+
40
+ ### Changed
41
+
42
+ - DateTime instances was replaced with Time.
43
+ - Utils method time_offset was renamed time_since.
44
+ - Ruby gem command build will search project for gemspec.
45
+
46
+ ### Fixed
47
+
48
+ - Project task archive was not enabled.
49
+
50
+ ## [0.3.9] - 2025-04-17
51
+
52
+ ### Fixed
53
+
54
+ - Project option parser did not separate custom values.
55
+
56
+ ## [0.2.9] - 2025-04-17
57
+
58
+ ### Changed
59
+
60
+ - Project log file naming supports more date formats.
61
+
62
+ ### Fixed
63
+
64
+ - Environment options did not always pass session object.
65
+ - Git command pull actions used unknown flags.
66
+ - Workspace describe did not merge multiple invocations.
67
+
68
+ ## [0.1.6] - 2025-04-17
69
+
70
+ ### Fixed
71
+
72
+ - User-defined program paths were not reduced to bare name.
73
+ - Project path validation did not include separator.
74
+
3
75
  ## [0.4.6] - 2025-04-12
4
76
 
5
77
  ### Added
@@ -490,6 +562,8 @@
490
562
 
491
563
  - Changelog was created.
492
564
 
565
+ [0.4.8]: https://github.com/anpham6/squared/releases/tag/v0.4.8-ruby
566
+ [0.4.7]: https://github.com/anpham6/squared/releases/tag/v0.4.7-ruby
493
567
  [0.4.6]: https://github.com/anpham6/squared/releases/tag/v0.4.6-ruby
494
568
  [0.4.5]: https://github.com/anpham6/squared/releases/tag/v0.4.5-ruby
495
569
  [0.4.4]: https://github.com/anpham6/squared/releases/tag/v0.4.4-ruby
@@ -497,6 +571,7 @@
497
571
  [0.4.2]: https://github.com/anpham6/squared/releases/tag/v0.4.2-ruby
498
572
  [0.4.1]: https://github.com/anpham6/squared/releases/tag/v0.4.1-ruby
499
573
  [0.4.0]: https://github.com/anpham6/squared/releases/tag/v0.4.0-ruby
574
+ [0.3.9]: https://github.com/anpham6/squared/releases/tag/v0.3.9-ruby
500
575
  [0.3.8]: https://github.com/anpham6/squared/releases/tag/v0.3.8-ruby
501
576
  [0.3.7]: https://github.com/anpham6/squared/releases/tag/v0.3.7-ruby
502
577
  [0.3.6]: https://github.com/anpham6/squared/releases/tag/v0.3.6-ruby
@@ -506,6 +581,7 @@
506
581
  [0.3.2]: https://github.com/anpham6/squared/releases/tag/v0.3.2-ruby
507
582
  [0.3.1]: https://github.com/anpham6/squared/releases/tag/v0.3.1-ruby
508
583
  [0.3.0]: https://github.com/anpham6/squared/releases/tag/v0.3.0-ruby
584
+ [0.2.9]: https://github.com/anpham6/squared/releases/tag/v0.2.9-ruby
509
585
  [0.2.8]: https://github.com/anpham6/squared/releases/tag/v0.2.8-ruby
510
586
  [0.2.7]: https://github.com/anpham6/squared/releases/tag/v0.2.7-ruby
511
587
  [0.2.6]: https://github.com/anpham6/squared/releases/tag/v0.2.6-ruby
@@ -515,6 +591,7 @@
515
591
  [0.2.2]: https://github.com/anpham6/squared/releases/tag/v0.2.2-ruby
516
592
  [0.2.1]: https://github.com/anpham6/squared/releases/tag/v0.2.1-ruby
517
593
  [0.2.0]: https://github.com/anpham6/squared/releases/tag/v0.2.0-ruby
594
+ [0.1.6]: https://github.com/anpham6/squared/releases/tag/v0.1.6-ruby
518
595
  [0.1.5]: https://github.com/anpham6/squared/releases/tag/v0.1.5-ruby
519
596
  [0.1.4]: https://github.com/anpham6/squared/releases/tag/v0.1.4-ruby
520
597
  [0.1.3]: https://github.com/anpham6/squared/releases/tag/v0.1.3-ruby
data/README.ruby.md CHANGED
@@ -278,6 +278,10 @@ Workspace::Application
278
278
  .with(:ruby) do
279
279
  run("gem build") # gem build
280
280
  # OR
281
+ run("gem build", on: { first: -> { p "2" }, last: -> { p "4" } }) do # run | depend | graph | clean | doc | lint | test
282
+ p "1"
283
+ end
284
+ # OR
281
285
  run(["gem build", "--force", { "RUBY_VERSION" => "3.4.0" }]) # RUBY_VERSION="3.4.0" gem build --force
282
286
  # OR
283
287
  run({ #
@@ -443,11 +447,13 @@ Most project classes will inherit from `Git` which enables these tasks:
443
447
  | rev | rev | commit branch output parseopt |
444
448
  | show | show | format oneline |
445
449
  | stash | stash | push pop apply drop list |
446
- | tag | tag | add delete list |
450
+ | tag | tag | add sign delete list |
447
451
 
448
452
  You can disable all of them at once using the `exclude` property.
449
453
 
450
454
  ```ruby
455
+ Workspace::Application.exclude('autostash', 'rebase')
456
+
451
457
  Workspace::Application
452
458
  .new
453
459
  .add("squared", exclude: :git)
@@ -462,11 +468,20 @@ Workspace::Application
462
468
  .pass("pull", ref: :node) { read_packagemanager(:private) }
463
469
  ```
464
470
 
471
+ ### Commit Hash
472
+
473
+ Commands which use commit hashes are parsed using string interpolation format as to not be confused for an option.
474
+
475
+ ```sh
476
+ rake squared:log:view[#{af012345}] # git log af012345
477
+ rake squared:log:view[H1,H5,all,lib,./H12345] # git log --all HEAD~1 HEAD~5 -- 'lib' 'H12345'
478
+ ```
479
+
465
480
  ## Environment
466
481
 
467
482
  ### Path
468
483
 
469
- All project executable programs can have their binary path set to a non-global alias.
484
+ All project binary programs can have their executable path set to a non-global alias.
470
485
 
471
486
  ```ruby
472
487
  Common::PATH.merge!({
@@ -483,44 +498,47 @@ Common::PATH.merge!({
483
498
 
484
499
  ### Build
485
500
 
486
- ```sh
501
+ ```ruby
487
502
  Workspace::Application
488
503
  .new
489
504
  .add("squared", run: "gcc a.c -o a.o", opts: { __debug__: { g: true, O2: true, c: nil }, c: true, j: 4 }) # gcc a.c -o a.o -c -j4
505
+ ```
490
506
 
491
- BUILD_TYPE # global
507
+ ```sh
508
+ BUILD_TYPE # global
509
+ ${PROG}_COLOR=0 # --no-color (e.g. GIT_COLOR)
492
510
 
493
511
  # :env :run :args :opts :type
494
512
  # LD_LIBRARY_PATH="path/to/lib" CFLAGS="-Wall" gcc a.c -o a.o -g -O2
495
- BUILD_${NAME} # gcc a.c -o a.o
496
- BUILD_${NAME}_OPTS # -g
497
- BUILD_${NAME}_ENV # {"LD_LIBRARY_PATH":"path/to/lib","CFLAGS":"-Wall"} (hash/json)
498
- BUILD_${NAME}_TYPE # debug
513
+ BUILD_${NAME} # gcc a.c -o a.o
514
+ BUILD_${NAME}_OPTS # -g
515
+ BUILD_${NAME}_ENV # {"LD_LIBRARY_PATH":"path/to/lib","CFLAGS":"-Wall"} (hash/json)
516
+ BUILD_${NAME}_TYPE # debug
499
517
 
500
518
  # :env :script :opts :args
501
519
  # NODE_ENV="production" NO_COLOR="1" npm run build:dev --loglevel=error --workspaces=false -- --quiet
502
- BUILD_${NAME} # build:dev
503
- BUILD_${NAME}_OPTS # --loglevel=error --workspaces=false
504
- BUILD_${NAME}_ENV # {"NODE_ENV":"production","NO_COLOR":"1"} (hash/json)
505
- BUILD_${NAME}_DEV # pattern,0,1 (:dev)
506
- BUILD_${NAME}_PROD # pattern,0,1 (:prod)
507
- ${REF}_${NAME}_OPTS # --quiet (e.g. NODE_SQUARED_OPTS)
520
+ BUILD_${NAME} # build:dev
521
+ BUILD_${NAME}_OPTS # --loglevel=error --workspaces=false
522
+ BUILD_${NAME}_ENV # {"NODE_ENV":"production","NO_COLOR":"1"} (hash/json)
523
+ BUILD_${NAME}_DEV # pattern,0,1 (:dev)
524
+ BUILD_${NAME}_PROD # pattern,0,1 (:prod)
525
+ ${REF}_${NAME}_OPTS # --quiet (e.g. NODE_SQUARED_OPTS)
508
526
 
509
- BUILD_${NAME}=0 # skip project
510
- BUILD_${NAME}_VERSION=0.1.0 # publish + detection
527
+ BUILD_${NAME}=0 # skip project
528
+ BUILD_${NAME}_VERSION=0.1.0 # publish + detection
511
529
 
512
- BANNER=0 # hide banner
513
- BANNER_${NAME}=0 #
530
+ BANNER=0 # hide banner
531
+ BANNER_${NAME}=0 #
514
532
 
515
- REVBUILD_FORCE=1 # Rebuild all targets
516
- REVBUILD_FORCE_${NAME}=1 # Rebuild project
533
+ REVBUILD_FORCE=1 # Rebuild all targets
534
+ REVBUILD_FORCE_${NAME}=1 # Rebuild project
517
535
  ```
518
536
 
519
537
  ### Graph
520
538
 
521
539
  ```sh
522
- GRAPH_${NAME} # depend,build => squared:depend + squared:build
523
- GRAPH_${NAME}_PASS # -emc,pir,express => pir + express
540
+ GRAPH_${NAME} # depend,build => squared:depend + squared:build
541
+ GRAPH_${NAME}_PASS # -emc,pir,express => pir + express
524
542
  ```
525
543
 
526
544
  ### Logger
@@ -528,22 +546,22 @@ GRAPH_${NAME}_PASS # -emc,pir,express => pir + express
528
546
  These global options also can target the project suffix `${NAME}`. (e.g. LOG_FILE_EMC)
529
547
 
530
548
  ```sh
531
- LOG_FILE # %Y-%m-%d.log
549
+ LOG_FILE # %Y-%m-%d.log
532
550
  # OR
533
- LOG_AUTO # year,y,month,m,day,d,1
551
+ LOG_AUTO # year,y,month,m,day,d,1
534
552
  # Optional
535
- LOG_DIR # exist?
536
- LOG_LEVEL # See gem "logger"
537
- LOG_COLUMNS # terminal width (default: 80)
553
+ LOG_DIR # exist?
554
+ LOG_LEVEL # See gem "logger"
555
+ LOG_COLUMNS # terminal width (default: 80)
538
556
  ```
539
557
 
540
558
  ### Git
541
559
 
542
560
  ```sh
543
- GIT_OPTIONS=q,strategy=ort # all
544
- GIT_OPTIONS_${NAME}=v,ff-only # project only
545
- GIT_AUTOSTASH=1 # rebase (all)
546
- GIT_AUTOSTASH_${NAME}=0 # rebase (project only)
561
+ GIT_OPTIONS=q,strategy=ort # all
562
+ GIT_OPTIONS_${NAME}=v,ff # project only
563
+ GIT_AUTOSTASH=1 # rebase (all)
564
+ GIT_AUTOSTASH_${NAME}=0 # rebase (project only)
547
565
  ```
548
566
 
549
567
  | Command | Flag | ENV |
@@ -567,7 +585,8 @@ GIT_AUTOSTASH_${NAME}=0 # rebase (project only)
567
585
  | checkout | branch | DETACH TRACK=s |
568
586
  | checkout | global path | HEAD=s PATHSPEC=s |
569
587
  | checkout | * | FORCE MERGE |
570
- | tag | add | SIGN HEAD=s |
588
+ | tag | add | SIGN FORCE HEAD=s |
589
+ | tag | sign | FORCE HEAD=s |
571
590
  | log | * | PATHSPEC=s |
572
591
  | diff | -between -contain | MERGE_BASE |
573
592
  | diff | head branch | INDEX=n |
@@ -588,6 +607,7 @@ DOCKER_OPTIONS=q,no-cache # all
588
607
  DOCKER_OPTIONS_${NAME}=v,no-cache=false # project only (override)
589
608
  DOCKER_TAG=latest # all
590
609
  DOCKER_TAG_${NAME}=v0.1.0 # project only (override)
610
+ DOCKER_ALL=1 # list every image/container
591
611
  ```
592
612
 
593
613
  | Command | Flag | ENV |
@@ -11,6 +11,7 @@ module Squared
11
11
  COMMON: true,
12
12
  VERBOSE: nil,
13
13
  BANNER: true,
14
+ CHOICE: 25,
14
15
  QUOTE: "'",
15
16
  SPACE: ' => ',
16
17
  GRAPH: ['|', '-', '|', '\\', '-'].freeze,
@@ -12,7 +12,7 @@ module Squared
12
12
  super[/[^:]+\z/, 0]
13
13
  end
14
14
 
15
- def_delegators :@data, :+, :each, :each_with_index, :entries, :to_a, :include?
15
+ def_delegators :@data, :+, :each, :each_with_index, :entries, :merge, :include?
16
16
 
17
17
  def initialize(data = [])
18
18
  @data = Set.new(data)
@@ -22,6 +22,10 @@ module Squared
22
22
  @data.add(val.to_sym)
23
23
  end
24
24
 
25
+ def to_a
26
+ @data.to_a.freeze
27
+ end
28
+
25
29
  def to_s
26
30
  @data.to_s.sub('Set', SymSet.to_s)
27
31
  end
@@ -71,13 +75,24 @@ module Squared
71
75
  block_given? ? ret.reject(&blk) : ret
72
76
  end
73
77
 
78
+ def and(*args)
79
+ self << '&&'
80
+ merge(args)
81
+ end
82
+
83
+ def or(*args)
84
+ self << '||'
85
+ merge(args)
86
+ end
87
+
88
+ def with(*args, &blk)
89
+ temp('&&', *args, &blk)
90
+ end
91
+
74
92
  def temp(*args, &blk)
75
93
  args.compact!
76
94
  ret = pass(&blk)
77
- unless args.empty?
78
- ret += args
79
- ret = Set.new(ret).to_a
80
- end
95
+ ret = Set.new(ret.concat(args)).to_a unless args.empty?
81
96
  ret.join(@delim)
82
97
  end
83
98
 
@@ -28,18 +28,22 @@ module Squared
28
28
  bright_cyan!: '106',
29
29
  bright_white!: '107'
30
30
  }.freeze
31
- AIX_GRAPH = ['│', '─', '├', '└', '┬'].freeze
32
- AIX_BORDER = ['│', '─', '╭', '╮', '╯', '╰', '├', '┤', '┬', '┴'].freeze
31
+ BOX_GRAPH = ['│', '─', '├', '└', '┬'].freeze
32
+ BOX_BORDER = ['│', '─', '╭', '╮', '╯', '╰', '├', '┤', '┬', '┴'].freeze
33
33
  TEXT_STYLE = [:bold, :dim, :italic, :underline, :blinking, nil, :inverse, :hidden, :strikethrough].freeze
34
- private_constant :AIX_TERM, :AIX_GRAPH, :AIX_BORDER, :TEXT_STYLE
34
+ private_constant :AIX_TERM, :BOX_GRAPH, :BOX_BORDER, :TEXT_STYLE
35
35
 
36
36
  def enable_aixterm
37
37
  unless (colors = __get__(:colors)).frozen?
38
38
  colors.merge!(AIX_TERM)
39
- ARG[:GRAPH] = AIX_GRAPH
40
- ARG[:BORDER] = AIX_BORDER
41
39
  end
42
- block_given? ? yield(self) : self
40
+ self
41
+ end
42
+
43
+ def enable_drawing
44
+ ARG[:GRAPH] = BOX_GRAPH
45
+ ARG[:BORDER] = BOX_BORDER
46
+ self
43
47
  end
44
48
 
45
49
  private
@@ -68,16 +72,16 @@ module Squared
68
72
  end
69
73
  if type.is_a?(::Numeric)
70
74
  f, b = type.to_s.split('.')
71
- s = wrap.(s, ['38', '5', f]) if f[0] != '-' && f.to_i <= 255
75
+ s = wrap.call(s, ['38', '5', f]) if f[0] != '-' && f.to_i <= 255
72
76
  if b
73
77
  b = b[0, 3]
74
- s = wrap.(s, ['48', '5', b]) unless b.to_i > 255
78
+ s = wrap.call(s, ['48', '5', b]) unless b.to_i > 255
75
79
  end
76
80
  else
77
81
  t = type.to_sym
78
82
  if (c = __get__(:colors)[t])
79
83
  if index == -1
80
- s = wrap.(s, [c])
84
+ s = wrap.call(s, [c])
81
85
  else
82
86
  code << c
83
87
  end
@@ -95,7 +99,7 @@ module Squared
95
99
  end
96
100
  return ret.join if index == -1
97
101
 
98
- ret = wrap.(ret, code) unless code.empty?
102
+ ret = wrap.call(ret, code) unless code.empty?
99
103
  return ret unless data
100
104
 
101
105
  out = ''.dup
@@ -133,9 +137,9 @@ module Squared
133
137
 
134
138
  set = ->(k, v) { data[k] = check_style(v, empty: empty) }
135
139
  if key.is_a?(::Hash)
136
- key.each { |k, v| set.(k, v || args) }
140
+ key.each { |k, v| set.call(k, v || args) }
137
141
  else
138
- set.(key.to_sym, args)
142
+ set.call(key.to_sym, args)
139
143
  end
140
144
  end
141
145
 
@@ -219,18 +223,18 @@ module Squared
219
223
  max = ->(v) { n = [n, v.max_by(&:size).size].max }
220
224
  set = lambda do |v|
221
225
  ret = as_a(v, :to_s)
222
- max.(ret)
226
+ max.call(ret)
223
227
  ret
224
228
  end
225
- title &&= set.(title)
226
- footer &&= set.(footer)
229
+ title &&= set.call(title)
230
+ footer &&= set.call(footer)
227
231
  if val.is_a?(::Array)
228
232
  lines = val.map(&:to_s)
229
233
  else
230
234
  lines = val.to_s.lines(chomp: true)
231
235
  lines[0] = "#{val.class}: #{lines.first}" if (err = val.is_a?(StandardError))
232
236
  end
233
- n = cols || max.(lines)
237
+ n = cols || max.call(lines)
234
238
  if $stdout.tty?
235
239
  require 'io/console'
236
240
  (n = [n, $stdout.winsize[1] - 4].min) rescue nil
@@ -253,13 +257,13 @@ module Squared
253
257
  end
254
258
  s
255
259
  end
256
- out << draw.(b2, b3)
260
+ out << draw.call(b2, b3)
257
261
  if title
258
- out += title.map { |t| pr.(t) }
259
- out << draw.(b6, b7)
262
+ out.concat(title.map { |t| pr.call(t) })
263
+ out << draw.call(b6, b7)
260
264
  end
261
- lines.each { |line| out << pr.(line) }
262
- out << draw.(b5, b4)
265
+ lines.each { |line| out << pr.call(line) }
266
+ out << draw.call(b5, b4)
263
267
  if footer
264
268
  unless sub.empty? && !right
265
269
  footer.map! do |s|
@@ -268,7 +272,7 @@ module Squared
268
272
  s
269
273
  end
270
274
  end
271
- out += footer
275
+ out.concat(footer)
272
276
  end
273
277
  if block_given?
274
278
  yield out
@@ -291,6 +295,10 @@ module Squared
291
295
  val.gsub(/\x1B\[(\d+;?)+m/, '')
292
296
  end
293
297
 
298
+ def stripext(val)
299
+ File.basename(val, File.extname(val))
300
+ end
301
+
294
302
  def raise_error(*args, hint: nil, kind: ArgumentError)
295
303
  raise kind, message(*args, hint: hint, empty: true), caller_locations(1).map(&:to_s)
296
304
  end
@@ -21,7 +21,7 @@ module Squared
21
21
  return false
22
22
  end
23
23
  attempts -= 1
24
- exit 1 unless attempts >= 0
24
+ exit 1 unless attempts > 0
25
25
  end
26
26
  rescue Interrupt
27
27
  puts
@@ -31,6 +31,44 @@ module Squared
31
31
  end
32
32
  end
33
33
  end
34
+
35
+ def choice(msg, list = nil, min: 1, max: 1, multiple: false, attempts: 5, timeout: 60)
36
+ require 'readline'
37
+ require 'timeout'
38
+ if list
39
+ items = []
40
+ list.each_with_index do |val, index|
41
+ puts "#{index.succ.to_s.rjust(2)}. #{val}"
42
+ items << val.chomp
43
+ end
44
+ max = items.size
45
+ msg = "#{msg}: [1-#{max}#{multiple ? '|,' : ''}] "
46
+ end
47
+ return unless max >= min
48
+
49
+ valid = ->(s) { s.match?(/^-?\d+$/) && s.to_i.between?(min, max) }
50
+ Timeout.timeout(timeout) do
51
+ begin
52
+ while (ch = Readline.readline(msg, true))
53
+ ch = ch.strip
54
+ if multiple
55
+ a = ch.split(/\s*,\s*/)
56
+ b = a.select { |s| valid.call(s) }.map!(&:to_i)
57
+ return items ? b.map! { |i| items[i - 1] } : b if a.size == b.size
58
+ elsif valid.call(ch)
59
+ return items ? items[ch.to_i - 1] : ch.to_i
60
+ end
61
+ attempts -= 1
62
+ exit 1 unless attempts > 0
63
+ end
64
+ rescue Interrupt
65
+ puts
66
+ exit 0
67
+ else
68
+ multiple ? [] : nil
69
+ end
70
+ end
71
+ end
34
72
  end
35
73
  end
36
74
  end
@@ -13,7 +13,7 @@ module Squared
13
13
  return val if !r[3] || (!r[5] && r[6].match?(/\s/))
14
14
 
15
15
  combine = lambda do |opt|
16
- if r[2] =~ /^(["'])(.+)\1$/
16
+ if r[2] =~ /\A(["'])(.+)\1\z/
17
17
  double = $1 == '"'
18
18
  r[2] = $2
19
19
  override = true
@@ -21,11 +21,13 @@ module Squared
21
21
  r[1] + r[2] + r[4] + shell_quote(opt, double: double, force: force, override: override)
22
22
  end
23
23
  if r[5] == r[7]
24
- r[5] ? val : combine.(r[6])
24
+ r[5] ? val : combine.call(r[6])
25
25
  else
26
26
  force = true
27
- combine.(r[5] + r[6] + r[7])
27
+ combine.call(r[5] + r[6] + r[7])
28
28
  end
29
+ elsif val.empty?
30
+ ''
29
31
  elsif Rake::Win32.windows?
30
32
  quote ? shell_quote(val, double: double, force: force) : val
31
33
  else
@@ -35,7 +37,7 @@ module Squared
35
37
 
36
38
  def shell_quote(val, option: true, force: true, double: false, override: false)
37
39
  val = val.to_s
38
- return val if !force && !val.include?(' ')
40
+ return val if val.empty? || (!force && !val.include?(' '))
39
41
  return val if option && val.match?(/(?:\A|\S=|[^=]\s+|#{Rake::Win32.windows? ? '[\\\/]' : '\/'})(["']).+\1\z/m)
40
42
 
41
43
  if double || Rake::Win32.windows? || (ARG[:QUOTE] == '"' && !override)
@@ -45,10 +47,10 @@ module Squared
45
47
  end
46
48
  end
47
49
 
48
- def shell_option(flag, val = nil, escape: true, quote: true, force: true, double: false, override: false,
49
- merge: false)
50
+ def shell_option(flag, val = nil, escape: true, quote: true, option: true, force: true, double: false,
51
+ merge: false, override: false)
50
52
  flag = flag.to_s
51
- if flag =~ /^(["'])(.+)\1$/
53
+ if flag =~ /\A(["'])(.+)\1\z/
52
54
  double = $1 == '"'
53
55
  flag = $2
54
56
  escape = false
@@ -66,8 +68,10 @@ module Squared
66
68
  "#{a}#{flag}#{if val
67
69
  "#{b}#{if escape
68
70
  shell_escape(val, quote: quote, double: double, override: override)
71
+ elsif quote
72
+ shell_quote(val, option: option, force: force, double: double, override: override)
69
73
  else
70
- quote ? shell_quote(val, force: force, double: double, override: override) : val
74
+ val
71
75
  end}"
72
76
  end}"
73
77
  end
@@ -88,8 +92,8 @@ module Squared
88
92
  shell_escape(val.start_with?('-') ? val : "--#{val}", double: double)
89
93
  end
90
94
 
91
- def quote_option(flag, val, double: false)
92
- shell_option(flag, val, escape: false, double: double)
95
+ def quote_option(flag, val, option: true, double: false, merge: false)
96
+ shell_option(flag, val, escape: false, option: option, double: double, merge: merge)
93
97
  end
94
98
 
95
99
  def basic_option(flag, val, merge: false)
@@ -23,14 +23,14 @@ module Squared
23
23
  def copy_dir(src, dest, glob = ['**/*'], create: false, link: nil, force: false, pass: nil, verbose: true)
24
24
  src = Pathname.new(src)
25
25
  dest = Pathname.new(dest)
26
- raise "#{dest} (not found)" if !create && !dest.parent.exist?
26
+ raise "#{dest.cleanpath} (not found)" if !create && !dest.parent.exist?
27
27
 
28
28
  subdir = {}
29
29
  dest.mkpath if create
30
30
  if pass
31
31
  exclude = []
32
32
  pass = [pass] unless pass.is_a?(::Array)
33
- pass.each { |val| exclude += Dir.glob(src.join(val)) }
33
+ pass.each { |val| exclude.concat(Dir.glob(src.join(val))) }
34
34
  end
35
35
  (glob.is_a?(::Array) ? glob : [glob]).each do |val|
36
36
  Dir.glob(src.join(val)) do |path|
@@ -89,7 +89,7 @@ module Squared
89
89
  case link
90
90
  when 'hard', 1
91
91
  FileUtils.ln(src, dest, force: force, verbose: verbose)
92
- when true, 'soft', 0
92
+ when TrueClass, 'soft', 0
93
93
  FileUtils.ln_s(src, dest, force: force, verbose: verbose)
94
94
  else
95
95
  FileUtils.cp(src, dest, verbose: verbose)