squared 0.6.17 → 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.
@@ -8,7 +8,7 @@ module Squared
8
8
  GEMNAME = /\A[A-Za-z\d][A-Za-z\d_.-]*\z/.freeze
9
9
  DIR_RUBY = (GEMFILE + Rake::Application::DEFAULT_RAKEFILES + ['README.rdoc']).freeze
10
10
  OPT_RUBY = {
11
- ruby: %w[a c l n p s S w 0=im? C=pm e=q E=bm F=qm i=bm? I=pm r=bm W=bm? x=pm? d|debug jit rjit verbose
11
+ ruby: %w[0=im? a c C=pm e=q E=bm F=qm i=bm? I=pm l n p r=bm s S w W=bm? x=pm? d|debug jit rjit verbose
12
12
  y|yydebug backtrace-limit=i crash-report=q disable=q dump=q enable=q encoding=b external-encoding=b
13
13
  internal-encoding=b parser=b].freeze,
14
14
  rake: %w[A|all B|build-all comments n|dry-run m|multitask P|prereqs q|quiet X|no-deprecation-warnings
@@ -21,7 +21,7 @@ module Squared
21
21
  nomultiline noprompt noscript nosingleline noverbose regexp-completor sample-book-mode script
22
22
  simple-prompt single-irb singleline tracer truncate-echo-on-assignment type-completor verbose
23
23
  back-trace-limit=i context-mode=i prompt=b prompt-mode=b].freeze,
24
- rbs: %w[I=pm r=bm no-collection no-stdlib collection=p log-level=b log-output=p repo=p].freeze,
24
+ rbs: %w[I=pm r=bm no-stdlib no-collection collection=p log-level=b log-output=p repo=p].freeze,
25
25
  rubocop: %w[D P r=bm auto-gen-config a|autocorrect A|autocorrect-all d|debug disable-pending-cops
26
26
  display-only-correctable display-only-fail-level-offenses display-only-failed
27
27
  display-only-safe-correctable S|display-style-guide display-time editor-mode enable-pending-cops
@@ -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+)?/ ? $&.sub(/^([a-z]+)-/i, '\1 ') : s }
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,20 +983,19 @@ 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 = []
937
997
  filter = kwargs.fetch(:filter, {})
938
998
  semver = filter[:semver]
939
- dryrun = filter[:dryrun] || dryrun?(prefix: 'gem')
940
999
  update = if sync && filter[:select]
941
1000
  semver ||= 'major'
942
1001
  items = []
@@ -950,7 +1009,7 @@ module Squared
950
1009
  []
951
1010
  end
952
1011
  out = ->(val) { sync ? puts(val) : buffer << val }
953
- pwd_set(pass: !gempwd.nil?, from: from) do
1012
+ pwd_set(op.target, from: from, pass: !gempwd.nil?) do
954
1013
  rows = [[%w[Gem Current Latest], nil]]
955
1014
  IO.popen(cmd).each do |line|
956
1015
  if line =~ /^(\S+) \((\S+) < ([^)]+)\)$/
@@ -1006,7 +1065,7 @@ module Squared
1006
1065
  unless stdin?
1007
1066
  sub_style! g, theme[:major]
1008
1067
  styles = %i[green bold]
1009
- pat = (pat.first if pre)
1068
+ pat = pre ? pat.first : nil
1010
1069
  latest << :bold
1011
1070
  end
1012
1071
  major[0] += 1
@@ -1045,10 +1104,9 @@ module Squared
1045
1104
  out.call(queue)
1046
1105
  queue = nil
1047
1106
  end
1048
- s = ('%s %s %s' % [g, b, h]).yield_self do |val|
1049
- items&.push([val, a])
1050
- '%*s %s' % [pad, "#{j}.", val]
1051
- end
1107
+ s = '%s %s %s' % [g, b, h]
1108
+ items&.push([s, a])
1109
+ s = '%*s %s' % [pad, "#{j}.", s]
1052
1110
  if ia
1053
1111
  unless confirm_semver(s, type)
1054
1112
  update.delete(a)
@@ -1070,7 +1128,7 @@ module Squared
1070
1128
  else
1071
1129
  if items
1072
1130
  update = choice('Select a package', items.map(&:first),
1073
- 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 }
1074
1132
  end
1075
1133
  unless Array(update).empty?
1076
1134
  opts = ['f']
@@ -1090,7 +1148,7 @@ module Squared
1090
1148
  'user-install'
1091
1149
  end
1092
1150
  end
1093
- if dryrun
1151
+ if filter[:dryrun]
1094
1152
  print_run gem_output('update -f', *update.quote!), false
1095
1153
  else
1096
1154
  gem(:update, *update, opts: opts)
@@ -1109,7 +1167,6 @@ module Squared
1109
1167
  when :dependency, :environment, :list, :search, :specification, :which
1110
1168
  op.concat(args)
1111
1169
  end
1112
- ia = op.remove(':')
1113
1170
  op.each do |opt|
1114
1171
  if gems && !opt.start_with?('-') && !opt.match?(GEMNAME)
1115
1172
  op.errors << opt
@@ -1132,8 +1189,8 @@ module Squared
1132
1189
  .clear(pass: false)
1133
1190
  end
1134
1191
  when :push
1135
- if op.empty? || ia
1136
- file = basepath(if !ia && (spec = gemspec)
1192
+ if op.empty? || (n = op.index(':'))
1193
+ file = basepath(if !n && (spec = gemspec)
1137
1194
  "#{spec.name}-#{spec.version}.gem"
1138
1195
  else
1139
1196
  choice_index 'Select a file', Dir.glob('*.gem', base: path)
@@ -1144,7 +1201,7 @@ module Squared
1144
1201
  raise_error ArgumentError, "unrecognized args: #{op.join(', ')}", hint: flag unless op.empty?
1145
1202
  end
1146
1203
  op.add_path(file)
1147
- return run_rb(from: from, interactive: ['Push', 'N', gemname]) unless with || !banner
1204
+ return run(from: from, interactive: ['Push', 'N', gemname]) unless with || !banner
1148
1205
  when :exec
1149
1206
  min = if op.arg?('g', 'gem')
1150
1207
  1
@@ -1158,9 +1215,7 @@ module Squared
1158
1215
  0
1159
1216
  end
1160
1217
  op.concat(args)
1161
- if (args = command_args(op.extras, min: min, force: min == 1 && op.empty?))
1162
- op.push(args)
1163
- end
1218
+ command_args(op.extras, min: min, force: min == 1 && op.empty?)
1164
1219
  op.append(quote: false)
1165
1220
  when :update
1166
1221
  if !op.arg?('n', 'bindir') && (bin = config_get('bin')) && Dir.exist?(bin)
@@ -1173,7 +1228,7 @@ module Squared
1173
1228
  end
1174
1229
  when :install, :uninstall, :pristine
1175
1230
  if flag == :install
1176
- post = if ia
1231
+ post = if op.remove(':')
1177
1232
  op.concat(args)
1178
1233
  readline('Enter command [args]', force: true)
1179
1234
  elsif op.empty?
@@ -1191,14 +1246,15 @@ module Squared
1191
1246
  else
1192
1247
  op.clear
1193
1248
  end
1194
- elsif (n = op.index { |val| val.match?(/(\A|[\w.-])@\d/) })
1249
+ elsif (n = op.index { |val| val.match?(/(\A|[a-z])@\d/) })
1195
1250
  name = op.remove_at(n)
1196
- pre, ver = if (n = name.index('@')) == 0
1251
+ n = name.index('@')
1252
+ pre, ver = if n == 0
1197
1253
  [gemname, name[1..-1]]
1198
1254
  else
1199
1255
  [name[0, n], name[n.succ..-1]]
1200
1256
  end
1201
- op.adjoin(pre, quote_option('version', ver))
1257
+ op.adjoin(pre, basic_option('version', ver))
1202
1258
  .clear
1203
1259
  end
1204
1260
  if flag == :install
@@ -1244,7 +1300,8 @@ module Squared
1244
1300
  end
1245
1301
  op.clear(errors: true) if gems
1246
1302
  print_run(op, banner, **kwargs)
1247
- 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)
1248
1305
  end
1249
1306
 
1250
1307
  def bundle(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
@@ -1290,7 +1347,7 @@ module Squared
1290
1347
  end
1291
1348
  opts.concat(args)
1292
1349
  when :plugin
1293
- case (plu = args.shift || opts.shift)
1350
+ case (plu = args.shift)
1294
1351
  when 'install', 'uninstall', 'help', 'list'
1295
1352
  cmd << plu
1296
1353
  else
@@ -1300,7 +1357,7 @@ module Squared
1300
1357
  op = OptionPartition.new(opts, bundleopts(if pre == 'ssl'
1301
1358
  :doctor_ssl
1302
1359
  elsif plu
1303
- plu == 'install' ? :plugin : :"plugin_#{plu}"
1360
+ plu == 'install' ? :plugin : symjoin('plugin', plu, char: '_')
1304
1361
  else
1305
1362
  flag
1306
1363
  end),
@@ -1308,7 +1365,6 @@ module Squared
1308
1365
  project: self, no: OPT_BUNDLE[:no][flag], args: flag == :exec || flag == :config)
1309
1366
  op.concat(args) unless pre
1310
1367
  output = false
1311
- invalid = ->(a) { raise_error ArgumentError, "unrecognized args: #{a.join(', ')}", hint: flag }
1312
1368
  case flag
1313
1369
  when :config
1314
1370
  if pre == 'list'
@@ -1354,7 +1410,7 @@ module Squared
1354
1410
  else
1355
1411
  val = readline('Enter arguments', force: false)
1356
1412
  op << (val.empty? ? 'list' : val)
1357
- output = val.match?(/^(un)?set/)
1413
+ output = val.start_with?('set', 'unset')
1358
1414
  end
1359
1415
  when :plugin
1360
1416
  case plu
@@ -1382,8 +1438,9 @@ module Squared
1382
1438
  op.clear
1383
1439
  end
1384
1440
  print_run(op, banner, **kwargs)
1385
- run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception), from: :"bundle:#{flag}")
1386
- .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)
1387
1444
  end
1388
1445
 
1389
1446
  def rake(*args, sync: true, banner: verbose?, with: nil, pass: PASS_RUBY[:rake], **kwargs)
@@ -1394,26 +1451,26 @@ module Squared
1394
1451
  op.append(escape: true)
1395
1452
  print_run(op, banner, **kwargs)
1396
1453
  var = { 'BANNER' => '0' } unless banner
1397
- 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)
1398
1455
  end
1399
1456
 
1400
1457
  def irb(*args, banner: verbose?, with: nil, pass: PASS_RUBY[:irb], **kwargs)
1401
1458
  opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
1402
1459
  op = OptionPartition.new(opts, OPT_RUBY[:irb], session('irb'), project: self, first: [/\.rb$/])
1403
1460
  r = []
1404
- r << 'bundler/setup' unless op.arg?('r')
1405
- 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]
1406
1463
  r.each { |val| op.add_option('r', val, merge: true) }
1407
- 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) })
1408
1465
  op.concat(args)
1409
1466
  op.append(delim: true)
1410
1467
  print_run(op, banner, **kwargs)
1411
- run(banner: false, exception: kwargs.fetch(:exception, exception), from: :irb)
1468
+ run(banner: false, exception: kwargs.fetch(:exception, exception?), from: :irb)
1412
1469
  end
1413
1470
 
1414
1471
  def rbs(flag, *args, banner: verbose?, with: nil, pass: nil, **kwargs)
1415
1472
  case pass
1416
- when NilClass
1473
+ when nil
1417
1474
  pass = PASS_RUBY[:rbs]
1418
1475
  when Array
1419
1476
  pass += PASS_RUBY[:rbs]
@@ -1426,7 +1483,7 @@ module Squared
1426
1483
  sig = args.shift
1427
1484
  y = option('y', ignore: false)
1428
1485
  i = 1
1429
- 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|
1430
1487
  dir = basepath sig, file.dirname
1431
1488
  dir.mkpath unless dir.exist?
1432
1489
  base = file.basename.to_s
@@ -1442,8 +1499,8 @@ module Squared
1442
1499
  end
1443
1500
  end
1444
1501
  unless status == 'ignored'
1445
- ret = run(op.target.temp(File.extname(base) == '.rbi' ? 'rbi' : 'rb', file, '>', rbs), banner: false,
1446
- series: true)
1502
+ ret = run(op.target.temp(File.extname(base) == '.rbi' ? 'rbi' : 'rb', file, '>', rbs),
1503
+ banner: false, series: true)
1447
1504
  if !ret
1448
1505
  status = 'FAIL'
1449
1506
  elsif File.empty?(rbs)
@@ -1457,7 +1514,7 @@ module Squared
1457
1514
  op.clear
1458
1515
  .append(*args)
1459
1516
  print_run(op, banner, **kwargs)
1460
- run(banner: false, exception: kwargs.fetch(:exception, exception), from: :"rbs:#{flag}")
1517
+ run(banner: false, exception: kwargs.fetch(:exception, exception?), from: symjoin('rbs', flag))
1461
1518
  end
1462
1519
  end
1463
1520
 
@@ -1476,12 +1533,11 @@ module Squared
1476
1533
  op.errors << val
1477
1534
  end
1478
1535
  end
1479
- op.swap
1480
- .map! { |val| basepath(val).relative_path_from(path) }
1536
+ op.swap.map! { |val| basepath(val).relative_path_from(path) }
1481
1537
  op.append(delim: true)
1482
1538
  .clear(errors: true)
1483
1539
  print_run(op, banner, **kwargs)
1484
- 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)
1485
1541
  end
1486
1542
 
1487
1543
  def gemspec
@@ -1506,7 +1562,7 @@ module Squared
1506
1562
  def copy?
1507
1563
  return true if @copy.is_a?(Hash) ? copy[:into] : super
1508
1564
  return gemdir? if gemdir
1509
- return false unless autodetect
1565
+ return false unless @autodetect
1510
1566
 
1511
1567
  set = lambda do |val, path|
1512
1568
  base = Pathname.new(path.strip)
@@ -1519,27 +1575,27 @@ module Squared
1519
1575
  end
1520
1576
  if version
1521
1577
  begin
1522
- case autodetect
1578
+ case @autodetect
1523
1579
  when 'rvm'
1524
1580
  pwd_set { `rvm info homes` }[/^\s+gem:\s+"(.+)"$/, 1]
1525
1581
  when 'rbenv'
1526
1582
  if pwd_set { `rbenv which ruby` } =~ %r{^(.+[\\/]versions[\\/](\d\.\d)\.[^\\/]+)[\\/]bin[\\/]ruby$}
1527
1583
  File.join($1, 'lib/ruby/gems', "#{$2}.0")
1528
1584
  end
1529
- when 'asdf'
1530
- pwd_set { `asdf where ruby`.chomp }.yield_self do |val|
1531
- val =~ /(\d\.\d)\.[^.]+$/ && File.join(val, 'lib/ruby/gems', "#{$1}.0")
1532
- 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)\.[^.]+$/
1533
1588
  when /bundler?/
1534
1589
  pwd_set { `bundle env` }[/^\s+Gem Path\s+(.+)$/, 1].split(File::PATH_SEPARATOR).find do |val|
1535
1590
  Dir.exist?(File.join(val, 'gems'))
1536
1591
  end
1537
1592
  else
1538
1593
  ENV['GEM_HOME'] || ENV['GEM_ROOT']
1539
- end.tap do |val|
1594
+ end
1595
+ .tap do |val|
1540
1596
  return true if val && set.call(version, val)
1541
1597
  end
1542
- rescue StandardError => e
1598
+ rescue => e
1543
1599
  log.debug e
1544
1600
  end
1545
1601
  pwd_set(pass: !gempwd.nil?) do
@@ -1573,11 +1629,11 @@ module Squared
1573
1629
  raise_error Errno::ENOENT, 'gems home'
1574
1630
  end
1575
1631
  end
1576
- rescue StandardError => e
1632
+ rescue => e
1577
1633
  log.error e
1578
1634
  self.version = nil
1579
1635
  @gemdir = nil
1580
- self.autodetect = false
1636
+ @autodetect = false
1581
1637
  else
1582
1638
  gemdir?
1583
1639
  end
@@ -1588,10 +1644,6 @@ module Squared
1588
1644
 
1589
1645
  private
1590
1646
 
1591
- def run_rb(**kwargs)
1592
- run(banner: !@session&.include?('--quiet'), **kwargs)
1593
- end
1594
-
1595
1647
  def ruby_session(*cmd, **kwargs)
1596
1648
  session('ruby', *preopts, *cmd, **kwargs)
1597
1649
  end
@@ -1601,7 +1653,9 @@ module Squared
1601
1653
  end
1602
1654
 
1603
1655
  def bundle_session(*cmd, **kwargs)
1604
- session('bundle', *cmd, *preopts, **kwargs).tap { append_nocolor }
1656
+ ret = session('bundle', *cmd, *preopts, **kwargs)
1657
+ append_nocolor
1658
+ ret
1605
1659
  end
1606
1660
 
1607
1661
  def rake_session(*cmd, **kwargs)
@@ -1631,22 +1685,47 @@ module Squared
1631
1685
  session_output('rake', *cmd, **kwargs)
1632
1686
  end
1633
1687
 
1634
- def config_get(key)
1635
- out = pwd_set { `#{bundle_output('config get --parseable', key)}`.chomp }
1636
- 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
1637
1695
 
1638
- case (out = $2)
1639
- when 'true'
1640
- true
1641
- when '', '[]'
1642
- nil
1643
- else
1644
- if out =~ /\A\[:(.+)\]\z/
1645
- $1.split(', :').map { |val| ((val.delete_prefix!('"') && val.delete_suffix!('"')) || val).to_sym }
1646
- else
1647
- out || false
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
1703
+
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
1648
1726
  end
1649
1727
  end
1728
+ ret unless first && ret.empty?
1650
1729
  end
1651
1730
 
1652
1731
  def config_set(key, *val, global: false)
@@ -1654,9 +1733,11 @@ module Squared
1654
1733
  end
1655
1734
 
1656
1735
  def unpack_get(tag, ext)
1657
- return super unless ext == 'gem'
1658
-
1659
- "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
1660
1741
  end
1661
1742
 
1662
1743
  def preopts
@@ -1683,9 +1764,8 @@ module Squared
1683
1764
 
1684
1765
  def raketasks
1685
1766
  @raketasks ||= [].tap do |ret|
1686
- opt = rakepwd
1687
- pwd_set(pass: !opt.nil?) do
1688
- 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|
1689
1769
  next unless line =~ /^rake ((?:[^\[: ]+:?)+)(\[[^\]]+\])?/
1690
1770
 
1691
1771
  ret << [$1, $2]
@@ -1700,12 +1780,15 @@ module Squared
1700
1780
  args << :install_a
1701
1781
  when :add, :plugin
1702
1782
  args << :common_git
1703
- when :binstubs, :cache
1704
- args << :common_all
1705
1783
  when :lock, :outdated
1706
1784
  args << :common_version
1707
1785
  end
1708
- 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
1709
1792
  end
1710
1793
 
1711
1794
  def gemopts(*args)
@@ -1724,8 +1807,15 @@ module Squared
1724
1807
  case args.first
1725
1808
  when :info, :list, :search
1726
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, [])
1727
1818
  end
1728
- OPT_GEM[:common] + args.flat_map { |name| OPT_GEM.fetch(name, []) }
1729
1819
  end
1730
1820
 
1731
1821
  def gempwd
@@ -1748,7 +1838,7 @@ module Squared
1748
1838
  if (spec = gemspec)
1749
1839
  lib.merge(spec.require_paths || [])
1750
1840
  end
1751
- lib.select { |file| exist?(file) }
1841
+ lib.select { |file| exist?(file, type: 'd') }
1752
1842
  end
1753
1843
  end
1754
1844
 
@@ -1763,6 +1853,26 @@ module Squared
1763
1853
 
1764
1854
  gemdir.exist? && !gemdir.empty? && gemdir.writable?
1765
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
1766
1876
  end
1767
1877
 
1768
1878
  Application.implement Ruby