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.
@@ -7,6 +7,9 @@ module Squared
7
7
  GEMFILE = %w[Gemfile Gemfile.lock gem.deps.rb Isolate].freeze
8
8
  DIR_RUBY = (GEMFILE + Rake::Application::DEFAULT_RAKEFILES + ['README.rdoc']).freeze
9
9
  OPT_RUBY = {
10
+ ruby: %w[a c l n p s S w d|debug jit rjit v|verbose y|yydebug 0=im? backtrace-limit=i crash-report=q
11
+ disable=q dump=q e=q enable=q encoding=b external-encoding=b Eex=m? F=qm i=m? internal-encoding=b
12
+ I=pm parser=b r=bm W=bm? x=pm?].freeze,
10
13
  rake: %w[A|all B|build-all comments n|dry-run p|execute-print=q m|multitask P|prereqs q|quiet
11
14
  X|no-deprecation-warnings N|no-search G|no-system nosearch nosystem rules s|silent g|system
12
15
  v|verbose backtrace=b? D|describe=q? e|execute=q E|execute-continue=q I|libdir=p job-stats=b?
@@ -33,20 +36,25 @@ module Squared
33
36
  OPT_GEM = {
34
37
  common: %w[backtrace debug q|quiet no-verbose norc silent V|verbose config-file=p].freeze,
35
38
  install: %w[version=b].freeze,
39
+ install_base: %w[f b|both clear-sources conservative default development development-all E|explain
40
+ ignore-dependencies l|local N|no-document r|remote w|vendor n|bindir=p build-root=p
41
+ bulk-threshold=i document=b? g|file=p? p|http-proxy=q i|install-dir=p platform=q s|source=q
42
+ target-rbconfig=p? P|trust-policy=b without=b].freeze,
36
43
  update: %w[system=b?].freeze,
44
+ uninstall: %w[a D I x vendor n|bindir=p i|install-dir=p platform=b v|version=b].freeze,
37
45
  outdated: %w[b|both clear-sources l|local no-http-proxy r|remote B|bulk-threshold=i p|http-proxy=q?
38
46
  platform=q source=q].freeze,
39
47
  push: %w[no-http-proxy attestation=p host=q key=b otp=b p|http-proxy=q?].freeze,
40
48
  build: %w[force strict o|output=p platform=q].freeze,
41
- exec: %w[conservative prerelease no-prerelease g|gem=b version=b].freeze,
49
+ exec: %w[conservative prerelease no-prerelease g|gem=v version=b].freeze,
42
50
  pristine: %w[all env-shebang extensions no-env-shebang no-extensions only-executables only-missing-extensions
43
51
  only-plugins n|bindir=p i|install-dir=p skip=b v|version=b].freeze,
44
- shared: %w[f b|both clear-sources conservative default development development-all E|explain
45
- ignore-dependencies l|local N|no-document r|remote w|vendor n|bindir=p build-root=p
46
- bulk-threshold=i document=b? g|file=p? p|http-proxy=q i|install-dir=p platform=q s|source=q
47
- target-rbconfig=p? P|trust-policy=b without=b].freeze,
48
- shared_no: %w[env-shebang force format-executable http-proxy lock minimal-deps post-install-message
49
- prerelease suggestions user-install wrappers].freeze
52
+ no: {
53
+ install: %w[env-shebang force format-executable http-proxy lock minimal-deps post-install-message
54
+ prerelease suggestions user-install wrappers].freeze,
55
+ uninstall: %w[abort-on-dependent all check-development executables force format-executable
56
+ ignore-dependencies user-install].freeze
57
+ }.freeze
50
58
  }.freeze
51
59
  private_constant :GEMFILE, :DIR_RUBY, :OPT_RUBY, :OPT_BUNDLE, :OPT_GEM
52
60
 
@@ -72,7 +80,8 @@ module Squared
72
80
  'install' => %i[redownload local prefer-local].freeze,
73
81
  'update' => %i[patch minor major all].freeze,
74
82
  'outdated' => %i[patch minor major].freeze,
75
- 'gem' => %i[install user-install update pristine outdated push build exec].freeze,
83
+ 'gem' => %i[install uninstall update pristine outdated push build exec].freeze,
84
+ 'ruby' => %i[file script version].freeze,
76
85
  'exec' => nil,
77
86
  'cache' => nil,
78
87
  'config' => nil,
@@ -119,7 +128,7 @@ module Squared
119
128
  return unless outdated? && ref?(Ruby.ref)
120
129
 
121
130
  namespace name do
122
- @@tasks[Ruby.ref].each do |action, flags|
131
+ Ruby.subtasks do |action, flags|
123
132
  next if @pass.include?(action)
124
133
 
125
134
  if flags.nil?
@@ -149,7 +158,7 @@ module Squared
149
158
  end
150
159
  when 'irb'
151
160
  next unless (spec = basepath("#{project}.gemspec") || basepath("#{name}.gemspec"))
152
- next unless basepath('lib').join("#{gemname = File.basename(spec, File.extname(spec))}.rb").exist?
161
+ next unless basepath('lib').join("#{gemname = stripext(spec)}.rb").exist?
153
162
 
154
163
  format_desc action, nil, 'opts*,args*'
155
164
  task action do |_, args|
@@ -177,16 +186,54 @@ module Squared
177
186
  end
178
187
  when 'gem'
179
188
  case flag
180
- when :outdated, :build, :push, :exec
189
+ when :outdated
190
+ format_desc action, flag, 'major|minor|patch|(i)nteractive?,opts*'
191
+ task flag, [:semver] do |_, args|
192
+ case (filter = args.semver)
193
+ when 'major', 'minor', 'patch', 'interactive', 'i'
194
+ filter = 'interactive' if filter == 'i'
195
+ args = args.to_a.drop(1)
196
+ else
197
+ filter = nil
198
+ args = args.to_a
199
+ end
200
+ gem!(flag, args, filter: filter)
201
+ end
202
+ when :build, :push, :exec
181
203
  format_desc(action, flag, 'opts*', before: flag == :exec ? 'command+' : nil)
182
204
  task flag do |_, args|
183
- gemx flag, args.to_a
205
+ gem! flag, args.to_a
184
206
  end
185
207
  else
186
208
  format_desc action, flag, "opts*,name+#{flag == :pristine ? '|name?@version' : ''}"
187
209
  task flag do |_, args|
188
210
  opts = param_guard(action, flag, args: args.to_a)
189
- gemx flag, opts
211
+ gem! flag, opts
212
+ end
213
+ end
214
+ when 'ruby'
215
+ case flag
216
+ when :file
217
+ format_desc action, flag, 'path,opts*,args*'
218
+ task flag, [:rb] do |_, args|
219
+ file = param_guard(action, flag, args: args, key: :rb)
220
+ ruby(flag, args.to_a.drop(1), file: file)
221
+ end
222
+ when :script
223
+ format_desc action, flag, 'command|RUBY_E,opts*,args*'
224
+ task flag, [:e] do |_, args|
225
+ if (command = ENV['RUBY_E'])
226
+ opts = args.to_a
227
+ else
228
+ command = param_guard(action, flag, args: args, key: :e)
229
+ opts = args.to_a.drop(1)
230
+ end
231
+ ruby(flag, opts, command: command)
232
+ end
233
+ when :version
234
+ format_desc action, flag
235
+ task flag do
236
+ ruby flag
190
237
  end
191
238
  end
192
239
  end
@@ -274,7 +321,7 @@ module Squared
274
321
  end
275
322
  minor_set = -> { styles[0] = cur[2] == lat[2] ? :yellow : :green }
276
323
  if data.empty?
277
- semmajor?(cur, lat) ? major_set.() : minor_set.()
324
+ semmajor?(cur, lat) ? major_set.call : minor_set.call
278
325
  else
279
326
  data.each do |val|
280
327
  break unless line =~ /(>=?|=|~>|!=|<=?) (#{Regexp.escape(val.join)})/
@@ -282,11 +329,11 @@ module Squared
282
329
  v = semver(val).join
283
330
  case $1
284
331
  when '>', '>='
285
- semmajor?(cur, lat) ? major_set.() : minor_set.()
332
+ semmajor?(cur, lat) ? major_set.call : minor_set.call
286
333
  when '<', '<='
287
334
  if c <= v
288
335
  if semmajor?(cur, lat)
289
- major_set.()
336
+ major_set.call
290
337
  else
291
338
  styles[0] = :yellow
292
339
  end
@@ -324,11 +371,11 @@ module Squared
324
371
  index: 2)
325
372
  end
326
373
  end
327
- out.("#{start.to_s.rjust(2)}. #{line}")
374
+ out.call("#{start.to_s.rjust(2)}. #{line}")
328
375
  elsif line.start_with?('Gem')
329
376
  unless stdin?
330
377
  sub = { pat: /^(.+)(?<!\dm)(Gem|Latest)(.+)$/, styles: theme[:header], index: 2 }
331
- out.(print_footer(" # #{line.chomp}", reverse: true, sub: [sub, sub]))
378
+ out.call(print_footer(" # #{line.chomp}", reverse: true, sub: [sub, sub]))
332
379
  end
333
380
  else
334
381
  next
@@ -371,50 +418,143 @@ module Squared
371
418
  run_rb(from: :update)
372
419
  end
373
420
 
374
- def gemx(flag, opts = [])
421
+ def ruby(flag, opts = [], file: nil, command: nil)
422
+ case flag
423
+ when :file, :script
424
+ op = OptionPartition.new(opts, OPT_RUBY[:ruby], ruby_session, project: self, args: true)
425
+ if file
426
+ op.extras.unshift(shell_quote(basepath(file)))
427
+ elsif command
428
+ op << quote_option('e', command, option: false)
429
+ end
430
+ if (args = ENV['RUBY_ARGS'])
431
+ op.extras << args
432
+ end
433
+ op.append(delim: true, escape: false, quote: false) unless op.empty?
434
+ when :version
435
+ pwd_set do
436
+ out = []
437
+ [
438
+ '$HOME/.rvm/bin/rvm',
439
+ '/usr/local/rvm/bin/rvm',
440
+ '/usr/share/rvm/bin/rvm',
441
+ "#{ENV.fetch('RBENV_ROOT', '$HOME/.rbenv')}/bin/rbenv",
442
+ '/usr/bin/rbenv',
443
+ '/usr/local/share/chruby/chruby.sh',
444
+ "#{ENV.fetch('ASDF_DATA_DIR', '$HOME/.asdf')}/plugins/ruby/bin/install",
445
+ ''
446
+ ].each do |val|
447
+ next unless val.empty? || File.exist?(val.sub('$HOME', Dir.home))
448
+
449
+ trim = ->(s) { s[/^\D+\d+\.\d+(?:\.\S+)?/, 0].sub(/^([a-z]+)-/i, '\1 ') }
450
+ ver = '.ruby-version'
451
+ out << trim.call(case (cmd = File.basename(val))
452
+ when 'rvm'
453
+ `rvm current`[/^\S+/, 0]
454
+ when 'rbenv'
455
+ name = `rbenv version-name`
456
+ name =~ SEM_VER ? "ruby #{name}" : name
457
+ when 'chruby.sh'
458
+ chruby = session_output 'source', val
459
+ `#{chruby.with('ruby --version')}`
460
+ when 'install'
461
+ ver = '.tool-versions'
462
+ `asdf current ruby`[/ruby\s+\S+/, 0].sub(/\s+/, ' ')
463
+ else
464
+ ver = nil
465
+ `ruby --version`
466
+ end)
467
+ unless val.empty?
468
+ out << trim.call(case cmd
469
+ when 'chruby.sh'
470
+ `#{chruby.with('chruby --version')}`.sub(':', '')
471
+ when 'install'
472
+ "asdf #{`asdf version`.sub(/^v/, '')}"
473
+ else
474
+ `#{cmd} --version`
475
+ end)
476
+ end
477
+ begin
478
+ out << ('which %s' % case cmd
479
+ when 'rbenv'
480
+ `rbenv which ruby`
481
+ when 'chruby.sh'
482
+ `#{chruby.with('which ruby')}`
483
+ when 'install'
484
+ `asdf which ruby`
485
+ else
486
+ `which ruby`
487
+ end)
488
+ rescue StandardError => e
489
+ log.debug e
490
+ end
491
+ if ver
492
+ path.ascend do |ent|
493
+ next unless (ent = ent.join(ver)).exist?
494
+
495
+ hint = File.read(ent).lines(chomp: true).reject(&:empty?).join(', ') rescue nil
496
+ out << message("found #{ent}", hint: hint)
497
+ end
498
+ end
499
+ break
500
+ end
501
+ out.map!(&:split)
502
+ pad = out.map(&:first).map!(&:size).max
503
+ puts(out.map! { |line| "#{line.first.rjust(pad)} #{line[1..-1].join(' ')}" })
504
+ end
505
+ return
506
+ end
507
+ run_rb(banner: op.arg?('v'), from: :"ruby:#{flag}")
508
+ end
509
+
510
+ def gem!(flag, opts = [], filter: nil)
375
511
  cmd = gem_session
376
512
  case flag
377
513
  when :outdated
378
514
  cmd << gempwd << 'outdated'
379
515
  when :push
380
516
  cmd << 'push' << project
381
- when :'user-install'
382
- cmd << 'install' << '--user-install'
383
517
  else
384
518
  cmd << flag
385
519
  end
386
- list = OPT_GEM[flag == :'user-install' ? :install : flag] + OPT_GEM[:common]
520
+ list = OPT_GEM[flag] + OPT_GEM[:common]
521
+ from = :"gem:#{flag}"
387
522
  case flag
388
- when :install, :'user-install', :update
389
- list += OPT_GEM[:shared]
390
- no = OPT_GEM[:shared_no]
523
+ when :install, :update
524
+ list.concat(OPT_GEM[:install_base])
525
+ no = OPT_GEM[:no][:install]
526
+ first = true
527
+ when :uninstall
528
+ no = OPT_GEM[:no][:uninstall]
391
529
  first = true
392
530
  when :pristine
393
531
  first = true
394
532
  end
395
533
  cmd.merge(preopts)
396
- opts, pat = option_sanitize(opts, list, no: no, first: first)
397
- out = []
398
- err = []
399
- opts.each do |opt|
400
- if opt =~ pat
534
+ op = OptionPartition.new(opts, list, cmd, project: self, no: no, first: first)
535
+ op.each do |opt|
536
+ if opt =~ op.values
401
537
  case $1
402
538
  when 'g', 'gem'
403
- cmd << (flag == :exec ? shell_option($1, $2) : quote_option($1, basepath($2)))
539
+ op << (flag == :exec ? shell_option($1, $2) : quote_option($1, basepath($2)))
404
540
  end
405
- elsif opt.match?(/^\w+=/) && !%i[outdated build push exec].include?(flag)
406
- err << opt
541
+ elsif opt.include?('=') && !%i[outdated build push exec].include?(flag)
542
+ op.errors << opt
407
543
  else
408
- out << opt
544
+ op.found << opt
409
545
  end
410
546
  end
411
- from = :"gem:#{flag == :'user-install' ? 'install' : flag}"
547
+ op.swap
412
548
  case flag
413
549
  when :outdated
414
550
  log.info cmd.to_s
415
- option_clear out
551
+ op.clear
416
552
  on :first, from
417
553
  print_item format_banner(cmd.to_s)
554
+ major = 0
555
+ minor = 0
556
+ patch = 0
557
+ update = []
418
558
  pwd_set(pass: !pwd.nil?, from: from) do
419
559
  items = [[%w[Gem Current Latest], nil]]
420
560
  IO.popen(cmd.done).each do |line|
@@ -422,9 +562,9 @@ module Squared
422
562
  cur = semscan($2)
423
563
  lat = semscan($3)
424
564
  items << [$~.to_a.drop(1), if semmajor?(cur, lat)
425
- %i[green bold]
565
+ 1
426
566
  else
427
- cur[2] == lat[2] ? [:yellow] : [:green]
567
+ cur[2] == lat[2] ? 3 : 2
428
568
  end]
429
569
  else
430
570
  puts line
@@ -443,9 +583,7 @@ module Squared
443
583
  e = b.size if b.size > e
444
584
  f = c.size if c.size > f
445
585
  end
446
- major = 0
447
- minor = 0
448
- patch = 0
586
+ j = 0
449
587
  items.each_with_index do |item, i|
450
588
  next if i == 0 && stdin?
451
589
 
@@ -459,69 +597,116 @@ module Squared
459
597
  puts line
460
598
  puts sub_style(ARG[:BORDER][1] * n, styles: borderstyle)
461
599
  else
462
- styles = item.last
463
- a = a.ljust(d)
464
- if styles.first == :green
465
- a = sub_style(a, styles: if styles.size == 2
466
- major += 1
467
- theme[:major]
468
- else
469
- minor += 1
470
- theme[:active]
471
- end)
600
+ g = a.ljust(d)
601
+ pat = [/\A([^.]+\.)([^.]+\..+)\z/, /\A([^.]+\.[^.]+\.)(.+)\z/]
602
+ pre = b.start_with?('0.')
603
+ latest = [theme[:current]]
604
+ case item.last
605
+ when 1
606
+ case filter
607
+ when 'major'
608
+ update << a
609
+ when 'minor', 'patch'
610
+ next
611
+ end
612
+ g = sub_style(g, styles: theme[:major])
613
+ major += 1
614
+ styles = %i[green bold]
615
+ pat = pre ? pat.first : nil
616
+ latest << :bold
617
+ when 2
618
+ case filter
619
+ when 'major', 'minor'
620
+ update << a
621
+ when 'patch'
622
+ next
623
+ end
624
+ g = sub_style(g, styles: theme[:active])
625
+ minor += 1
626
+ styles = %i[green]
627
+ pat = pre ? pat.last : pat.first
472
628
  else
629
+ case filter
630
+ when 'major', 'minor', 'patch'
631
+ update << a
632
+ end
473
633
  patch += 1
634
+ styles = %i[yellow]
635
+ pat = pat.last
474
636
  end
475
637
  b = b.rjust(e)
476
- b = sub_style(b, styles: theme[:current]) if theme[:current]
477
- c = sub_style(c.rjust(f), *colormap(styles))
478
- puts "#{"#{i}.".rjust(pad)} #{a} #{b} #{c}"
638
+ b = sub_style(b, *colormap(styles), pat: pat, index: 2)
639
+ h = sub_style(c.rjust(f), styles: latest.flatten.compact, pat: pat, index: 2)
640
+ j += 1
641
+ puts "#{"#{j}.".rjust(pad)} #{g} #{b} #{h}"
642
+ update << a if filter == 'interactive' && confirm_outdated(a, c, item.last)
479
643
  end
480
644
  end
481
- unless stdin?
482
- status = print_footer("major #{major} / minor #{minor} / patch #{patch}", right: true).split("\n")
483
- status[1] = sub_style(status[1], pat: /^( +major )(\d+)(.+)$/, styles: theme[:major], index: 2)
484
- status[1] = sub_style(status[1], pat: /^(.+)(minor )(\d+)(.+)$/, styles: theme[:active], index: 3)
485
- puts status
486
- end
487
645
  end
488
646
  end
647
+ unless update.empty?
648
+ cmd = gem_output 'update', '-f'
649
+ if (val = option('document', prefix: 'gem', ignore: false))
650
+ cmd << case val
651
+ when '0', 'false'
652
+ '--no-document'
653
+ else
654
+ basic_option('document', val)
655
+ end
656
+ end
657
+ if (val = option('user-install', prefix: 'gem', ignore: false))
658
+ cmd << case val
659
+ when '0', 'false'
660
+ '--no-user-install'
661
+ else
662
+ '--user-install'
663
+ end
664
+ end
665
+ cmd.merge(update)
666
+ run(cmd, banner: false, from: :'gem:update')
667
+ end
668
+ unless stdin?
669
+ status = print_footer("major #{major} / minor #{minor} / patch #{patch}", right: true).split("\n")
670
+ status[1] = sub_style(status[1], pat: /^( +major )(\d+)(.+)$/, styles: theme[:major], index: 2)
671
+ status[1] = sub_style(status[1], pat: /^(.+)(minor )(\d+)(.+)$/, styles: theme[:active], index: 3)
672
+ puts status
673
+ end
489
674
  on :last, from
490
675
  return
491
676
  when :build, :push
492
- if !out.empty?
493
- if flag == :build && out.size == 1
494
- cmd << shell_quote(basepath(out.first))
677
+ if !op.empty?
678
+ if flag == :build && op.size == 1
679
+ op << shell_quote(basepath(op.first))
495
680
  else
496
- raise_error("unknown args: #{out.join(', ')}", hint: flag)
681
+ raise_error("unknown args: #{op.join(', ')}", hint: flag)
497
682
  end
498
683
  elsif flag == :build
499
- cmd << "#{project}.gemspec"
684
+ spec = [basepath("#{project}.gemspec"), basepath("#{name}.gemspec"), *Dir.glob(basepath('*.gemspec'))]
685
+ op << File.basename(spec) if (spec = spec.find { |file| File.exist?(file) })
500
686
  end
501
687
  when :exec
502
- raise_error('no command given', hint: flag) if out.empty?
503
- cmd << project << out.join(' ')
688
+ raise_error('missing command', hint: flag) if op.empty?
689
+ op << project << op.join(' ')
504
690
  else
505
- raise_error('no gemname given', hint: flag) if out.empty? && !session_arg?('system')
691
+ raise_error('missing gemname', hint: flag) if op.empty? && !op.arg?('system')
506
692
  if flag == :pristine
507
- if session_arg?('all')
508
- append_repeat 'skip', out
509
- out.clear
510
- elsif (n = out.first.index('@'))
511
- name = out.first
693
+ if op.arg?('all')
694
+ append_repeat 'skip', op.extras
695
+ op.clear
696
+ elsif (n = op.first.index('@'))
697
+ name = op.shift
512
698
  if n == 0
513
- cmd << project
699
+ op << project
514
700
  ver = name[1..-1]
515
701
  else
516
- cmd << shell_escape(name[0, n])
702
+ op << shell_escape(name[0, n])
517
703
  ver = name[n + 1..-1]
518
704
  end
519
- cmd << shell_option('version', ver)
520
- out.clear
705
+ op << shell_option('version', ver)
706
+ op.clear
521
707
  end
522
708
  end
523
- append_value(out, escape: true)
524
- option_clear err
709
+ op.append.clear(errors: true)
525
710
  end
526
711
  run_rb(from: from)
527
712
  end
@@ -530,7 +715,8 @@ module Squared
530
715
  cmd = bundle_session flag
531
716
  args = case flag
532
717
  when 'exec', 'cache', 'check'
533
- option_sanitize(args, OPT_BUNDLE[flag.to_sym] + OPT_BUNDLE[:common], args: flag == :exec).first
718
+ list = OPT_BUNDLE[flag.to_sym] + OPT_BUNDLE[:common]
719
+ OptionPartition.new(args, list, cmd, project: self, args: flag == :exec).extras
534
720
  else
535
721
  args.flatten
536
722
  end
@@ -545,24 +731,23 @@ module Squared
545
731
  end
546
732
 
547
733
  def rake(*args, opts: [])
548
- target = [quote_option('f', rakefile)]
549
- args += option_sanitize(opts, OPT_RUBY[:rake], target: target).first
734
+ op = OptionPartition.new(opts, OPT_RUBY[:rake], [quote_option('f', rakefile)], project: self)
735
+ args.concat(op.extras)
550
736
  if args.empty?
551
737
  args << nil
552
738
  else
553
739
  args.flatten!
554
740
  end
555
- cmd = rake_output(*target)
741
+ cmd = rake_output(*op.to_a)
556
742
  args.map! { |val| cmd.temp(val) }
557
743
  run_s(args, banner: false, from: :rake)
558
744
  end
559
745
 
560
746
  def irb(name, opts = [], path: basepath('lib'))
561
- cmd = session 'irb'
562
- args = option_sanitize(opts, OPT_RUBY[:irb], first: true).first
563
- as_a(name).each { |val| cmd << shell_option('r', val) }
564
- as_a(path).each { |val| cmd << quote_option('I', val) }
565
- cmd.merge(args)
747
+ op = OptionPartition.new(opts, OPT_RUBY[:irb], session('irb'), project: self, first: true)
748
+ as_a(name).each { |val| op << shell_option('r', val) }
749
+ as_a(path).each { |val| op << quote_option('I', val) }
750
+ op.merge(op.extras)
566
751
  run(banner: false)
567
752
  end
568
753
 
@@ -595,7 +780,7 @@ module Squared
595
780
  ver.each do |val|
596
781
  next unless out =~ /\(#{Regexp.escape(val)}(?:,[^)]+|\b)\):([^\n]+)/
597
782
 
598
- set.(val, $1)
783
+ set.call(val, $1)
599
784
  break
600
785
  end
601
786
  end
@@ -607,14 +792,14 @@ module Squared
607
792
 
608
793
  lib = Regexp.new(['', 'gems', "#{project}-([^#{File::SEPARATOR}]+)", ''].join(File::SEPARATOR))
609
794
  if (ver = path[lib, 1]) && (val = path[/\A(.+)#{gempath(ver[1])}/, 1])
610
- set.(ver, val)
795
+ set.call(ver, val)
611
796
  end
612
797
  end
613
798
  if RUBY_VERSION >= '2.6'
614
799
  target = RUBY_VERSION.start_with?('2.6') ? RubyVM : $LOAD_PATH
615
- parse.(target.resolve_feature_path(project)&.last)
800
+ parse.call(target.resolve_feature_path(project)&.last)
616
801
  end
617
- pwd_set { parse.(`#{bundle_output('show', project)}`) } unless @gemdir
802
+ pwd_set { parse.call(`#{bundle_output('show', project)}`) } unless @gemdir
618
803
  end
619
804
  raise_error('parse failed', hint: @version || 'path') unless @gemdir
620
805
  rescue StandardError => e
@@ -637,18 +822,28 @@ module Squared
637
822
  end
638
823
 
639
824
  def append_bundle(opts, list, target: @session, append: nil)
640
- out = option_sanitize(opts, list, target: target).first
825
+ op = OptionPartition.new(opts, list, target, project: self)
641
826
  if append
642
827
  if append.is_a?(Regexp)
643
- out, err = out.partition { |val| !val.match?(append) }
644
- option_clear(err, target: target)
828
+ op.each do |opt|
829
+ if opt.match?(append)
830
+ op.errors << opt
831
+ else
832
+ op.found << opt
833
+ end
834
+ end
835
+ op.swap.clear(errors: true)
645
836
  end
646
- append_value(out, target: target, escape: true)
837
+ op.append(escape: true)
647
838
  else
648
- option_clear(out, target: target)
839
+ op.clear
649
840
  end
650
841
  end
651
842
 
843
+ def ruby_session(*cmd, **kwargs)
844
+ session('ruby', *preopts, *cmd, **kwargs)
845
+ end
846
+
652
847
  def gem_session(*cmd, **kwargs)
653
848
  ret = session('gem', *cmd, **kwargs)
654
849
  return ret if cmd.empty?
@@ -672,6 +867,10 @@ module Squared
672
867
  session_output('gem', *cmd, **kwargs)
673
868
  end
674
869
 
870
+ def ruby_output(*cmd, **kwargs)
871
+ session_output('ruby', *cmd, **kwargs)
872
+ end
873
+
675
874
  def bundle_output(*cmd, **kwargs)
676
875
  session_output('bundle', *cmd, **kwargs)
677
876
  end
@@ -696,7 +895,7 @@ module Squared
696
895
  end
697
896
 
698
897
  def preopts
699
- verbosity > 0 && !session_arg?('quiet') ? ['--verbose'] : []
898
+ verbosetype > 1 && !session_arg?('quiet') ? ['--verbose'] : []
700
899
  end
701
900
 
702
901
  def gemdir?