squared 0.4.32 → 0.4.34

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: 913d20d3c4a6de7260752fb6603305cee9af765f2885ae74a57d145abe0c2e4e
4
- data.tar.gz: 4c07a15fb013d871a8ebf0f93343146b4366a127ca1f1c18467648d9f7c4ca44
3
+ metadata.gz: 0d59b575971efdb7998b924fa5ca7815c4aeaeeaeecb756197fbc79b11433743
4
+ data.tar.gz: 9cb428fb205f51803c0b7576b71d78165a8987925f90c04462c266bd03ca48b9
5
5
  SHA512:
6
- metadata.gz: 13a87e14e520b801a71e00c5012eb06e8a557b1f6c7d14cf30702e0a4b2ed0d87bb5535d8ffc970275da0f7a65958562e4ca586cebca847c886a80a6df438d5b
7
- data.tar.gz: 48626887ad8b660747dc389e321c2b179b9855cba8ddc9c98d3e48416b87d3ab5899de9f416db068ce47e983f56ce27174057ebed38b148b5331cc4088e12e9c
6
+ metadata.gz: 5504784e072989e9e4a25ee08aa2f8bc5b9b73d4476aa9ad5d4633b08b2546847b9945de201f964b04a52124b2e75bc2f9341321348f429e2dc9ea0c4bae3fd8
7
+ data.tar.gz: 73786a35bd4255024f235090cc8b11883b95efa8903e2034b8f5d13353fb7a7756ed8d8d335f42756232d595a00bf10cdafb099c3065906617575e3cd035bebf
data/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.34] - 2025-12-26
4
+
5
+ ### Added
6
+
7
+ - Project public base method scope for nested tasks was created.
8
+ - Ruby task copy can autodetect "env" using [GEM_HOME|GEM_ROOT].
9
+
10
+ ### Changed
11
+
12
+ - Python virtual environment did not install poetry during initialization.
13
+
14
+ ### Fixed
15
+
16
+ - Workspace global banner never referenced the correct hash key.
17
+ - Python task depend without editable did not append context directory.
18
+ - Docker task build did not parse DOCKER_OPTIONS as command options.
19
+ - Project base method build did not call Method routines.
20
+ - Bundler autodetect did not check for valid gems directory.
21
+ - Ruby copy to version detection did not check for valid gemspec.
22
+
23
+ ## [0.4.33] - 2025-12-07
24
+
25
+ ### Added
26
+
27
+ - Ruby attribute setter gemdir for copy method was created.
28
+ - Project inline run executables binary path are replaced with global alias.
29
+
30
+ ### Fixed
31
+
32
+ - Workspace class Series created non-existent keys when queried.
33
+ - Application class did not support changing base Project class.
34
+ - Git command rev action build did not check build? method.
35
+ - Python command build action python did not use outdir option.
36
+
3
37
  ## [0.4.32] - 2025-11-25
4
38
 
5
39
  ### Fixed
@@ -1130,6 +1164,9 @@
1130
1164
 
1131
1165
  - Changelog was created.
1132
1166
 
1167
+ [0.4.34]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.34
1168
+ [0.4.33]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.33
1169
+ [0.4.32]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.32
1133
1170
  [0.4.31]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.31
1134
1171
  [0.4.30]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.30
1135
1172
  [0.4.29]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.29
@@ -60,7 +60,7 @@ module Squared
60
60
  while (ch = Readline.readline(msg))
61
61
  unless (ch = ch.strip).empty?
62
62
  if multiple
63
- a = ch.split(/\s*,\s*/)
63
+ a = ch.split(',').map!(&:strip)
64
64
  b = a.select { |s| valid.call(s) }.map!(&:to_i).sort
65
65
  next unless a.size == b.size
66
66
  return items ? b.map! { |i| items[i - 1] } : b unless multiple.is_a?(::Numeric) && multiple != b.size
@@ -110,6 +110,7 @@ module Squared
110
110
  end
111
111
 
112
112
  def shell_bin(name, env: true)
113
+ require_relative 'base'
113
114
  key = name.to_s.upcase
114
115
  key = File.basename(key, '.*') if Rake::Win32.windows?
115
116
  shell_quote((env && ENV["PATH_#{key}"]) || PATH[key] || PATH[key.to_sym] || name,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- VERSION = '0.4.32'
4
+ VERSION = '0.4.34'
5
5
  end
@@ -274,9 +274,9 @@ module Squared
274
274
 
275
275
  def pass(name, group: @group, ref: @ref, &blk)
276
276
  data = if group
277
- @pass[:group][group]
277
+ @pass[:group][group.to_s]
278
278
  elsif ref
279
- @pass[:ref][ref]
279
+ @pass[:ref][ref.to_sym]
280
280
  else
281
281
  @pass[:global]
282
282
  end
@@ -336,9 +336,9 @@ module Squared
336
336
  end
337
337
  proj = ((if !ref.is_a?(Class)
338
338
  Application.find(ref, path: path)
339
- elsif ref < Project::Base
339
+ elsif ref < Application.impl_project
340
340
  ref
341
- end) || @kind[name]&.last || Project::Base).new(self, path, name, **kwargs)
341
+ end) || @kind[name]&.last || Application.impl_project).new(self, path, name, **kwargs)
342
342
  proj.__send__(:index_set, @project.size)
343
343
  @project[name] = proj
344
344
  __get__(:project)[name] = proj unless kwargs[:private]
@@ -573,7 +573,7 @@ module Squared
573
573
  return ret if group && (ret = @banner[:group][group.to_sym])
574
574
 
575
575
  ref.reverse_each { |val| return ret if (ret = @banner[:ref][val]) }
576
- @banner[:ref][:'']
576
+ @banner[:ref][:_]
577
577
  end
578
578
 
579
579
  def enabled?
@@ -460,8 +460,14 @@ module Squared
460
460
  cmd = []
461
461
  var = {}
462
462
  args.each do |val|
463
- next instance_exec(*val[1..-1], &val.first) if val.first.is_a?(Proc)
464
-
463
+ case val.first
464
+ when Proc
465
+ instance_exec(*val[1..-1], &val.first)
466
+ next
467
+ when Method
468
+ val.first.call(*val[1..-1])
469
+ next
470
+ end
465
471
  a, b, c, d, e = val
466
472
  case b
467
473
  when Hash
@@ -471,7 +477,7 @@ module Squared
471
477
  end
472
478
  d = append_hash(d, target: []).join(' ') if d.is_a?(Hash)
473
479
  if a
474
- cmd << [a, d, b].compact.join(' ')
480
+ cmd << [replace_bin(a), d, b].compact.join(' ')
475
481
  else
476
482
  next unless respond_to?(:compose)
477
483
 
@@ -485,7 +491,7 @@ module Squared
485
491
  if cmd
486
492
  return run_b(cmd, sync: sync, from: from) if cmd.is_a?(Proc) || cmd.is_a?(Method)
487
493
 
488
- cmd = as_get(cmd, from)
494
+ cmd = replace_bin as_get(cmd, from)
489
495
  opts = compose(opts, script: false) if opts && respond_to?(:compose)
490
496
  flags = append_hash(flags, target: []).join(' ') if flags.is_a?(Hash)
491
497
  case opts
@@ -859,6 +865,12 @@ module Squared
859
865
  end
860
866
  end
861
867
 
868
+ def scope(*args, **kwargs, &blk)
869
+ namespace name do
870
+ task(*args, **kwargs, &blk)
871
+ end
872
+ end
873
+
862
874
  def variable_set(key, *args, **kwargs, &blk)
863
875
  if variables.include?(key) || blocks.include?(key)
864
876
  val = case args.size
@@ -891,7 +903,8 @@ module Squared
891
903
  when :env
892
904
  run_set(output[0], *args, **kwargs)
893
905
  when :dependfile
894
- @dependfile = basepath(*args)
906
+ @dependindex = nil
907
+ @dependfile = val.nil? ? nil : basepath(*args)
895
908
  else
896
909
  if block_given?
897
910
  if blocks.include?(key)
@@ -1595,6 +1608,13 @@ module Squared
1595
1608
  ret
1596
1609
  end
1597
1610
 
1611
+ def replace_bin(val)
1612
+ a, b = val.split(' ', 2)
1613
+ return val if val.start_with?(/["']/) || a.include?(File::Separator)
1614
+
1615
+ [shell_bin(a), b].compact.join(' ')
1616
+ end
1617
+
1598
1618
  def parse_json(val, kind: Hash, hint: nil)
1599
1619
  ret = JSON.parse(val)
1600
1620
  raise_error("invalid JSON #{kind.name}", val, hint: hint) if kind && !ret.is_a?(kind)
@@ -13,7 +13,7 @@ module Squared
13
13
  buildx: {
14
14
  common: %w[builder=b D|debug],
15
15
  build: %w[add-host=q annotation=q attest=q build-arg=qq build-context=qq cache-from=q cache-to=q
16
- cgroup-parent=b iidfile=p label=q a-file=p network=b no-cache-filter=b o|output=q platform=b
16
+ cgroup-parent=b iidfile=p label=q network=b no-cache-filter=b o|output=q platform=q
17
17
  q|quiet secret=qq shm-size=b ssh=qq t|tag=b target=b ulimit=q].freeze,
18
18
  bake: %w[print list=q set=q].freeze,
19
19
  shared: %w[check load no-cache pull push allow=q call=b? f|file=p metadata-file=p progress=b provenance=q
@@ -41,14 +41,14 @@ module Squared
41
41
  device-read-bps=q device-read-iops=q device-write-bps=q device-write-iops=q
42
42
  disable-content-trust=b? dns=q dns-option=q dns-search=q domainname=b entrypoint=q e|env=qq
43
43
  env-file=p expose=q gpus=q group-add=b health-cmd=q health-interval=b health-retries=i
44
- health-start-interval=q health-start-period=q health-timeout=q io-maxbandwidth=b io-maxiops=b
45
- ip=b ip6=q ipc=b isolation=b kernel-memory=b l|label=q label-file=q link=b link-local-ip=q
46
- log-driver=b log-opt=q mac-address=q m|memory=b memory-reservation=b memory-swap=n
47
- memory-swappiness=n mount=qq name=b network=b network-alias=b oom-score-adj=b pid=b pids-limit=n
48
- platform=b p|publish=q pull=b restart=b runtime=b security-opt=q shm-size=b stop-signal=b
49
- stop-timeout=i storage-opt=q sysctl=q tmpfs=q ulimit=q u|user=b userns=b uts=b v|volume=q
50
- volume-driver=b volumes-from=b w|workdir=q].freeze,
51
- run: %w[d|detach detach-keys=q hostname=q sig-proxy=b?].freeze,
44
+ health-start-interval=q health-start-period=q health-timeout=q hostname=q io-maxbandwidth=b
45
+ io-maxiops=b ip=b ip6=q ipc=b isolation=b kernel-memory=b l|label=q label-file=q link=b
46
+ link-local-ip=q log-driver=b log-opt=q mac-address=q m|memory=b memory-reservation=b
47
+ memory-swap=n memory-swappiness=n mount=qq name=b network=b network-alias=b oom-score-adj=b
48
+ pid=b pids-limit=n platform=q p|publish=q pull=b restart=b runtime=b security-opt=q shm-size=b
49
+ stop-signal=b stop-timeout=i storage-opt=q sysctl=q tmpfs=q ulimit=q u|user=b userns=b uts=b
50
+ v|volume=q volume-driver=b volumes-from=b w|workdir=q].freeze,
51
+ run: %w[d|detach detach-keys=q sig-proxy=b?].freeze,
52
52
  update: %w[blkio-weight=i cpu-period=i cpu-quota=i cpu-rt-period=i cpu-rt-runtime=i c|cpu-shares=i cpus=f
53
53
  cpuset-cpus=b cpuset-mems=b m|memory=b memory-reservation=b memory-swap=b pids-limit=n
54
54
  restart=q].freeze,
@@ -64,9 +64,9 @@ module Squared
64
64
  }.freeze,
65
65
  image: {
66
66
  list: %w[a|all q|quiet digests no-trunc tree f|filter=q format=q].freeze,
67
- push: %w[a|all-tags disable-content-trust=b? platform=b q|quiet].freeze,
68
- rm: %w[f|force no-prune platform=b].freeze,
69
- save: %w[o|output=p platform=b].freeze
67
+ push: %w[a|all-tags disable-content-trust=b? platform=q q|quiet].freeze,
68
+ rm: %w[f|force no-prune platform=q].freeze,
69
+ save: %w[o|output=p platform=q].freeze
70
70
  }.freeze,
71
71
  network: {
72
72
  connect: %w[alias=b driver-opt=q gw-priority=n ip=b ip6=q link=b link-local-ip=q].freeze,
@@ -262,21 +262,20 @@ module Squared
262
262
  def compose(opts, flags = nil, script: false, args: nil, from: :run, **)
263
263
  return opts if script == false
264
264
 
265
- ret = docker_session
266
265
  if from == :run
267
266
  if bake?(n = filetype)
268
- ret << 'buildx bake'
267
+ ret = docker_session 'buildx bake'
269
268
  append_file n
270
269
  from = :bake
271
270
  elsif compose?(n)
272
- ret << 'compose build'
271
+ ret = docker_session 'compose build'
273
272
  append_file n
274
273
  from = :compose
275
274
  else
276
- ret << 'build'
275
+ ret = docker_session 'build'
277
276
  end
278
277
  else
279
- ret << from
278
+ ret = docker_session from
280
279
  end
281
280
  case opts
282
281
  when String
@@ -863,6 +863,8 @@ module Squared
863
863
  rev_parse(flag, ref: ref, size: size)
864
864
  end
865
865
  when :build
866
+ next unless build?
867
+
866
868
  format_desc action, flag, 'opts*'
867
869
  task flag do |_, args|
868
870
  revbuild flag, args.to_a
@@ -1114,7 +1116,7 @@ module Squared
1114
1116
  end
1115
1117
  end
1116
1118
  out = choice_index('Choose a stash', git_spawn('stash list', stdout: false),
1117
- values: values, column: /^[^@]+@\{(\d+)\}/, force: true)
1119
+ values: values, column: /^[^@]+@\{(\d+)\}/)
1118
1120
  if values
1119
1121
  op.merge(out.reverse)
1120
1122
  else
@@ -1766,7 +1768,7 @@ module Squared
1766
1768
  files << "#{sub_style(b, styles: color(:red))} #{a}"
1767
1769
  end
1768
1770
  unless files.empty?
1769
- files = choice_index('Select files', files, multiple: true, force: true, trim: /^\S+\s/,
1771
+ files = choice_index('Select files', files, multiple: true, trim: /^\S+\s/,
1770
1772
  accept: [['Add?', false, true]])
1771
1773
  end
1772
1774
  op.swap(list + files)
@@ -1833,7 +1835,7 @@ module Squared
1833
1835
  return args ? [IO.popen(cmd), banner || '', from] : IO.popen(cmd)
1834
1836
  elsif stdin? ? sync : stdout
1835
1837
  print_item banner unless multiple
1836
- ret = `#{cmd}`
1838
+ ret = `#{cmd}`.chomp
1837
1839
  if !ret.empty?
1838
1840
  puts ret
1839
1841
  elsif success?(!banner.nil?)
@@ -27,7 +27,8 @@ module Squared
27
27
  use-running-store-server use-store-server child-concurrency=i hoist-pattern=q lockfile-dir=p
28
28
  modules-dir=p network-concurrency=i package-import-method=b public-hoist-pattern=q
29
29
  reporter=b].freeze,
30
- install_base: %w[global-dir ignore-scripts offline prefer-offline store-dir=p virtual-store-dir=p].freeze,
30
+ install_base: %w[dangerously-allow-all-builds global-dir ignore-scripts offline prefer-offline store-dir=p
31
+ virtual-store-dir=p].freeze,
31
32
  install_no: %w[frozen-lockfile verify-store-integrity].freeze,
32
33
  install_as: %w[D|dev no-optional P|prod].freeze,
33
34
  update: %w[g|global i|interactive L|latest depth=i].freeze,
@@ -448,6 +449,7 @@ module Squared
448
449
  split_escape(val).each { |opt| cmd << shell_option('public-hoist-pattern', opt) }
449
450
  end
450
451
  cmd << '--ignore-workspace' if env('NODE_WORKSPACES', equals: '0')
452
+ cmd << '--dangerously-allow-all-builds' if option('approve-builds')
451
453
  append_nocolor
452
454
  else
453
455
  cmd = session 'npm', 'install'
@@ -30,15 +30,15 @@ module Squared
30
30
  OPT_POETRY = {
31
31
  common: %w[ansi no-ansi no-cache n|no-interaction no-plugins q|quiet v|verbose P|project=p].freeze,
32
32
  build: %w[clean config-settings=qq f|format=b o|output=p].freeze,
33
- publish: %w[build dry-run skip-existing cert=p client-cert=p dist-dir=p p|password=b r|repository=b
34
- u|username=b].freeze
33
+ publish: %w[build dry-run skip-existing cert=p client-cert=p dist-dir=p p|password=q r|repository=b
34
+ u|username=qq].freeze
35
35
  }.freeze
36
36
  OPT_PDM = {
37
37
  common: %w[I|ignore-python no-cache n|non-interactive].freeze,
38
38
  build: %w[C=bm no-clean no-isolation no-sdist no-wheel quiet verbose config-setting=q d|dest=p p|project=p
39
39
  k|skip=b].freeze,
40
40
  publish: %w[no-build no-very-ssl quiet S|sign skip-existing verbose ca-certs=p c|comment=q d|dest=p
41
- i|identity=b P|password=q p|project=p r|repository=q k|skip=b u|username=b].freeze
41
+ i|identity=b P|password=q p|project=p r|repository=q k|skip=b u|username=qq].freeze
42
42
  }.freeze
43
43
  OPT_HATCH = {
44
44
  common: %w[color interactive no-color no-interactive cache-dir=p config=p data-dir=p e|env=b p|project=b
@@ -50,7 +50,7 @@ module Squared
50
50
  OPT_TWINE = {
51
51
  publish: %w[attestations disable-progress-bar non-interactive s|sign skip-existing verbose cert=p
52
52
  client-cert=p c|comment=q config-file=p i|identity=b p|password=q r|repository=b repository-url=q
53
- sign-with=b u|username=q].freeze
53
+ sign-with=b u|username=qq].freeze
54
54
  }.freeze
55
55
  private_constant :DEP_PYTHON, :DIR_PYTHON, :OPT_PYTHON, :OPT_PIP, :OPT_POETRY, :OPT_PDM, :OPT_HATCH, :OPT_TWINE
56
56
 
@@ -78,7 +78,7 @@ module Squared
78
78
 
79
79
  attr_reader :venv, :editable
80
80
 
81
- def initialize(*, editable: '.', verbose: nil, **kwargs)
81
+ def initialize(*, editable: '.', **kwargs)
82
82
  super
83
83
  if @pass.include?(Python.ref)
84
84
  initialize_ref Python.ref
@@ -307,10 +307,10 @@ module Squared
307
307
  next unless build_backend == 'hatchling.build'
308
308
  end
309
309
  format_desc(action, flag, 'opts*', after: case flag
310
- when :python then 'srcdir?'
311
310
  when :poetry then 'output?'
312
311
  when :pdm then 'dest?'
313
312
  when :hatch then 'location?'
313
+ else 'outdir?'
314
314
  end)
315
315
  task flag do |_, args|
316
316
  build! flag, args.to_a
@@ -343,6 +343,7 @@ module Squared
343
343
  cmd << '--no-root' if option('no-root')
344
344
  else
345
345
  cmd = pip_session 'install'
346
+ cmd << '--upgrade-strategy=eager' if env('PYTHON_UPDATE')
346
347
  if flag
347
348
  case flag
348
349
  when :user
@@ -478,9 +479,6 @@ module Squared
478
479
 
479
480
  def build!(flag, opts = [])
480
481
  case flag
481
- when :python
482
- cmd, opts = python_session('-m build', opts: opts)
483
- list = OPT_PYTHON[:build]
484
482
  when :poetry
485
483
  cmd = poetry_session 'build'
486
484
  list = OPT_POETRY[:build] + OPT_POETRY[:common]
@@ -490,37 +488,31 @@ module Squared
490
488
  when :hatch
491
489
  cmd, opts = hatch_session('build', opts: opts)
492
490
  list = OPT_HATCH[:build]
491
+ else
492
+ cmd, opts = python_session('-m build', opts: opts)
493
+ list = OPT_PYTHON[:build]
493
494
  end
494
- srcdir = nil
495
495
  op = OptionPartition.new(opts, list, cmd, project: self, single: singleopt(flag))
496
- op.each do |opt|
497
- if !srcdir && basepath(opt.chomp('*')).exist? && projectpath?(opt.chomp('*'))
498
- srcdir = opt
499
- else
500
- op.found << opt
501
- end
502
- end
503
- op.swap
504
496
  case flag
505
- when :poetry, :pdm
506
- if srcdir
507
- args = flag == :pdm ? ['d', 'dest'] : ['o', 'output']
508
- if op.arg?(*args)
509
- op.push(srcdir)
510
- else
511
- op << quote_option(args.last, basepath(srcdir))
512
- end
513
- srcdir = nil
514
- end
515
497
  when :hatch
516
- if ENV['HATCH_BUILD_LOCATION']
517
- srcdir = nil
518
- else
519
- srcdir ||= path
498
+ if !ENV['HATCH_BUILD_LOCATION'] && (outdir ||= op.shift)
499
+ op.add_path(outdir)
500
+ end
501
+ else
502
+ unless op.empty?
503
+ args = case flag
504
+ when :poetry
505
+ %w[o output]
506
+ when :pdm
507
+ %w[d dest]
508
+ else
509
+ srcdir = true
510
+ %w[o outdir]
511
+ end
512
+ op << quote_option(args.last, basepath(op.shift)) unless op.arg?(*args)
520
513
  end
521
- op << basic_option('p', project) unless ENV['HATCH_PROJECT'] || op.arg?('p', 'project')
522
514
  end
523
- op.add_path(srcdir) if srcdir
515
+ op.exist?(add: true, first: true) if srcdir
524
516
  op.clear
525
517
  run(from: :"#{flag}:build")
526
518
  end
@@ -581,17 +573,21 @@ module Squared
581
573
  def variable_set(key, *val, **)
582
574
  case key
583
575
  when :dependfile
584
- req = basepath(*val)
585
- if (index = DEP_PYTHON.index(req.basename.to_s))
586
- @dependindex = index
587
- @dependfile = req
576
+ if val.first.nil?
577
+ super
588
578
  else
589
- log.warn "variable_set: @#{key}=#{req} (not supported)"
579
+ req = basepath(*val)
580
+ if (index = DEP_PYTHON.index(req.basename.to_s))
581
+ @dependindex = index
582
+ @dependfile = req
583
+ else
584
+ log.warn "variable_set: @#{key}=#{req} (not supported)"
585
+ end
590
586
  end
591
587
  when :editable
592
588
  editable_set val.first
593
589
  when :venv
594
- instance_variable_set(:"@#{key}", val.empty? ? nil : basepath(*val))
590
+ @venv = val.empty? || val.first.nil? ? nil : basepath(*val)
595
591
  else
596
592
  super
597
593
  end
@@ -612,10 +608,11 @@ module Squared
612
608
  end
613
609
 
614
610
  def python_session(*cmd, opts: nil)
615
- return session('python', *preopts(quiet: false), *cmd, path: venv.nil?) unless opts
611
+ pre = preopts(quiet: false)
612
+ return session('python', *pre, *cmd, path: venv.nil?) unless opts
616
613
 
617
614
  op = OptionPartition.new(opts, OPT_PYTHON[:common], project: self, single: singleopt(:python))
618
- ret = session('python', *op.to_a, *cmd, path: venv.nil?)
615
+ ret = session('python', *pre, *op.to_a, *cmd, path: venv.nil?)
619
616
  [ret, op.extras]
620
617
  end
621
618
 
@@ -638,8 +635,8 @@ module Squared
638
635
  def create_session(*cmd, name:, common:, opts: nil)
639
636
  return session(name, *preopts, *cmd, path: venv.nil?) unless opts
640
637
 
641
- op = OptionPartition.new(opts, common, project: self, single: singleopt)
642
- ret = session(name, *op.to_a, *cmd, path: venv.nil?)
638
+ op = OptionPartition.new(opts, common, project: self, single: singleopt(name.to_sym))
639
+ ret = session(name, *preopts, *op.to_a, *cmd, path: venv.nil?)
643
640
  [ret, op.extras]
644
641
  end
645
642
 
@@ -693,14 +690,16 @@ module Squared
693
690
  OptionPartition.delete_key(target, 'e', 'editable')
694
691
  case val
695
692
  when '0', 'false'
696
- return
693
+ return unless installable?
697
694
  else
698
695
  val = basepath val
699
696
  end
700
- elsif session_arg?('e', 'editable', target: target) || !(val = editable)
697
+ elsif session_arg?('e', 'editable', target: target) || !installable?
701
698
  return
699
+ else
700
+ val = editable
702
701
  end
703
- target << quote_option('e', basepath(val))
702
+ target << (val ? quote_option('e', basepath(val)) : '.')
704
703
  end
705
704
 
706
705
  def append_global(target: @session)
@@ -876,6 +875,7 @@ module Squared
876
875
  .clear(pass: false)
877
876
  status = op.arg?(/\A-v+\z/)
878
877
  run(op, env, exception: true, banner: banner)
878
+ install(:upgrade, ['poetry']) if poetry?
879
879
  puts(dir.directory? ? "Success: #{dir}" : 'Failed') if banner && !status
880
880
  end
881
881
 
@@ -24,10 +24,10 @@ module Squared
24
24
  common: %w[no-color V|verbose r|retry=i].freeze,
25
25
  install: %w[frozen no-cache no-prune system binstubs=p? path=p standalone=q? target-rbconfig=p trust-policy=b
26
26
  with=q without=q].freeze,
27
- install_base: %w[force full-index quiet redownload gemfile=p j|jobs=i].freeze,
27
+ install_base: %w[force full-index local quiet redownload gemfile=p j|jobs=i].freeze,
28
28
  update: %w[all conservative local major minor patch pre ruby strict bundler=b? g|group=q source=b].freeze,
29
- outdated: %w[filter-major filter-minor filter-patch groups local parseable pre only-explicit strict
30
- update-strict group=q source=b].freeze,
29
+ outdated: %w[filter-major filter-minor filter-patch filter-strict groups local parseable porcelain pre
30
+ only-explicit strict update-strict group=q source=b].freeze,
31
31
  exec: %w[gemfile=p].freeze,
32
32
  cache: %w[all all-platforms frozen no-all no-install no-prune quiet cache-path=p gemfile=p path=p].freeze,
33
33
  check: %w[dry-run gemfile=p path=p].freeze
@@ -38,7 +38,7 @@ module Squared
38
38
  install_base: %w[E f w b|both clear-sources conservative default development development-all explain
39
39
  ignore-dependencies l|local N|no-document r|remote vendor n|bindir=p build-root=p
40
40
  B|bulk-threshold=i document=b? g|file=p? p|http-proxy=q? i|install-dir=p platform=q
41
- s|source=q target-rbconfig=p? P|trust-policy=b without=b].freeze,
41
+ s|source=q target-rbconfig=p? P|trust-policy=b without=q].freeze,
42
42
  update: %w[system=b?].freeze,
43
43
  uninstall: %w[a D I x vendor n|bindir=p i|install-dir=p platform=b v|version=q].freeze,
44
44
  outdated: %w[b|both clear-sources l|local r|remote B|bulk-threshold=i p|http-proxy=q? platform=q
@@ -69,7 +69,7 @@ module Squared
69
69
  end
70
70
 
71
71
  def bannerargs
72
- %i[dependfile gemname].freeze
72
+ %i[dependfile gemname gemdir].freeze
73
73
  end
74
74
 
75
75
  def config?(val)
@@ -93,6 +93,8 @@ module Squared
93
93
  'irb' => nil
94
94
  })
95
95
 
96
+ attr_reader :gemdir
97
+
96
98
  def initialize(*, autodetect: false, gemspec: nil, **kwargs)
97
99
  super
98
100
  if @pass.include?(Ruby.ref)
@@ -126,6 +128,14 @@ module Squared
126
128
  end
127
129
  end
128
130
 
131
+ def gemdir=(val)
132
+ @gemdir = if val.is_a?(Pathname)
133
+ val
134
+ else
135
+ Pathname.new(val).realdirpath rescue nil
136
+ end
137
+ end
138
+
129
139
  def ref
130
140
  Ruby.ref
131
141
  end
@@ -241,7 +251,7 @@ module Squared
241
251
  else
242
252
  a, b, c = choice_index('Select a file', Dir.glob(file || path.join('*.rb')),
243
253
  values: (file ? [] : ['Options']).push('Arguments'),
244
- force: true, series: true)
254
+ series: true)
245
255
  if file
246
256
  file = a
247
257
  b
@@ -287,7 +297,7 @@ module Squared
287
297
  end
288
298
  end
289
299
 
290
- def copy(from: gemlib, into: @gemdir, override: false, **kwargs)
300
+ def copy(from: gemlib, into: gemdir, override: false, **kwargs)
291
301
  return if @copy == false
292
302
 
293
303
  glob = kwargs[:include]
@@ -716,7 +726,7 @@ module Squared
716
726
  "#{spec.name}-#{spec.version}.gem"
717
727
  else
718
728
  gems = Dir.glob(basepath('*.gem')).map { |val| File.basename(val) }
719
- choice_index('Select a file', gems, force: true)
729
+ choice_index 'Select a file', gems
720
730
  end)
721
731
  else
722
732
  file = op.shift
@@ -844,62 +854,63 @@ module Squared
844
854
 
845
855
  def copy?
846
856
  return true if @copy.is_a?(Hash) ? copy[:into] : super
847
- return gemdir? if @gemdir
857
+ return gemdir? if gemdir
848
858
 
849
859
  if version
850
860
  begin
851
861
  case @autodetect
852
862
  when 'rvm'
853
- @gemdir = pwd_set { `rvm info homes` }[/^\s+gem:\s+"(.+)"$/, 1]
863
+ self.gemdir = pwd_set { `rvm info homes` }[/^\s+gem:\s+"(.+)"$/, 1]
854
864
  when 'rbenv'
855
865
  if pwd_set { `rbenv which ruby` } =~ %r{^(.+[\\/]versions[\\/](\d\.\d)\.[^\\/]+)[\\/]bin[\\/]ruby$}
856
- @gemdir = File.join($1, 'lib/ruby/gems', "#{$2}.0")
866
+ self.gemdir = File.join($1, 'lib/ruby/gems', "#{$2}.0")
857
867
  end
858
868
  when 'asdf'
859
- @gemdir = pwd_set { `asdf where ruby` }
860
- @gemdir = @gemdir =~ /(\d\.\d)\.[^.]+$/ && File.join(@gemdir, 'lib/ruby/gems', "#{$1}.0")
869
+ val = pwd_set { `asdf where ruby` }
870
+ self.gemdir = File.join(val, 'lib/ruby/gems', "#{$1}.0") if val =~ /(\d\.\d)\.[^.]+$/
871
+ when 'env'
872
+ ENV['GEM_HOME'] || ENV['GEM_ROOT']
861
873
  when /bundler?/
862
- @gemdir = pwd_set { `bundle env` }[/^\s+Gem Home\s+(.+)$/, 1]
874
+ path = pwd_set { `bundle env` }[/^\s+Gem Path\s+(.+)$/, 1]
875
+ self.gemdir = path.split(File::PATH_SEPARATOR).find { |val| Dir.exist?(val) }
863
876
  end
864
877
  rescue StandardError => e
865
878
  log.debug e
866
- else
867
- @gemdir = Pathname.new(@gemdir) if @gemdir
868
879
  end
869
880
  return true if gemdir?
870
881
  end
871
882
  return false unless @autodetect
872
883
 
873
884
  set = lambda do |val, path|
874
- if (ver = version) && ver != val
875
- log.warn "using version #{val} (given #{ver})"
876
- end
885
+ base = Pathname.new(path.strip)
886
+ return false unless base.join(gempath(val, 'specification')).exist?
887
+
888
+ log.warn "using version #{val} (given #{version})" if version && version != val
877
889
  self.version = val
878
- @gemdir = Pathname.new(path.strip) + gempath
890
+ self.gemdir = base + gempath
879
891
  end
880
892
  if version
881
893
  opt = gempwd
882
894
  pwd_set(pass: !opt.nil?) do
883
895
  out = `#{gem_output(opt, 'list --local -d', gemname)}`
884
- if out =~ /#{Regexp.escape(gemname)} \(([^)]+)\)/
896
+ if out =~ /#{Regexp.escape(gemname)}\s+\((.+)\)$/
885
897
  split_escape($1)
886
898
  .unshift(@version)
887
899
  .uniq
888
900
  .each do |val|
889
- next unless out =~ /\(#{Regexp.escape(val)}(?:,[^)]+|\b)\):([^\n]+)/
901
+ next unless out =~ /(?:\(#{Regexp.escape(val)}[^)]*\)|Installed at):\s+(.+)$/
890
902
 
891
- set.call(val, $1)
892
- return gemdir? if @gemdir
903
+ return gemdir? if set.call(val, $1)
893
904
  end
894
905
  end
895
906
  end
896
- @gemdir = Pathname.new(Gem.dir) + gempath
907
+ self.gemdir = Pathname.new(Gem.dir) + gempath
897
908
  else
898
909
  parse = lambda do |path|
899
910
  next unless path
900
911
 
901
912
  lib = Regexp.new(['', 'gems', "#{gemname}-([^#{File::SEPARATOR}]+)", ''].join(File::SEPARATOR))
902
- if (ver = path[lib, 1]) && (val = path[/\A(.+)#{gempath(ver[1])}/, 1])
913
+ if (ver = path[lib, 1]) && (val = path[/\A(.+)#{Regexp.escape(gempath(ver))}/, 1])
903
914
  set.call(ver, val)
904
915
  end
905
916
  end
@@ -907,7 +918,7 @@ module Squared
907
918
  target = RUBY_VERSION.start_with?('2.6') ? RubyVM : $LOAD_PATH
908
919
  parse.call(target.resolve_feature_path(gemname)&.last)
909
920
  end
910
- if !@gemdir && !pwd_set { parse.call(`#{bundle_output('show', gemname)}`) }
921
+ if !gemdir && !pwd_set { parse.call(`#{bundle_output('show', gemname)}`) }
911
922
  raise_error 'gems directory not found'
912
923
  end
913
924
  end
@@ -1042,14 +1053,16 @@ module Squared
1042
1053
  end
1043
1054
  end
1044
1055
 
1045
- def gempath(val = version)
1046
- File.join('gems', "#{gemname}-#{val}")
1056
+ def gempath(val = version, dir = 'gems')
1057
+ ret = File.join(dir, "#{gemname}-#{val}")
1058
+ ret += '.gemspec' if dir == 'specifications'
1059
+ ret
1047
1060
  end
1048
1061
 
1049
1062
  def gemdir?
1050
- return false unless @gemdir
1063
+ return false unless gemdir
1051
1064
 
1052
- @gemdir.exist? && !@gemdir.empty?
1065
+ gemdir.exist? && !gemdir.empty?
1053
1066
  end
1054
1067
 
1055
1068
  alias read_rakefile raketasks
@@ -114,7 +114,7 @@ module Squared
114
114
 
115
115
  def matchopts(list, value = false)
116
116
  a, b = Array(list).partition { |val| val.size == 1 || val.match?(OPT_SINGLE) }
117
- return /\A#{shortopt(*a)}}/ if b.empty?
117
+ return /\A#{shortopt(*a)}/ if b.empty?
118
118
  return /\A#{longopt(*b, value)}/ if a.empty?
119
119
 
120
120
  /\A(?:#{shortopt(*a)}|#{longopt(*b, value)})/
@@ -135,9 +135,9 @@ module Squared
135
135
 
136
136
  def_delegators :@target, :+, :-, :<<, :any?, :none?, :include?, :add, :add?, :find, :find_all, :find_index,
137
137
  :merge, :compact, :delete, :delete?, :delete_if, :grep, :grep_v, :inspect, :to_a, :to_s
138
- def_delegators :@extras, :empty?, :each, :each_with_index, :partition, :dup, :first, :last, :shift, :unshift,
139
- :pop, :push, :concat, :index, :join, :map, :map!, :detect, :select, :select!, :reject, :size,
140
- :delete_at
138
+ def_delegators :@extras, :empty?, :member?, :each, :each_with_index, :each_with_object, :partition, :dup,
139
+ :first, :last, :shift, :unshift, :pop, :push, :concat, :index, :join, :map, :map!, :detect,
140
+ :select, :select!, :reject, :size
141
141
 
142
142
  def_delegator :@extras, :delete, :remove
143
143
  def_delegator :@extras, :delete_at, :remove_at
@@ -309,6 +309,7 @@ module Squared
309
309
  else
310
310
  add val
311
311
  end
312
+ found << val if args.empty?
312
313
  end
313
314
  self
314
315
  end
@@ -405,26 +406,26 @@ module Squared
405
406
  end
406
407
 
407
408
  def splice(*exclude, quote: true, delim: true, path: false, pattern: false, &blk)
408
- found, other = if block_given?
409
- partition(&blk)
410
- elsif exclude.first.is_a?(Symbol)
411
- partition(&exclude.first)
412
- else
413
- partition do |val|
414
- next false if pattern && OptionPartition.pattern?(val)
415
-
416
- exclude.none? { |pat| val.match?(Regexp.new(pat)) }
417
- end
418
- end
419
- unless found.empty?
409
+ temp, other = if block_given?
410
+ partition(&blk)
411
+ elsif exclude.first.is_a?(Symbol)
412
+ partition(&exclude.first)
413
+ else
414
+ partition do |val|
415
+ next false if pattern && OptionPartition.pattern?(val)
416
+
417
+ exclude.none? { |pat| val.match?(Regexp.new(pat)) }
418
+ end
419
+ end
420
+ unless temp.empty?
420
421
  add '--' if delim
421
422
  extras.clear
422
423
  concat other
423
424
  if path
424
- found.each { |val| add_path(val) }
425
+ temp.each { |val| add_path(val) }
425
426
  else
426
- found.map! { |val| shell_quote(val) } if quote
427
- merge found
427
+ temp.map! { |val| shell_quote(val) } if quote
428
+ merge temp
428
429
  end
429
430
  end
430
431
  self
@@ -228,10 +228,10 @@ module Squared
228
228
  return false unless root.directory?
229
229
 
230
230
  path = sub_style(root, styles: theme[:inline])
231
+ timeout = env('REPO_TIMEOUT').to_i
232
+ timeout = 15 unless timeout > 0
231
233
  @repo_override = Common::Prompt.confirm(
232
- "#{log_title(:warn)} \"#{path}\" is not empty. Continue with installation? [y/N] ",
233
- 'N',
234
- timeout: env('REPO_TIMEOUT', 15, ignore: '0')
234
+ "#{log_title(:warn)} \"#{path}\" is not empty. Continue with installation? [y/N] ", 'N', timeout: timeout
235
235
  )
236
236
  end
237
237
 
@@ -31,15 +31,19 @@ module Squared
31
31
  end
32
32
  elsif (data = TASK_BATCH[obj])
33
33
  args.each { |ref| data.delete(ref) }
34
- TASK_KEYS.delete(obj) if data.empty?
34
+ if data.empty?
35
+ TASK_KEYS.delete(obj)
36
+ TASK_BATCH.delete(obj)
37
+ end
35
38
  end
36
39
  end
37
40
 
38
41
  def alias(ref, obj)
39
42
  if obj.is_a?(Hash)
40
43
  obj.each { |key, val| TASK_ALIAS[key][ref] = val }
41
- else
42
- TASK_ALIAS[obj]&.delete(ref)
44
+ elsif TASK_ALIAS.key?(obj)
45
+ TASK_ALIAS[obj].delete(ref)
46
+ TASK_ALIAS.delete(obj) if TASK_ALIAS[obj].empty?
43
47
  end
44
48
  end
45
49
 
@@ -165,6 +169,8 @@ module Squared
165
169
  end
166
170
 
167
171
  def alias_get(key)
172
+ return unless TASK_ALIAS.key?(key)
173
+
168
174
  TASK_ALIAS[key]
169
175
  end
170
176
 
@@ -182,12 +188,14 @@ module Squared
182
188
  end
183
189
 
184
190
  def extend?(obj, key)
185
- return false unless (items = TASK_EXTEND[key]) && !(items = items.select { |kind| obj.is_a?(kind) }).empty?
191
+ return false unless TASK_EXTEND.key?(key)
186
192
 
187
193
  meth = :"#{key}?"
188
194
  ret = false
189
- items.each do |kind|
190
- if kind.instance_methods.include?(meth)
195
+ TASK_EXTEND[key].each do |kind|
196
+ next unless obj.is_a?(kind)
197
+
198
+ if kind.method_defined?(meth)
191
199
  out = obj.__send__(meth)
192
200
  return true if out == 1
193
201
  return out if obj.ref?(kind.ref)
@@ -228,7 +236,7 @@ module Squared
228
236
  end
229
237
 
230
238
  def exclude?(key, empty = false)
231
- @exclude.include?(key) || (empty && @data[key].empty?)
239
+ @exclude.include?(key) || (empty && (!@data.key?(key) || @data[key].empty?))
232
240
  end
233
241
 
234
242
  private
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: squared
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.32
4
+ version: 0.4.34
5
5
  platform: ruby
6
6
  authors:
7
7
  - An Pham