squared 0.6.13 → 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,118 +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 =~ SEM_VER) == 0 ? "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
- exit 1 unless (cur = `asdf current #{opt.join(' ')}`[/^\S+\s+\S+/, 0])
826
- cur.sub(/\s+/, ' ')
827
- else
828
- ver = nil
829
- `ruby --version`
830
- end
831
- end)
832
- break if workspace.windows?
833
-
834
- unless val.empty?
835
- out << trim.call(case cmd
836
- when 'chruby.sh'
837
- `#{chruby.with('chruby --version')}`.sub(':', '')
838
- when 'asdf'
839
- "asdf #{`asdf version`.delete_prefix('v')}"
840
- else
841
- `#{cmd} --version`
842
- end)
843
- end
844
- begin
845
- out << ('which %s' % case cmd
846
- when 'rbenv'
847
- `rbenv which ruby`
848
- when 'chruby.sh'
849
- `#{chruby.with('which ruby')}`
850
- when 'asdf'
851
- `asdf which #{@asdf.first}`
852
- else
853
- `which ruby`
854
- end)
855
- rescue StandardError => e
856
- log.debug e
857
- end
858
- if ver
859
- path.ascend do |ent|
860
- next unless (ent += ver).exist?
861
-
862
- hint = File.read(ent).lines(chomp: true).reject(&:empty?).join(', ') rescue nil
863
- out << message("found #{ent}", hint: hint)
864
- end
865
- end
866
- break
867
- end
868
- out.map!(&:split)
869
- pad = out.map(&:first).map!(&:size).max
870
- print_item
871
- puts(out.map! { |line| '%*s %s' % [pad, line.first, line[1..-1].join(' ')] })
872
- end
873
- 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
874
939
  end
875
940
  opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
876
941
  op = OptionPartition.new(opts, OPT_RUBY[:ruby], ruby_session, project: self, multiple: [/^-e/], args: true,
@@ -895,13 +960,8 @@ module Squared
895
960
  op.append(delim: true, escape: kwargs.fetch(:escape, false), quote: kwargs.fetch(:quote, false))
896
961
  end
897
962
  end
898
- from = if flag
899
- :"ruby:#{flag}"
900
- else
901
- print_run(op, banner, **kwargs)
902
- :ruby
903
- end
904
- 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)
905
965
  end
906
966
 
907
967
  def gem(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
@@ -923,14 +983,14 @@ module Squared
923
983
  end
924
984
  op = OptionPartition.new(opts, gemopts(flag), gem_session(flag),
925
985
  project: self, no: OPT_GEM[:no][flag == :update ? :install : flag])
926
- from = :"gem:#{flag}"
986
+ from = symjoin 'gem', flag
927
987
  if flag == :outdated
928
988
  op.adjoin(gempwd, start: 0) if gempwd
929
989
  op.clear
930
990
  cmd = session_done op.target
931
991
  log.info cmd
932
992
  on :first, from
933
- banner = format_banner(cmd)
993
+ banner = format_banner cmd
934
994
  print_item banner if sync
935
995
  major = [0, 0, 0]
936
996
  buffer = []
@@ -949,7 +1009,7 @@ module Squared
949
1009
  []
950
1010
  end
951
1011
  out = ->(val) { sync ? puts(val) : buffer << val }
952
- pwd_set(pass: !gempwd.nil?, from: from) do
1012
+ pwd_set(op.target, from: from, pass: !gempwd.nil?) do
953
1013
  rows = [[%w[Gem Current Latest], nil]]
954
1014
  IO.popen(cmd).each do |line|
955
1015
  if line =~ /^(\S+) \((\S+) < ([^)]+)\)$/
@@ -1005,7 +1065,7 @@ module Squared
1005
1065
  unless stdin?
1006
1066
  sub_style! g, theme[:major]
1007
1067
  styles = %i[green bold]
1008
- pat = (pat.first if pre)
1068
+ pat = pre ? pat.first : nil
1009
1069
  latest << :bold
1010
1070
  end
1011
1071
  major[0] += 1
@@ -1044,10 +1104,9 @@ module Squared
1044
1104
  out.call(queue)
1045
1105
  queue = nil
1046
1106
  end
1047
- s = ('%s %s %s' % [g, b, h]).yield_self do |val|
1048
- items&.push([val, a])
1049
- '%*s %s' % [pad, "#{j}.", val]
1050
- end
1107
+ s = '%s %s %s' % [g, b, h]
1108
+ items&.push([s, a])
1109
+ s = '%*s %s' % [pad, "#{j}.", s]
1051
1110
  if ia
1052
1111
  unless confirm_semver(s, type)
1053
1112
  update.delete(a)
@@ -1069,7 +1128,7 @@ module Squared
1069
1128
  else
1070
1129
  if items
1071
1130
  update = choice('Select a package', items.map(&:first),
1072
- 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 }
1073
1132
  end
1074
1133
  unless Array(update).empty?
1075
1134
  opts = ['f']
@@ -1108,7 +1167,6 @@ module Squared
1108
1167
  when :dependency, :environment, :list, :search, :specification, :which
1109
1168
  op.concat(args)
1110
1169
  end
1111
- ia = op.remove(':')
1112
1170
  op.each do |opt|
1113
1171
  if gems && !opt.start_with?('-') && !opt.match?(GEMNAME)
1114
1172
  op.errors << opt
@@ -1143,7 +1201,7 @@ module Squared
1143
1201
  raise_error ArgumentError, "unrecognized args: #{op.join(', ')}", hint: flag unless op.empty?
1144
1202
  end
1145
1203
  op.add_path(file)
1146
- return run_rb(from: from, interactive: ['Push', 'N', gemname]) unless with || !banner
1204
+ return run(from: from, interactive: ['Push', 'N', gemname]) unless with || !banner
1147
1205
  when :exec
1148
1206
  min = if op.arg?('g', 'gem')
1149
1207
  1
@@ -1157,9 +1215,7 @@ module Squared
1157
1215
  0
1158
1216
  end
1159
1217
  op.concat(args)
1160
- if (args = command_args(op.extras, min: min, force: min == 1 && op.empty?))
1161
- op.push(args)
1162
- end
1218
+ command_args(op.extras, min: min, force: min == 1 && op.empty?)
1163
1219
  op.append(quote: false)
1164
1220
  when :update
1165
1221
  if !op.arg?('n', 'bindir') && (bin = config_get('bin')) && Dir.exist?(bin)
@@ -1172,7 +1228,7 @@ module Squared
1172
1228
  end
1173
1229
  when :install, :uninstall, :pristine
1174
1230
  if flag == :install
1175
- post = if ia
1231
+ post = if op.remove(':')
1176
1232
  op.concat(args)
1177
1233
  readline('Enter command [args]', force: true)
1178
1234
  elsif op.empty?
@@ -1190,14 +1246,15 @@ module Squared
1190
1246
  else
1191
1247
  op.clear
1192
1248
  end
1193
- elsif (n = op.index { |val| val.match?(/(\A|[\w.-])@\d/) })
1249
+ elsif (n = op.index { |val| val.match?(/(\A|[a-z])@\d/) })
1194
1250
  name = op.remove_at(n)
1195
- pre, ver = if (n = name.index('@')) == 0
1251
+ n = name.index('@')
1252
+ pre, ver = if n == 0
1196
1253
  [gemname, name[1..-1]]
1197
1254
  else
1198
1255
  [name[0, n], name[n.succ..-1]]
1199
1256
  end
1200
- op.adjoin(pre, quote_option('version', ver))
1257
+ op.adjoin(pre, basic_option('version', ver))
1201
1258
  .clear
1202
1259
  end
1203
1260
  if flag == :install
@@ -1243,7 +1300,8 @@ module Squared
1243
1300
  end
1244
1301
  op.clear(errors: true) if gems
1245
1302
  print_run(op, banner, **kwargs)
1246
- 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)
1247
1305
  end
1248
1306
 
1249
1307
  def bundle(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
@@ -1289,7 +1347,7 @@ module Squared
1289
1347
  end
1290
1348
  opts.concat(args)
1291
1349
  when :plugin
1292
- case (plu = args.shift || opts.shift)
1350
+ case (plu = args.shift)
1293
1351
  when 'install', 'uninstall', 'help', 'list'
1294
1352
  cmd << plu
1295
1353
  else
@@ -1299,7 +1357,7 @@ module Squared
1299
1357
  op = OptionPartition.new(opts, bundleopts(if pre == 'ssl'
1300
1358
  :doctor_ssl
1301
1359
  elsif plu
1302
- plu == 'install' ? :plugin : :"plugin_#{plu}"
1360
+ plu == 'install' ? :plugin : symjoin('plugin', plu, char: '_')
1303
1361
  else
1304
1362
  flag
1305
1363
  end),
@@ -1307,7 +1365,6 @@ module Squared
1307
1365
  project: self, no: OPT_BUNDLE[:no][flag], args: flag == :exec || flag == :config)
1308
1366
  op.concat(args) unless pre
1309
1367
  output = false
1310
- invalid = ->(a) { raise_error ArgumentError, "unrecognized args: #{a.join(', ')}", hint: flag }
1311
1368
  case flag
1312
1369
  when :config
1313
1370
  if pre == 'list'
@@ -1353,7 +1410,7 @@ module Squared
1353
1410
  else
1354
1411
  val = readline('Enter arguments', force: false)
1355
1412
  op << (val.empty? ? 'list' : val)
1356
- output = val.match?(/^(un)?set/)
1413
+ output = val.start_with?('set', 'unset')
1357
1414
  end
1358
1415
  when :plugin
1359
1416
  case plu
@@ -1381,8 +1438,9 @@ module Squared
1381
1438
  op.clear
1382
1439
  end
1383
1440
  print_run(op, banner, **kwargs)
1384
- run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception), from: :"bundle:#{flag}")
1385
- .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)
1386
1444
  end
1387
1445
 
1388
1446
  def rake(*args, sync: true, banner: verbose?, with: nil, pass: PASS_RUBY[:rake], **kwargs)
@@ -1393,26 +1451,26 @@ module Squared
1393
1451
  op.append(escape: true)
1394
1452
  print_run(op, banner, **kwargs)
1395
1453
  var = { 'BANNER' => '0' } unless banner
1396
- 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)
1397
1455
  end
1398
1456
 
1399
1457
  def irb(*args, banner: verbose?, with: nil, pass: PASS_RUBY[:irb], **kwargs)
1400
1458
  opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
1401
1459
  op = OptionPartition.new(opts, OPT_RUBY[:irb], session('irb'), project: self, first: [/\.rb$/])
1402
1460
  r = []
1403
- r << 'bundler/setup' unless op.arg?('r')
1404
- 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]
1405
1463
  r.each { |val| op.add_option('r', val, merge: true) }
1406
- 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) })
1407
1465
  op.concat(args)
1408
1466
  op.append(delim: true)
1409
1467
  print_run(op, banner, **kwargs)
1410
- run(banner: false, exception: kwargs.fetch(:exception, exception), from: :irb)
1468
+ run(banner: false, exception: kwargs.fetch(:exception, exception?), from: :irb)
1411
1469
  end
1412
1470
 
1413
1471
  def rbs(flag, *args, banner: verbose?, with: nil, pass: nil, **kwargs)
1414
1472
  case pass
1415
- when NilClass
1473
+ when nil
1416
1474
  pass = PASS_RUBY[:rbs]
1417
1475
  when Array
1418
1476
  pass += PASS_RUBY[:rbs]
@@ -1425,7 +1483,7 @@ module Squared
1425
1483
  sig = args.shift
1426
1484
  y = option('y', ignore: false)
1427
1485
  i = 1
1428
- 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|
1429
1487
  dir = basepath sig, file.dirname
1430
1488
  dir.mkpath unless dir.exist?
1431
1489
  base = file.basename.to_s
@@ -1441,8 +1499,8 @@ module Squared
1441
1499
  end
1442
1500
  end
1443
1501
  unless status == 'ignored'
1444
- ret = run(op.target.temp(File.extname(base) == '.rbi' ? 'rbi' : 'rb', file, '>', rbs), banner: false,
1445
- series: true)
1502
+ ret = run(op.target.temp(File.extname(base) == '.rbi' ? 'rbi' : 'rb', file, '>', rbs),
1503
+ banner: false, series: true)
1446
1504
  if !ret
1447
1505
  status = 'FAIL'
1448
1506
  elsif File.empty?(rbs)
@@ -1456,7 +1514,7 @@ module Squared
1456
1514
  op.clear
1457
1515
  .append(*args)
1458
1516
  print_run(op, banner, **kwargs)
1459
- run(banner: false, exception: kwargs.fetch(:exception, exception), from: :"rbs:#{flag}")
1517
+ run(banner: false, exception: kwargs.fetch(:exception, exception?), from: symjoin('rbs', flag))
1460
1518
  end
1461
1519
  end
1462
1520
 
@@ -1475,12 +1533,11 @@ module Squared
1475
1533
  op.errors << val
1476
1534
  end
1477
1535
  end
1478
- op.swap
1479
- .map! { |val| basepath(val).relative_path_from(path) }
1536
+ op.swap.map! { |val| basepath(val).relative_path_from(path) }
1480
1537
  op.append(delim: true)
1481
1538
  .clear(errors: true)
1482
1539
  print_run(op, banner, **kwargs)
1483
- 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)
1484
1541
  end
1485
1542
 
1486
1543
  def gemspec
@@ -1505,7 +1562,7 @@ module Squared
1505
1562
  def copy?
1506
1563
  return true if @copy.is_a?(Hash) ? copy[:into] : super
1507
1564
  return gemdir? if gemdir
1508
- return false unless autodetect
1565
+ return false unless @autodetect
1509
1566
 
1510
1567
  set = lambda do |val, path|
1511
1568
  base = Pathname.new(path.strip)
@@ -1518,27 +1575,27 @@ module Squared
1518
1575
  end
1519
1576
  if version
1520
1577
  begin
1521
- case autodetect
1578
+ case @autodetect
1522
1579
  when 'rvm'
1523
1580
  pwd_set { `rvm info homes` }[/^\s+gem:\s+"(.+)"$/, 1]
1524
1581
  when 'rbenv'
1525
1582
  if pwd_set { `rbenv which ruby` } =~ %r{^(.+[\\/]versions[\\/](\d\.\d)\.[^\\/]+)[\\/]bin[\\/]ruby$}
1526
1583
  File.join($1, 'lib/ruby/gems', "#{$2}.0")
1527
1584
  end
1528
- when 'asdf'
1529
- pwd_set { `asdf where ruby`.chomp }.yield_self do |val|
1530
- val =~ /(\d\.\d)\.[^.]+$/ && File.join(val, 'lib/ruby/gems', "#{$1}.0")
1531
- 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)\.[^.]+$/
1532
1588
  when /bundler?/
1533
1589
  pwd_set { `bundle env` }[/^\s+Gem Path\s+(.+)$/, 1].split(File::PATH_SEPARATOR).find do |val|
1534
1590
  Dir.exist?(File.join(val, 'gems'))
1535
1591
  end
1536
1592
  else
1537
1593
  ENV['GEM_HOME'] || ENV['GEM_ROOT']
1538
- end.tap do |val|
1594
+ end
1595
+ .tap do |val|
1539
1596
  return true if val && set.call(version, val)
1540
1597
  end
1541
- rescue StandardError => e
1598
+ rescue => e
1542
1599
  log.debug e
1543
1600
  end
1544
1601
  pwd_set(pass: !gempwd.nil?) do
@@ -1572,11 +1629,11 @@ module Squared
1572
1629
  raise_error Errno::ENOENT, 'gems home'
1573
1630
  end
1574
1631
  end
1575
- rescue StandardError => e
1632
+ rescue => e
1576
1633
  log.error e
1577
1634
  self.version = nil
1578
1635
  @gemdir = nil
1579
- self.autodetect = false
1636
+ @autodetect = false
1580
1637
  else
1581
1638
  gemdir?
1582
1639
  end
@@ -1587,10 +1644,6 @@ module Squared
1587
1644
 
1588
1645
  private
1589
1646
 
1590
- def run_rb(**kwargs)
1591
- run(banner: !@session&.include?('--quiet'), **kwargs)
1592
- end
1593
-
1594
1647
  def ruby_session(*cmd, **kwargs)
1595
1648
  session('ruby', *preopts, *cmd, **kwargs)
1596
1649
  end
@@ -1600,7 +1653,9 @@ module Squared
1600
1653
  end
1601
1654
 
1602
1655
  def bundle_session(*cmd, **kwargs)
1603
- session('bundle', *cmd, *preopts, **kwargs).tap { append_nocolor }
1656
+ ret = session('bundle', *cmd, *preopts, **kwargs)
1657
+ append_nocolor
1658
+ ret
1604
1659
  end
1605
1660
 
1606
1661
  def rake_session(*cmd, **kwargs)
@@ -1630,22 +1685,47 @@ module Squared
1630
1685
  session_output('rake', *cmd, **kwargs)
1631
1686
  end
1632
1687
 
1633
- def config_get(key)
1634
- out = pwd_set { `#{bundle_output('config get --parseable', key)}`.chomp }
1635
- 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
1636
1703
 
1637
- case (out = $2)
1638
- when 'true'
1639
- true
1640
- when '', '[]'
1641
- nil
1642
- else
1643
- if out =~ /\A\[:(.+)\]\z/
1644
- $1.split(', :').map { |val| ((val.delete_prefix!('"') && val.delete_suffix!('"')) || val).to_sym }
1645
- else
1646
- 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
1647
1726
  end
1648
1727
  end
1728
+ ret unless first && ret.empty?
1649
1729
  end
1650
1730
 
1651
1731
  def config_set(key, *val, global: false)
@@ -1653,9 +1733,11 @@ module Squared
1653
1733
  end
1654
1734
 
1655
1735
  def unpack_get(tag, ext)
1656
- return super unless ext == 'gem'
1657
-
1658
- "https://rubygems.org/downloads/#{tag.sub_ext('.gem')}"
1736
+ if ext == 'gem'
1737
+ "https://rubygems.org/downloads/#{File.basename(tag, '.gem')}.gem"
1738
+ else
1739
+ super
1740
+ end
1659
1741
  end
1660
1742
 
1661
1743
  def preopts
@@ -1682,9 +1764,8 @@ module Squared
1682
1764
 
1683
1765
  def raketasks
1684
1766
  @raketasks ||= [].tap do |ret|
1685
- opt = rakepwd
1686
- pwd_set(pass: !opt.nil?) do
1687
- 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|
1688
1769
  next unless line =~ /^rake ((?:[^\[: ]+:?)+)(\[[^\]]+\])?/
1689
1770
 
1690
1771
  ret << [$1, $2]
@@ -1699,12 +1780,15 @@ module Squared
1699
1780
  args << :install_a
1700
1781
  when :add, :plugin
1701
1782
  args << :common_git
1702
- when :binstubs, :cache
1703
- args << :common_all
1704
1783
  when :lock, :outdated
1705
1784
  args << :common_version
1706
1785
  end
1707
- 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
1708
1792
  end
1709
1793
 
1710
1794
  def gemopts(*args)
@@ -1723,8 +1807,15 @@ module Squared
1723
1807
  case args.first
1724
1808
  when :info, :list, :search
1725
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, [])
1726
1818
  end
1727
- OPT_GEM[:common] + args.flat_map { |name| OPT_GEM.fetch(name, []) }
1728
1819
  end
1729
1820
 
1730
1821
  def gempwd
@@ -1747,7 +1838,7 @@ module Squared
1747
1838
  if (spec = gemspec)
1748
1839
  lib.merge(spec.require_paths || [])
1749
1840
  end
1750
- lib.select { |file| exist?(file) }
1841
+ lib.select { |file| exist?(file, type: 'd') }
1751
1842
  end
1752
1843
  end
1753
1844
 
@@ -1762,6 +1853,26 @@ module Squared
1762
1853
 
1763
1854
  gemdir.exist? && !gemdir.empty? && gemdir.writable?
1764
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
1765
1876
  end
1766
1877
 
1767
1878
  Application.implement Ruby