squared 0.6.8 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,23 +39,22 @@ module Squared
39
39
  }.freeze
40
40
  OPT_BUNDLE = {
41
41
  common: %w[no-color V|verbose r|retry=i].freeze,
42
- common_all: %w[all all-platforms path=p].freeze,
43
42
  common_git: %w[branch=q git=q path=p ref=q].freeze,
44
43
  common_version: %w[local major minor patch pre strict].freeze,
45
44
  add: %w[optimistic quiet skip-install strict github=q glob=q g|group=q require=q s|source=q
46
45
  v|version=q].freeze,
47
- binstubs: %w[force standalone shebang=q].freeze,
48
- cache: %w[frozen no-all no-install no-prune quiet cache-path=p gemfile=p].freeze,
46
+ binstubs: %w[all-platforms force standalone shebang=q].freeze,
47
+ cache: %w[all-platforms no-install quiet cache-path=p gemfile=p].freeze,
49
48
  check: %w[dry-run gemfile=p path=p].freeze,
50
49
  clean: %w[dry-run force].freeze,
51
- config: %w[global local].freeze,
50
+ config: %w[global local skip-parseable].freeze,
52
51
  doctor: %w[quiet ssl gemfile=p].freeze,
53
52
  doctor_ssl: %w[host=q tls-version=b verify-mode=b].freeze,
54
53
  exec: %w[gemfile=p].freeze,
55
- gem: %w[b|bin git no-exe rubocop ci=b e|edit=p? ext=b github-username=q linter=b t|test=b?].freeze,
56
- init: %w[gemfile=p gemspec=p].freeze,
57
- install: %w[frozen no-cache no-prune system binstubs=p? path=p standalone=q? target-rbconfig=p trust-policy=b
58
- with=q without=q].freeze,
54
+ gem: %w[b|bin git ci=b e|edit=p? ext=b github-username=q linter=b t|test=b?].freeze,
55
+ init: %w[gemspec=p].freeze,
56
+ install: %w[no-cache no-lock prefer-local binstubs=p? lockfile=p standalone=q? target-rbconfig=p
57
+ trust-policy=b].freeze,
59
58
  install_a: %w[force full-index local quiet redownload gemfile=p j|jobs=i].freeze,
60
59
  lock: %w[add-checksums conservative full-index normalize-platforms print add-platform=q bundler=b? gemfile=p
61
60
  lockfile=p remove-platform=p update=q?].freeze,
@@ -65,10 +64,19 @@ module Squared
65
64
  platform: %w[ruby].freeze,
66
65
  plugin: %w[source=q version=q].freeze,
67
66
  plugin_uninstall: %w[all].freeze,
68
- remove: %w[install].freeze,
69
- show: %w[outdated paths].freeze,
70
- update: %w[all conservative local major minor patch pre ruby strict bundler=b? g|group=q source=q].freeze,
67
+ show: %w[paths].freeze,
68
+ update: %w[all conservativepre ruby strict bundler=b? g|group=q source=q].freeze,
69
+ v3: {
70
+ binstubs: %w[all].freeze,
71
+ cache: %w[all frozen no-prune].freeze,
72
+ gem: %w[rubocop].freeze,
73
+ init: %w[gemfile=p].freeze,
74
+ install: %w[frozen no-prune system gemfile=p path=p with=q without=q].freeze,
75
+ remove: %w[install].freeze,
76
+ show: %w[outdated].freeze
77
+ }.freeze,
71
78
  no: {
79
+ config: %w[parseable].freeze,
72
80
  gem: %w[changelog ci coc exe linter mit test].freeze
73
81
  }.freeze
74
82
  }.freeze
@@ -78,20 +86,21 @@ module Squared
78
86
  common_domain: %w[b|both clear-sources l|local r|remote].freeze,
79
87
  common_otp: %w[host=q k|key=b otp=b].freeze,
80
88
  common_all: %w[a|all e|exact v|version=q].freeze,
81
- build: %w[C=p force strict o|output=p platform=q].freeze,
82
- cert: %w[a|add=p b|build=q C|certificate=p d|days=i l|list=q A|key-algorithm=b K|private-key=p r|remove=q
89
+ common_platform: %w[platform=q v|version=q].freeze,
90
+ build: %w[force strict o|output=p platform=q].freeze,
91
+ cert: %w[a|add=p b|build=q C|certificate=p d|days=i l|list=q? A|key-algorithm=b K|private-key=p r|remove=q
83
92
  R|re-sign s|sign=p].freeze,
84
93
  check: %w[a v|version=q].freeze,
85
94
  cleanup: %w[D n d|dry-run].freeze,
86
95
  contents: %w[l all s|spec-dir=q v|version=q].freeze,
87
- dependency: %w[R pipe platform=q v|version=q].freeze,
96
+ dependency: %w[R pipe].freeze,
88
97
  exec: %w[conservative g|gem=b v|version=q].freeze,
89
- fetch: %w[clear-sources platform=q v|version=q].freeze,
98
+ fetch: %w[clear-sources].freeze,
90
99
  generate_index: %w[update d|directory=p].freeze,
91
100
  info: %w[i I].freeze,
92
101
  install: %w[v|version=q].freeze,
93
- install_a: %w[E f w conservative default development development-all explain ignore-dependencies N|no-document
94
- vendor n|bindir=p build-root=p document=b? g|file=p? i|install-dir=p platform=q
102
+ install_a: %w[E f w conservative development development-all explain ignore-dependencies N|no-document vendor
103
+ n|bindir=p j|build-jobs=i build-root=p document=b? g|file=p? i|install-dir=p platform=q
95
104
  target-rbconfig=p? P|trust-policy=b without=q].freeze,
96
105
  list: %w[d i I].freeze,
97
106
  lock: %w[s].freeze,
@@ -104,14 +113,19 @@ module Squared
104
113
  rdoc: %w[all v|version=q].freeze,
105
114
  rebuild: %w[C=p diff force strict gemspec=p original=p source=q].freeze,
106
115
  search: %w[d i I].freeze,
116
+ server: %w[b|bind=q d|dir=p l|launch=q p|port=b].freeze,
107
117
  signin: %w[otp=b host=q].freeze,
108
- sources: %w[f c|clear-all l|list u|update a|add=q p|http-proxy=q? r|remove=q].freeze,
109
- specification: %w[all marshal ruby yaml platform=q v|version=q].freeze,
110
- uninstall: %w[a D I x vendor n|bindir=p i|install-dir=p platform=q v|version=q].freeze,
118
+ sources: %w[f c|clear-all l|list u|update a|add=q append=q p|http-proxy=q? prepend=q r|remove=q].freeze,
119
+ specification: %w[all marshal ruby yaml].freeze,
120
+ uninstall: %w[a D I x vendor n|bindir=p i|install-dir=p].freeze,
111
121
  unpack: %w[spec target=p P|trust-policy=b v|version=q],
112
122
  update: %w[system=b?].freeze,
113
123
  which: %w[a g].freeze,
114
- yank: %w[platform=q v|version=q].freeze,
124
+ v3: {
125
+ build: %w[C=p].freeze,
126
+ install: %w[default].freeze,
127
+ update: %w[default].freeze
128
+ }.freeze,
115
129
  no: {
116
130
  check: %w[alien doctor dry-run gems].freeze,
117
131
  cleanup: %w[check-development user-install].freeze,
@@ -131,6 +145,7 @@ module Squared
131
145
  push: %w[http-proxy].freeze,
132
146
  rdoc: %w[overwrite rdoc ri].freeze,
133
147
  search: %w[details http-proxy installed prerelease versions].freeze,
148
+ server: %w[daemon].freeze,
134
149
  sources: %w[force http-proxy].freeze,
135
150
  specification: %w[prerelease].freeze,
136
151
  uninstall: %w[abort-on-dependent all check-development executables force format-executable
@@ -148,7 +163,8 @@ module Squared
148
163
  contents: %w[s spec-dir].freeze,
149
164
  dependency: %w[s source].freeze,
150
165
  install: %w[document s source without].freeze,
151
- pristine: %w[skip].freeze
166
+ pristine: %w[skip].freeze,
167
+ sources: %w[append prepend].freeze
152
168
  }.freeze,
153
169
  bundle: {
154
170
  install: %w[standalone with without].freeze,
@@ -164,7 +180,7 @@ module Squared
164
180
  end
165
181
 
166
182
  def bannerargs
167
- %i[dependfile gemname gemdir].freeze
183
+ %i[version dependfile gemname gemdir].freeze
168
184
  end
169
185
 
170
186
  def config?(val)
@@ -178,7 +194,7 @@ module Squared
178
194
  'outdated' => %i[major minor patch].freeze,
179
195
  'ruby' => %i[file script version].freeze,
180
196
  'gem' => %i[install uninstall outdated update pristine build push exec command].freeze,
181
- 'bundle' => %i[install update cache exec config reinstall command].freeze,
197
+ 'bundle' => %i[config install update cache exec reinstall command].freeze,
182
198
  'rake' => nil,
183
199
  'irb' => nil,
184
200
  'rbs' => nil,
@@ -187,25 +203,21 @@ module Squared
187
203
 
188
204
  attr_reader :gemdir
189
205
 
190
- def initialize(*, autodetect: false, gemspec: nil, steep: 'Steepfile', rubocop: '.rubocop.yml', asdf: 'ruby',
191
- **kwargs)
206
+ def initialize(*, autodetect: false, steep: 'Steepfile', rubocop: '.rubocop.yml', asdf: 'ruby', **kwargs)
192
207
  super
193
208
  if @pass.include?(Ruby.ref)
194
209
  initialize_ref Ruby.ref
195
- initialize_logger(**kwargs)
210
+ initialize_logger kwargs[:log]
196
211
  else
197
212
  initialize_build(Ruby.ref, **kwargs)
198
213
  initialize_env(**kwargs)
199
214
  end
200
215
  dependfile_set GEMFILE
216
+ gemfile_set kwargs[:gemspec]
201
217
  @autodetect = autodetect
202
- @gemfile = if gemspec == false
203
- false
204
- elsif gemspec
205
- basepath(gemspec.include?('.') ? gemspec : "#{gemspec}.gemspec")
206
- end
207
218
  @steepfile = basepath! steep if steep
208
219
  @rubocopfile = Pathname.new(rubocop).realpath rescue basepath!(Dir.home, '.rubocop.yml') if rubocop
220
+ @rubygems = kwargs.fetch(:rubygems, 0)
209
221
  return unless rakefile && @output[0].nil? && @copy.nil? && !version && !@autodetect
210
222
 
211
223
  begin
@@ -218,7 +230,7 @@ module Squared
218
230
  @clean = "#{cmd} clean" if @clean.nil?
219
231
  break
220
232
  end
221
- rescue StandardError => e
233
+ rescue => e
222
234
  log.error e
223
235
  end
224
236
  end
@@ -278,7 +290,7 @@ module Squared
278
290
  elsif exception
279
291
  indexerror n, tasks
280
292
  else
281
- log.warn "rake task #{n} of #{tasks.size} (out of range)"
293
+ log.warn "rake task #{n} of #{tasks.size}".subhint('out of range')
282
294
  opts.clear
283
295
  next
284
296
  end
@@ -326,32 +338,28 @@ module Squared
326
338
  val = File.join(val, '**/*.rb') unless val.include?('*') || val.match?(/\.[a-z\d]+$/i)
327
339
  Dir.glob(val, base: path)
328
340
  end
329
- files = if args.empty?
330
- choice_index('Select files', list, multiple: true, series: true,
341
+ if args.empty?
342
+ files = choice_index('Select files', list, multiple: true, series: true,
331
343
  accept: [accept_y('Generate?')])
332
- else
333
- list.map! { |val| basepath(val).to_s }
334
- [].tap do |out|
335
- ret = []
336
- args.each do |val|
337
- if val.include?('*')
338
- ret.concat(Dir.glob(val, base: path))
339
- elsif !(file = basepath!(val))
340
- print_error(val, hint: 'not found')
341
- elsif file.directory?
342
- ret.concat(file.glob('**/*.rb'))
343
- else
344
- ret << val
345
- end
346
- end
347
- ret = ret.select { |val| list.include?(basepath(val).to_s) }
348
- if ret.empty?
349
- print_error('steep', 'no files matched', hint: "#{key}:check")
350
- exit 1
351
- end
352
- out.replace(ret.uniq)
353
- end
354
- end
344
+ else
345
+ list.map! { |val| basepath(val).to_s }
346
+ files = args.each_with_object([]) do |val, out|
347
+ if val.include?('*')
348
+ out.concat(Dir.glob(val, base: path))
349
+ elsif !(file = basepath!(val))
350
+ print_error(val, hint: 'not found')
351
+ elsif file.directory?
352
+ out.concat(file.glob('**/*.rb'))
353
+ else
354
+ out << val
355
+ end
356
+ end.select { |val| list.include?(basepath(val).to_s) }
357
+ if files.empty?
358
+ print_error('steep', 'no files matched', hint: "#{key}:check")
359
+ exit 1
360
+ end
361
+ files.uniq!
362
+ end
355
363
  sig = if (n = sig.index(args.first))
356
364
  args.shift
357
365
  sig[n]
@@ -370,15 +378,15 @@ module Squared
370
378
  format_desc action, nil, 'opts*,path*/:'
371
379
  task action do |_, args|
372
380
  opts, args = args.to_a.partition do |val|
373
- next true if val.match?(/\A(?:(?:[A-Z]|[a-z-]+)=.|[a-z]+(?:-[a-z]+)*\Z)/)
381
+ next true if val.match?(/(^([A-Z]|[a-z-]+)=.|^[a-z]+(-[a-z]+)*$)/)
374
382
 
375
383
  !val.include?('*') && !val.end_with?('/')
376
384
  end
377
385
  if opts.delete(':')
378
386
  args << (Dir.exist?('lib') ? 'lib/' : '**/*.rb') if args.empty?
379
- list = args.map! { |val| val.end_with?('/') || Dir.exist?(val) ? File.join(val, '**/*.rb') : val }
387
+ args = args.map { |val| val.end_with?('/') || Dir.exist?(val) ? File.join(val, '**/*.rb') : val }
380
388
  .flat_map { |val| Dir.glob(val, base: path) }
381
- args = choice_index('Select files', list, multiple: true)
389
+ args = choice_index('Select files', args, multiple: true)
382
390
  end
383
391
  rubocop(*args, opts: opts, banner: true)
384
392
  end
@@ -436,18 +444,18 @@ module Squared
436
444
  end
437
445
  when 'bundle'
438
446
  case flag
447
+ when :config
448
+ format_desc action, flag, 'list|set|get|unset?,opts*,args*'
449
+ task flag do |_, args|
450
+ bundle(flag, *args.to_a, banner: true)
451
+ end
439
452
  when :install, :update, :cache, :exec
440
453
  format_desc(action, flag, 'opts*', after: case flag
441
454
  when :update then 'gems*'
442
455
  when :exec then 'command,args*|:'
443
456
  end)
444
457
  task flag do |_, args|
445
- bundle(flag, opts: args.to_a, banner: true)
446
- end
447
- when :config
448
- format_desc action, flag, 'list|set|get|unset?,args*'
449
- task flag do |_, args|
450
- bundle(flag, *args.to_a, banner: true)
458
+ bundle(flag, opts: args.to_a, banner: flag == :exec ? verbose? : true)
451
459
  end
452
460
  when :reinstall
453
461
  format_desc action, flag, 'f/orce?,opts*'
@@ -501,13 +509,168 @@ module Squared
501
509
  when :script
502
510
  format_desc action, flag, 'opts*'
503
511
  task flag do |_, args|
504
- command = ENV['RUBY_E'] || readline('Enter script', force: true, multiline: %w[## ;])
512
+ args = args.to_a
513
+ multiline = case args.first
514
+ when ';', '#'
515
+ args.shift
516
+ else
517
+ %w[## ;]
518
+ end
519
+ msg = if Readline.respond_to?(:readmultiline)
520
+ 'ruby>'
521
+ else
522
+ multiline = Array(multiline)
523
+ 'Enter script'
524
+ end
525
+ command = ENV['RUBY_E'] || readline(msg, force: true, multiline: multiline)
505
526
  ruby(flag, opts: args.to_a, command: command)
506
527
  end
507
528
  when :version
508
529
  format_desc action, flag
509
530
  task flag do
510
- ruby flag
531
+ pwd_set do
532
+ out = []
533
+ order = { 'rbenv' => -1, 'rvm' => -1, 'chruby' => -1, 'mise' => -1 }
534
+ ENV.fetch('PATH', '').split(':').each_with_index do |val, index|
535
+ order.each_key do |key|
536
+ next unless val.match?(%r{[/.]#{key}/})
537
+
538
+ order[key] = index
539
+ break
540
+ end
541
+ end
542
+ if @asdf
543
+ [File.join(ENV.fetch('ASDF_DATA_DIR', '$HOME/.asdf'), "installs/#{@asdf.first}")]
544
+ else
545
+ [
546
+ "#{ENV.fetch('RBENV_ROOT', '$HOME/.rbenv')}/bin/rbenv",
547
+ '$HOME/.rvm/bin/rvm',
548
+ '$HOME/.local/bin/mise',
549
+ '/usr/bin/rbenv',
550
+ '/usr/bin/mise',
551
+ '/usr/local/rvm/bin/rvm',
552
+ '/usr/share/rvm/bin/rvm',
553
+ '/usr/local/share/chruby/chruby.sh'
554
+ ]
555
+ .sort do |a, b|
556
+ c = -1
557
+ d = -1
558
+ order.each do |key, val|
559
+ pat = %r{/\.?#{key}}
560
+ c = val if a.match?(pat)
561
+ d = val if b.match?(pat)
562
+ end
563
+ if c == d
564
+ 0
565
+ elsif c == -1
566
+ 1
567
+ elsif d == -1
568
+ -1
569
+ else
570
+ c < d ? -1 : 1
571
+ end
572
+ end
573
+ .push('')
574
+ end
575
+ .each do |val|
576
+ next unless val.empty? || File.exist?(val.sub('$HOME', Dir.home))
577
+
578
+ trim = ->(s) { s[/^\D+\d+\.\d+(?:\.\S+)?/, 0].sub(/^([a-z]+)-/i, '\1 ') }
579
+ ver = %w[.tool-versions .ruby-version]
580
+ out << trim.call(case (cmd = File.basename(val))
581
+ when 'rvm'
582
+ ver.shift
583
+ `rvm current`[/^\S+/, 0]
584
+ when 'rbenv'
585
+ ver.shift
586
+ name = `rbenv version-name`
587
+ name.match?(SEM_VER) ? "ruby #{name}" : name
588
+ when 'chruby.sh'
589
+ ver.shift
590
+ chruby = session_output 'source', val
591
+ `#{chruby.with('ruby --version')}`
592
+ when 'mise'
593
+ data = parse_json(`mise ls ruby --json`, kind: Array)
594
+ data = data.find { |item| item['active'] } || data.first
595
+ "ruby #{data['version']}"
596
+ else
597
+ if @asdf
598
+ cmd = 'asdf'
599
+ if @@asdf.config && File.exist?(@@asdf.config)
600
+ pat = /legacy_version_file\s+=\s+(yes|no)/
601
+ ver.pop unless File.read(@@asdf.config)[pat, 1] == 'yes'
602
+ else
603
+ ver.pop
604
+ end
605
+ opt = [@asdf.first]
606
+ opt.unshift('--no-header') unless @@asdf.version == 15
607
+ cur = `asdf current #{opt.join(' ')}`
608
+ if cur.match?(/\sfalse\b/)
609
+ `ruby --version`
610
+ else
611
+ cur[/^\S+\s+\S+/, 0].sub(/\s+/, ' ')
612
+ end
613
+ else
614
+ ver = nil
615
+ `ruby --version`
616
+ end
617
+ end)
618
+ break if workspace.windows?
619
+
620
+ unless val.empty?
621
+ out << trim.call(case cmd
622
+ when 'chruby.sh'
623
+ `#{chruby.with('chruby --version')}`.sub(':', '')
624
+ when 'asdf'
625
+ "asdf #{`asdf version`.delete_prefix('v')}"
626
+ when 'mise'
627
+ data = parse_json `mise version --json`
628
+ "mise #{data['latest']}"
629
+ else
630
+ `#{cmd} --version`
631
+ end)
632
+ end
633
+ begin
634
+ out << ('which %s' % case cmd
635
+ when 'rbenv'
636
+ `rbenv which ruby`
637
+ when 'chruby.sh'
638
+ `#{chruby.with('which ruby')}`
639
+ when 'asdf'
640
+ `asdf which #{@asdf.first}`
641
+ when 'mise'
642
+ `mise which ruby`
643
+ else
644
+ `which ruby`
645
+ end)
646
+ rescue => e
647
+ log.debug e
648
+ end
649
+ if ver
650
+ catch :found do
651
+ path.ascend do |dir|
652
+ ver.filter { |val| dir.join(val).exist? }.each do |val|
653
+ dir += val
654
+ hint = File.read(dir)
655
+ .lines
656
+ .map { |s| s.sub(/#.*$/, '').strip }
657
+ .reject(&:empty?)
658
+ .join(', ')
659
+ out << message("found #{dir}", hint: hint)
660
+ throw :found if hint.include?(out.first[/^ruby (.+)$/, 1])
661
+ rescue
662
+ nil
663
+ end
664
+ end
665
+ end
666
+ end
667
+ break
668
+ end
669
+ out.map!(&:split)
670
+ pad = as_a(out, :first, :size).max
671
+ print_item
672
+ puts(out.map { |line| '%*s %s' % [pad, line.first, line[1..-1].join(' ')] })
673
+ end
511
674
  end
512
675
  end
513
676
  end
@@ -523,25 +686,37 @@ module Squared
523
686
  super
524
687
  elsif outdated?
525
688
  workspace.rev_clear(name, sync: sync)
526
- cmd = bundle_session 'install'
527
- option('binstubs') do |val|
528
- next if val == '0' || val == 'false'
529
-
530
- run(bundle_output('binstubs --all', case val
531
- when '1', 'true'
532
- nil
533
- else
534
- if val.start_with?('~')
535
- val = File.join(Dir.home, val == '~' ? '.bundle' : val[1..-1])
536
- if prod?
537
- config_set('binstubs', shell_quote(val), global: true)
538
- val = nil
539
- end
689
+ if env('UPDATE') || env('RUBY_UPDATE')
690
+ cmd = bundle_session 'update --all'
691
+ option('version') do |val|
692
+ case val
693
+ when 'patch', 'minor', 'major'
694
+ cmd << "--#{val}"
695
+ end
696
+ end
697
+ cmd << '--strict' if option('strict')
698
+ cmd << '--conservative' if option('conservative')
699
+ else
700
+ cmd = bundle_session 'install'
701
+ option('binstubs') do |val|
702
+ next if val == '0' || val == 'false'
703
+
704
+ run(bundle_output('binstubs --all', case val
705
+ when '1', 'true'
706
+ nil
540
707
  else
541
- val = basepath val
542
- end
543
- quote_option('path', val) if val
544
- end), exception: false, banner: false, series: true)
708
+ if val.start_with?('~')
709
+ val = File.join(Dir.home, val == '~' ? '.bundle' : val[1..-1])
710
+ if prod?
711
+ config_set('binstubs', shell_quote(val), global: true)
712
+ val = nil
713
+ end
714
+ else
715
+ val = basepath val
716
+ end
717
+ quote_option('path', val) if val
718
+ end), exception: false, banner: false, series: true)
719
+ end
545
720
  end
546
721
  if prod? && !config_get('without')
547
722
  if semgte?('3')
@@ -550,8 +725,8 @@ module Squared
550
725
  cmd << '--without=development'
551
726
  end
552
727
  end
553
- option('jobs') { |n| cmd << "-j#{n}" if n.to_i > 0 }
554
- run_rb(from: :depend, sync: sync)
728
+ option('jobs', pat: /^\d+$/) { |val| cmd << "-j#{val}" }
729
+ run(sync: sync, from: :depend)
555
730
  end
556
731
  end
557
732
 
@@ -580,8 +755,8 @@ module Squared
580
755
  c = glob[i] || glob.first
581
756
  log.info "cp #{a + c} #{b}"
582
757
  copy_dir(a, b, c, pass: pass, verbose: !silent?)
583
- rescue StandardError => e
584
- on_error e, :copy
758
+ rescue => e
759
+ on_error e
585
760
  end
586
761
  on :last, :copy
587
762
  end
@@ -617,7 +792,7 @@ module Squared
617
792
  on :first, :outdated
618
793
  banner = format_banner cmd.to_s
619
794
  print_item banner if sync
620
- pwd_set(from: :outdated) do
795
+ pwd_set(cmd, dryrun: dryrun) do
621
796
  tc = theme[:current]
622
797
  start = 0
623
798
  found = 0
@@ -724,7 +899,7 @@ module Squared
724
899
  unless Array(items).empty?
725
900
  gems = if se
726
901
  choice('Select a package', items.map(&:first),
727
- multiple: true, force: false, index: true, border: true).map! { |n| items[n.pred].last }
902
+ multiple: true, force: false, index: true, border: true).map { |n| items[n.pred].last }
728
903
  else
729
904
  items.map(&:last)
730
905
  end
@@ -746,7 +921,7 @@ module Squared
746
921
  break
747
922
  end
748
923
  end
749
- rescue StandardError => e
924
+ rescue => e
750
925
  log.debug e
751
926
  end
752
927
  puts print_footer(status || 'Updates are available', right: status.include?('/'))
@@ -757,116 +932,10 @@ module Squared
757
932
  on :last, :outdated
758
933
  end
759
934
 
760
- def ruby(*args, flag: nil, sync: true, banner: verbose?, with: nil, pass: PASS_RUBY[:ruby], **kwargs)
761
- flag = args.shift if !flag && args.first.is_a?(Symbol)
762
- if flag == :version
763
- pwd_set do
764
- out = []
765
- order = { 'rbenv' => -1, 'rvm' => -1, 'asdf' => -1, 'chruby' => -1 }
766
- ENV.fetch('PATH', '').split(':').each_with_index do |val, index|
767
- order.each_key do |key|
768
- if val.match?(%r{[/.]#{key}/})
769
- order[key] = index
770
- break
771
- end
772
- end
773
- end
774
- paths = [
775
- "#{ENV.fetch('RBENV_ROOT', '$HOME/.rbenv')}/bin/rbenv",
776
- '$HOME/.rvm/bin/rvm',
777
- @asdf ? "#{ENV.fetch('ASDF_DATA_DIR', '$HOME/.asdf')}/installs/#{@asdf.first}" : nil,
778
- '/usr/bin/rbenv',
779
- '/usr/local/rvm/bin/rvm',
780
- '/usr/share/rvm/bin/rvm',
781
- '/usr/local/share/chruby/chruby.sh'
782
- ].compact
783
- paths.sort do |a, b|
784
- c = -1
785
- d = -1
786
- order.each do |key, val|
787
- pat = %r{/\.?#{key}}
788
- c = val if a.match?(pat)
789
- d = val if b.match?(pat)
790
- end
791
- if c == d
792
- 0
793
- elsif c == -1
794
- 1
795
- elsif d == -1
796
- -1
797
- else
798
- c < d ? -1 : 1
799
- end
800
- end
801
- .push('')
802
- .each do |val|
803
- next unless val.empty? || File.exist?(val.sub('$HOME', Dir.home))
804
-
805
- trim = ->(s) { s[/^\D+\d+\.\d+(?:\.\S+)?/, 0].sub(/^([a-z]+)-/i, '\1 ') }
806
- ver = '.ruby-version'
807
- out << trim.call(case (cmd = File.basename(val))
808
- when 'rvm'
809
- `rvm current`[/^\S+/, 0]
810
- when 'rbenv'
811
- name = `rbenv version-name`
812
- name.match?(SEM_VER) ? "ruby #{name}" : name
813
- when 'chruby.sh'
814
- chruby = session_output 'source', val
815
- `#{chruby.with('ruby --version')}`
816
- else
817
- if @asdf
818
- cmd = 'asdf'
819
- ver = '.tool-versions'
820
- opt = [@asdf.first]
821
- opt.unshift('--no-header') unless @@asdf.version == 15
822
- `asdf current #{opt.join(' ')}`[/^\S+\s+\S+/, 0].sub(/\s+/, ' ')
823
- else
824
- ver = nil
825
- `ruby --version`
826
- end
827
- end)
828
- break if workspace.windows?
829
-
830
- unless val.empty?
831
- out << trim.call(case cmd
832
- when 'chruby.sh'
833
- `#{chruby.with('chruby --version')}`.sub(':', '')
834
- when 'asdf'
835
- "asdf #{`asdf version`.delete_prefix('v')}"
836
- else
837
- `#{cmd} --version`
838
- end)
839
- end
840
- begin
841
- out << ('which %s' % case cmd
842
- when 'rbenv'
843
- `rbenv which ruby`
844
- when 'chruby.sh'
845
- `#{chruby.with('which ruby')}`
846
- when 'asdf'
847
- `asdf which #{@asdf.first}`
848
- else
849
- `which ruby`
850
- end)
851
- rescue StandardError => e
852
- log.debug e
853
- end
854
- if ver
855
- path.ascend do |ent|
856
- next unless (ent += ver).exist?
857
-
858
- hint = File.read(ent).lines(chomp: true).reject(&:empty?).join(', ') rescue nil
859
- out << message("found #{ent}", hint: hint)
860
- end
861
- end
862
- break
863
- end
864
- out.map!(&:split)
865
- pad = out.map(&:first).map!(&:size).max
866
- print_item
867
- puts(out.map! { |line| '%*s %s' % [pad, line.first, line[1..-1].join(' ')] })
868
- end
869
- return
935
+ def ruby(*args, sync: true, banner: verbose?, with: nil, pass: PASS_RUBY[:ruby], **kwargs)
936
+ if args.first.is_a?(Symbol)
937
+ flag = args.shift
938
+ from = symjoin 'ruby', flag
870
939
  end
871
940
  opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
872
941
  op = OptionPartition.new(opts, OPT_RUBY[:ruby], ruby_session, project: self, multiple: [/^-e/], args: true,
@@ -891,13 +960,8 @@ module Squared
891
960
  op.append(delim: true, escape: kwargs.fetch(:escape, false), quote: kwargs.fetch(:quote, false))
892
961
  end
893
962
  end
894
- from = if flag
895
- :"ruby:#{flag}"
896
- else
897
- print_run(op, banner, **kwargs)
898
- :ruby
899
- end
900
- run_rb(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception), from: from)
963
+ print_run(op, banner, **kwargs) unless flag
964
+ run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception?), from: from || :ruby)
901
965
  end
902
966
 
903
967
  def gem(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
@@ -919,14 +983,14 @@ module Squared
919
983
  end
920
984
  op = OptionPartition.new(opts, gemopts(flag), gem_session(flag),
921
985
  project: self, no: OPT_GEM[:no][flag == :update ? :install : flag])
922
- from = :"gem:#{flag}"
986
+ from = symjoin 'gem', flag
923
987
  if flag == :outdated
924
988
  op.adjoin(gempwd, start: 0) if gempwd
925
989
  op.clear
926
990
  cmd = session_done op.target
927
991
  log.info cmd
928
992
  on :first, from
929
- banner = format_banner(cmd)
993
+ banner = format_banner cmd
930
994
  print_item banner if sync
931
995
  major = [0, 0, 0]
932
996
  buffer = []
@@ -945,7 +1009,7 @@ module Squared
945
1009
  []
946
1010
  end
947
1011
  out = ->(val) { sync ? puts(val) : buffer << val }
948
- pwd_set(pass: !gempwd.nil?, from: from) do
1012
+ pwd_set(op.target, from: from, pass: !gempwd.nil?) do
949
1013
  rows = [[%w[Gem Current Latest], nil]]
950
1014
  IO.popen(cmd).each do |line|
951
1015
  if line =~ /^(\S+) \((\S+) < ([^)]+)\)$/
@@ -1001,7 +1065,7 @@ module Squared
1001
1065
  unless stdin?
1002
1066
  sub_style! g, theme[:major]
1003
1067
  styles = %i[green bold]
1004
- pat = (pat.first if pre)
1068
+ pat = pre ? pat.first : nil
1005
1069
  latest << :bold
1006
1070
  end
1007
1071
  major[0] += 1
@@ -1040,10 +1104,9 @@ module Squared
1040
1104
  out.call(queue)
1041
1105
  queue = nil
1042
1106
  end
1043
- s = ('%s %s %s' % [g, b, h]).yield_self do |val|
1044
- items&.push([val, a])
1045
- '%*s %s' % [pad, "#{j}.", val]
1046
- end
1107
+ s = '%s %s %s' % [g, b, h]
1108
+ items&.push([s, a])
1109
+ s = '%*s %s' % [pad, "#{j}.", s]
1047
1110
  if ia
1048
1111
  unless confirm_semver(s, type)
1049
1112
  update.delete(a)
@@ -1065,7 +1128,7 @@ module Squared
1065
1128
  else
1066
1129
  if items
1067
1130
  update = choice('Select a package', items.map(&:first),
1068
- multiple: true, force: false, index: true, border: true).map! { |n| items[n.pred].last }
1131
+ multiple: true, force: false, index: true, border: true).map { |n| items[n.pred].last }
1069
1132
  end
1070
1133
  unless Array(update).empty?
1071
1134
  opts = ['f']
@@ -1138,7 +1201,7 @@ module Squared
1138
1201
  raise_error ArgumentError, "unrecognized args: #{op.join(', ')}", hint: flag unless op.empty?
1139
1202
  end
1140
1203
  op.add_path(file)
1141
- return run_rb(from: from, interactive: ['Push', 'N', gemname]) unless with || !banner
1204
+ return run(from: from, interactive: ['Push', 'N', gemname]) unless with || !banner
1142
1205
  when :exec
1143
1206
  min = if op.arg?('g', 'gem')
1144
1207
  1
@@ -1152,9 +1215,7 @@ module Squared
1152
1215
  0
1153
1216
  end
1154
1217
  op.concat(args)
1155
- if (args = command_args(op.extras, min: min, force: min == 1 && op.empty?))
1156
- op.push(args)
1157
- end
1218
+ command_args(op.extras, min: min, force: min == 1 && op.empty?)
1158
1219
  op.append(quote: false)
1159
1220
  when :update
1160
1221
  if !op.arg?('n', 'bindir') && (bin = config_get('bin')) && Dir.exist?(bin)
@@ -1187,7 +1248,8 @@ module Squared
1187
1248
  end
1188
1249
  elsif (n = op.index { |val| val.match?(/(\A|[a-z])@\d/) })
1189
1250
  name = op.remove_at(n)
1190
- pre, ver = if (n = name.index('@')) == 0
1251
+ n = name.index('@')
1252
+ pre, ver = if n == 0
1191
1253
  [gemname, name[1..-1]]
1192
1254
  else
1193
1255
  [name[0, n], name[n.succ..-1]]
@@ -1238,7 +1300,8 @@ module Squared
1238
1300
  end
1239
1301
  op.clear(errors: true) if gems
1240
1302
  print_run(op, banner, **kwargs)
1241
- run_rb(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception), from: from)
1303
+ run(banner: banner && %w[q silent quiet no-verbose].none? { |s| op.include?(shell_option(s)) },
1304
+ exception: kwargs.fetch(:exception, exception?), from: from)
1242
1305
  end
1243
1306
 
1244
1307
  def bundle(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
@@ -1258,21 +1321,33 @@ module Squared
1258
1321
  when :cache, :check, :clean, :init, :install, :lock, :pack, :package, :platform
1259
1322
  pre = true
1260
1323
  opts.concat(args)
1324
+ when :config
1325
+ if args.empty?
1326
+ pre = true
1327
+ else
1328
+ case (pre = args.shift)
1329
+ when 'list', 'get', 'set', 'unset'
1330
+ cmd << pre
1331
+ else
1332
+ args.unshift(pre)
1333
+ end
1334
+ opts.concat(args)
1335
+ end
1261
1336
  when :doctor
1262
1337
  case (pre = (val = args.shift) || opts.shift)
1263
1338
  when 'diagnose', 'ssl'
1264
1339
  cmd << pre
1265
1340
  else
1266
1341
  if val
1267
- args << val
1342
+ args.unshift(val)
1268
1343
  elsif pre
1269
- opts << pre
1344
+ opts.unshift(pre)
1270
1345
  end
1271
1346
  pre = true
1272
1347
  end
1273
1348
  opts.concat(args)
1274
1349
  when :plugin
1275
- case (plu = args.shift || opts.shift)
1350
+ case (plu = args.shift)
1276
1351
  when 'install', 'uninstall', 'help', 'list'
1277
1352
  cmd << plu
1278
1353
  else
@@ -1282,58 +1357,60 @@ module Squared
1282
1357
  op = OptionPartition.new(opts, bundleopts(if pre == 'ssl'
1283
1358
  :doctor_ssl
1284
1359
  elsif plu
1285
- plu == 'install' ? :plugin : :"plugin_#{plu}"
1360
+ plu == 'install' ? :plugin : symjoin('plugin', plu, char: '_')
1286
1361
  else
1287
1362
  flag
1288
1363
  end),
1289
- cmd, project: self, no: OPT_BUNDLE[:no][flag], args: flag == :exec)
1364
+ cmd,
1365
+ project: self, no: OPT_BUNDLE[:no][flag], args: flag == :exec || flag == :config)
1290
1366
  op.concat(args) unless pre
1291
1367
  output = false
1292
- invalid = ->(a) { raise_error ArgumentError, "unrecognized args: #{a.join(', ')}", hint: flag }
1293
1368
  case flag
1294
1369
  when :config
1295
- if op.empty?
1296
- op << (val = readline('Enter arguments', force: false))
1297
- output = val.match?(/(?:un)?set/)
1298
- else
1370
+ if pre == 'list'
1371
+ op.clear
1372
+ elsif !op.empty?
1299
1373
  a = op.dup
1300
- b, c, d = op.slice!(0, 3)
1301
- e = op.arg?('global', 'local')
1302
- op << b
1303
- getname = -> { op << (c || readline('Enter name', force: true)) }
1304
- case b
1305
- when 'list'
1306
- nil
1374
+ b, c = op.slice!(0, 2)
1375
+ d = op.arg?('global', 'local')
1376
+ getname = -> { op << (b || readline('Enter name', force: true)) }
1377
+ case pre
1307
1378
  when 'get'
1308
1379
  getname.call
1309
1380
  when 'set'
1310
- if e
1311
- op << c
1312
- c = d
1313
- d = op.shift
1381
+ if d
1382
+ op << b
1383
+ b = c
1384
+ c = op.shift
1314
1385
  end
1315
1386
  getname.call
1316
- op << (d || readline('Enter value', force: true))
1387
+ op << (c || readline('Enter value', force: true))
1317
1388
  output = true
1318
1389
  when 'unset'
1319
- if e
1320
- op << c
1321
- c = d
1390
+ if d
1391
+ op << b
1392
+ b = c
1322
1393
  end
1323
1394
  getname.call
1324
1395
  output = true
1325
1396
  else
1326
- if b
1327
- op << b
1328
- if c
1329
- op.add_quote(c)
1330
- output = true
1331
- end
1397
+ if c && !op.arg?('parseable', 'no-parseable')
1398
+ op.adjoin('set') << b
1399
+ op.add_quote(c)
1400
+ output = true
1401
+ exit 1 unless confirm_basic('Confirm?', op, 'Y')
1402
+ elsif b
1403
+ op.adjoin('get') << b
1404
+ op.unshift(c) if c
1332
1405
  else
1333
1406
  invalid.call(a)
1334
1407
  end
1335
1408
  end
1336
1409
  op.clear
1410
+ else
1411
+ val = readline('Enter arguments', force: false)
1412
+ op << (val.empty? ? 'list' : val)
1413
+ output = val.start_with?('set', 'unset')
1337
1414
  end
1338
1415
  when :plugin
1339
1416
  case plu
@@ -1361,8 +1438,9 @@ module Squared
1361
1438
  op.clear
1362
1439
  end
1363
1440
  print_run(op, banner, **kwargs)
1364
- run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception), from: :"bundle:#{flag}")
1365
- .tap { |ret| success?(ret, banner, output) }
1441
+ ret = run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception?),
1442
+ from: symjoin('bundle', flag))
1443
+ success?(ret, banner, output)
1366
1444
  end
1367
1445
 
1368
1446
  def rake(*args, sync: true, banner: verbose?, with: nil, pass: PASS_RUBY[:rake], **kwargs)
@@ -1373,26 +1451,26 @@ module Squared
1373
1451
  op.append(escape: true)
1374
1452
  print_run(op, banner, **kwargs)
1375
1453
  var = { 'BANNER' => '0' } unless banner
1376
- run(op, var, sync: sync, banner: false, exception: kwargs.fetch(:exception, exception), from: :rake)
1454
+ run(op, var, sync: sync, banner: false, exception: kwargs.fetch(:exception, exception?), from: :rake)
1377
1455
  end
1378
1456
 
1379
1457
  def irb(*args, banner: verbose?, with: nil, pass: PASS_RUBY[:irb], **kwargs)
1380
1458
  opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
1381
1459
  op = OptionPartition.new(opts, OPT_RUBY[:irb], session('irb'), project: self, first: [/\.rb$/])
1382
1460
  r = []
1383
- r << 'bundler/setup' unless op.arg?('r')
1384
- r << kwargs[:name] if kwargs[:name]
1461
+ r << 'bundler/setup' if !op.arg?('r') && bundle_load
1462
+ r.concat(Array(kwargs[:name])) if kwargs[:name]
1385
1463
  r.each { |val| op.add_option('r', val, merge: true) }
1386
- Array(kwargs.fetch(:path, gemlib)).each { |val| op << quote_option('I', val, merge: true) }
1464
+ op.merge((gemlib + Array(kwargs[:path])).map { |val| quote_option('I', val, merge: true) })
1387
1465
  op.concat(args)
1388
1466
  op.append(delim: true)
1389
1467
  print_run(op, banner, **kwargs)
1390
- run(banner: false, exception: kwargs.fetch(:exception, exception), from: :irb)
1468
+ run(banner: false, exception: kwargs.fetch(:exception, exception?), from: :irb)
1391
1469
  end
1392
1470
 
1393
1471
  def rbs(flag, *args, banner: verbose?, with: nil, pass: nil, **kwargs)
1394
1472
  case pass
1395
- when NilClass
1473
+ when nil
1396
1474
  pass = PASS_RUBY[:rbs]
1397
1475
  when Array
1398
1476
  pass += PASS_RUBY[:rbs]
@@ -1405,7 +1483,7 @@ module Squared
1405
1483
  sig = args.shift
1406
1484
  y = option('y', ignore: false)
1407
1485
  i = 1
1408
- args.map! { |val| basepath(val).relative_path_from(path) }.each do |file|
1486
+ args.map { |val| basepath(val).relative_path_from(path) }.each do |file|
1409
1487
  dir = basepath sig, file.dirname
1410
1488
  dir.mkpath unless dir.exist?
1411
1489
  base = file.basename.to_s
@@ -1421,8 +1499,8 @@ module Squared
1421
1499
  end
1422
1500
  end
1423
1501
  unless status == 'ignored'
1424
- ret = run(op.target.temp(File.extname(base) == '.rbi' ? 'rbi' : 'rb', file, '>', rbs), banner: false,
1425
- series: true)
1502
+ ret = run(op.target.temp(File.extname(base) == '.rbi' ? 'rbi' : 'rb', file, '>', rbs),
1503
+ banner: false, series: true)
1426
1504
  if !ret
1427
1505
  status = 'FAIL'
1428
1506
  elsif File.empty?(rbs)
@@ -1436,7 +1514,7 @@ module Squared
1436
1514
  op.clear
1437
1515
  .append(*args)
1438
1516
  print_run(op, banner, **kwargs)
1439
- run(banner: false, exception: kwargs.fetch(:exception, exception), from: :"rbs:#{flag}")
1517
+ run(banner: false, exception: kwargs.fetch(:exception, exception?), from: symjoin('rbs', flag))
1440
1518
  end
1441
1519
  end
1442
1520
 
@@ -1455,12 +1533,11 @@ module Squared
1455
1533
  op.errors << val
1456
1534
  end
1457
1535
  end
1458
- op.swap
1459
- .map! { |val| basepath(val).relative_path_from(path) }
1536
+ op.swap.map! { |val| basepath(val).relative_path_from(path) }
1460
1537
  op.append(delim: true)
1461
1538
  .clear(errors: true)
1462
1539
  print_run(op, banner, **kwargs)
1463
- run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception), from: :rubocop)
1540
+ run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception?), from: :rubocop)
1464
1541
  end
1465
1542
 
1466
1543
  def gemspec
@@ -1485,7 +1562,17 @@ module Squared
1485
1562
  def copy?
1486
1563
  return true if @copy.is_a?(Hash) ? copy[:into] : super
1487
1564
  return gemdir? if gemdir
1565
+ return false unless @autodetect
1566
+
1567
+ set = lambda do |val, path|
1568
+ base = Pathname.new(path.strip)
1569
+ dir = base + gempath
1570
+ return false unless dir.writable? && base.join(gempath(val, 'specifications')).exist?
1488
1571
 
1572
+ log.warn "using version #{val}".subhint("given #{version}") if version && version != val
1573
+ self.version = val
1574
+ self.gemdir = dir
1575
+ end
1489
1576
  if version
1490
1577
  begin
1491
1578
  case @autodetect
@@ -1495,37 +1582,24 @@ module Squared
1495
1582
  if pwd_set { `rbenv which ruby` } =~ %r{^(.+[\\/]versions[\\/](\d\.\d)\.[^\\/]+)[\\/]bin[\\/]ruby$}
1496
1583
  File.join($1, 'lib/ruby/gems', "#{$2}.0")
1497
1584
  end
1498
- when 'asdf'
1499
- pwd_set { `asdf where ruby` }.yield_self do |val|
1500
- val =~ /(\d\.\d)\.[^.]+$/ && File.join(val, 'lib/ruby/gems', "#{$1}.0")
1501
- end
1502
- when 'env'
1503
- ENV['GEM_HOME'] || ENV['GEM_ROOT']
1585
+ when 'asdf', 'mise'
1586
+ val = pwd_set { `#{@autodetect} where ruby`.chomp }
1587
+ File.join(val, 'lib/ruby/gems', "#{$1}.0") if val =~ /(\d\.\d)\.[^.]+$/
1504
1588
  when /bundler?/
1505
1589
  pwd_set { `bundle env` }[/^\s+Gem Path\s+(.+)$/, 1].split(File::PATH_SEPARATOR).find do |val|
1506
- Dir.exist?(val)
1590
+ Dir.exist?(File.join(val, 'gems'))
1507
1591
  end
1508
- end.tap { |val| self.gemdir = val if val }
1509
- rescue StandardError => e
1592
+ else
1593
+ ENV['GEM_HOME'] || ENV['GEM_ROOT']
1594
+ end
1595
+ .tap do |val|
1596
+ return true if val && set.call(version, val)
1597
+ end
1598
+ rescue => e
1510
1599
  log.debug e
1511
1600
  end
1512
- return true if gemdir?
1513
- end
1514
- return false unless @autodetect
1515
-
1516
- set = lambda do |val, path|
1517
- base = Pathname.new(path.strip)
1518
- dir = base + gempath
1519
- return false unless dir.writable? && base.join(gempath(val, 'specification')).exist?
1520
-
1521
- log.warn "using version #{val}".subhint("given #{version}") if version && version != val
1522
- self.version = val
1523
- self.gemdir = dir
1524
- end
1525
- if version
1526
- opt = gempwd
1527
- pwd_set(pass: !opt.nil?) do
1528
- out = `#{gem_output(opt, 'list --local -d', gemname)}`
1601
+ pwd_set(pass: !gempwd.nil?) do
1602
+ out = `#{gem_output(gempwd, 'list --local -d', gemname)}`
1529
1603
  next unless out =~ /#{Regexp.escape(gemname)}\s+\((.+)\)$/
1530
1604
 
1531
1605
  split_escape($1)
@@ -1555,7 +1629,7 @@ module Squared
1555
1629
  raise_error Errno::ENOENT, 'gems home'
1556
1630
  end
1557
1631
  end
1558
- rescue StandardError => e
1632
+ rescue => e
1559
1633
  log.error e
1560
1634
  self.version = nil
1561
1635
  @gemdir = nil
@@ -1570,10 +1644,6 @@ module Squared
1570
1644
 
1571
1645
  private
1572
1646
 
1573
- def run_rb(**kwargs)
1574
- run(banner: !@session&.include?('--quiet'), **kwargs)
1575
- end
1576
-
1577
1647
  def ruby_session(*cmd, **kwargs)
1578
1648
  session('ruby', *preopts, *cmd, **kwargs)
1579
1649
  end
@@ -1583,7 +1653,9 @@ module Squared
1583
1653
  end
1584
1654
 
1585
1655
  def bundle_session(*cmd, **kwargs)
1586
- session('bundle', *cmd, *preopts, **kwargs).tap { append_nocolor }
1656
+ ret = session('bundle', *cmd, *preopts, **kwargs)
1657
+ append_nocolor
1658
+ ret
1587
1659
  end
1588
1660
 
1589
1661
  def rake_session(*cmd, **kwargs)
@@ -1613,22 +1685,47 @@ module Squared
1613
1685
  session_output('rake', *cmd, **kwargs)
1614
1686
  end
1615
1687
 
1616
- def config_get(key)
1617
- out = pwd_set { `#{bundle_output('config get --parseable', key)}`.chomp }
1618
- return unless out =~ /\A([^=]+)=(.*)\z/ && $1 == key
1688
+ def bundle_load
1689
+ require 'bundler' unless defined?(Bundler)
1690
+ rescue LoadError
1691
+ nil
1692
+ else
1693
+ Bundler::VERSION
1694
+ end
1695
+
1696
+ def gemfile_set(val)
1697
+ @gemfile = if val == false
1698
+ false
1699
+ elsif val
1700
+ basepath(val).sub_ext('.gemspec')
1701
+ end
1702
+ end
1619
1703
 
1620
- case (out = $2)
1621
- when 'true'
1622
- true
1623
- when '', '[]'
1624
- nil
1625
- else
1626
- if out =~ /\A\[:(.+)\]\z/
1627
- $1.split(', :').map { |val| ((val.delete_prefix!('"') && val.delete_suffix!('"')) || val).to_sym }
1628
- else
1629
- out || false
1704
+ def config_get(key, first: true)
1705
+ ret = []
1706
+ pwd_set do
1707
+ IO.popen(bundle_output('config get --parseable', key).to_s).each do |line|
1708
+ line.chomp!
1709
+ next unless line[/\A([^=]+)=(.*)\z/, 1] == key
1710
+
1711
+ val = case (val = $2)
1712
+ when 'true'
1713
+ true
1714
+ when '', '[]'
1715
+ nil
1716
+ else
1717
+ if val =~ /\A\[:(.+)\]\z/
1718
+ $1.split(', :').map { |s| (s[/\A"(.+)"\z/, 1] || s).to_sym }
1719
+ else
1720
+ val || false
1721
+ end
1722
+ end
1723
+ return val if first
1724
+
1725
+ ret << val
1630
1726
  end
1631
1727
  end
1728
+ ret unless first && ret.empty?
1632
1729
  end
1633
1730
 
1634
1731
  def config_set(key, *val, global: false)
@@ -1636,9 +1733,11 @@ module Squared
1636
1733
  end
1637
1734
 
1638
1735
  def unpack_get(tag, ext)
1639
- return super unless ext == 'gem'
1640
-
1641
- "https://rubygems.org/downloads/#{File.basename(tag, '.gem')}.gem"
1736
+ if ext == 'gem'
1737
+ "https://rubygems.org/downloads/#{File.basename(tag, '.gem')}.gem"
1738
+ else
1739
+ super
1740
+ end
1642
1741
  end
1643
1742
 
1644
1743
  def preopts
@@ -1665,9 +1764,8 @@ module Squared
1665
1764
 
1666
1765
  def raketasks
1667
1766
  @raketasks ||= [].tap do |ret|
1668
- opt = rakepwd
1669
- pwd_set(pass: !opt.nil?) do
1670
- IO.popen(rake_output(opt, '-AT').to_s).each do |line|
1767
+ pwd_set(pass: !rakepwd.nil?) do
1768
+ IO.popen(rake_output(rakepwd, '-AT').to_s).each do |line|
1671
1769
  next unless line =~ /^rake ((?:[^\[: ]+:?)+)(\[[^\]]+\])?/
1672
1770
 
1673
1771
  ret << [$1, $2]
@@ -1682,12 +1780,15 @@ module Squared
1682
1780
  args << :install_a
1683
1781
  when :add, :plugin
1684
1782
  args << :common_git
1685
- when :binstubs, :cache
1686
- args << :common_all
1687
1783
  when :lock, :outdated
1688
1784
  args << :common_version
1689
1785
  end
1690
- OPT_BUNDLE[:common] + args.flat_map { |name| OPT_BUNDLE.fetch(name, []) }
1786
+ OPT_BUNDLE[:common] + args.flat_map do |name|
1787
+ opts = OPT_BUNDLE.fetch(name, [])
1788
+ next opts if compat?(4, gem: 'bundle')
1789
+
1790
+ opts + OPT_BUNDLE[:v3].fetch(name, [])
1791
+ end
1691
1792
  end
1692
1793
 
1693
1794
  def gemopts(*args)
@@ -1706,8 +1807,15 @@ module Squared
1706
1807
  case args.first
1707
1808
  when :info, :list, :search
1708
1809
  args << :common_all
1810
+ when :dependency, :fetch, :specification, :uninstall, :yank
1811
+ args << :common_platform
1812
+ end
1813
+ OPT_GEM[:common] + args.flat_map do |name|
1814
+ opts = OPT_GEM.fetch(name, [])
1815
+ next opts if compat?(4)
1816
+
1817
+ opts + OPT_GEM[:v3].fetch(name, [])
1709
1818
  end
1710
- OPT_GEM[:common] + args.flat_map { |name| OPT_GEM.fetch(name, []) }
1711
1819
  end
1712
1820
 
1713
1821
  def gempwd
@@ -1730,7 +1838,7 @@ module Squared
1730
1838
  if (spec = gemspec)
1731
1839
  lib.merge(spec.require_paths || [])
1732
1840
  end
1733
- lib.select { |file| exist?(file) }
1841
+ lib.select { |file| exist?(file, type: 'd') }
1734
1842
  end
1735
1843
  end
1736
1844
 
@@ -1745,6 +1853,26 @@ module Squared
1745
1853
 
1746
1854
  gemdir.exist? && !gemdir.empty? && gemdir.writable?
1747
1855
  end
1856
+
1857
+ def compat?(min, max = Float::INFINITY, gem: nil)
1858
+ return false unless !gem || (bundle = gem.match?(/bundler?/))
1859
+
1860
+ n = @rubygems
1861
+ if n.is_a?(Numeric) && n > 0
1862
+ max = max.to_f unless max.is_a?(Numeric)
1863
+ n >= min.to_f && n < max
1864
+ else
1865
+ ver = case n
1866
+ when String
1867
+ n
1868
+ when Array
1869
+ n[bundle ? 1 : 0]
1870
+ else
1871
+ min.tap { min = (bundle && bundle_load) || Gem::VERSION }
1872
+ end
1873
+ semgte?(ver.to_s, min.to_s) && (max == Float::INFINITY || !semgte?(ver.to_s, max.to_s))
1874
+ end
1875
+ end
1748
1876
  end
1749
1877
 
1750
1878
  Application.implement Ruby