squared 0.5.18 → 0.6.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.
@@ -5,6 +5,7 @@ module Squared
5
5
  module Project
6
6
  class Ruby < Git
7
7
  GEMFILE = %w[Gemfile Gemfile.lock gem.deps.rb gems.rb Isolate].freeze
8
+ GEMNAME = /\A[A-Za-z\d][A-Za-z\d_.-]*\z/.freeze
8
9
  DIR_RUBY = (GEMFILE + Rake::Application::DEFAULT_RAKEFILES + ['README.rdoc']).freeze
9
10
  OPT_RUBY = {
10
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,8 +13,9 @@ module Squared
12
13
  internal-encoding=b parser=b].freeze,
13
14
  rake: %w[A|all B|build-all comments n|dry-run m|multitask P|prereqs q|quiet X|no-deprecation-warnings
14
15
  N|no-search G|no-system nosearch nosystem rules s|silent g|system v|verbose backtrace=b?
15
- D|describe=q? e|execute=q E|execute-continue=q p|execute-print=q job-stats=b? j|jobs=i? I|libdir=p
16
- R|rakelib=p rakelibdir=p r|require=b suppress-backtrace=q T|tasks=q? t|trace=b? W|where=q?].freeze,
16
+ D|describe=q? e|execute=q E|execute-continue=q p|execute-print=q f|rakefile=p job-stats=b? j|jobs=i?
17
+ I|libdir=p R|rakelib=p rakelibdir=p r|require=b suppress-backtrace=q T|tasks=q? t|trace=b?
18
+ W|where=q?].freeze,
17
19
  irb: %w[d f U w E=b I=p r=b W=im? autocomplete colorize echo echo-on-assignment extra-doc-dir inf-ruby-mode
18
20
  inspect multiline no-pager noautocomplete nocolorize noecho noecho-on-assignment noinspect
19
21
  nomultiline noprompt noscript nosingleline noverbose regexp-completor sample-book-mode script
@@ -22,48 +24,121 @@ module Squared
22
24
  }.freeze
23
25
  OPT_BUNDLE = {
24
26
  common: %w[no-color V|verbose r|retry=i].freeze,
27
+ common_all: %w[all all-platforms path=p].freeze,
28
+ common_git: %w[branch=q git=q path=p ref=q].freeze,
29
+ add: %w[optimistic quiet skip-install strict github=q glob=q g|group=q require=q s|source=q
30
+ v|version=q].freeze,
31
+ binstubs: %w[force standalone shebang=q].freeze,
32
+ cache: %w[frozen no-all no-install no-prune quiet cache-path=p gemfile=p].freeze,
33
+ check: %w[dry-run gemfile=p path=p].freeze,
34
+ clean: %w[dry-run force].freeze,
35
+ config: %w[global local].freeze,
36
+ doctor: %w[quiet gemfile=p].freeze,
37
+ exec: %w[gemfile=p].freeze,
38
+ gem: %w[b|bin git no-exe rubocop ci=b e|edit=p? ext=b github-username=q linter=b t|test=b?].freeze,
39
+ init: %w[gemfile=p gemspec=p].freeze,
25
40
  install: %w[frozen no-cache no-prune system binstubs=p? path=p standalone=q? target-rbconfig=p trust-policy=b
26
41
  with=q without=q].freeze,
27
- install_base: %w[force full-index quiet redownload gemfile=p j|jobs=i].freeze,
28
- update: %w[all conservative local major minor patch pre ruby strict bundler=b? g|group=q source=b].freeze,
42
+ install_a: %w[force full-index quiet redownload gemfile=p j|jobs=i].freeze,
43
+ lock: %w[add-checksums conservative full-index local major minor normalize-platforms patch pre print strict
44
+ add-platform=q bundler=b? gemfile=p lockfile=p remove-platform=p update=q?].freeze,
45
+ open: %w[path=p].freeze,
29
46
  outdated: %w[filter-major filter-minor filter-patch groups local parseable pre only-explicit strict
30
- update-strict group=q source=b].freeze,
31
- exec: %w[gemfile=p].freeze,
32
- cache: %w[all all-platforms frozen no-all no-install no-prune quiet cache-path=p gemfile=p path=p].freeze,
33
- check: %w[dry-run gemfile=p path=p].freeze
47
+ update-strict group=q source=q].freeze,
48
+ platform: %w[ruby].freeze,
49
+ plugin: %w[source=q version=q].freeze,
50
+ remove: %w[install].freeze,
51
+ show: %w[outdated paths].freeze,
52
+ update: %w[all conservative local major minor patch pre ruby strict bundler=b? g|group=q source=q].freeze,
53
+ no: {
54
+ gem: %w[changelog ci coc linter mit test].freeze
55
+ }.freeze
34
56
  }.freeze
35
57
  OPT_GEM = {
36
58
  common: %w[backtrace debug q|quiet no-verbose norc silent V|verbose config-file=p].freeze,
37
- install: %w[version=q].freeze,
38
- install_base: %w[E f w b|both clear-sources conservative default development development-all explain
39
- ignore-dependencies l|local N|no-document r|remote vendor n|bindir=p build-root=p
40
- B|bulk-threshold=i document=b? g|file=p? p|http-proxy=q? i|install-dir=p platform=q
41
- s|source=q target-rbconfig=p? P|trust-policy=b without=b].freeze,
42
- update: %w[system=b?].freeze,
43
- uninstall: %w[a D I x vendor n|bindir=p i|install-dir=p platform=b v|version=q].freeze,
44
- outdated: %w[b|both clear-sources l|local r|remote B|bulk-threshold=i p|http-proxy=q? platform=q
45
- source=q].freeze,
46
- push: %w[attestation=p host=q p|http-proxy=q? k|key=b otp=b].freeze,
59
+ common_url: %w[B|bulk-threshold=i p|http-proxy=q? s|source=q].freeze,
60
+ common_domain: %w[b|both clear-sources l|local r|remote].freeze,
61
+ common_otp: %w[host=q k|key=b otp=b].freeze,
62
+ common_all: %w[a|all e|exact v|version=q].freeze,
47
63
  build: %w[C=p force strict o|output=p platform=q].freeze,
64
+ 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
65
+ R|re-sign s|sign=p].freeze,
66
+ check: %w[a v|version=q].freeze,
67
+ cleanup: %w[n d|dry-run].freeze,
68
+ contents: %w[l all s|spec-dir=p v|version=q].freeze,
69
+ dependency: %w[R pipe platform=q v|version=q].freeze,
48
70
  exec: %w[conservative g|gem=b v|version=q].freeze,
71
+ fetch: %w[clear-sources platform=q v|version=q].freeze,
72
+ generate_index: %w[update d|directory=p].freeze,
73
+ info: %w[i I].freeze,
74
+ install: %w[v|version=q].freeze,
75
+ install_a: %w[E f w conservative default development development-all explain ignore-dependencies N|no-document
76
+ vendor n|bindir=p build-root=p document=b? g|file=p? i|install-dir=p platform=q
77
+ target-rbconfig=p? P|trust-policy=b without=b].freeze,
78
+ list: %w[d i I].freeze,
79
+ lock: %w[s].freeze,
80
+ open: %w[e|editor=p v|version=q].freeze,
81
+ outdated: %w[C=p platform=q].freeze,
82
+ owner: %w[a|add=q r|remove=q p|http-proxy=q?].freeze,
49
83
  pristine: %w[E all only-executables only-missing-extensions only-plugins n|bindir=p i|install-dir=p skip=b
50
84
  v|version=q].freeze,
85
+ push: %w[attestation=p p|http-proxy=q?].freeze,
86
+ rdoc: %w[all v|version=q].freeze,
87
+ rebuild: %w[C=p diff force strict gemspec=p original=p source=q].freeze,
88
+ search: %w[d i I].freeze,
89
+ signin: %w[otp=b host=q].freeze,
90
+ sources: %w[f c|clear-all l|list u|update a|add=q p|http-proxy=q? r|remove=q].freeze,
91
+ specification: %w[all marshal ruby yaml platform=q v|version=q].freeze,
92
+ uninstall: %w[a D I x vendor n|bindir=p i|install-dir=p platform=q v|version=q].freeze,
93
+ unpack: %w[spec target=p P|trust-policy=b v|version=q],
94
+ update: %w[system=b?].freeze,
95
+ which: %w[a g].freeze,
96
+ yank: %w[platform=q v|version=q].freeze,
51
97
  no: {
98
+ check: %w[alien doctor dry-run gems].freeze,
99
+ cleanup: %w[check-development user-install].freeze,
100
+ contents: %w[lib-only prefix show-install-dir].freeze,
101
+ dependency: %w[http-proxy reverse-dependencies].freeze,
102
+ exec: %w[prerelease].freeze,
103
+ fetch: %w[http-proxy prerelease suggestions].freeze,
104
+ generate_index: %w[compact modern].freeze,
105
+ info: %w[http-proxy installed prerelease versions].freeze,
52
106
  install: %w[env-shebang force format-executable http-proxy lock minimal-deps post-install-message prerelease
53
107
  suggestions user-install wrappers].freeze,
54
- uninstall: %w[abort-on-dependent all check-development executables force format-executable
55
- ignore-dependencies user-install].freeze,
108
+ list: %w[details http-proxy installed prerelease versions].freeze,
109
+ lock: %w[strict].freeze,
56
110
  outdated: %w[http-proxy].freeze,
111
+ owner: %w[http-proxy].freeze,
112
+ pristine: %w[env-shebang extensions].freeze,
57
113
  push: %w[http-proxy].freeze,
58
- exec: %w[prerelease].freeze,
59
- pristine: %w[env-shebang extensions].freeze
114
+ rdoc: %w[overwrite rdoc ri].freeze,
115
+ search: %w[details http-proxy installed prerelease versions].freeze,
116
+ sources: %w[force http-proxy].freeze,
117
+ specification: %w[prerelease].freeze,
118
+ uninstall: %w[abort-on-dependent all check-development executables force format-executable
119
+ ignore-dependencies user-install].freeze,
120
+ which: %w[all gems-first].freeze
60
121
  }.freeze
61
122
  }.freeze
62
- private_constant :GEMFILE, :DIR_RUBY, :OPT_RUBY, :OPT_BUNDLE, :OPT_GEM
123
+ PASS_RUBY = {
124
+ ruby: %w[I disable enable dump r s].freeze,
125
+ rake: %w[I libdir r require].freeze,
126
+ irb: %w[I r].freeze,
127
+ gem: {
128
+ contents: %w[s spec-dir].freeze,
129
+ dependency: %w[s source].freeze,
130
+ install: %w[document s source without].freeze,
131
+ pristine: %w[skip].freeze
132
+ }.freeze,
133
+ bundle: {
134
+ install: %w[standalone with without].freeze,
135
+ lock: %w[add-platform remove-platform update].freeze,
136
+ update: %w[g group source].freeze
137
+ }.freeze
138
+ }.freeze
139
+ private_constant :GEMFILE, :GEMNAME, :DIR_RUBY, :OPT_RUBY, :OPT_BUNDLE, :OPT_GEM, :PASS_RUBY
63
140
 
64
141
  class << self
65
- def populate(*); end
66
-
67
142
  def tasks
68
143
  [:outdated].freeze
69
144
  end
@@ -80,15 +155,10 @@ module Squared
80
155
  end
81
156
 
82
157
  subtasks({
83
- 'install' => %i[redownload local prefer-local].freeze,
84
- 'update' => %i[patch minor major all].freeze,
85
158
  'outdated' => %i[patch minor major].freeze,
86
- 'gem' => %i[install uninstall update pristine outdated build push exec].freeze,
87
159
  'ruby' => %i[file script version].freeze,
88
- 'exec' => nil,
89
- 'cache' => nil,
90
- 'config' => nil,
91
- 'check' => nil,
160
+ 'gem' => %i[install uninstall outdated update pristine build push exec command].freeze,
161
+ 'bundle' => %i[install update cache exec config reinstall command].freeze,
92
162
  'rake' => nil,
93
163
  'irb' => nil
94
164
  })
@@ -109,7 +179,7 @@ module Squared
109
179
  elsif gemspec
110
180
  basepath(gemspec.include?('.') ? gemspec : "#{gemspec}.gemspec")
111
181
  end
112
- return if !@output[0].nil? || !@copy.nil? || version || @autodetect || !rakefile
182
+ return unless rakefile && @output[0].nil? && @copy.nil? && !version && !@autodetect
113
183
 
114
184
  begin
115
185
  File.foreach(rakefile) do |line|
@@ -146,16 +216,15 @@ module Squared
146
216
  format_desc action, nil, "task+,opts*|#{indexchar}index+|#,pattern*"
147
217
  task action, [:command] do |_, args|
148
218
  if args.command == '#'
149
- format_list(raketasks, "rake[#{indexchar}N]", 'tasks', grep: args.extras, from: rakefile,
150
- each: ->(val) { val[0] + val[1].to_s })
219
+ format_list(raketasks, "rake[#{indexchar}N]", 'tasks', grep: args.extras, from: rakefile, &:join)
151
220
  else
152
- args, opts = args.to_a.partition { |val| indexitem(val) }
153
- if args.empty?
154
- rake(opts: opts)
221
+ items, opts = args.to_a.partition { |val| indexitem(val) }
222
+ if items.empty?
223
+ rake(banner: true, opts: opts)
155
224
  else
156
225
  tasks = raketasks
157
- while (n, pre = indexitem(args.shift))
158
- if (item = tasks[n - 1])
226
+ while (n, pre = indexitem(items.shift))
227
+ if (item = tasks[n.pred])
159
228
  cmd = pre ? "#{pre} #{item.first}" : item.first
160
229
  elsif exception
161
230
  indexerror n, tasks
@@ -164,9 +233,9 @@ module Squared
164
233
  next
165
234
  end
166
235
  if opts.empty?
167
- rake cmd
236
+ rake(cmd, banner: true)
168
237
  else
169
- rake(cmd + shell_escape("[#{opts.join(',')}]"))
238
+ rake(cmd + "[#{opts.join(',')}]", banner: true)
170
239
  opts.clear
171
240
  end
172
241
  end
@@ -176,42 +245,39 @@ module Squared
176
245
  when 'irb'
177
246
  format_desc action, nil, 'opts*,args*|:'
178
247
  task action do |_, args|
179
- args = args.to_a
180
- name = gemlib.any? { |file| basepath(file).join("#{gemname}.rb").exist? } ? gemname : nil
181
- irb(name, args, args: (readline('Enter file [arguments]', force: false) if args.delete(':')))
182
- end
183
- else
184
- format_desc(action, nil, 'opts*', before: case action
185
- when 'cache', 'check' then nil
186
- else 'command+'
187
- end)
188
- task action do |_, args|
189
- bundle(action, *args.to_a)
248
+ opts = args.to_a
249
+ args = Array(opts.delete(':') && readline('Enter file [arguments]', force: false))
250
+ name = gemname if gemlib.any? { |file| exist?(file, "#{gemname}.rb") }
251
+ irb(*args, opts: opts, name: name, verbose: false)
190
252
  end
191
253
  end
192
254
  else
193
255
  namespace action do
194
256
  flags.each do |flag|
195
257
  case action
196
- when 'install', 'update', 'outdated'
197
- format_desc action, flag, 'opts*'
258
+ when 'outdated'
259
+ format_desc action, flag, 's/elect?,u/pdate?,opts*'
198
260
  task flag do |_, args|
199
261
  __send__ action, flag, args.to_a
200
262
  end
201
263
  when 'gem'
202
264
  case flag
203
265
  when :outdated
204
- format_desc action, flag, 'major|minor|patch|interactive?,opts*'
266
+ format_desc action, flag, 'semver?=major|minor|patch,i/nteractive?,s/elect?,u/pdate?,opts*'
205
267
  task flag, [:semver] do |_, args|
206
- case (filter = args.semver)
207
- when 'major', 'minor', 'patch', 'interactive', 'i'
208
- filter = 'interactive' if filter == 'i'
209
- args = args.extras
210
- else
211
- filter = nil
212
- args = args.to_a
213
- end
214
- gem!(flag, args, filter: filter)
268
+ opts = case (semver = args.semver)
269
+ when 'major', 'minor', 'patch'
270
+ args.extras
271
+ else
272
+ semver = nil
273
+ args.to_a
274
+ end
275
+ gem(flag, opts: opts, banner: true, filter: {
276
+ semver: semver,
277
+ update: opts.delete('u') || opts.delete('update'),
278
+ interactive: opts.delete('i') || opts.delete('interactive'),
279
+ select: opts.delete('s') || opts.delete('select')
280
+ })
215
281
  end
216
282
  when :build, :push, :exec, :update
217
283
  format_desc(action, flag, 'opts*', after: case flag
@@ -220,13 +286,60 @@ module Squared
220
286
  when :update then 'name*'
221
287
  end)
222
288
  task flag do |_, args|
223
- gem! flag, args.to_a
289
+ gem(flag, opts: args.to_a, banner: true)
224
290
  end
225
- else
291
+ when :install, :uninstall, :pristine
226
292
  format_desc(action, flag, 'opts*', after: flag == :pristine ? 'name*|name?@version' : 'name*')
227
293
  task flag do |_, args|
228
- args = param_guard(action, flag, args: args.to_a)
229
- gem! flag, args
294
+ opts = param_guard(action, flag, args: args.to_a)
295
+ gem(flag, opts: opts, banner: true)
296
+ end
297
+ when :command
298
+ format_desc action, flag, 'command,opts*,args*'
299
+ task flag, [:command] do |_, args|
300
+ command = param_guard(action, flag, key: :command, args: args)
301
+ gem(command.to_sym, opts: args.extras, banner: true)
302
+ end
303
+ end
304
+ when 'bundle'
305
+ case flag
306
+ when :install, :update, :cache, :exec
307
+ format_desc(action, flag, 'opts*', after: case flag
308
+ when :update then 'gems*'
309
+ when :exec then 'command,args*|:'
310
+ end)
311
+ task flag do |_, args|
312
+ bundle(flag, opts: args.to_a, banner: true)
313
+ end
314
+ when :config
315
+ format_desc action, flag, 'list|set|get|unset?,args*'
316
+ task flag do |_, args|
317
+ bundle(flag, *args.to_a, banner: true)
318
+ end
319
+ when :reinstall
320
+ format_desc action, flag, 'f/orce?,opts*'
321
+ task flag do |_, args|
322
+ opts = args.to_a
323
+ opts << 'redownload' if opts.delete('f') || opts.delete('force')
324
+ if (lock = basepath('Gemfile.lock')).exist?
325
+ config = basepath '.bundle', 'config'
326
+ if config.exist? && config.read.match?(/\bBUNDLE_FROZEN:\s+"true"/)
327
+ if opts.include?('redownload')
328
+ run(bundle_output('config unset frozen'), banner: false)
329
+ else
330
+ print_error('Gemfile.lock is frozen', subject: name, hint: flag)
331
+ lock = nil
332
+ end
333
+ end
334
+ lock&.delete
335
+ end
336
+ bundle(:install, opts: opts, banner: true)
337
+ end
338
+ when :command
339
+ format_desc action, flag, 'command,opts*,args*'
340
+ task flag, [:command] do |_, args|
341
+ command = param_guard(action, flag, key: :command, args: args)
342
+ bundle(command.to_sym, opts: args.extras, banner: true)
230
343
  end
231
344
  end
232
345
  when 'ruby'
@@ -234,35 +347,34 @@ module Squared
234
347
  when :file
235
348
  format_desc action, flag, 'path,opts*,args*'
236
349
  task flag, [:rb] do |_, args|
237
- file = args.rb
238
350
  opts = args.extras
239
- args = if file && !file.include?('*')
240
- ENV['RUBY_ARGS']
241
- else
242
- a, b, c = choice_index('Select a file', Dir.glob(file || '*.rb', base: path),
243
- values: (file ? [] : ['Options']).push('Arguments'),
244
- force: true, series: true)
245
- if file
246
- file = a
247
- b
248
- else
249
- file = a
250
- opts.concat(OptionPartition.strip(b))
251
- c
252
- end
253
- end
254
- ruby(flag, opts, file: file, args: args)
351
+ args = Array(if (file = args.rb) && !file.include?('*')
352
+ ENV['RUBY_ARGS']
353
+ else
354
+ a, b, c = choice_index('Select a file', Dir.glob(file || '*.rb', base: path),
355
+ values: (file ? [] : ['Options']).push('Arguments'),
356
+ force: true, series: true)
357
+ if file
358
+ file = a
359
+ b
360
+ else
361
+ file = a
362
+ opts.concat(OptionPartition.strip(b))
363
+ c
364
+ end
365
+ end)
366
+ ruby(*args, flag: flag, opts: opts, file: file)
255
367
  end
256
368
  when :script
257
369
  format_desc action, flag, 'opts*'
258
370
  task flag do |_, args|
259
- command = ENV['RUBY_E'] || readline('Enter script', force: true, multiline: ['##', ';'])
260
- ruby(flag, args.to_a, command: command)
371
+ command = ENV['RUBY_E'] || readline('Enter script', force: true, multiline: %w[## ;])
372
+ ruby(flag: flag, opts: args.to_a, command: command)
261
373
  end
262
374
  when :version
263
375
  format_desc action, flag
264
376
  task flag do
265
- ruby flag
377
+ ruby(flag: flag)
266
378
  end
267
379
  end
268
380
  end
@@ -294,8 +406,6 @@ module Squared
294
406
  end
295
407
 
296
408
  def copy(from: gemlib, into: @gemdir, override: false, **kwargs)
297
- return if @copy == false
298
-
299
409
  glob = kwargs[:include]
300
410
  pass = kwargs[:exclude]
301
411
  if @copy && !override
@@ -305,6 +415,8 @@ module Squared
305
415
  glob = @copy[:include] if @copy.key?(:include)
306
416
  pass = @copy[:exclude] if @copy.key?(:exclude)
307
417
  into = @copy[:into] if @copy.key?(:into)
418
+ elsif @copy == false
419
+ return
308
420
  end
309
421
  return unless into
310
422
 
@@ -317,11 +429,9 @@ module Squared
317
429
  b = dest + val
318
430
  c = glob[i] || glob.first
319
431
  log.info "cp #{a + c} #{b}"
320
- begin
321
- copy_dir(a, b, c, pass: pass, verbose: verbosetype > 0)
322
- rescue StandardError => e
323
- on_error e, :copy
324
- end
432
+ copy_dir(a, b, c, pass: pass, verbose: verbosetype > 0)
433
+ rescue StandardError => e
434
+ on_error e, :copy
325
435
  end
326
436
  on :last, :copy
327
437
  end
@@ -329,14 +439,21 @@ module Squared
329
439
  def outdated(flag = nil, opts = [], sync: invoked_sync?('outdated', flag))
330
440
  cmd = bundle_output 'outdated'
331
441
  if flag
442
+ se = (opts.delete('s') || opts.delete('select')) && !stdin?
443
+ up = opts.delete('u') || opts.delete('update')
332
444
  cmd << "--#{flag}"
333
- append_bundle(opts, OPT_BUNDLE[:outdated] + OPT_BUNDLE[:common], target: cmd)
445
+ OptionPartition.new(opts, bundleopts(:outdated), cmd, project: self)
446
+ .clear
334
447
  end
335
448
  log.info cmd.to_s
336
449
  on :first, :outdated
337
450
  banner = format_banner cmd.to_s
338
- print_item banner if sync
451
+ items = if sync
452
+ print_item banner
453
+ [] if flag && (se || up)
454
+ end
339
455
  pwd_set(from: :outdated) do
456
+ tc = theme[:current]
340
457
  start = 0
341
458
  found = 0
342
459
  major = 0
@@ -393,26 +510,26 @@ module Squared
393
510
  end
394
511
  end
395
512
  end
513
+ name = line[/^\S+/, 0]
396
514
  unless styles.empty?
397
515
  case styles.first
398
516
  when :green
399
- line = sub_style(line, pat: /^(\S+)(.+)$/, styles: theme[styles.last == :bold ? :major : :active])
517
+ line = sub_style(line, **opt_style(theme[styles.last == :bold ? :major : :active], /^(\S+)(.+)$/))
400
518
  found += 1
401
519
  when :yellow
402
520
  found += 1
403
521
  end
404
- if theme[:current]
405
- line = sub_style(line, styles: theme[:current], pat: /^(.+)(#{Regexp.escape(c)})(.+)$/, index: 2)
406
- end
407
- line = sub_style(line, *colormap(styles), pat: /^((?:\S+\s+){2})(#{Regexp.escape(l)})(.*)$/,
408
- index: 2)
522
+ l = Regexp.escape(l)
523
+ line = sub_style(line, **opt_style(tc, /^(.+)(#{Regexp.escape(c)})(.+)$/, 2)) if tc
524
+ line = sub_style(line, **opt_style(colormap(styles), /^((?:\S+\s+){2})(#{l})(.*)$/, 2))
409
525
  end
410
526
  end
411
- out.call('%2d. %s' % [start, line])
527
+ items&.push([line, name])
528
+ out.call('%2d. %s' % [start, line]) unless se
412
529
  elsif line.start_with?('Gem')
413
530
  unless stdin?
414
- sub = { pat: /^(.+)(?<!\dm)(Gem|Latest)(.+)$/, styles: theme[:header], index: 2 }
415
- out.call(print_footer(" # #{line.chomp}", reverse: true, sub: [sub, sub]))
531
+ sub = [opt_style(theme[:header], /^(.+)(?<!\dm)(Gem|Latest)(.+)$/, 2)] * 2
532
+ out.call(print_footer(" # #{line.chomp}", reverse: true, sub: sub))
416
533
  end
417
534
  else
418
535
  next
@@ -424,6 +541,15 @@ module Squared
424
541
  puts buffer
425
542
  end
426
543
  if found > 0
544
+ if items
545
+ gems = if se
546
+ choice('Select a package', items.map(&:first),
547
+ multiple: true, force: false, index: true, border: true).map! { |n| items[n.pred].last }
548
+ else
549
+ items.map(&:last)
550
+ end
551
+ bundle(:update, *gems, opts: [flag.to_s])
552
+ end
427
553
  begin
428
554
  if major == 0 && dependfile.read =~ /\b(?:source\s+(["'])((?~\1))\1|remote:\s+(\S+))/
429
555
  status = ($2 || $3).chomp('/')
@@ -432,52 +558,18 @@ module Squared
432
558
  rescue StandardError => e
433
559
  log.debug e
434
560
  ensure
435
- status ||= 'Updates are available'
561
+ status ||= (items ? 'Updates were made' : 'Updates are available')
436
562
  end
437
563
  puts print_footer(empty_status(status, 'major', major, always: !right), right: right)
438
- elsif start == 0
564
+ elsif start == 0 && banner
439
565
  puts 'No updates were found'
440
566
  end
441
567
  end
442
568
  on :last, :outdated
443
569
  end
444
570
 
445
- def install(flag, opts = [])
446
- bundle_session 'install', "--#{flag}"
447
- op = append_bundle opts, OPT_BUNDLE[:install_base] + OPT_BUNDLE[:install] + OPT_BUNDLE[:common]
448
- if op.arg?('force')
449
- op.delete('--force')
450
- if flag != :redownload
451
- op << '--redownload'
452
- elsif (lock = basepath('Gemfile.lock')).exist?
453
- config = basepath '.bundle', 'config'
454
- lock.delete unless config.exist? && config.read.match?(/\bBUNDLE_FROZEN:\s+"true"/)
455
- end
456
- end
457
- run_rb(from: :install)
458
- end
459
-
460
- def update(flag, opts = [])
461
- bundle_session 'update', "--#{flag}"
462
- append_bundle(opts, OPT_BUNDLE[:install_base] + OPT_BUNDLE[:update] + OPT_BUNDLE[:common],
463
- append: flag == :all ? nil : /\A[a-z-]+=/)
464
- run_rb(from: :update)
465
- end
466
-
467
- def ruby(flag, opts = [], file: nil, command: nil, args: nil)
468
- case flag
469
- when :file, :script
470
- op = OptionPartition.new(opts, OPT_RUBY[:ruby], ruby_session, project: self, args: true)
471
- if command
472
- op << quote_option('e', command, option: false)
473
- elsif file
474
- op.unshift(basepath(file))
475
- end
476
- unless op.arg?('e')
477
- op.push(args) if args
478
- op.append(delim: true, escape: false, quote: false) unless op.empty?
479
- end
480
- when :version
571
+ def ruby(*args, flag: nil, sync: true, banner: verbose?, with: nil, pass: PASS_RUBY[:ruby], **kwargs)
572
+ if flag == :version
481
573
  pwd_set do
482
574
  out = []
483
575
  order = { 'rbenv' => -1, 'rvm' => -1, 'asdf' => -1, 'chruby' => -1 }
@@ -520,15 +612,14 @@ module Squared
520
612
  .each do |val|
521
613
  next unless val.empty? || File.exist?(val.sub('$HOME', Dir.home))
522
614
 
523
- trim = ->(s) { s[/\A\D+\d+\.\d+(?:\.\S+)?/, 0].sub(/\A([a-z]+)-/i, '\1 ') }
615
+ trim = ->(s) { s[/^\D+\d+\.\d+(?:\.\S+)?/, 0].sub(/^([a-z]+)-/i, '\1 ') }
524
616
  ver = '.ruby-version'
525
617
  out << trim.call(case (cmd = File.basename(val))
526
618
  when 'rvm'
527
619
  `rvm current`[/^\S+/, 0]
528
620
  when 'rbenv'
529
- `rbenv version-name`.yield_self do |name|
530
- name.match?(SEM_VER) ? "ruby #{name}" : name
531
- end
621
+ name = `rbenv version-name`
622
+ name.match?(SEM_VER) ? "ruby #{name}" : name
532
623
  when 'chruby.sh'
533
624
  chruby = session_output 'source', val
534
625
  `#{chruby.with('ruby --version')}`
@@ -537,7 +628,7 @@ module Squared
537
628
  cmd = 'asdf'
538
629
  ver = '.tool-versions'
539
630
  opt = [@asdf.first]
540
- opt.unshift('--no-header') unless @@asdf[1] == 15
631
+ opt.unshift('--no-header') unless @@asdf.version == 15
541
632
  `asdf current #{opt.join(' ')}`[/^\S+\s+\S+/, 0].sub(/\s+/, ' ')
542
633
  else
543
634
  ver = nil
@@ -586,138 +677,199 @@ module Squared
586
677
  end
587
678
  return
588
679
  end
589
- run_rb(banner: false, from: :"ruby:#{flag}")
590
- end
591
-
592
- def gem!(flag, opts = [], filter: nil)
593
- cmd = gem_session
594
- case flag
595
- when :outdated
596
- cmd << gempwd << flag
597
- else
598
- cmd << flag
680
+ op = OptionPartition.new(session_opts(with, args: args, kwargs: kwargs, pass: pass), OPT_RUBY[:ruby],
681
+ ruby_session, project: self, args: true)
682
+ if (val = kwargs[:file])
683
+ op.unshift(shell_quote(basepath(val)))
684
+ elsif (val = kwargs[:command])
685
+ op << quote_option('e', val)
686
+ end
687
+ append = lambda do |file|
688
+ op.concat(args)
689
+ unless op.empty?
690
+ op.exist?(add: true) if file
691
+ op.append(delim: true, escape: false, quote: false)
692
+ end
599
693
  end
600
- list = OPT_GEM[flag] + OPT_GEM[:common]
601
- from = :"gem:#{flag}"
602
694
  case flag
603
- when :install, :update
604
- list.concat(OPT_GEM[:install_base])
695
+ when :file
696
+ append.call(false)
697
+ when :script
698
+ op.clear
699
+ else
700
+ append.call(true)
701
+ print_run(op, banner, **kwargs)
605
702
  end
606
- op = OptionPartition.new(opts, list, cmd, project: self, no: OPT_GEM[:no][flag])
607
- op.each do |opt|
608
- if !opt.match?(/\A[A-Za-z\d][A-Za-z\d_.-]*\z/) && %i[install uninstall update pristine].include?(flag)
609
- op.errors << opt
610
- else
611
- op.found << opt
612
- end
703
+ run_rb(sync: sync, banner: banner, from: flag.is_a?(Symbol) ? :"ruby:#{flag}" : :ruby)
704
+ end
705
+
706
+ def gem(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
707
+ flag = flag.to_sym
708
+ if pass.nil?
709
+ pass = case flag
710
+ when :install, :update
711
+ PASS_RUBY[:gem][:install]
712
+ when :dependency, :fetch, :info, :list, :outdated, :search, :specification
713
+ PASS_RUBY[:gem][:dependency]
714
+ else
715
+ PASS_RUBY[:gem].fetch(flag, [])
716
+ end
613
717
  end
614
- op.swap
718
+ opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
615
719
  case flag
616
- when :outdated
720
+ when :build, :cert, :generate_index, :mirror, :outdated, :push, :server, :signin, :signout, :sources, :stale
721
+ opts.concat(args)
722
+ end
723
+ op = OptionPartition.new(opts, gemopts(flag), gem_session(flag, *preopts), project: self,
724
+ no: OPT_GEM[:no][flag])
725
+ from = :"gem:#{flag}"
726
+ if flag == :outdated
727
+ op.adjoin(gempwd, start: 0) if gempwd
617
728
  op.clear
618
- cmd = cmd.done
729
+ cmd = session_done op.target
619
730
  log.info cmd
620
731
  on :first, from
621
- print_item format_banner(cmd)
622
- major = 0
623
- minor = 0
624
- patch = 0
625
- update = []
732
+ banner = format_banner(cmd)
733
+ print_item banner if sync
734
+ major = [0, 0, 0]
735
+ buffer = []
736
+ filter = kwargs.fetch(:filter, {})
737
+ semver = filter[:semver]
738
+ update = if sync && filter[:select]
739
+ semver ||= 'major'
740
+ items = []
741
+ nil
742
+ elsif sync && filter[:interactive]
743
+ semver ||= 'major'
744
+ ia = true
745
+ []
746
+ elsif filter[:update]
747
+ semver ||= 'minor'
748
+ []
749
+ end
750
+ out = ->(val) { sync ? puts(val) : buffer << val }
626
751
  pwd_set(pass: !gempwd.nil?, from: from) do
627
- items = [[%w[Gem Current Latest], nil]]
752
+ rows = [[%w[Gem Current Latest], nil]]
628
753
  IO.popen(cmd).each do |line|
629
754
  if line =~ /^(\S+) \((\S+) < ([^)]+)\)$/
630
755
  cur = semscan $2
631
756
  lat = semscan $3
632
- items << [$~.to_a.drop(1), if semmajor?(cur, lat)
633
- 1
634
- else
635
- cur[2] == lat[2] ? 3 : 2
636
- end]
757
+ rows << [$~.to_a.drop(1), if semmajor?(cur, lat)
758
+ 1
759
+ else
760
+ cur[2] == lat[2] ? 3 : 2
761
+ end]
637
762
  else
638
- puts line
763
+ out.call(line)
639
764
  end
640
765
  end
641
- if items.size > 1
642
- pad = [items.size.to_s.size + 1, 3].max
766
+ if rows.size > 1
767
+ pad = [rows.size.to_s.size.succ, 3].max
643
768
  d = 0
644
769
  e = 0
645
770
  f = 0
646
771
  j = 0
647
772
  queue = nil
648
- items.each do |item|
649
- a, b, c = item.first
773
+ rows.each do |row|
774
+ a, b, c = row.first
650
775
  d = a.size if a.size > d
651
776
  e = b.size if b.size > e
652
777
  f = c.size if c.size > f
653
778
  end
654
- items.each_with_index do |item, i|
779
+ rows.each_with_index do |row, i|
655
780
  next if i == 0 && stdin?
656
781
 
657
- a, b, c = item.first
782
+ a, b, c = row.first
658
783
  if i == 0
659
784
  line = '%-*s %-*s %*s %*s' % [pad, ' #', d, a, e, b, f, c]
660
785
  n = line.size
661
- 2.times do
662
- line = sub_style(line, pat: /^(.+)(?<!\dm)(#{a}|#{c})(.*)$/, styles: theme[:header], index: 2)
663
- end
664
- queue = [line, sub_style(ARG[:BORDER][1] * n, styles: borderstyle)]
786
+ queue = if stdin?
787
+ [line, ARG[:BORDER][1] * n]
788
+ else
789
+ 2.times do
790
+ line = sub_style(line, **opt_style(theme[:header], /^(.+)(?<!\dm)(#{a}|#{c})(.*)$/, 2))
791
+ end
792
+ [line, sub_style(ARG[:BORDER][1] * n, styles: borderstyle)]
793
+ end
665
794
  else
666
795
  g = a.ljust(d)
667
796
  pat = [/^([^.]+\.)([^.]+\..+)$/, /^([^.]+\.[^.]+\.)(.+)$/]
668
797
  pre = b.start_with?('0.')
669
798
  latest = [theme[:latest]]
670
- case item.last
799
+ case row.last
671
800
  when 1
672
- case filter
801
+ case semver
673
802
  when 'major'
674
- update << a
803
+ update&.push(a)
675
804
  when 'minor', 'patch'
676
805
  next
677
806
  end
678
- g = sub_style(g, styles: theme[:major])
679
- major += 1
680
- styles = %i[green bold]
681
- pat = pre ? pat.first : nil
682
- latest << :bold
807
+ unless stdin?
808
+ g = sub_style(g, styles: theme[:major])
809
+ styles = %i[green bold]
810
+ pat = (pat.first if pre)
811
+ latest << :bold
812
+ end
813
+ major[0] += 1
683
814
  when 2
684
- case filter
815
+ case semver
685
816
  when 'major', 'minor'
686
- update << a
817
+ update&.push(a)
687
818
  when 'patch'
688
819
  next
689
820
  end
690
- g = sub_style(g, styles: theme[:active])
691
- minor += 1
692
- styles = %i[green]
693
- pat = pre ? pat.last : pat.first
821
+ unless stdin?
822
+ g = sub_style(g, styles: theme[:active])
823
+ styles = %i[green]
824
+ pat = pre ? pat.last : pat.first
825
+ end
826
+ major[1] += 1
694
827
  else
695
- case filter
828
+ case semver
696
829
  when 'major', 'minor', 'patch'
697
- update << a
830
+ update&.push(a)
698
831
  end
699
- patch += 1
700
- styles = %i[yellow]
701
- pat = pat.last
832
+ unless stdin?
833
+ styles = %i[yellow]
834
+ pat = pat.last
835
+ end
836
+ major[2] += 1
837
+ end
838
+ b = b.rjust(e)
839
+ h = c.rjust(f)
840
+ unless stdin?
841
+ b = sub_style(b, **opt_style(colormap(styles), pat, 2))
842
+ h = sub_style(h, **opt_style(latest.flatten.compact, pat, 2))
702
843
  end
703
- b = sub_style(b.rjust(e), *colormap(styles), pat: pat, index: 2)
704
- h = sub_style(c.rjust(f), styles: latest.flatten.compact, pat: pat, index: 2)
705
844
  j += 1
706
845
  if queue
707
- puts queue
846
+ out.call(queue)
708
847
  queue = nil
709
848
  end
710
- puts '%*s %s %s %s' % [pad, "#{j}.", g, b, h]
711
- update << a if filter == 'interactive' && confirm_outdated(a, c, item.last)
849
+ s = '%s %s %s' % [g, b, h]
850
+ if items
851
+ items << [s, a]
852
+ else
853
+ out.call('%*s %s' % [pad, "#{j}.", s])
854
+ end
855
+ update.delete(a) if ia && !confirm_outdated(a, c, row.last, timeout: 0)
712
856
  end
713
857
  end
714
858
  end
715
859
  end
716
- if major + minor + patch == 0
860
+ unless sync
861
+ print_item banner
862
+ puts buffer
863
+ end
864
+ if major.sum == 0
717
865
  puts 'No updates were found'
718
866
  else
719
- unless update.empty?
720
- cmd = gem_output 'update', '-f'
867
+ if items
868
+ update = choice('Select a package', items.map(&:first),
869
+ multiple: true, force: false, index: true, border: true).map! { |n| items[n.pred].last }
870
+ end
871
+ if update && !update.empty?
872
+ cmd = gem_output 'update -f'
721
873
  option('document', prefix: 'gem', ignore: false) do |val|
722
874
  cmd << case val
723
875
  when '0', 'false'
@@ -735,18 +887,40 @@ module Squared
735
887
  end
736
888
  end
737
889
  cmd.merge(update)
738
- run(cmd, banner: false, from: :'gem:update')
890
+ run(cmd, sync: sync, banner: false, from: :'gem:update')
739
891
  end
740
- print_status(major, minor, patch, from: :outdated)
892
+ print_status(*major, from: :outdated)
741
893
  end
742
894
  on :last, from
743
895
  return
896
+ end
897
+ case flag
898
+ when :check, :cleanup, :contents, :fetch, :info, :lock, :open, :owner, :pristine, :rdoc, :rebuild, :uninstall,
899
+ :unpack, :update, :yank
900
+ gems = true
901
+ op.concat(args)
902
+ when :dependency, :environment, :list, :search, :specification, :which
903
+ op.concat(args)
904
+ end
905
+ op.each do |opt|
906
+ if gems && !opt.start_with?('-') && !opt.match?(GEMNAME)
907
+ op.errors << opt
908
+ else
909
+ op.found << opt
910
+ end
911
+ end
912
+ op.swap do |a, b|
913
+ return -1 if a.start_with?('-')
914
+
915
+ b.start_with?('-') ? 1 : 0
916
+ end
917
+ case flag
744
918
  when :build
745
919
  if op.empty?
746
- raise_error('gemspec not found', hint: project) unless gemfile
920
+ raise_error Errno::ENOENT, 'gemspec', hint: project unless gemfile
747
921
  op.add_path(gemfile)
748
922
  else
749
- op.add_path(op.shift)
923
+ op.add_first(path: true)
750
924
  .clear(pass: false)
751
925
  end
752
926
  when :push
@@ -757,38 +931,48 @@ module Squared
757
931
  choice_index('Select a file', Dir.glob('*.gem', base: path), force: true)
758
932
  end)
759
933
  else
760
- file = op.shift.yield_self { |val| val.include?('.') ? val : "#{val}.gem" }
761
- raise_error("unknown args: #{op.join(', ')}", hint: flag) unless op.empty?
934
+ file = basepath(op.shift.yield_self { |val| val.include?('.') ? val : "#{val}.gem" })
935
+ raise_error Errno::ENOENT, file, hint: flag unless file.exist?
936
+ raise_error ArgumentError, "unrecognized args: #{op.join(', ')}", hint: flag unless op.empty?
762
937
  end
763
- raise_error('gem not found', hint: file) unless op.exist?(file)
764
938
  op.add_path(file)
765
- run_rb(from: from, interactive: "Push #{sub_style(gemname, styles: theme[:active])}")
766
- return
939
+ return run_rb(from: from, interactive: ['Push', 'N', gemname]) unless with || !banner
767
940
  when :exec
768
941
  min = if op.arg?('g', 'gem')
769
942
  1
770
- elsif op.empty?
943
+ elsif !op.empty?
944
+ op.add_first
945
+ 0
946
+ elsif args.empty?
771
947
  op << basic_option('gem', gemname)
772
948
  1
773
949
  else
774
- op << op.shift
775
950
  0
776
951
  end
952
+ op.concat(args)
777
953
  if (args = command_args(op.extras, min: min, force: min == 1 && op.empty?))
778
954
  op.push(args)
779
955
  end
780
956
  op.append(quote: false)
781
957
  when :update
782
- unless op.arg?('system')
783
- if op.empty?
784
- op << gemname
785
- else
786
- op.append
787
- end
958
+ if op.arg?('system')
959
+ op.add_first(quote: false) { |val| val if val.match?(SEM_VER) }
960
+ else
961
+ op.append
788
962
  end
789
- op.clear(errors: true)
790
963
  when :install, :uninstall, :pristine
791
- raise_error('missing gemname', hint: flag) if op.empty?
964
+ if flag == :install
965
+ post = if op.remove(':')
966
+ op.concat(args)
967
+ readline('Enter command [args]', force: true)
968
+ elsif op.empty?
969
+ op.concat(args)
970
+ nil
971
+ elsif !args.empty?
972
+ args.join(' ')
973
+ end
974
+ end
975
+ raise_error ArgumentError, 'missing gem name', hint: flag if op.empty?
792
976
  if op.arg?('all')
793
977
  if flag == :pristine
794
978
  append_repeat 'skip', op.extras
@@ -797,79 +981,190 @@ module Squared
797
981
  op.clear
798
982
  end
799
983
  elsif (n = op.index { |val| val.match?(/(\A|[a-z])@\d/) })
800
- name = op.delete_at(n)
984
+ name = op.remove_at(n)
801
985
  pre, ver = if (n = name.index('@')) == 0
802
986
  [gemname, name[1..-1]]
803
987
  else
804
- [name[0, n], name[(n + 1)..-1]]
988
+ [name[0, n], name[n.succ..-1]]
805
989
  end
806
- op.adjoin(pre, basic_option('version', ver))
990
+ op.adjoin(pre, shell_option('version', ver))
807
991
  .clear
808
- elsif flag == :install
992
+ end
993
+ if flag == :install
809
994
  op.append_any
810
995
  else
811
996
  op.append
812
997
  end
813
- op.clear(errors: true)
814
- op.delim << readline('Enter command [args]', force: true) if flag == :install && op.remove(':')
815
- else
998
+ op << '--' << post if post
999
+ when :check, :cleanup, :contents, :fetch, :list, :lock, :rdoc
816
1000
  op.append
1001
+ when :dependency, :info, :search
1002
+ op.add_first(quote: true, expect: case flag
1003
+ when :dependency, :search then 'no pattern for gem name'
1004
+ else 'missing gem name'
1005
+ end)
1006
+ .clear
1007
+ when :environment
1008
+ unless op.empty?
1009
+ case (action = op.shift)
1010
+ when 'home', 'path', 'user_gemhome', 'version', 'remotesources', 'platform', 'credentials'
1011
+ op << action
1012
+ op.clear
1013
+ else
1014
+ raise_error ArgumentError, "unrecognized arg: #{action}", hint: flag
1015
+ end
1016
+ end
1017
+ when :open, :owner, :unpack, :yank
1018
+ op.add_first(gemname)
1019
+ .clear
1020
+ when :rebuild
1021
+ op.add_first(expect: 'missing gem name')
1022
+ .add_first(expect: 'missing gem version')
1023
+ .clear
1024
+ when :specification
1025
+ op.add_first(expect: 'missing gem name')
1026
+ .add_first
1027
+ .clear
1028
+ when :which
1029
+ op.splice(path: true) { |val| op.exist?(val) }
1030
+ .clear
1031
+ else
1032
+ op.clear
817
1033
  end
818
- run_rb(from: from)
1034
+ op.clear(errors: true) if gems
1035
+ print_run(op, banner, **kwargs)
1036
+ run_rb(sync: sync, banner: banner, from: from)
819
1037
  end
820
1038
 
821
- def bundle(flag, *args)
822
- cmd = bundle_session flag
823
- args = case flag
824
- when 'exec', 'cache', 'check'
825
- list = OPT_BUNDLE[flag.to_sym] + OPT_BUNDLE[:common]
826
- OptionPartition.new(args, list, cmd, project: self, args: flag == 'exec').extras
827
- else
828
- args.flatten
829
- end
1039
+ def bundle(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
1040
+ flag = flag.to_sym
1041
+ if pass.nil?
1042
+ pass = case flag
1043
+ when :add, :outdated, :update
1044
+ PASS_RUBY[:bundle][:update]
1045
+ else
1046
+ PASS_RUBY[:bundle].fetch(flag, [])
1047
+ end
1048
+ end
1049
+ opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
830
1050
  case flag
831
- when 'exec', 'config'
832
- cmd << readline('Enter arguments', force: true) if args.empty?
833
- when 'cache', 'check'
834
- option_clear args
835
- args.clear
1051
+ when :cache, :check, :clean, :doctor, :init, :install, :lock, :pack, :package, :platform
1052
+ pre = true
1053
+ opts.concat(args)
836
1054
  end
837
- cmd.merge(args)
838
- run(from: :"bundle:#{flag}")
839
- end
840
-
841
- def rake(*args, opts: [])
842
- op = OptionPartition.new(opts, OPT_RUBY[:rake], [(quote_option('f', rakefile) if rakefile)].compact,
843
- project: self)
844
- args.concat(op.extras)
845
- if args.empty?
846
- args << nil
1055
+ op = OptionPartition.new(opts, bundleopts(flag), bundle_session(flag),
1056
+ project: self, no: OPT_BUNDLE[:no][flag], args: flag == :exec)
1057
+ op.concat(args) unless pre
1058
+ output = false
1059
+ invalid = ->(a) { raise_error ArgumentError, "unrecognized args: #{a.join(', ')}", hint: flag }
1060
+ case flag
1061
+ when :config
1062
+ if op.empty?
1063
+ op << (val = readline('Enter arguments', force: false))
1064
+ output = val.match?(/(?:un)?set/)
1065
+ else
1066
+ a = op.dup
1067
+ b = op.shift
1068
+ c = op.shift
1069
+ d = op.shift
1070
+ e = op.arg?('global', 'local')
1071
+ op << b
1072
+ getname = -> { op << (c || readline('Enter name', force: true)) }
1073
+ case b
1074
+ when 'list'
1075
+ nil
1076
+ when 'get'
1077
+ getname.call
1078
+ when 'set'
1079
+ if e
1080
+ op << c
1081
+ c = d
1082
+ d = op.shift
1083
+ end
1084
+ getname.call
1085
+ op << (d || readline('Enter value', force: true))
1086
+ output = true
1087
+ when 'unset'
1088
+ if e
1089
+ op << c
1090
+ c = d
1091
+ end
1092
+ getname.call
1093
+ output = true
1094
+ else
1095
+ if b
1096
+ op << b
1097
+ if c
1098
+ op.add_quote(c)
1099
+ output = true
1100
+ end
1101
+ else
1102
+ invalid.call(a)
1103
+ end
1104
+ end
1105
+ op.clear
1106
+ end
1107
+ when :plugin
1108
+ a = op.dup
1109
+ case (b = op.shift)
1110
+ when 'install', 'uninstall', 'help'
1111
+ op.append
1112
+ when 'list'
1113
+ op.clear
1114
+ else
1115
+ invalid.call(a)
1116
+ end
1117
+ op.adjoin(b, with: 'plugin')
1118
+ when :exec
1119
+ if op.empty? || (op.delete(':') && op.append(quote: false))
1120
+ op << readline('Enter arguments', force: true)
1121
+ else
1122
+ op.append(quote: false)
1123
+ end
1124
+ when :binstubs, :outdated, :remove, :update
1125
+ op.append(filter: GEMNAME)
1126
+ .clear(errors: true)
1127
+ when :add, :open, :show
1128
+ op.add_first(expect: 'missing gem name')
1129
+ .clear
1130
+ when :console, :gem
1131
+ op.add_first
1132
+ .clear
847
1133
  else
848
- args.flatten!
1134
+ op.clear
849
1135
  end
850
- cmd = rake_output(*op.to_a)
851
- args.map! { |val| cmd.temp(val) }
852
- run_s(args, banner: false, from: :rake)
1136
+ print_run(op, banner, **kwargs)
1137
+ run(sync: sync, banner: banner, from: :"bundle:#{flag}").tap { |ret| success?(ret, banner, output) }
1138
+ end
1139
+
1140
+ def rake(*args, sync: true, banner: verbose?, with: nil, pass: PASS_RUBY[:rake], **kwargs)
1141
+ opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
1142
+ op = OptionPartition.new(opts, OPT_RUBY[:rake], rake_session, project: self)
1143
+ op.adjoin(quote_option('f', rakefile)) if rakefile && !op.arg?('f', 'rakefile')
1144
+ op.concat(args)
1145
+ op.append(escape: true)
1146
+ print_run(op, banner, **kwargs)
1147
+ run(op, ({ 'BANNER' => '0' } unless banner), sync: sync, banner: false, from: :rake)
853
1148
  end
854
1149
 
855
- def irb(name = nil, opts = [], path: gemlib, args: nil)
1150
+ def irb(*args, banner: verbose?, with: nil, pass: PASS_RUBY[:irb], **kwargs)
1151
+ opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
856
1152
  op = OptionPartition.new(opts, OPT_RUBY[:irb], session('irb'), project: self, first: [/\.rb$/])
857
- r = args ? [] : ['bundler/setup']
858
- r << name if name
859
- r.each { |val| op << shell_option('r', val, merge: true) }
860
- Array(path).each { |val| op << quote_option('I', val, merge: true) }
861
- if args
862
- op.delim << args
863
- op.clear
864
- else
865
- op.append(delim: true)
866
- end
867
- run(banner: false)
1153
+ r = []
1154
+ r << 'bundler/setup' unless op.arg?('r')
1155
+ r << kwargs[:name] if kwargs[:name]
1156
+ r.each { |val| op.add_option('r', val, merge: true) }
1157
+ Array(kwargs.fetch(:path, gemlib)).each { |val| op << quote_option('I', val, merge: true) }
1158
+ op.concat(args)
1159
+ op.append(delim: true)
1160
+ print_run(op, banner, **kwargs)
1161
+ run(banner: false, from: :irb)
868
1162
  end
869
1163
 
870
1164
  def gemspec
871
- @gemspec = !gemfile.nil? && Gem::Specification.load(gemfile.to_s) rescue false if @gemspec.nil?
872
- @gemspec || nil
1165
+ return @gemspec || nil unless @gemspec.nil?
1166
+
1167
+ @gemspec = gemfile && Gem::Specification.load(gemfile.to_s) rescue false
873
1168
  end
874
1169
 
875
1170
  def gemname
@@ -888,21 +1183,20 @@ module Squared
888
1183
  begin
889
1184
  case @autodetect
890
1185
  when 'rvm'
891
- @gemdir = pwd_set { `rvm info homes` }[/^\s+gem:\s+"(.+)"$/, 1]
1186
+ pwd_set { `rvm info homes` }[/^\s+gem:\s+"(.+)"$/, 1]
892
1187
  when 'rbenv'
893
1188
  if pwd_set { `rbenv which ruby` } =~ %r{^(.+[\\/]versions[\\/](\d\.\d)\.[^\\/]+)[\\/]bin[\\/]ruby$}
894
- @gemdir = File.join($1, 'lib/ruby/gems', "#{$2}.0")
1189
+ File.join($1, 'lib/ruby/gems', "#{$2}.0")
895
1190
  end
896
1191
  when 'asdf'
897
- @gemdir = pwd_set { `asdf where ruby` }
898
- @gemdir = @gemdir =~ /(\d\.\d)\.[^.]+$/ && File.join(@gemdir, 'lib/ruby/gems', "#{$1}.0")
1192
+ pwd_set { `asdf where ruby` }.yield_self do |val|
1193
+ val =~ /(\d\.\d)\.[^.]+$/ && File.join(val, 'lib/ruby/gems', "#{$1}.0")
1194
+ end
899
1195
  when /bundler?/
900
- @gemdir = pwd_set { `bundle env` }[/^\s+Gem Home\s+(.+)$/, 1]
901
- end
1196
+ pwd_set { `bundle env` }[/^\s+Gem Home\s+(.+)$/, 1]
1197
+ end.tap { |val| @gemdir = Pathname.new(val) if val }
902
1198
  rescue StandardError => e
903
1199
  log.debug e
904
- else
905
- @gemdir = Pathname.new(@gemdir) if @gemdir
906
1200
  end
907
1201
  return true if gemdir?
908
1202
  end
@@ -910,7 +1204,7 @@ module Squared
910
1204
 
911
1205
  set = lambda do |val, path|
912
1206
  if (ver = version) && ver != val
913
- log.warn "using version #{val} (given #{ver})"
1207
+ log.warn "using version #{val}".subhint("given #{ver}")
914
1208
  end
915
1209
  self.version = val
916
1210
  @gemdir = Pathname.new(path.strip) + gempath
@@ -919,17 +1213,17 @@ module Squared
919
1213
  opt = gempwd
920
1214
  pwd_set(pass: !opt.nil?) do
921
1215
  out = `#{gem_output(opt, 'list --local -d', gemname)}`
922
- if out =~ /#{Regexp.escape(gemname)} \(([^)]+)\)/
923
- split_escape($1)
924
- .unshift(version)
925
- .uniq
926
- .each do |val|
927
- next unless out =~ /\(#{Regexp.escape(val)}(?:,[^)]+|\b)\):([^\n]+)/
928
-
929
- set.call(val, $1)
930
- return gemdir? if @gemdir
931
- end
932
- end
1216
+ next unless out =~ /#{Regexp.escape(gemname)} \(([^)]+)\)/
1217
+
1218
+ split_escape($1)
1219
+ .unshift(version)
1220
+ .uniq
1221
+ .each do |val|
1222
+ next unless out =~ /\(#{Regexp.escape(val)}[^)]*\):([^\n]+)/
1223
+
1224
+ set.call(val, $1)
1225
+ return gemdir? if @gemdir
1226
+ end
933
1227
  end
934
1228
  @gemdir = Pathname.new(Gem.dir) + gempath
935
1229
  else
@@ -945,8 +1239,8 @@ module Squared
945
1239
  target = RUBY_VERSION.start_with?('2.6') ? RubyVM : $LOAD_PATH
946
1240
  parse.call(target.resolve_feature_path(gemname)&.last)
947
1241
  end
948
- if !@gemdir && !pwd_set { parse.call(`#{bundle_output('show', gemname)}`) }
949
- raise_error 'gems directory not found'
1242
+ unless @gemdir || pwd_set { parse.call(`#{bundle_output('show', gemname)}`) }
1243
+ raise_error Errno::ENOENT, 'gems home'
950
1244
  end
951
1245
  end
952
1246
  rescue StandardError => e
@@ -959,7 +1253,7 @@ module Squared
959
1253
  end
960
1254
 
961
1255
  def outdated?
962
- dependtype > 0 && !task_pass?('outdated')
1256
+ dependtype > 0
963
1257
  end
964
1258
 
965
1259
  private
@@ -968,36 +1262,23 @@ module Squared
968
1262
  run(banner: !@session&.include?('--quiet'), **kwargs)
969
1263
  end
970
1264
 
971
- def append_bundle(opts, list, target: @session, append: nil)
972
- op = OptionPartition.new(opts, list, target, project: self)
973
- if append
974
- if append.is_a?(Regexp)
975
- op.each do |opt|
976
- if opt.match?(append)
977
- op.errors << opt
978
- else
979
- op.found << opt
980
- end
981
- end
982
- op.swap.clear(errors: true)
983
- end
984
- op.append(escape: true)
985
- else
986
- op.clear
987
- end
988
- op
989
- end
990
-
991
1265
  def ruby_session(*cmd, **kwargs)
992
1266
  session('ruby', *preopts, *cmd, **kwargs)
993
1267
  end
994
1268
 
995
1269
  def gem_session(*cmd, **kwargs)
996
- session('gem', *cmd, *preopts, **kwargs)
1270
+ ret = session('gem', *cmd, **kwargs)
1271
+ return ret if cmd.empty?
1272
+
1273
+ ret.merge(preopts)
997
1274
  end
998
1275
 
999
1276
  def bundle_session(*cmd, **kwargs)
1000
- session('bundle', *cmd, *preopts, **kwargs).tap { append_nocolor }
1277
+ ret = session('bundle', *cmd, **kwargs)
1278
+ return ret if cmd.empty?
1279
+
1280
+ append_nocolor
1281
+ ret.merge(preopts)
1001
1282
  end
1002
1283
 
1003
1284
  def rake_session(*cmd, **kwargs)
@@ -1043,7 +1324,7 @@ module Squared
1043
1324
  end
1044
1325
 
1045
1326
  def preopts
1046
- verbosetype > 1 ? ['--verbose'] : []
1327
+ verbose? && !session_arg?('quiet') ? ['--verbose'] : []
1047
1328
  end
1048
1329
 
1049
1330
  def variables
@@ -1051,11 +1332,10 @@ module Squared
1051
1332
  end
1052
1333
 
1053
1334
  def rakefile
1054
- if @rakefile.nil?
1055
- file = Rake::Application::DEFAULT_RAKEFILES.find { |val| basepath(val).exist? }
1056
- @rakefile = !file.nil? && basepath(file)
1057
- end
1058
- @rakefile || nil
1335
+ return @rakefile || nil unless @rakefile.nil?
1336
+
1337
+ file = Rake::Application::DEFAULT_RAKEFILES.find { |val| exist?(val) }
1338
+ @rakefile = file ? basepath(file) : false
1059
1339
  end
1060
1340
 
1061
1341
  def rakepwd
@@ -1077,6 +1357,38 @@ module Squared
1077
1357
  end
1078
1358
  end
1079
1359
 
1360
+ def bundleopts(*args)
1361
+ case args.first
1362
+ when :install, :update
1363
+ args << :install_a
1364
+ when :add, :plugin
1365
+ args << :common_git
1366
+ when :binstubs, :cache
1367
+ args << :common_all
1368
+ end
1369
+ OPT_BUNDLE[:common] + args.flat_map { |name| OPT_BUNDLE.fetch(name, []) }
1370
+ end
1371
+
1372
+ def gemopts(*args)
1373
+ case args.first
1374
+ when :install, :update
1375
+ args << :common_url << :common_domain << :install_a
1376
+ when :dependency, :info, :outdated, :search, :specification
1377
+ args << :common_url << :common_domain
1378
+ when :fetch, :list
1379
+ args << :common_url
1380
+ when :owner, :push, :yank
1381
+ args << :common_otp
1382
+ when :package, :pack
1383
+ args << :cache
1384
+ end
1385
+ case args.first
1386
+ when :info, :list, :search
1387
+ args << :common_all
1388
+ end
1389
+ OPT_GEM[:common] + args.flat_map { |name| OPT_GEM.fetch(name, []) }
1390
+ end
1391
+
1080
1392
  def gempwd
1081
1393
  return unless !pwd? && semgte?(Gem::VERSION, '3.4.2')
1082
1394
 
@@ -1084,20 +1396,20 @@ module Squared
1084
1396
  end
1085
1397
 
1086
1398
  def gemfile
1087
- if @gemfile.nil?
1088
- @gemfile = [project, name].map! { |val| basepath("#{val}.gemspec") }
1089
- .concat(path.glob('*.gemspec'))
1090
- .find(&:exist?) || false
1091
- end
1092
- @gemfile || nil
1399
+ return @gemfile || nil unless @gemfile.nil?
1400
+
1401
+ @gemfile = [project, name].map! { |val| basepath("#{val}.gemspec") }
1402
+ .concat(path.glob('*.gemspec'))
1403
+ .find(&:exist?) || false
1093
1404
  end
1094
1405
 
1095
1406
  def gemlib
1096
- @gemlib ||= Set.new(['lib']).yield_self do |lib|
1407
+ @gemlib ||= begin
1408
+ lib = Set.new(['lib'])
1097
1409
  if (spec = gemspec)
1098
1410
  lib.merge(spec.require_paths || [])
1099
1411
  end
1100
- lib.select { |file| basepath(file).exist? }
1412
+ lib.select { |file| exist?(file) }
1101
1413
  end
1102
1414
  end
1103
1415
 
@@ -1106,9 +1418,7 @@ module Squared
1106
1418
  end
1107
1419
 
1108
1420
  def gemdir?
1109
- return false unless @gemdir
1110
-
1111
- @gemdir.exist? && !@gemdir.empty?
1421
+ !@gemdir.nil? && @gemdir.exist? && !@gemdir.empty?
1112
1422
  end
1113
1423
  end
1114
1424