squared 0.5.23 → 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,54 +24,127 @@ 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 local 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,
29
- outdated: %w[filter-major filter-minor filter-patch filter-strict groups local parseable porcelain pre
30
- only-explicit strict 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
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,
46
+ outdated: %w[filter-major filter-minor filter-patch groups local parseable pre only-explicit strict
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=q].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
121
+ }.freeze
122
+ }.freeze
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
60
137
  }.freeze
61
138
  }.freeze
62
- private_constant :GEMFILE, :DIR_RUBY, :OPT_RUBY, :OPT_BUNDLE, :OPT_GEM
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
70
145
 
71
146
  def bannerargs
72
- %i[dependfile gemname gemdir].freeze
147
+ %i[dependfile gemname].freeze
73
148
  end
74
149
 
75
150
  def config?(val)
@@ -80,21 +155,14 @@ 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
  })
95
165
 
96
- attr_reader :gemdir
97
-
98
166
  def initialize(*, autodetect: false, gemspec: nil, asdf: 'ruby', **kwargs)
99
167
  super
100
168
  if @pass.include?(Ruby.ref)
@@ -111,7 +179,7 @@ module Squared
111
179
  elsif gemspec
112
180
  basepath(gemspec.include?('.') ? gemspec : "#{gemspec}.gemspec")
113
181
  end
114
- return if !@output[0].nil? || !@copy.nil? || version || @autodetect || !rakefile
182
+ return unless rakefile && @output[0].nil? && @copy.nil? && !version && !@autodetect
115
183
 
116
184
  begin
117
185
  File.foreach(rakefile) do |line|
@@ -128,14 +196,6 @@ module Squared
128
196
  end
129
197
  end
130
198
 
131
- def gemdir=(val)
132
- @gemdir = if val.is_a?(Pathname)
133
- val
134
- else
135
- Pathname.new(val).realdirpath rescue nil
136
- end
137
- end
138
-
139
199
  def ref
140
200
  Ruby.ref
141
201
  end
@@ -156,16 +216,15 @@ module Squared
156
216
  format_desc action, nil, "task+,opts*|#{indexchar}index+|#,pattern*"
157
217
  task action, [:command] do |_, args|
158
218
  if args.command == '#'
159
- format_list(raketasks, "rake[#{indexchar}N]", 'tasks', grep: args.extras, from: rakefile,
160
- each: ->(val) { val[0] + val[1].to_s })
219
+ format_list(raketasks, "rake[#{indexchar}N]", 'tasks', grep: args.extras, from: rakefile, &:join)
161
220
  else
162
- args, opts = args.to_a.partition { |val| indexitem(val) }
163
- if args.empty?
164
- rake(opts: opts)
221
+ items, opts = args.to_a.partition { |val| indexitem(val) }
222
+ if items.empty?
223
+ rake(banner: true, opts: opts)
165
224
  else
166
225
  tasks = raketasks
167
- while (n, pre = indexitem(args.shift))
168
- if (item = tasks[n - 1])
226
+ while (n, pre = indexitem(items.shift))
227
+ if (item = tasks[n.pred])
169
228
  cmd = pre ? "#{pre} #{item.first}" : item.first
170
229
  elsif exception
171
230
  indexerror n, tasks
@@ -174,9 +233,9 @@ module Squared
174
233
  next
175
234
  end
176
235
  if opts.empty?
177
- rake cmd
236
+ rake(cmd, banner: true)
178
237
  else
179
- rake(cmd + shell_escape("[#{opts.join(',')}]"))
238
+ rake(cmd + "[#{opts.join(',')}]", banner: true)
180
239
  opts.clear
181
240
  end
182
241
  end
@@ -186,42 +245,39 @@ module Squared
186
245
  when 'irb'
187
246
  format_desc action, nil, 'opts*,args*|:'
188
247
  task action do |_, args|
189
- args = args.to_a
190
- name = gemlib.any? { |file| basepath(file).join("#{gemname}.rb").exist? } ? gemname : nil
191
- irb(name, args, args: (readline('Enter file [arguments]', force: false) if args.delete(':')))
192
- end
193
- else
194
- format_desc(action, nil, 'opts*', before: case action
195
- when 'cache', 'check' then nil
196
- else 'command+'
197
- end)
198
- task action do |_, args|
199
- 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)
200
252
  end
201
253
  end
202
254
  else
203
255
  namespace action do
204
256
  flags.each do |flag|
205
257
  case action
206
- when 'install', 'update', 'outdated'
207
- format_desc action, flag, 'opts*'
258
+ when 'outdated'
259
+ format_desc action, flag, 's/elect?,u/pdate?,opts*'
208
260
  task flag do |_, args|
209
261
  __send__ action, flag, args.to_a
210
262
  end
211
263
  when 'gem'
212
264
  case flag
213
265
  when :outdated
214
- 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*'
215
267
  task flag, [:semver] do |_, args|
216
- case (filter = args.semver)
217
- when 'major', 'minor', 'patch', 'interactive', 'i'
218
- filter = 'interactive' if filter == 'i'
219
- args = args.extras
220
- else
221
- filter = nil
222
- args = args.to_a
223
- end
224
- 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
+ })
225
281
  end
226
282
  when :build, :push, :exec, :update
227
283
  format_desc(action, flag, 'opts*', after: case flag
@@ -230,13 +286,60 @@ module Squared
230
286
  when :update then 'name*'
231
287
  end)
232
288
  task flag do |_, args|
233
- gem! flag, args.to_a
289
+ gem(flag, opts: args.to_a, banner: true)
234
290
  end
235
- else
291
+ when :install, :uninstall, :pristine
236
292
  format_desc(action, flag, 'opts*', after: flag == :pristine ? 'name*|name?@version' : 'name*')
237
293
  task flag do |_, args|
238
- args = param_guard(action, flag, args: args.to_a)
239
- 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)
240
343
  end
241
344
  end
242
345
  when 'ruby'
@@ -244,35 +347,34 @@ module Squared
244
347
  when :file
245
348
  format_desc action, flag, 'path,opts*,args*'
246
349
  task flag, [:rb] do |_, args|
247
- file = args.rb
248
350
  opts = args.extras
249
- args = if file && !file.include?('*')
250
- ENV['RUBY_ARGS']
251
- else
252
- a, b, c = choice_index('Select a file', Dir.glob(file || '*.rb', base: path),
253
- values: (file ? [] : ['Options']).push('Arguments'),
254
- series: true)
255
- if file
256
- file = a
257
- b
258
- else
259
- file = a
260
- opts.concat(OptionPartition.strip(b))
261
- c
262
- end
263
- end
264
- 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)
265
367
  end
266
368
  when :script
267
369
  format_desc action, flag, 'opts*'
268
370
  task flag do |_, args|
269
- command = ENV['RUBY_E'] || readline('Enter script', force: true, multiline: ['##', ';'])
270
- 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)
271
373
  end
272
374
  when :version
273
375
  format_desc action, flag
274
376
  task flag do
275
- ruby flag
377
+ ruby(flag: flag)
276
378
  end
277
379
  end
278
380
  end
@@ -303,9 +405,7 @@ module Squared
303
405
  end
304
406
  end
305
407
 
306
- def copy(from: gemlib, into: gemdir, override: false, **kwargs)
307
- return if @copy == false
308
-
408
+ def copy(from: gemlib, into: @gemdir, override: false, **kwargs)
309
409
  glob = kwargs[:include]
310
410
  pass = kwargs[:exclude]
311
411
  if @copy && !override
@@ -315,6 +415,8 @@ module Squared
315
415
  glob = @copy[:include] if @copy.key?(:include)
316
416
  pass = @copy[:exclude] if @copy.key?(:exclude)
317
417
  into = @copy[:into] if @copy.key?(:into)
418
+ elsif @copy == false
419
+ return
318
420
  end
319
421
  return unless into
320
422
 
@@ -327,11 +429,9 @@ module Squared
327
429
  b = dest + val
328
430
  c = glob[i] || glob.first
329
431
  log.info "cp #{a + c} #{b}"
330
- begin
331
- copy_dir(a, b, c, pass: pass, verbose: verbosetype > 0)
332
- rescue StandardError => e
333
- on_error e, :copy
334
- end
432
+ copy_dir(a, b, c, pass: pass, verbose: verbosetype > 0)
433
+ rescue StandardError => e
434
+ on_error e, :copy
335
435
  end
336
436
  on :last, :copy
337
437
  end
@@ -339,14 +439,21 @@ module Squared
339
439
  def outdated(flag = nil, opts = [], sync: invoked_sync?('outdated', flag))
340
440
  cmd = bundle_output 'outdated'
341
441
  if flag
442
+ se = (opts.delete('s') || opts.delete('select')) && !stdin?
443
+ up = opts.delete('u') || opts.delete('update')
342
444
  cmd << "--#{flag}"
343
- append_bundle(opts, OPT_BUNDLE[:outdated] + OPT_BUNDLE[:common], target: cmd)
445
+ OptionPartition.new(opts, bundleopts(:outdated), cmd, project: self)
446
+ .clear
344
447
  end
345
448
  log.info cmd.to_s
346
449
  on :first, :outdated
347
450
  banner = format_banner cmd.to_s
348
- print_item banner if sync
451
+ items = if sync
452
+ print_item banner
453
+ [] if flag && (se || up)
454
+ end
349
455
  pwd_set(from: :outdated) do
456
+ tc = theme[:current]
350
457
  start = 0
351
458
  found = 0
352
459
  major = 0
@@ -403,26 +510,26 @@ module Squared
403
510
  end
404
511
  end
405
512
  end
513
+ name = line[/^\S+/, 0]
406
514
  unless styles.empty?
407
515
  case styles.first
408
516
  when :green
409
- 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+)(.+)$/))
410
518
  found += 1
411
519
  when :yellow
412
520
  found += 1
413
521
  end
414
- if theme[:current]
415
- line = sub_style(line, styles: theme[:current], pat: /^(.+)(#{Regexp.escape(c)})(.+)$/, index: 2)
416
- end
417
- line = sub_style(line, *colormap(styles), pat: /^((?:\S+\s+){2})(#{Regexp.escape(l)})(.*)$/,
418
- 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))
419
525
  end
420
526
  end
421
- out.call('%2d. %s' % [start, line])
527
+ items&.push([line, name])
528
+ out.call('%2d. %s' % [start, line]) unless se
422
529
  elsif line.start_with?('Gem')
423
530
  unless stdin?
424
- sub = { pat: /^(.+)(?<!\dm)(Gem|Latest)(.+)$/, styles: theme[:header], index: 2 }
425
- 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))
426
533
  end
427
534
  else
428
535
  next
@@ -434,6 +541,15 @@ module Squared
434
541
  puts buffer
435
542
  end
436
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
437
553
  begin
438
554
  if major == 0 && dependfile.read =~ /\b(?:source\s+(["'])((?~\1))\1|remote:\s+(\S+))/
439
555
  status = ($2 || $3).chomp('/')
@@ -442,52 +558,18 @@ module Squared
442
558
  rescue StandardError => e
443
559
  log.debug e
444
560
  ensure
445
- status ||= 'Updates are available'
561
+ status ||= (items ? 'Updates were made' : 'Updates are available')
446
562
  end
447
563
  puts print_footer(empty_status(status, 'major', major, always: !right), right: right)
448
- elsif start == 0
564
+ elsif start == 0 && banner
449
565
  puts 'No updates were found'
450
566
  end
451
567
  end
452
568
  on :last, :outdated
453
569
  end
454
570
 
455
- def install(flag, opts = [])
456
- bundle_session 'install', "--#{flag}"
457
- op = append_bundle opts, OPT_BUNDLE[:install_base] + OPT_BUNDLE[:install] + OPT_BUNDLE[:common]
458
- if op.arg?('force')
459
- op.delete('--force')
460
- if flag != :redownload
461
- op << '--redownload'
462
- elsif (lock = basepath('Gemfile.lock')).exist?
463
- config = basepath '.bundle', 'config'
464
- lock.delete unless config.exist? && config.read.match?(/\bBUNDLE_FROZEN:\s+"true"/)
465
- end
466
- end
467
- run_rb(from: :install)
468
- end
469
-
470
- def update(flag, opts = [])
471
- bundle_session 'update', "--#{flag}"
472
- append_bundle(opts, OPT_BUNDLE[:install_base] + OPT_BUNDLE[:update] + OPT_BUNDLE[:common],
473
- append: flag == :all ? nil : /\A[a-z-]+=/)
474
- run_rb(from: :update)
475
- end
476
-
477
- def ruby(flag, opts = [], file: nil, command: nil, args: nil)
478
- case flag
479
- when :file, :script
480
- op = OptionPartition.new(opts, OPT_RUBY[:ruby], ruby_session, project: self, args: true)
481
- if command
482
- op << quote_option('e', command, option: false)
483
- elsif file
484
- op.unshift(basepath(file))
485
- end
486
- unless op.arg?('e')
487
- op.push(args) if args
488
- op.append(delim: true, escape: false, quote: false) unless op.empty?
489
- end
490
- when :version
571
+ def ruby(*args, flag: nil, sync: true, banner: verbose?, with: nil, pass: PASS_RUBY[:ruby], **kwargs)
572
+ if flag == :version
491
573
  pwd_set do
492
574
  out = []
493
575
  order = { 'rbenv' => -1, 'rvm' => -1, 'asdf' => -1, 'chruby' => -1 }
@@ -530,15 +612,14 @@ module Squared
530
612
  .each do |val|
531
613
  next unless val.empty? || File.exist?(val.sub('$HOME', Dir.home))
532
614
 
533
- 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 ') }
534
616
  ver = '.ruby-version'
535
617
  out << trim.call(case (cmd = File.basename(val))
536
618
  when 'rvm'
537
619
  `rvm current`[/^\S+/, 0]
538
620
  when 'rbenv'
539
- `rbenv version-name`.yield_self do |name|
540
- (name =~ SEM_VER) == 0 ? "ruby #{name}" : name
541
- end
621
+ name = `rbenv version-name`
622
+ name.match?(SEM_VER) ? "ruby #{name}" : name
542
623
  when 'chruby.sh'
543
624
  chruby = session_output 'source', val
544
625
  `#{chruby.with('ruby --version')}`
@@ -547,9 +628,8 @@ module Squared
547
628
  cmd = 'asdf'
548
629
  ver = '.tool-versions'
549
630
  opt = [@asdf.first]
550
- opt.unshift('--no-header') unless @@asdf[1] == 15
551
- exit 1 unless (cur = `asdf current #{opt.join(' ')}`[/^\S+\s+\S+/, 0])
552
- cur.sub(/\s+/, ' ')
631
+ opt.unshift('--no-header') unless @@asdf.version == 15
632
+ `asdf current #{opt.join(' ')}`[/^\S+\s+\S+/, 0].sub(/\s+/, ' ')
553
633
  else
554
634
  ver = nil
555
635
  `ruby --version`
@@ -597,138 +677,199 @@ module Squared
597
677
  end
598
678
  return
599
679
  end
600
- run_rb(banner: false, from: :"ruby:#{flag}")
601
- end
602
-
603
- def gem!(flag, opts = [], filter: nil)
604
- cmd = gem_session
605
- case flag
606
- when :outdated
607
- cmd << gempwd << flag
608
- else
609
- 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
610
693
  end
611
- list = OPT_GEM[flag] + OPT_GEM[:common]
612
- from = :"gem:#{flag}"
613
694
  case flag
614
- when :install, :update
615
- 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)
616
702
  end
617
- op = OptionPartition.new(opts, list, cmd, project: self, no: OPT_GEM[:no][flag])
618
- op.each do |opt|
619
- if !opt.match?(/\A[A-Za-z\d][A-Za-z\d_.-]*\z/) && %i[install uninstall update pristine].include?(flag)
620
- op.errors << opt
621
- else
622
- op.found << opt
623
- 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
624
717
  end
625
- op.swap
718
+ opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
626
719
  case flag
627
- 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
628
728
  op.clear
629
- cmd = cmd.done
729
+ cmd = session_done op.target
630
730
  log.info cmd
631
731
  on :first, from
632
- print_item format_banner(cmd)
633
- major = 0
634
- minor = 0
635
- patch = 0
636
- 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 }
637
751
  pwd_set(pass: !gempwd.nil?, from: from) do
638
- items = [[%w[Gem Current Latest], nil]]
752
+ rows = [[%w[Gem Current Latest], nil]]
639
753
  IO.popen(cmd).each do |line|
640
754
  if line =~ /^(\S+) \((\S+) < ([^)]+)\)$/
641
755
  cur = semscan $2
642
756
  lat = semscan $3
643
- items << [$~.to_a.drop(1), if semmajor?(cur, lat)
644
- 1
645
- else
646
- cur[2] == lat[2] ? 3 : 2
647
- end]
757
+ rows << [$~.to_a.drop(1), if semmajor?(cur, lat)
758
+ 1
759
+ else
760
+ cur[2] == lat[2] ? 3 : 2
761
+ end]
648
762
  else
649
- puts line
763
+ out.call(line)
650
764
  end
651
765
  end
652
- if items.size > 1
653
- pad = [items.size.to_s.size + 1, 3].max
766
+ if rows.size > 1
767
+ pad = [rows.size.to_s.size.succ, 3].max
654
768
  d = 0
655
769
  e = 0
656
770
  f = 0
657
771
  j = 0
658
772
  queue = nil
659
- items.each do |item|
660
- a, b, c = item.first
773
+ rows.each do |row|
774
+ a, b, c = row.first
661
775
  d = a.size if a.size > d
662
776
  e = b.size if b.size > e
663
777
  f = c.size if c.size > f
664
778
  end
665
- items.each_with_index do |item, i|
779
+ rows.each_with_index do |row, i|
666
780
  next if i == 0 && stdin?
667
781
 
668
- a, b, c = item.first
782
+ a, b, c = row.first
669
783
  if i == 0
670
784
  line = '%-*s %-*s %*s %*s' % [pad, ' #', d, a, e, b, f, c]
671
785
  n = line.size
672
- 2.times do
673
- line = sub_style(line, pat: /^(.+)(?<!\dm)(#{a}|#{c})(.*)$/, styles: theme[:header], index: 2)
674
- end
675
- 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
676
794
  else
677
795
  g = a.ljust(d)
678
796
  pat = [/^([^.]+\.)([^.]+\..+)$/, /^([^.]+\.[^.]+\.)(.+)$/]
679
797
  pre = b.start_with?('0.')
680
798
  latest = [theme[:latest]]
681
- case item.last
799
+ case row.last
682
800
  when 1
683
- case filter
801
+ case semver
684
802
  when 'major'
685
- update << a
803
+ update&.push(a)
686
804
  when 'minor', 'patch'
687
805
  next
688
806
  end
689
- g = sub_style(g, styles: theme[:major])
690
- major += 1
691
- styles = %i[green bold]
692
- pat = pre ? pat.first : nil
693
- 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
694
814
  when 2
695
- case filter
815
+ case semver
696
816
  when 'major', 'minor'
697
- update << a
817
+ update&.push(a)
698
818
  when 'patch'
699
819
  next
700
820
  end
701
- g = sub_style(g, styles: theme[:active])
702
- minor += 1
703
- styles = %i[green]
704
- 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
705
827
  else
706
- case filter
828
+ case semver
707
829
  when 'major', 'minor', 'patch'
708
- update << a
830
+ update&.push(a)
831
+ end
832
+ unless stdin?
833
+ styles = %i[yellow]
834
+ pat = pat.last
709
835
  end
710
- patch += 1
711
- styles = %i[yellow]
712
- pat = pat.last
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))
713
843
  end
714
- b = sub_style(b.rjust(e), *colormap(styles), pat: pat, index: 2)
715
- h = sub_style(c.rjust(f), styles: latest.flatten.compact, pat: pat, index: 2)
716
844
  j += 1
717
845
  if queue
718
- puts queue
846
+ out.call(queue)
719
847
  queue = nil
720
848
  end
721
- puts '%*s %s %s %s' % [pad, "#{j}.", g, b, h]
722
- 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)
723
856
  end
724
857
  end
725
858
  end
726
859
  end
727
- if major + minor + patch == 0
860
+ unless sync
861
+ print_item banner
862
+ puts buffer
863
+ end
864
+ if major.sum == 0
728
865
  puts 'No updates were found'
729
866
  else
730
- unless update.empty?
731
- 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'
732
873
  option('document', prefix: 'gem', ignore: false) do |val|
733
874
  cmd << case val
734
875
  when '0', 'false'
@@ -746,18 +887,40 @@ module Squared
746
887
  end
747
888
  end
748
889
  cmd.merge(update)
749
- run(cmd, banner: false, from: :'gem:update')
890
+ run(cmd, sync: sync, banner: false, from: :'gem:update')
750
891
  end
751
- print_status(major, minor, patch, from: :outdated)
892
+ print_status(*major, from: :outdated)
752
893
  end
753
894
  on :last, from
754
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
755
918
  when :build
756
919
  if op.empty?
757
- raise_error('gemspec not found', hint: project) unless gemfile
920
+ raise_error Errno::ENOENT, 'gemspec', hint: project unless gemfile
758
921
  op.add_path(gemfile)
759
922
  else
760
- op.add_path(op.shift)
923
+ op.add_first(path: true)
761
924
  .clear(pass: false)
762
925
  end
763
926
  when :push
@@ -765,41 +928,51 @@ module Squared
765
928
  file = basepath(if !n && (spec = gemspec)
766
929
  "#{spec.name}-#{spec.version}.gem"
767
930
  else
768
- choice_index 'Select a file', Dir.glob('*.gem', base: path)
931
+ choice_index('Select a file', Dir.glob('*.gem', base: path), force: true)
769
932
  end)
770
933
  else
771
- file = op.shift.yield_self { |val| val.include?('.') ? val : "#{val}.gem" }
772
- 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?
773
937
  end
774
- raise_error('gem not found', hint: file) unless op.exist?(file)
775
938
  op.add_path(file)
776
- run_rb(from: from, interactive: "Push #{sub_style(gemname, styles: theme[:active])}")
777
- return
939
+ return run_rb(from: from, interactive: ['Push', 'N', gemname]) unless with || !banner
778
940
  when :exec
779
941
  min = if op.arg?('g', 'gem')
780
942
  1
781
- elsif op.empty?
943
+ elsif !op.empty?
944
+ op.add_first
945
+ 0
946
+ elsif args.empty?
782
947
  op << basic_option('gem', gemname)
783
948
  1
784
949
  else
785
- op << op.shift
786
950
  0
787
951
  end
952
+ op.concat(args)
788
953
  if (args = command_args(op.extras, min: min, force: min == 1 && op.empty?))
789
954
  op.push(args)
790
955
  end
791
956
  op.append(quote: false)
792
957
  when :update
793
- unless op.arg?('system')
794
- if op.empty?
795
- op << gemname
796
- else
797
- op.append
798
- end
958
+ if op.arg?('system')
959
+ op.add_first(quote: false) { |val| val if val.match?(SEM_VER) }
960
+ else
961
+ op.append
799
962
  end
800
- op.clear(errors: true)
801
963
  when :install, :uninstall, :pristine
802
- 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?
803
976
  if op.arg?('all')
804
977
  if flag == :pristine
805
978
  append_repeat 'skip', op.extras
@@ -807,80 +980,191 @@ module Squared
807
980
  else
808
981
  op.clear
809
982
  end
810
- elsif (n = op.index { |val| val.match?(/(\A|[\w.-])@\d/) })
811
- name = op.delete_at(n)
983
+ elsif (n = op.index { |val| val.match?(/(\A|[a-z])@\d/) })
984
+ name = op.remove_at(n)
812
985
  pre, ver = if (n = name.index('@')) == 0
813
986
  [gemname, name[1..-1]]
814
987
  else
815
- [name[0, n], name[(n + 1)..-1]]
988
+ [name[0, n], name[n.succ..-1]]
816
989
  end
817
- op.adjoin(pre, basic_option('version', ver))
990
+ op.adjoin(pre, shell_option('version', ver))
818
991
  .clear
819
- elsif flag == :install
992
+ end
993
+ if flag == :install
820
994
  op.append_any
821
995
  else
822
996
  op.append
823
997
  end
824
- op.clear(errors: true)
825
- op.delim << readline('Enter command [args]', force: true) if flag == :install && op.remove(':')
826
- else
998
+ op << '--' << post if post
999
+ when :check, :cleanup, :contents, :fetch, :list, :lock, :rdoc
827
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
828
1033
  end
829
- 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)
830
1037
  end
831
1038
 
832
- def bundle(flag, *args)
833
- cmd = bundle_session flag
834
- args = case flag
835
- when 'exec', 'cache', 'check'
836
- list = OPT_BUNDLE[flag.to_sym] + OPT_BUNDLE[:common]
837
- OptionPartition.new(args, list, cmd, project: self, args: flag == 'exec').extras
838
- else
839
- args.flatten
840
- 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)
841
1050
  case flag
842
- when 'exec', 'config'
843
- cmd << readline('Enter arguments', force: true) if args.empty?
844
- when 'cache', 'check'
845
- option_clear args
846
- args.clear
1051
+ when :cache, :check, :clean, :doctor, :init, :install, :lock, :pack, :package, :platform
1052
+ pre = true
1053
+ opts.concat(args)
847
1054
  end
848
- cmd.merge(args)
849
- run(from: :"bundle:#{flag}")
850
- end
851
-
852
- def rake(*args, opts: [])
853
- op = OptionPartition.new(opts, OPT_RUBY[:rake], [(quote_option('f', rakefile) if rakefile)].compact,
854
- project: self)
855
- args.concat(op.extras)
856
- if args.empty?
857
- 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
858
1133
  else
859
- args.flatten!
1134
+ op.clear
860
1135
  end
861
- cmd = rake_output(*op.to_a)
862
- args.map! { |val| cmd.temp(val) }
863
- 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)
864
1148
  end
865
1149
 
866
- 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)
867
1152
  op = OptionPartition.new(opts, OPT_RUBY[:irb], session('irb'), project: self, first: [/\.rb$/])
868
- r = args ? [] : ['bundler/setup']
869
- r << name if name
870
- r.each { |val| op << shell_option('r', val, merge: true) }
871
- Array(path).each { |val| op << quote_option('I', val, merge: true) }
872
- if args
873
- op.delim << args
874
- op.clear
875
- else
876
- op.append(delim: true)
877
- end
878
- 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)
879
1162
  end
880
1163
 
881
1164
  def gemspec
882
- @gemspec = !gemfile.nil? && Gem::Specification.load(gemfile.to_s) rescue false if @gemspec.nil?
883
- @gemspec || nil
1165
+ return @gemspec || nil unless @gemspec.nil?
1166
+
1167
+ @gemspec = gemfile && Gem::Specification.load(gemfile.to_s) rescue false
884
1168
  end
885
1169
 
886
1170
  def gemname
@@ -893,59 +1177,61 @@ module Squared
893
1177
 
894
1178
  def copy?
895
1179
  return true if @copy.is_a?(Hash) ? copy[:into] : super
896
- return gemdir? if gemdir
897
- return false unless @autodetect
1180
+ return gemdir? if @gemdir
898
1181
 
899
- set = lambda do |val, path|
900
- base = Pathname.new(path.strip)
901
- return false unless base.join(gempath(val, 'specifications')).exist?
902
-
903
- log.warn "using version #{val} (given #{version})" if version && version != val
904
- self.version = val
905
- self.gemdir = base + gempath
906
- end
907
1182
  if version
908
1183
  begin
909
1184
  case @autodetect
910
1185
  when 'rvm'
911
- self.gemdir = pwd_set { `rvm info homes` }[/^\s+gem:\s+"(.+)"$/, 1]
1186
+ pwd_set { `rvm info homes` }[/^\s+gem:\s+"(.+)"$/, 1]
912
1187
  when 'rbenv'
913
1188
  if pwd_set { `rbenv which ruby` } =~ %r{^(.+[\\/]versions[\\/](\d\.\d)\.[^\\/]+)[\\/]bin[\\/]ruby$}
914
- self.gemdir = File.join($1, 'lib/ruby/gems', "#{$2}.0")
1189
+ File.join($1, 'lib/ruby/gems', "#{$2}.0")
915
1190
  end
916
1191
  when 'asdf'
917
- val = pwd_set { `asdf where ruby`.chomp }
918
- self.gemdir = File.join(val, 'lib/ruby/gems', "#{$1}.0") if val =~ /(\d\.\d)\.[^.]+$/
1192
+ pwd_set { `asdf where ruby` }.yield_self do |val|
1193
+ val =~ /(\d\.\d)\.[^.]+$/ && File.join(val, 'lib/ruby/gems', "#{$1}.0")
1194
+ end
919
1195
  when /bundler?/
920
- path = pwd_set { `bundle env` }[/^\s+Gem Path\s+(.+)$/, 1]
921
- self.gemdir = path.split(File::PATH_SEPARATOR).find { |val| Dir.exist?(val) }
922
- else
923
- self.gemdir = ENV['GEM_HOME'] || ENV['GEM_ROOT']
924
- end
925
- return true if gemdir?
1196
+ pwd_set { `bundle env` }[/^\s+Gem Home\s+(.+)$/, 1]
1197
+ end.tap { |val| @gemdir = Pathname.new(val) if val }
926
1198
  rescue StandardError => e
927
1199
  log.debug e
928
1200
  end
929
- pwd_set(pass: !gempwd.nil?) do
930
- out = `#{gem_output(gempwd, 'list --local -d', gemname)}`
931
- if out =~ /#{Regexp.escape(gemname)}\s+\((.+)\)$/
932
- split_escape($1)
933
- .unshift(version)
934
- .uniq
935
- .each do |val|
936
- next unless out =~ /(?:\(#{Regexp.escape(val)}[^)]*\)|Installed at):\s+(.+)$/
937
-
938
- return gemdir? if set.call(val, $1)
939
- end
940
- end
1201
+ return true if gemdir?
1202
+ end
1203
+ return false unless @autodetect
1204
+
1205
+ set = lambda do |val, path|
1206
+ if (ver = version) && ver != val
1207
+ log.warn "using version #{val}".subhint("given #{ver}")
941
1208
  end
942
- self.gemdir = Pathname.new(Gem.dir) + gempath
1209
+ self.version = val
1210
+ @gemdir = Pathname.new(path.strip) + gempath
1211
+ end
1212
+ if version
1213
+ opt = gempwd
1214
+ pwd_set(pass: !opt.nil?) do
1215
+ out = `#{gem_output(opt, 'list --local -d', gemname)}`
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
1227
+ end
1228
+ @gemdir = Pathname.new(Gem.dir) + gempath
943
1229
  else
944
1230
  parse = lambda do |path|
945
1231
  next unless path
946
1232
 
947
1233
  lib = Regexp.new(['', 'gems', "#{gemname}-([^#{File::SEPARATOR}]+)", ''].join(File::SEPARATOR))
948
- if (ver = path[lib, 1]) && (val = path[/\A(.+)#{Regexp.escape(gempath(ver))}/, 1])
1234
+ if (ver = path[lib, 1]) && (val = path[/\A(.+)#{gempath(ver[1])}/, 1])
949
1235
  set.call(ver, val)
950
1236
  end
951
1237
  end
@@ -953,8 +1239,8 @@ module Squared
953
1239
  target = RUBY_VERSION.start_with?('2.6') ? RubyVM : $LOAD_PATH
954
1240
  parse.call(target.resolve_feature_path(gemname)&.last)
955
1241
  end
956
- if !gemdir && !pwd_set { parse.call(`#{bundle_output('show', gemname)}`) }
957
- raise_error 'gems directory not found'
1242
+ unless @gemdir || pwd_set { parse.call(`#{bundle_output('show', gemname)}`) }
1243
+ raise_error Errno::ENOENT, 'gems home'
958
1244
  end
959
1245
  end
960
1246
  rescue StandardError => e
@@ -967,7 +1253,7 @@ module Squared
967
1253
  end
968
1254
 
969
1255
  def outdated?
970
- dependtype > 0 && !task_pass?('outdated')
1256
+ dependtype > 0
971
1257
  end
972
1258
 
973
1259
  private
@@ -976,36 +1262,23 @@ module Squared
976
1262
  run(banner: !@session&.include?('--quiet'), **kwargs)
977
1263
  end
978
1264
 
979
- def append_bundle(opts, list, target: @session, append: nil)
980
- op = OptionPartition.new(opts, list, target, project: self)
981
- if append
982
- if append.is_a?(Regexp)
983
- op.each do |opt|
984
- if opt.match?(append)
985
- op.errors << opt
986
- else
987
- op.found << opt
988
- end
989
- end
990
- op.swap.clear(errors: true)
991
- end
992
- op.append(escape: true)
993
- else
994
- op.clear
995
- end
996
- op
997
- end
998
-
999
1265
  def ruby_session(*cmd, **kwargs)
1000
1266
  session('ruby', *preopts, *cmd, **kwargs)
1001
1267
  end
1002
1268
 
1003
1269
  def gem_session(*cmd, **kwargs)
1004
- session('gem', *cmd, *preopts, **kwargs)
1270
+ ret = session('gem', *cmd, **kwargs)
1271
+ return ret if cmd.empty?
1272
+
1273
+ ret.merge(preopts)
1005
1274
  end
1006
1275
 
1007
1276
  def bundle_session(*cmd, **kwargs)
1008
- 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)
1009
1282
  end
1010
1283
 
1011
1284
  def rake_session(*cmd, **kwargs)
@@ -1029,19 +1302,19 @@ module Squared
1029
1302
  end
1030
1303
 
1031
1304
  def config_get(key)
1032
- out = pwd_set { `#{bundle_output('config get --parseable', key)}`.chomp }
1033
- return unless out =~ /\A([^=]+)=(.*)\z/ && $1 == key
1034
-
1035
- case (out = $2)
1305
+ ret = if pwd_set { `#{bundle_output('config get --parseable', key)}` } =~ /\A([^=]+)=(.*)\z/ && $1 == key
1306
+ $2.chomp
1307
+ end
1308
+ case ret
1036
1309
  when 'true'
1037
1310
  true
1038
1311
  when '', '[]'
1039
1312
  nil
1040
1313
  else
1041
- if out =~ /\A\[:(.+)\]\z/
1042
- $1.split(', :').map { |val| ((val.delete_prefix!('"') && val.delete_suffix!('"')) || val).to_sym }
1314
+ if ret =~ /\A\[:(.+)\]\z/
1315
+ $1.split(', :').map! { |val| ((val.delete_prefix!('"') && val.delete_suffix!('"')) || val).to_sym }
1043
1316
  else
1044
- out || false
1317
+ ret || false
1045
1318
  end
1046
1319
  end
1047
1320
  end
@@ -1051,7 +1324,7 @@ module Squared
1051
1324
  end
1052
1325
 
1053
1326
  def preopts
1054
- verbosetype > 1 ? ['--verbose'] : []
1327
+ verbose? && !session_arg?('quiet') ? ['--verbose'] : []
1055
1328
  end
1056
1329
 
1057
1330
  def variables
@@ -1059,11 +1332,10 @@ module Squared
1059
1332
  end
1060
1333
 
1061
1334
  def rakefile
1062
- if @rakefile.nil?
1063
- file = Rake::Application::DEFAULT_RAKEFILES.find { |val| basepath(val).exist? }
1064
- @rakefile = !file.nil? && basepath(file)
1065
- end
1066
- @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
1067
1339
  end
1068
1340
 
1069
1341
  def rakepwd
@@ -1085,6 +1357,38 @@ module Squared
1085
1357
  end
1086
1358
  end
1087
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
+
1088
1392
  def gempwd
1089
1393
  return unless !pwd? && semgte?(Gem::VERSION, '3.4.2')
1090
1394
 
@@ -1092,33 +1396,29 @@ module Squared
1092
1396
  end
1093
1397
 
1094
1398
  def gemfile
1095
- if @gemfile.nil?
1096
- @gemfile = [project, name].map! { |val| basepath("#{val}.gemspec") }
1097
- .concat(path.glob('*.gemspec'))
1098
- .find(&:exist?) || false
1099
- end
1100
- @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
1101
1404
  end
1102
1405
 
1103
1406
  def gemlib
1104
- @gemlib ||= Set.new(['lib']).yield_self do |lib|
1407
+ @gemlib ||= begin
1408
+ lib = Set.new(['lib'])
1105
1409
  if (spec = gemspec)
1106
1410
  lib.merge(spec.require_paths || [])
1107
1411
  end
1108
- lib.select { |file| basepath(file).exist? }
1412
+ lib.select { |file| exist?(file) }
1109
1413
  end
1110
1414
  end
1111
1415
 
1112
- def gempath(val = version, dir = 'gems')
1113
- ret = File.join(dir, "#{gemname}-#{val}")
1114
- ret += '.gemspec' if dir == 'specifications'
1115
- ret
1416
+ def gempath(val = version)
1417
+ File.join('gems', "#{gemname}-#{val}")
1116
1418
  end
1117
1419
 
1118
1420
  def gemdir?
1119
- return false unless gemdir
1120
-
1121
- gemdir.exist? && !gemdir.empty?
1421
+ !@gemdir.nil? && @gemdir.exist? && !@gemdir.empty?
1122
1422
  end
1123
1423
  end
1124
1424