squared 0.6.10 → 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
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,9 +64,17 @@ 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: {
72
79
  config: %w[parseable].freeze,
73
80
  gem: %w[changelog ci coc exe linter mit test].freeze
@@ -79,20 +86,21 @@ module Squared
79
86
  common_domain: %w[b|both clear-sources l|local r|remote].freeze,
80
87
  common_otp: %w[host=q k|key=b otp=b].freeze,
81
88
  common_all: %w[a|all e|exact v|version=q].freeze,
82
- build: %w[C=p force strict o|output=p platform=q].freeze,
83
- 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
84
92
  R|re-sign s|sign=p].freeze,
85
93
  check: %w[a v|version=q].freeze,
86
94
  cleanup: %w[D n d|dry-run].freeze,
87
95
  contents: %w[l all s|spec-dir=q v|version=q].freeze,
88
- dependency: %w[R pipe platform=q v|version=q].freeze,
96
+ dependency: %w[R pipe].freeze,
89
97
  exec: %w[conservative g|gem=b v|version=q].freeze,
90
- fetch: %w[clear-sources platform=q v|version=q].freeze,
98
+ fetch: %w[clear-sources].freeze,
91
99
  generate_index: %w[update d|directory=p].freeze,
92
100
  info: %w[i I].freeze,
93
101
  install: %w[v|version=q].freeze,
94
- install_a: %w[E f w conservative default development development-all explain ignore-dependencies N|no-document
95
- 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
96
104
  target-rbconfig=p? P|trust-policy=b without=q].freeze,
97
105
  list: %w[d i I].freeze,
98
106
  lock: %w[s].freeze,
@@ -105,14 +113,19 @@ module Squared
105
113
  rdoc: %w[all v|version=q].freeze,
106
114
  rebuild: %w[C=p diff force strict gemspec=p original=p source=q].freeze,
107
115
  search: %w[d i I].freeze,
116
+ server: %w[b|bind=q d|dir=p l|launch=q p|port=b].freeze,
108
117
  signin: %w[otp=b host=q].freeze,
109
- sources: %w[f c|clear-all l|list u|update a|add=q p|http-proxy=q? r|remove=q].freeze,
110
- specification: %w[all marshal ruby yaml platform=q v|version=q].freeze,
111
- 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,
112
121
  unpack: %w[spec target=p P|trust-policy=b v|version=q],
113
122
  update: %w[system=b?].freeze,
114
123
  which: %w[a g].freeze,
115
- 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,
116
129
  no: {
117
130
  check: %w[alien doctor dry-run gems].freeze,
118
131
  cleanup: %w[check-development user-install].freeze,
@@ -132,6 +145,7 @@ module Squared
132
145
  push: %w[http-proxy].freeze,
133
146
  rdoc: %w[overwrite rdoc ri].freeze,
134
147
  search: %w[details http-proxy installed prerelease versions].freeze,
148
+ server: %w[daemon].freeze,
135
149
  sources: %w[force http-proxy].freeze,
136
150
  specification: %w[prerelease].freeze,
137
151
  uninstall: %w[abort-on-dependent all check-development executables force format-executable
@@ -149,7 +163,8 @@ module Squared
149
163
  contents: %w[s spec-dir].freeze,
150
164
  dependency: %w[s source].freeze,
151
165
  install: %w[document s source without].freeze,
152
- pristine: %w[skip].freeze
166
+ pristine: %w[skip].freeze,
167
+ sources: %w[append prepend].freeze
153
168
  }.freeze,
154
169
  bundle: {
155
170
  install: %w[standalone with without].freeze,
@@ -165,7 +180,7 @@ module Squared
165
180
  end
166
181
 
167
182
  def bannerargs
168
- %i[dependfile gemname gemdir].freeze
183
+ %i[version dependfile gemname gemdir].freeze
169
184
  end
170
185
 
171
186
  def config?(val)
@@ -179,7 +194,7 @@ module Squared
179
194
  'outdated' => %i[major minor patch].freeze,
180
195
  'ruby' => %i[file script version].freeze,
181
196
  'gem' => %i[install uninstall outdated update pristine build push exec command].freeze,
182
- 'bundle' => %i[install update cache exec config reinstall command].freeze,
197
+ 'bundle' => %i[config install update cache exec reinstall command].freeze,
183
198
  'rake' => nil,
184
199
  'irb' => nil,
185
200
  'rbs' => nil,
@@ -187,28 +202,23 @@ module Squared
187
202
  })
188
203
 
189
204
  attr_reader :gemdir
190
- attr_accessor :autodetect
191
205
 
192
- def initialize(*, autodetect: false, gemspec: nil, steep: 'Steepfile', rubocop: '.rubocop.yml', asdf: 'ruby',
193
- **kwargs)
206
+ def initialize(*, autodetect: false, steep: 'Steepfile', rubocop: '.rubocop.yml', asdf: 'ruby', **kwargs)
194
207
  super
195
208
  if @pass.include?(Ruby.ref)
196
209
  initialize_ref Ruby.ref
197
- initialize_logger(**kwargs)
210
+ initialize_logger kwargs[:log]
198
211
  else
199
212
  initialize_build(Ruby.ref, **kwargs)
200
213
  initialize_env(**kwargs)
201
214
  end
202
215
  dependfile_set GEMFILE
203
- self.autodetect = autodetect
204
- @gemfile = if gemspec == false
205
- false
206
- elsif gemspec
207
- basepath(gemspec.include?('.') ? gemspec : "#{gemspec}.gemspec")
208
- end
216
+ gemfile_set kwargs[:gemspec]
217
+ @autodetect = autodetect
209
218
  @steepfile = basepath! steep if steep
210
219
  @rubocopfile = Pathname.new(rubocop).realpath rescue basepath!(Dir.home, '.rubocop.yml') if rubocop
211
- return unless rakefile && @output[0].nil? && @copy.nil? && !version && !self.autodetect
220
+ @rubygems = kwargs.fetch(:rubygems, 0)
221
+ return unless rakefile && @output[0].nil? && @copy.nil? && !version && !@autodetect
212
222
 
213
223
  begin
214
224
  File.foreach(rakefile) do |line|
@@ -220,7 +230,7 @@ module Squared
220
230
  @clean = "#{cmd} clean" if @clean.nil?
221
231
  break
222
232
  end
223
- rescue StandardError => e
233
+ rescue => e
224
234
  log.error e
225
235
  end
226
236
  end
@@ -280,7 +290,7 @@ module Squared
280
290
  elsif exception
281
291
  indexerror n, tasks
282
292
  else
283
- log.warn "rake task #{n} of #{tasks.size} (out of range)"
293
+ log.warn "rake task #{n} of #{tasks.size}".subhint('out of range')
284
294
  opts.clear
285
295
  next
286
296
  end
@@ -328,32 +338,28 @@ module Squared
328
338
  val = File.join(val, '**/*.rb') unless val.include?('*') || val.match?(/\.[a-z\d]+$/i)
329
339
  Dir.glob(val, base: path)
330
340
  end
331
- files = if args.empty?
332
- choice_index('Select files', list, multiple: true, series: true,
341
+ if args.empty?
342
+ files = choice_index('Select files', list, multiple: true, series: true,
333
343
  accept: [accept_y('Generate?')])
334
- else
335
- list.map! { |val| basepath(val).to_s }
336
- [].tap do |out|
337
- ret = []
338
- args.each do |val|
339
- if val.include?('*')
340
- ret.concat(Dir.glob(val, base: path))
341
- elsif !(file = basepath!(val))
342
- print_error(val, hint: 'not found')
343
- elsif file.directory?
344
- ret.concat(file.glob('**/*.rb'))
345
- else
346
- ret << val
347
- end
348
- end
349
- ret = ret.select { |val| list.include?(basepath(val).to_s) }
350
- if ret.empty?
351
- print_error('steep', 'no files matched', hint: "#{key}:check")
352
- exit 1
353
- end
354
- out.replace(ret.uniq)
355
- end
356
- 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
357
363
  sig = if (n = sig.index(args.first))
358
364
  args.shift
359
365
  sig[n]
@@ -372,15 +378,15 @@ module Squared
372
378
  format_desc action, nil, 'opts*,path*/:'
373
379
  task action do |_, args|
374
380
  opts, args = args.to_a.partition do |val|
375
- 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]+)*$)/)
376
382
 
377
383
  !val.include?('*') && !val.end_with?('/')
378
384
  end
379
385
  if opts.delete(':')
380
386
  args << (Dir.exist?('lib') ? 'lib/' : '**/*.rb') if args.empty?
381
- 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 }
382
388
  .flat_map { |val| Dir.glob(val, base: path) }
383
- args = choice_index('Select files', list, multiple: true)
389
+ args = choice_index('Select files', args, multiple: true)
384
390
  end
385
391
  rubocop(*args, opts: opts, banner: true)
386
392
  end
@@ -438,6 +444,11 @@ module Squared
438
444
  end
439
445
  when 'bundle'
440
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
441
452
  when :install, :update, :cache, :exec
442
453
  format_desc(action, flag, 'opts*', after: case flag
443
454
  when :update then 'gems*'
@@ -446,11 +457,6 @@ module Squared
446
457
  task flag do |_, args|
447
458
  bundle(flag, opts: args.to_a, banner: flag == :exec ? verbose? : true)
448
459
  end
449
- when :config
450
- format_desc action, flag, 'list|set|get|unset?,opts*,args*'
451
- task flag do |_, args|
452
- bundle(flag, *args.to_a, banner: true)
453
- end
454
460
  when :reinstall
455
461
  format_desc action, flag, 'f/orce?,opts*'
456
462
  task flag do |_, args|
@@ -503,13 +509,168 @@ module Squared
503
509
  when :script
504
510
  format_desc action, flag, 'opts*'
505
511
  task flag do |_, args|
506
- 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)
507
526
  ruby(flag, opts: args.to_a, command: command)
508
527
  end
509
528
  when :version
510
529
  format_desc action, flag
511
530
  task flag do
512
- 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
513
674
  end
514
675
  end
515
676
  end
@@ -525,25 +686,37 @@ module Squared
525
686
  super
526
687
  elsif outdated?
527
688
  workspace.rev_clear(name, sync: sync)
528
- cmd = bundle_session 'install'
529
- option('binstubs') do |val|
530
- next if val == '0' || val == 'false'
531
-
532
- run(bundle_output('binstubs --all', case val
533
- when '1', 'true'
534
- nil
535
- else
536
- if val.start_with?('~')
537
- val = File.join(Dir.home, val == '~' ? '.bundle' : val[1..-1])
538
- if prod?
539
- config_set('binstubs', shell_quote(val), global: true)
540
- val = nil
541
- 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
542
707
  else
543
- val = basepath val
544
- end
545
- quote_option('path', val) if val
546
- 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
547
720
  end
548
721
  if prod? && !config_get('without')
549
722
  if semgte?('3')
@@ -552,8 +725,8 @@ module Squared
552
725
  cmd << '--without=development'
553
726
  end
554
727
  end
555
- option('jobs') { |n| cmd << "-j#{n}" if n.to_i > 0 }
556
- run_rb(sync: sync, from: :depend)
728
+ option('jobs', pat: /^\d+$/) { |val| cmd << "-j#{val}" }
729
+ run(sync: sync, from: :depend)
557
730
  end
558
731
  end
559
732
 
@@ -582,8 +755,8 @@ module Squared
582
755
  c = glob[i] || glob.first
583
756
  log.info "cp #{a + c} #{b}"
584
757
  copy_dir(a, b, c, pass: pass, verbose: !silent?)
585
- rescue StandardError => e
586
- on_error e, :copy
758
+ rescue => e
759
+ on_error e
587
760
  end
588
761
  on :last, :copy
589
762
  end
@@ -619,7 +792,7 @@ module Squared
619
792
  on :first, :outdated
620
793
  banner = format_banner cmd.to_s
621
794
  print_item banner if sync
622
- pwd_set(from: :outdated) do
795
+ pwd_set(cmd, dryrun: dryrun) do
623
796
  tc = theme[:current]
624
797
  start = 0
625
798
  found = 0
@@ -726,7 +899,7 @@ module Squared
726
899
  unless Array(items).empty?
727
900
  gems = if se
728
901
  choice('Select a package', items.map(&:first),
729
- 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 }
730
903
  else
731
904
  items.map(&:last)
732
905
  end
@@ -748,7 +921,7 @@ module Squared
748
921
  break
749
922
  end
750
923
  end
751
- rescue StandardError => e
924
+ rescue => e
752
925
  log.debug e
753
926
  end
754
927
  puts print_footer(status || 'Updates are available', right: status.include?('/'))
@@ -759,117 +932,10 @@ module Squared
759
932
  on :last, :outdated
760
933
  end
761
934
 
762
- def ruby(*args, flag: nil, sync: true, banner: verbose?, with: nil, pass: PASS_RUBY[:ruby], **kwargs)
763
- flag = args.shift if !flag && args.first.is_a?(Symbol)
764
- if flag == :version
765
- pwd_set do
766
- out = []
767
- order = { 'rbenv' => -1, 'rvm' => -1, 'asdf' => -1, 'chruby' => -1 }
768
- ENV.fetch('PATH', '').split(':').each_with_index do |val, index|
769
- order.each_key do |key|
770
- next unless val.match?(%r{[/.]#{key}/})
771
-
772
- order[key] = index
773
- break
774
- end
775
- end
776
- if @asdf
777
- [File.join(ENV.fetch('ASDF_DATA_DIR', '$HOME/.asdf'), "installs/#{@asdf.first}")]
778
- else
779
- [
780
- "#{ENV.fetch('RBENV_ROOT', '$HOME/.rbenv')}/bin/rbenv",
781
- '$HOME/.rvm/bin/rvm',
782
- '/usr/bin/rbenv',
783
- '/usr/local/rvm/bin/rvm',
784
- '/usr/share/rvm/bin/rvm',
785
- '/usr/local/share/chruby/chruby.sh'
786
- ].sort do |a, b|
787
- c = -1
788
- d = -1
789
- order.each do |key, val|
790
- pat = %r{/\.?#{key}}
791
- c = val if a.match?(pat)
792
- d = val if b.match?(pat)
793
- end
794
- if c == d
795
- 0
796
- elsif c == -1
797
- 1
798
- elsif d == -1
799
- -1
800
- else
801
- c < d ? -1 : 1
802
- end
803
- end
804
- .push('')
805
- end.each do |val|
806
- next unless val.empty? || File.exist?(val.sub('$HOME', Dir.home))
807
-
808
- trim = ->(s) { s[/^\D+\d+\.\d+(?:\.\S+)?/, 0].sub(/^([a-z]+)-/i, '\1 ') }
809
- ver = '.ruby-version'
810
- out << trim.call(case (cmd = File.basename(val))
811
- when 'rvm'
812
- `rvm current`[/^\S+/, 0]
813
- when 'rbenv'
814
- name = `rbenv version-name`
815
- name.match?(SEM_VER) ? "ruby #{name}" : name
816
- when 'chruby.sh'
817
- chruby = session_output 'source', val
818
- `#{chruby.with('ruby --version')}`
819
- else
820
- if @asdf
821
- cmd = 'asdf'
822
- ver = '.tool-versions'
823
- opt = [@asdf.first]
824
- opt.unshift('--no-header') unless @@asdf.version == 15
825
- `asdf current #{opt.join(' ')}`[/^\S+\s+\S+/, 0].sub(/\s+/, ' ')
826
- else
827
- ver = nil
828
- `ruby --version`
829
- end
830
- end)
831
- break if workspace.windows?
832
-
833
- unless val.empty?
834
- out << trim.call(case cmd
835
- when 'chruby.sh'
836
- `#{chruby.with('chruby --version')}`.sub(':', '')
837
- when 'asdf'
838
- "asdf #{`asdf version`.delete_prefix('v')}"
839
- else
840
- `#{cmd} --version`
841
- end)
842
- end
843
- begin
844
- out << ('which %s' % case cmd
845
- when 'rbenv'
846
- `rbenv which ruby`
847
- when 'chruby.sh'
848
- `#{chruby.with('which ruby')}`
849
- when 'asdf'
850
- `asdf which #{@asdf.first}`
851
- else
852
- `which ruby`
853
- end)
854
- rescue StandardError => e
855
- log.debug e
856
- end
857
- if ver
858
- path.ascend do |ent|
859
- next unless (ent += ver).exist?
860
-
861
- hint = File.read(ent).lines(chomp: true).reject(&:empty?).join(', ') rescue nil
862
- out << message("found #{ent}", hint: hint)
863
- end
864
- end
865
- break
866
- end
867
- out.map!(&:split)
868
- pad = out.map(&:first).map!(&:size).max
869
- print_item
870
- puts(out.map! { |line| '%*s %s' % [pad, line.first, line[1..-1].join(' ')] })
871
- end
872
- 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
873
939
  end
874
940
  opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
875
941
  op = OptionPartition.new(opts, OPT_RUBY[:ruby], ruby_session, project: self, multiple: [/^-e/], args: true,
@@ -894,13 +960,8 @@ module Squared
894
960
  op.append(delim: true, escape: kwargs.fetch(:escape, false), quote: kwargs.fetch(:quote, false))
895
961
  end
896
962
  end
897
- from = if flag
898
- :"ruby:#{flag}"
899
- else
900
- print_run(op, banner, **kwargs)
901
- :ruby
902
- end
903
- 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)
904
965
  end
905
966
 
906
967
  def gem(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
@@ -922,14 +983,14 @@ module Squared
922
983
  end
923
984
  op = OptionPartition.new(opts, gemopts(flag), gem_session(flag),
924
985
  project: self, no: OPT_GEM[:no][flag == :update ? :install : flag])
925
- from = :"gem:#{flag}"
986
+ from = symjoin 'gem', flag
926
987
  if flag == :outdated
927
988
  op.adjoin(gempwd, start: 0) if gempwd
928
989
  op.clear
929
990
  cmd = session_done op.target
930
991
  log.info cmd
931
992
  on :first, from
932
- banner = format_banner(cmd)
993
+ banner = format_banner cmd
933
994
  print_item banner if sync
934
995
  major = [0, 0, 0]
935
996
  buffer = []
@@ -948,7 +1009,7 @@ module Squared
948
1009
  []
949
1010
  end
950
1011
  out = ->(val) { sync ? puts(val) : buffer << val }
951
- pwd_set(pass: !gempwd.nil?, from: from) do
1012
+ pwd_set(op.target, from: from, pass: !gempwd.nil?) do
952
1013
  rows = [[%w[Gem Current Latest], nil]]
953
1014
  IO.popen(cmd).each do |line|
954
1015
  if line =~ /^(\S+) \((\S+) < ([^)]+)\)$/
@@ -1004,7 +1065,7 @@ module Squared
1004
1065
  unless stdin?
1005
1066
  sub_style! g, theme[:major]
1006
1067
  styles = %i[green bold]
1007
- pat = (pat.first if pre)
1068
+ pat = pre ? pat.first : nil
1008
1069
  latest << :bold
1009
1070
  end
1010
1071
  major[0] += 1
@@ -1043,10 +1104,9 @@ module Squared
1043
1104
  out.call(queue)
1044
1105
  queue = nil
1045
1106
  end
1046
- s = ('%s %s %s' % [g, b, h]).yield_self do |val|
1047
- items&.push([val, a])
1048
- '%*s %s' % [pad, "#{j}.", val]
1049
- end
1107
+ s = '%s %s %s' % [g, b, h]
1108
+ items&.push([s, a])
1109
+ s = '%*s %s' % [pad, "#{j}.", s]
1050
1110
  if ia
1051
1111
  unless confirm_semver(s, type)
1052
1112
  update.delete(a)
@@ -1068,7 +1128,7 @@ module Squared
1068
1128
  else
1069
1129
  if items
1070
1130
  update = choice('Select a package', items.map(&:first),
1071
- 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 }
1072
1132
  end
1073
1133
  unless Array(update).empty?
1074
1134
  opts = ['f']
@@ -1107,7 +1167,6 @@ module Squared
1107
1167
  when :dependency, :environment, :list, :search, :specification, :which
1108
1168
  op.concat(args)
1109
1169
  end
1110
- ia = op.remove(':')
1111
1170
  op.each do |opt|
1112
1171
  if gems && !opt.start_with?('-') && !opt.match?(GEMNAME)
1113
1172
  op.errors << opt
@@ -1142,7 +1201,7 @@ module Squared
1142
1201
  raise_error ArgumentError, "unrecognized args: #{op.join(', ')}", hint: flag unless op.empty?
1143
1202
  end
1144
1203
  op.add_path(file)
1145
- return run_rb(from: from, interactive: ['Push', 'N', gemname]) unless with || !banner
1204
+ return run(from: from, interactive: ['Push', 'N', gemname]) unless with || !banner
1146
1205
  when :exec
1147
1206
  min = if op.arg?('g', 'gem')
1148
1207
  1
@@ -1156,9 +1215,7 @@ module Squared
1156
1215
  0
1157
1216
  end
1158
1217
  op.concat(args)
1159
- if (args = command_args(op.extras, min: min, force: min == 1 && op.empty?))
1160
- op.push(args)
1161
- end
1218
+ command_args(op.extras, min: min, force: min == 1 && op.empty?)
1162
1219
  op.append(quote: false)
1163
1220
  when :update
1164
1221
  if !op.arg?('n', 'bindir') && (bin = config_get('bin')) && Dir.exist?(bin)
@@ -1171,7 +1228,7 @@ module Squared
1171
1228
  end
1172
1229
  when :install, :uninstall, :pristine
1173
1230
  if flag == :install
1174
- post = if ia
1231
+ post = if op.remove(':')
1175
1232
  op.concat(args)
1176
1233
  readline('Enter command [args]', force: true)
1177
1234
  elsif op.empty?
@@ -1189,14 +1246,15 @@ module Squared
1189
1246
  else
1190
1247
  op.clear
1191
1248
  end
1192
- elsif (n = op.index { |val| val.match?(/(\A|[\w.-])@\d/) })
1249
+ elsif (n = op.index { |val| val.match?(/(\A|[a-z])@\d/) })
1193
1250
  name = op.remove_at(n)
1194
- pre, ver = if (n = name.index('@')) == 0
1251
+ n = name.index('@')
1252
+ pre, ver = if n == 0
1195
1253
  [gemname, name[1..-1]]
1196
1254
  else
1197
1255
  [name[0, n], name[n.succ..-1]]
1198
1256
  end
1199
- op.adjoin(pre, quote_option('version', ver))
1257
+ op.adjoin(pre, basic_option('version', ver))
1200
1258
  .clear
1201
1259
  end
1202
1260
  if flag == :install
@@ -1242,7 +1300,8 @@ module Squared
1242
1300
  end
1243
1301
  op.clear(errors: true) if gems
1244
1302
  print_run(op, banner, **kwargs)
1245
- 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)
1246
1305
  end
1247
1306
 
1248
1307
  def bundle(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
@@ -1288,7 +1347,7 @@ module Squared
1288
1347
  end
1289
1348
  opts.concat(args)
1290
1349
  when :plugin
1291
- case (plu = args.shift || opts.shift)
1350
+ case (plu = args.shift)
1292
1351
  when 'install', 'uninstall', 'help', 'list'
1293
1352
  cmd << plu
1294
1353
  else
@@ -1298,7 +1357,7 @@ module Squared
1298
1357
  op = OptionPartition.new(opts, bundleopts(if pre == 'ssl'
1299
1358
  :doctor_ssl
1300
1359
  elsif plu
1301
- plu == 'install' ? :plugin : :"plugin_#{plu}"
1360
+ plu == 'install' ? :plugin : symjoin('plugin', plu, char: '_')
1302
1361
  else
1303
1362
  flag
1304
1363
  end),
@@ -1306,7 +1365,6 @@ module Squared
1306
1365
  project: self, no: OPT_BUNDLE[:no][flag], args: flag == :exec || flag == :config)
1307
1366
  op.concat(args) unless pre
1308
1367
  output = false
1309
- invalid = ->(a) { raise_error ArgumentError, "unrecognized args: #{a.join(', ')}", hint: flag }
1310
1368
  case flag
1311
1369
  when :config
1312
1370
  if pre == 'list'
@@ -1352,7 +1410,7 @@ module Squared
1352
1410
  else
1353
1411
  val = readline('Enter arguments', force: false)
1354
1412
  op << (val.empty? ? 'list' : val)
1355
- output = val.match?(/^(un)?set/)
1413
+ output = val.start_with?('set', 'unset')
1356
1414
  end
1357
1415
  when :plugin
1358
1416
  case plu
@@ -1380,8 +1438,9 @@ module Squared
1380
1438
  op.clear
1381
1439
  end
1382
1440
  print_run(op, banner, **kwargs)
1383
- run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception), from: :"bundle:#{flag}")
1384
- .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)
1385
1444
  end
1386
1445
 
1387
1446
  def rake(*args, sync: true, banner: verbose?, with: nil, pass: PASS_RUBY[:rake], **kwargs)
@@ -1392,26 +1451,26 @@ module Squared
1392
1451
  op.append(escape: true)
1393
1452
  print_run(op, banner, **kwargs)
1394
1453
  var = { 'BANNER' => '0' } unless banner
1395
- 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)
1396
1455
  end
1397
1456
 
1398
1457
  def irb(*args, banner: verbose?, with: nil, pass: PASS_RUBY[:irb], **kwargs)
1399
1458
  opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
1400
1459
  op = OptionPartition.new(opts, OPT_RUBY[:irb], session('irb'), project: self, first: [/\.rb$/])
1401
1460
  r = []
1402
- r << 'bundler/setup' unless op.arg?('r')
1403
- 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]
1404
1463
  r.each { |val| op.add_option('r', val, merge: true) }
1405
- 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) })
1406
1465
  op.concat(args)
1407
1466
  op.append(delim: true)
1408
1467
  print_run(op, banner, **kwargs)
1409
- run(banner: false, exception: kwargs.fetch(:exception, exception), from: :irb)
1468
+ run(banner: false, exception: kwargs.fetch(:exception, exception?), from: :irb)
1410
1469
  end
1411
1470
 
1412
1471
  def rbs(flag, *args, banner: verbose?, with: nil, pass: nil, **kwargs)
1413
1472
  case pass
1414
- when NilClass
1473
+ when nil
1415
1474
  pass = PASS_RUBY[:rbs]
1416
1475
  when Array
1417
1476
  pass += PASS_RUBY[:rbs]
@@ -1424,7 +1483,7 @@ module Squared
1424
1483
  sig = args.shift
1425
1484
  y = option('y', ignore: false)
1426
1485
  i = 1
1427
- 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|
1428
1487
  dir = basepath sig, file.dirname
1429
1488
  dir.mkpath unless dir.exist?
1430
1489
  base = file.basename.to_s
@@ -1440,8 +1499,8 @@ module Squared
1440
1499
  end
1441
1500
  end
1442
1501
  unless status == 'ignored'
1443
- ret = run(op.target.temp(File.extname(base) == '.rbi' ? 'rbi' : 'rb', file, '>', rbs), banner: false,
1444
- series: true)
1502
+ ret = run(op.target.temp(File.extname(base) == '.rbi' ? 'rbi' : 'rb', file, '>', rbs),
1503
+ banner: false, series: true)
1445
1504
  if !ret
1446
1505
  status = 'FAIL'
1447
1506
  elsif File.empty?(rbs)
@@ -1455,7 +1514,7 @@ module Squared
1455
1514
  op.clear
1456
1515
  .append(*args)
1457
1516
  print_run(op, banner, **kwargs)
1458
- run(banner: false, exception: kwargs.fetch(:exception, exception), from: :"rbs:#{flag}")
1517
+ run(banner: false, exception: kwargs.fetch(:exception, exception?), from: symjoin('rbs', flag))
1459
1518
  end
1460
1519
  end
1461
1520
 
@@ -1474,12 +1533,11 @@ module Squared
1474
1533
  op.errors << val
1475
1534
  end
1476
1535
  end
1477
- op.swap
1478
- .map! { |val| basepath(val).relative_path_from(path) }
1536
+ op.swap.map! { |val| basepath(val).relative_path_from(path) }
1479
1537
  op.append(delim: true)
1480
1538
  .clear(errors: true)
1481
1539
  print_run(op, banner, **kwargs)
1482
- 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)
1483
1541
  end
1484
1542
 
1485
1543
  def gemspec
@@ -1504,7 +1562,7 @@ module Squared
1504
1562
  def copy?
1505
1563
  return true if @copy.is_a?(Hash) ? copy[:into] : super
1506
1564
  return gemdir? if gemdir
1507
- return false unless autodetect
1565
+ return false unless @autodetect
1508
1566
 
1509
1567
  set = lambda do |val, path|
1510
1568
  base = Pathname.new(path.strip)
@@ -1517,27 +1575,27 @@ module Squared
1517
1575
  end
1518
1576
  if version
1519
1577
  begin
1520
- case autodetect
1578
+ case @autodetect
1521
1579
  when 'rvm'
1522
1580
  pwd_set { `rvm info homes` }[/^\s+gem:\s+"(.+)"$/, 1]
1523
1581
  when 'rbenv'
1524
1582
  if pwd_set { `rbenv which ruby` } =~ %r{^(.+[\\/]versions[\\/](\d\.\d)\.[^\\/]+)[\\/]bin[\\/]ruby$}
1525
1583
  File.join($1, 'lib/ruby/gems', "#{$2}.0")
1526
1584
  end
1527
- when 'asdf'
1528
- pwd_set { `asdf where ruby`.chomp }.yield_self do |val|
1529
- val =~ /(\d\.\d)\.[^.]+$/ && File.join(val, 'lib/ruby/gems', "#{$1}.0")
1530
- end
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)\.[^.]+$/
1531
1588
  when /bundler?/
1532
1589
  pwd_set { `bundle env` }[/^\s+Gem Path\s+(.+)$/, 1].split(File::PATH_SEPARATOR).find do |val|
1533
1590
  Dir.exist?(File.join(val, 'gems'))
1534
1591
  end
1535
1592
  else
1536
1593
  ENV['GEM_HOME'] || ENV['GEM_ROOT']
1537
- end.tap do |val|
1594
+ end
1595
+ .tap do |val|
1538
1596
  return true if val && set.call(version, val)
1539
1597
  end
1540
- rescue StandardError => e
1598
+ rescue => e
1541
1599
  log.debug e
1542
1600
  end
1543
1601
  pwd_set(pass: !gempwd.nil?) do
@@ -1571,11 +1629,11 @@ module Squared
1571
1629
  raise_error Errno::ENOENT, 'gems home'
1572
1630
  end
1573
1631
  end
1574
- rescue StandardError => e
1632
+ rescue => e
1575
1633
  log.error e
1576
1634
  self.version = nil
1577
1635
  @gemdir = nil
1578
- self.autodetect = false
1636
+ @autodetect = false
1579
1637
  else
1580
1638
  gemdir?
1581
1639
  end
@@ -1586,10 +1644,6 @@ module Squared
1586
1644
 
1587
1645
  private
1588
1646
 
1589
- def run_rb(**kwargs)
1590
- run(banner: !@session&.include?('--quiet'), **kwargs)
1591
- end
1592
-
1593
1647
  def ruby_session(*cmd, **kwargs)
1594
1648
  session('ruby', *preopts, *cmd, **kwargs)
1595
1649
  end
@@ -1599,7 +1653,9 @@ module Squared
1599
1653
  end
1600
1654
 
1601
1655
  def bundle_session(*cmd, **kwargs)
1602
- session('bundle', *cmd, *preopts, **kwargs).tap { append_nocolor }
1656
+ ret = session('bundle', *cmd, *preopts, **kwargs)
1657
+ append_nocolor
1658
+ ret
1603
1659
  end
1604
1660
 
1605
1661
  def rake_session(*cmd, **kwargs)
@@ -1629,22 +1685,47 @@ module Squared
1629
1685
  session_output('rake', *cmd, **kwargs)
1630
1686
  end
1631
1687
 
1632
- def config_get(key)
1633
- out = pwd_set { `#{bundle_output('config get --parseable', key)}`.chomp }
1634
- 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
1635
1703
 
1636
- case (out = $2)
1637
- when 'true'
1638
- true
1639
- when '', '[]'
1640
- nil
1641
- else
1642
- if out =~ /\A\[:(.+)\]\z/
1643
- $1.split(', :').map { |val| ((val.delete_prefix!('"') && val.delete_suffix!('"')) || val).to_sym }
1644
- else
1645
- 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
1646
1726
  end
1647
1727
  end
1728
+ ret unless first && ret.empty?
1648
1729
  end
1649
1730
 
1650
1731
  def config_set(key, *val, global: false)
@@ -1652,9 +1733,11 @@ module Squared
1652
1733
  end
1653
1734
 
1654
1735
  def unpack_get(tag, ext)
1655
- return super unless ext == 'gem'
1656
-
1657
- "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
1658
1741
  end
1659
1742
 
1660
1743
  def preopts
@@ -1681,9 +1764,8 @@ module Squared
1681
1764
 
1682
1765
  def raketasks
1683
1766
  @raketasks ||= [].tap do |ret|
1684
- opt = rakepwd
1685
- pwd_set(pass: !opt.nil?) do
1686
- 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|
1687
1769
  next unless line =~ /^rake ((?:[^\[: ]+:?)+)(\[[^\]]+\])?/
1688
1770
 
1689
1771
  ret << [$1, $2]
@@ -1698,12 +1780,15 @@ module Squared
1698
1780
  args << :install_a
1699
1781
  when :add, :plugin
1700
1782
  args << :common_git
1701
- when :binstubs, :cache
1702
- args << :common_all
1703
1783
  when :lock, :outdated
1704
1784
  args << :common_version
1705
1785
  end
1706
- 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
1707
1792
  end
1708
1793
 
1709
1794
  def gemopts(*args)
@@ -1722,8 +1807,15 @@ module Squared
1722
1807
  case args.first
1723
1808
  when :info, :list, :search
1724
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, [])
1725
1818
  end
1726
- OPT_GEM[:common] + args.flat_map { |name| OPT_GEM.fetch(name, []) }
1727
1819
  end
1728
1820
 
1729
1821
  def gempwd
@@ -1746,7 +1838,7 @@ module Squared
1746
1838
  if (spec = gemspec)
1747
1839
  lib.merge(spec.require_paths || [])
1748
1840
  end
1749
- lib.select { |file| exist?(file) }
1841
+ lib.select { |file| exist?(file, type: 'd') }
1750
1842
  end
1751
1843
  end
1752
1844
 
@@ -1761,6 +1853,26 @@ module Squared
1761
1853
 
1762
1854
  gemdir.exist? && !gemdir.empty? && gemdir.writable?
1763
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
1764
1876
  end
1765
1877
 
1766
1878
  Application.implement Ruby