squared 0.5.13 → 0.6.1

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,58 +13,134 @@ 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
20
22
  simple-prompt single-irb singleline tracer truncate-echo-on-assignment type-completor verbose
21
- back-trace-limit=i context-mode=i prompt=b prompt-mode=b].freeze
23
+ back-trace-limit=i context-mode=i prompt=b prompt-mode=b].freeze,
24
+ rbs: %w[I=pm r=bm no-stdlib no-collection collection=p log-level=b log-output=p repo=p].freeze
22
25
  }.freeze
23
26
  OPT_BUNDLE = {
24
27
  common: %w[no-color V|verbose r|retry=i].freeze,
28
+ common_all: %w[all all-platforms path=p].freeze,
29
+ common_git: %w[branch=q git=q path=p ref=q].freeze,
30
+ add: %w[optimistic quiet skip-install strict github=q glob=q g|group=q require=q s|source=q
31
+ v|version=q].freeze,
32
+ binstubs: %w[force standalone shebang=q].freeze,
33
+ cache: %w[frozen no-all no-install no-prune quiet cache-path=p gemfile=p].freeze,
34
+ check: %w[dry-run gemfile=p path=p].freeze,
35
+ clean: %w[dry-run force].freeze,
36
+ config: %w[global local].freeze,
37
+ doctor: %w[quiet gemfile=p].freeze,
38
+ exec: %w[gemfile=p].freeze,
39
+ gem: %w[b|bin git no-exe rubocop ci=b e|edit=p? ext=b github-username=q linter=b t|test=b?].freeze,
40
+ init: %w[gemfile=p gemspec=p].freeze,
25
41
  install: %w[frozen no-cache no-prune system binstubs=p? path=p standalone=q? target-rbconfig=p trust-policy=b
26
42
  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,
43
+ install_a: %w[force full-index quiet redownload gemfile=p j|jobs=i].freeze,
44
+ lock: %w[add-checksums conservative full-index local major minor normalize-platforms patch pre print strict
45
+ add-platform=q bundler=b? gemfile=p lockfile=p remove-platform=p update=q?].freeze,
46
+ open: %w[path=p].freeze,
29
47
  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
48
+ update-strict group=q source=q].freeze,
49
+ platform: %w[ruby].freeze,
50
+ plugin: %w[source=q version=q].freeze,
51
+ remove: %w[install].freeze,
52
+ show: %w[outdated paths].freeze,
53
+ update: %w[all conservative local major minor patch pre ruby strict bundler=b? g|group=q source=q].freeze,
54
+ no: {
55
+ gem: %w[changelog ci coc linter mit test].freeze
56
+ }.freeze
34
57
  }.freeze
35
58
  OPT_GEM = {
36
59
  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,
60
+ common_url: %w[B|bulk-threshold=i p|http-proxy=q? s|source=q].freeze,
61
+ common_domain: %w[b|both clear-sources l|local r|remote].freeze,
62
+ common_otp: %w[host=q k|key=b otp=b].freeze,
63
+ common_all: %w[a|all e|exact v|version=q].freeze,
47
64
  build: %w[C=p force strict o|output=p platform=q].freeze,
65
+ 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
66
+ R|re-sign s|sign=p].freeze,
67
+ check: %w[a v|version=q].freeze,
68
+ cleanup: %w[n d|dry-run].freeze,
69
+ contents: %w[l all s|spec-dir=p v|version=q].freeze,
70
+ dependency: %w[R pipe platform=q v|version=q].freeze,
48
71
  exec: %w[conservative g|gem=b v|version=q].freeze,
72
+ fetch: %w[clear-sources platform=q v|version=q].freeze,
73
+ generate_index: %w[update d|directory=p].freeze,
74
+ info: %w[i I].freeze,
75
+ install: %w[v|version=q].freeze,
76
+ install_a: %w[E f w conservative default development development-all explain ignore-dependencies N|no-document
77
+ vendor n|bindir=p build-root=p document=b? g|file=p? i|install-dir=p platform=q
78
+ target-rbconfig=p? P|trust-policy=b without=b].freeze,
79
+ list: %w[d i I].freeze,
80
+ lock: %w[s].freeze,
81
+ open: %w[e|editor=p v|version=q].freeze,
82
+ outdated: %w[C=p platform=q].freeze,
83
+ owner: %w[a|add=q r|remove=q p|http-proxy=q?].freeze,
49
84
  pristine: %w[E all only-executables only-missing-extensions only-plugins n|bindir=p i|install-dir=p skip=b
50
85
  v|version=q].freeze,
86
+ push: %w[attestation=p p|http-proxy=q?].freeze,
87
+ rdoc: %w[all v|version=q].freeze,
88
+ rebuild: %w[C=p diff force strict gemspec=p original=p source=q].freeze,
89
+ search: %w[d i I].freeze,
90
+ signin: %w[otp=b host=q].freeze,
91
+ sources: %w[f c|clear-all l|list u|update a|add=q p|http-proxy=q? r|remove=q].freeze,
92
+ specification: %w[all marshal ruby yaml platform=q v|version=q].freeze,
93
+ uninstall: %w[a D I x vendor n|bindir=p i|install-dir=p platform=q v|version=q].freeze,
94
+ unpack: %w[spec target=p P|trust-policy=b v|version=q],
95
+ update: %w[system=b?].freeze,
96
+ which: %w[a g].freeze,
97
+ yank: %w[platform=q v|version=q].freeze,
51
98
  no: {
99
+ check: %w[alien doctor dry-run gems].freeze,
100
+ cleanup: %w[check-development user-install].freeze,
101
+ contents: %w[lib-only prefix show-install-dir].freeze,
102
+ dependency: %w[http-proxy reverse-dependencies].freeze,
103
+ exec: %w[prerelease].freeze,
104
+ fetch: %w[http-proxy prerelease suggestions].freeze,
105
+ generate_index: %w[compact modern].freeze,
106
+ info: %w[http-proxy installed prerelease versions].freeze,
52
107
  install: %w[env-shebang force format-executable http-proxy lock minimal-deps post-install-message prerelease
53
108
  suggestions user-install wrappers].freeze,
54
- uninstall: %w[abort-on-dependent all check-development executables force format-executable
55
- ignore-dependencies user-install].freeze,
109
+ list: %w[details http-proxy installed prerelease versions].freeze,
110
+ lock: %w[strict].freeze,
56
111
  outdated: %w[http-proxy].freeze,
112
+ owner: %w[http-proxy].freeze,
113
+ pristine: %w[env-shebang extensions].freeze,
57
114
  push: %w[http-proxy].freeze,
58
- exec: %w[prerelease].freeze,
59
- pristine: %w[env-shebang extensions].freeze
115
+ rdoc: %w[overwrite rdoc ri].freeze,
116
+ search: %w[details http-proxy installed prerelease versions].freeze,
117
+ sources: %w[force http-proxy].freeze,
118
+ specification: %w[prerelease].freeze,
119
+ uninstall: %w[abort-on-dependent all check-development executables force format-executable
120
+ ignore-dependencies user-install].freeze,
121
+ which: %w[all gems-first].freeze
60
122
  }.freeze
61
123
  }.freeze
62
- private_constant :GEMFILE, :DIR_RUBY, :OPT_RUBY, :OPT_BUNDLE, :OPT_GEM
124
+ PASS_RUBY = {
125
+ ruby: %w[I disable enable dump r s].freeze,
126
+ rake: %w[I libdir r require].freeze,
127
+ irb: %w[I r].freeze,
128
+ rbs: %w[I r repo].freeze,
129
+ gem: {
130
+ contents: %w[s spec-dir].freeze,
131
+ dependency: %w[s source].freeze,
132
+ install: %w[document s source without].freeze,
133
+ pristine: %w[skip].freeze
134
+ }.freeze,
135
+ bundle: {
136
+ install: %w[standalone with without].freeze,
137
+ lock: %w[add-platform remove-platform update].freeze,
138
+ update: %w[g group source].freeze
139
+ }.freeze
140
+ }.freeze
141
+ private_constant :GEMFILE, :GEMNAME, :DIR_RUBY, :OPT_RUBY, :OPT_BUNDLE, :OPT_GEM, :PASS_RUBY
63
142
 
64
143
  class << self
65
- def populate(*); end
66
-
67
144
  def tasks
68
145
  [:outdated].freeze
69
146
  end
@@ -80,20 +157,16 @@ module Squared
80
157
  end
81
158
 
82
159
  subtasks({
83
- 'install' => %i[redownload local prefer-local].freeze,
84
- 'update' => %i[patch minor major all].freeze,
85
160
  'outdated' => %i[patch minor major].freeze,
86
- 'gem' => %i[install uninstall update pristine outdated build push exec].freeze,
87
161
  'ruby' => %i[file script version].freeze,
88
- 'exec' => nil,
89
- 'cache' => nil,
90
- 'config' => nil,
91
- 'check' => nil,
162
+ 'gem' => %i[install uninstall outdated update pristine build push exec command].freeze,
163
+ 'bundle' => %i[install update cache exec config reinstall command].freeze,
164
+ 'rbs' => nil,
92
165
  'rake' => nil,
93
166
  'irb' => nil
94
167
  })
95
168
 
96
- def initialize(*, autodetect: false, gemspec: nil, asdf: 'ruby', **kwargs)
169
+ def initialize(*, autodetect: false, gemspec: nil, steep: 'Steepfile', asdf: 'ruby', **kwargs)
97
170
  super
98
171
  if @pass.include?(Ruby.ref)
99
172
  initialize_ref Ruby.ref
@@ -109,7 +182,8 @@ module Squared
109
182
  elsif gemspec
110
183
  basepath(gemspec.include?('.') ? gemspec : "#{gemspec}.gemspec")
111
184
  end
112
- return if !@output[0].nil? || !@copy.nil? || version || @autodetect || !rakefile
185
+ @steepfile = basepath(steep).yield_self { |file| file if file.exist? }
186
+ return unless rakefile && @output[0].nil? && @copy.nil? && !version && !@autodetect
113
187
 
114
188
  begin
115
189
  File.foreach(rakefile) do |line|
@@ -146,16 +220,15 @@ module Squared
146
220
  format_desc action, nil, "task+,opts*|#{indexchar}index+|#,pattern*"
147
221
  task action, [:command] do |_, args|
148
222
  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 })
223
+ format_list(raketasks, "rake[#{indexchar}N]", 'tasks', grep: args.extras, from: rakefile, &:join)
151
224
  else
152
- args, opts = args.to_a.partition { |val| indexitem(val) }
153
- if args.empty?
154
- rake(opts: opts)
225
+ items, opts = args.to_a.partition { |val| indexitem(val) }
226
+ if items.empty?
227
+ rake(banner: true, opts: opts)
155
228
  else
156
229
  tasks = raketasks
157
- while (n, pre = indexitem(args.shift))
158
- if (item = tasks[n - 1])
230
+ while (n, pre = indexitem(items.shift))
231
+ if (item = tasks[n.pred])
159
232
  cmd = pre ? "#{pre} #{item.first}" : item.first
160
233
  elsif exception
161
234
  indexerror n, tasks
@@ -164,9 +237,9 @@ module Squared
164
237
  next
165
238
  end
166
239
  if opts.empty?
167
- rake cmd
240
+ rake(cmd, banner: true)
168
241
  else
169
- rake(cmd + shell_escape("[#{opts.join(',')}]"))
242
+ rake(cmd + "[#{opts.join(',')}]", banner: true)
170
243
  opts.clear
171
244
  end
172
245
  end
@@ -176,42 +249,105 @@ module Squared
176
249
  when 'irb'
177
250
  format_desc action, nil, 'opts*,args*|:'
178
251
  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(':')))
252
+ opts = args.to_a
253
+ args = Array(opts.delete(':') && readline('Enter file [arguments]', force: false))
254
+ name = gemname if gemlib.any? { |file| exist?(file, "#{gemname}.rb") }
255
+ irb(*args, opts: opts, name: name, verbose: false)
182
256
  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)
257
+ when 'rbs'
258
+ next unless @steepfile
259
+
260
+ data = {}
261
+ target = nil
262
+ File.foreach(@steepfile) do |line|
263
+ if line =~ /^\s*target(?:\s+|\(\s*)(?::(\S+)|(["'])(.+)\2)/
264
+ target = [[], []]
265
+ data[$1 || $3.gsub(/[: ]/, '-')] = target
266
+ next
267
+ end
268
+ next unless target && line =~ /^\s*(check|signature)\s+(["'])(.+)\2/
269
+
270
+ target[$1 == 'check' ? 1 : 0] << $3
271
+ end
272
+ next if data.empty?
273
+
274
+ namespace 'rbs' do
275
+ data.each do |key, item|
276
+ sig, lib = item
277
+ next if sig.empty? || lib.empty?
278
+
279
+ format_desc action, key, 'sig?,path*'
280
+ task key do |_, args|
281
+ args = args.to_a
282
+ list = []
283
+ lib.each do |val|
284
+ val = File.join(val, '**/*.rb') unless val.include?('*') || val.end_with?('.rb')
285
+ list.concat(Dir.glob(val, base: path))
286
+ end
287
+ if args.empty?
288
+ files = choice_index('Select files', list, multiple: true, force: true, series: true,
289
+ accept: [accept_y('Generate?')])
290
+ else
291
+ files = []
292
+ args.each do |val|
293
+ if val.include?('*')
294
+ files.concat(Dir.glob(val, base: path))
295
+ elsif !(file = basepath(val)).exist?
296
+ print_error(val, hint: 'not found')
297
+ elsif file.directory?
298
+ files.concat(file.glob('**/*.rb'))
299
+ else
300
+ files << val
301
+ end
302
+ end
303
+ list.map! { |val| basepath(val).to_s }
304
+ files = files.select { |val| list.include?(basepath(val).to_s) }
305
+ if files.empty?
306
+ print_error('steep', 'no files matched', hint: "#{key}:check")
307
+ exit 1
308
+ end
309
+ files.uniq!
310
+ end
311
+ sig = if (n = sig.index(args.first))
312
+ args.shift
313
+ sig[n]
314
+ elsif sig.size > 1
315
+ choice_index('Select a sig', sig, force: true, series: true)
316
+ else
317
+ sig.first
318
+ end
319
+ rbs(:prototype, sig, *files)
320
+ end
321
+ end
190
322
  end
191
323
  end
192
324
  else
193
325
  namespace action do
194
326
  flags.each do |flag|
195
327
  case action
196
- when 'install', 'update', 'outdated'
197
- format_desc action, flag, 'opts*'
328
+ when 'outdated'
329
+ format_desc action, flag, 's/elect?,u/pdate?,opts*'
198
330
  task flag do |_, args|
199
331
  __send__ action, flag, args.to_a
200
332
  end
201
333
  when 'gem'
202
334
  case flag
203
335
  when :outdated
204
- format_desc action, flag, 'major|minor|patch|interactive?,opts*'
336
+ format_desc action, flag, 'semver?=major|minor|patch,i/nteractive?,s/elect?,u/pdate?,opts*'
205
337
  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)
338
+ opts = case (semver = args.semver)
339
+ when 'major', 'minor', 'patch'
340
+ args.extras
341
+ else
342
+ semver = nil
343
+ args.to_a
344
+ end
345
+ gem(flag, opts: opts, banner: true, filter: {
346
+ semver: semver,
347
+ update: opts.delete('u') || opts.delete('update'),
348
+ interactive: opts.delete('i') || opts.delete('interactive'),
349
+ select: opts.delete('s') || opts.delete('select')
350
+ })
215
351
  end
216
352
  when :build, :push, :exec, :update
217
353
  format_desc(action, flag, 'opts*', after: case flag
@@ -220,13 +356,60 @@ module Squared
220
356
  when :update then 'name*'
221
357
  end)
222
358
  task flag do |_, args|
223
- gem! flag, args.to_a
359
+ gem(flag, opts: args.to_a, banner: true)
224
360
  end
225
- else
361
+ when :install, :uninstall, :pristine
226
362
  format_desc(action, flag, 'opts*', after: flag == :pristine ? 'name*|name?@version' : 'name*')
227
363
  task flag do |_, args|
228
- args = param_guard(action, flag, args: args.to_a)
229
- gem! flag, args
364
+ opts = param_guard(action, flag, args: args.to_a)
365
+ gem(flag, opts: opts, banner: true)
366
+ end
367
+ when :command
368
+ format_desc action, flag, 'command,opts*,args*'
369
+ task flag, [:command] do |_, args|
370
+ command = param_guard(action, flag, key: :command, args: args)
371
+ gem(command.to_sym, opts: args.extras, banner: true)
372
+ end
373
+ end
374
+ when 'bundle'
375
+ case flag
376
+ when :install, :update, :cache, :exec
377
+ format_desc(action, flag, 'opts*', after: case flag
378
+ when :update then 'gems*'
379
+ when :exec then 'command,args*|:'
380
+ end)
381
+ task flag do |_, args|
382
+ bundle(flag, opts: args.to_a, banner: true)
383
+ end
384
+ when :config
385
+ format_desc action, flag, 'list|set|get|unset?,args*'
386
+ task flag do |_, args|
387
+ bundle(flag, *args.to_a, banner: true)
388
+ end
389
+ when :reinstall
390
+ format_desc action, flag, 'f/orce?,opts*'
391
+ task flag do |_, args|
392
+ opts = args.to_a
393
+ opts << 'redownload' if opts.delete('f') || opts.delete('force')
394
+ if (lock = basepath('Gemfile.lock')).exist?
395
+ config = basepath '.bundle', 'config'
396
+ if config.exist? && config.read.match?(/\bBUNDLE_FROZEN:\s+"true"/)
397
+ if opts.include?('redownload')
398
+ run(bundle_output('config unset frozen'), banner: false)
399
+ else
400
+ print_error('Gemfile.lock is frozen', subject: name, hint: flag)
401
+ lock = nil
402
+ end
403
+ end
404
+ lock&.delete
405
+ end
406
+ bundle(:install, opts: opts, banner: true)
407
+ end
408
+ when :command
409
+ format_desc action, flag, 'command,opts*,args*'
410
+ task flag, [:command] do |_, args|
411
+ command = param_guard(action, flag, key: :command, args: args)
412
+ bundle(command.to_sym, opts: args.extras, banner: true)
230
413
  end
231
414
  end
232
415
  when 'ruby'
@@ -234,35 +417,34 @@ module Squared
234
417
  when :file
235
418
  format_desc action, flag, 'path,opts*,args*'
236
419
  task flag, [:rb] do |_, args|
237
- file = args.rb
238
420
  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)
421
+ args = Array(if (file = args.rb) && !file.include?('*')
422
+ ENV['RUBY_ARGS']
423
+ else
424
+ a, b, c = choice_index('Select a file', Dir.glob(file || '*.rb', base: path),
425
+ values: (file ? [] : ['Options']).push('Arguments'),
426
+ force: true, series: true)
427
+ if file
428
+ file = a
429
+ b
430
+ else
431
+ file = a
432
+ opts.concat(OptionPartition.strip(b))
433
+ c
434
+ end
435
+ end)
436
+ ruby(*args, flag: flag, opts: opts, file: file)
255
437
  end
256
438
  when :script
257
439
  format_desc action, flag, 'opts*'
258
440
  task flag do |_, args|
259
- command = ENV['RUBY_E'] || readline('Enter script', force: true, multiline: ['##', ';'])
260
- ruby(flag, args.to_a, command: command)
441
+ command = ENV['RUBY_E'] || readline('Enter script', force: true, multiline: %w[## ;])
442
+ ruby(flag: flag, opts: args.to_a, command: command)
261
443
  end
262
444
  when :version
263
445
  format_desc action, flag
264
446
  task flag do
265
- ruby flag
447
+ ruby(flag: flag)
266
448
  end
267
449
  end
268
450
  end
@@ -317,11 +499,9 @@ module Squared
317
499
  b = dest + val
318
500
  c = glob[i] || glob.first
319
501
  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
502
+ copy_dir(a, b, c, pass: pass, verbose: verbosetype > 0)
503
+ rescue StandardError => e
504
+ on_error e, :copy
325
505
  end
326
506
  on :last, :copy
327
507
  end
@@ -329,14 +509,21 @@ module Squared
329
509
  def outdated(flag = nil, opts = [], sync: invoked_sync?('outdated', flag))
330
510
  cmd = bundle_output 'outdated'
331
511
  if flag
512
+ se = (opts.delete('s') || opts.delete('select')) && !stdin?
513
+ up = opts.delete('u') || opts.delete('update')
332
514
  cmd << "--#{flag}"
333
- append_bundle(opts, OPT_BUNDLE[:outdated] + OPT_BUNDLE[:common], target: cmd)
515
+ OptionPartition.new(opts, bundleopts(:outdated), cmd, project: self)
516
+ .clear
334
517
  end
335
518
  log.info cmd.to_s
336
519
  on :first, :outdated
337
520
  banner = format_banner cmd.to_s
338
- print_item banner if sync
521
+ items = if sync
522
+ print_item banner
523
+ [] if flag && (se || up)
524
+ end
339
525
  pwd_set(from: :outdated) do
526
+ tc = theme[:current]
340
527
  start = 0
341
528
  found = 0
342
529
  major = 0
@@ -393,26 +580,26 @@ module Squared
393
580
  end
394
581
  end
395
582
  end
583
+ name = line[/^\S+/, 0]
396
584
  unless styles.empty?
397
585
  case styles.first
398
586
  when :green
399
- line = sub_style(line, pat: /^(\S+)(.+)$/, styles: theme[styles.last == :bold ? :major : :active])
587
+ line = sub_style(line, **opt_style(theme[styles.last == :bold ? :major : :active], /^(\S+)(.+)$/))
400
588
  found += 1
401
589
  when :yellow
402
590
  found += 1
403
591
  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)
592
+ l = Regexp.escape(l)
593
+ line = sub_style(line, **opt_style(tc, /^(.+)(#{Regexp.escape(c)})(.+)$/, 2)) if tc
594
+ line = sub_style(line, **opt_style(colormap(styles), /^((?:\S+\s+){2})(#{l})(.*)$/, 2))
409
595
  end
410
596
  end
411
- out.call('%2d. %s' % [start, line])
597
+ items&.push([line, name])
598
+ out.call('%2d. %s' % [start, line]) unless se
412
599
  elsif line.start_with?('Gem')
413
600
  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]))
601
+ sub = [opt_style(theme[:header], /^(.+)(?<!\dm)(Gem|Latest)(.+)$/, 2)] * 2
602
+ out.call(print_footer(" # #{line.chomp}", reverse: true, sub: sub))
416
603
  end
417
604
  else
418
605
  next
@@ -424,6 +611,15 @@ module Squared
424
611
  puts buffer
425
612
  end
426
613
  if found > 0
614
+ if items
615
+ gems = if se
616
+ choice('Select a package', items.map(&:first),
617
+ multiple: true, force: false, index: true, border: true).map! { |n| items[n.pred].last }
618
+ else
619
+ items.map(&:last)
620
+ end
621
+ bundle(:update, *gems, opts: [flag.to_s])
622
+ end
427
623
  begin
428
624
  if major == 0 && dependfile.read =~ /\b(?:source\s+(["'])((?~\1))\1|remote:\s+(\S+))/
429
625
  status = ($2 || $3).chomp('/')
@@ -432,50 +628,18 @@ module Squared
432
628
  rescue StandardError => e
433
629
  log.debug e
434
630
  ensure
435
- status ||= 'Updates are available'
631
+ status ||= (items ? 'Updates were made' : 'Updates are available')
436
632
  end
437
633
  puts print_footer(empty_status(status, 'major', major, always: !right), right: right)
438
- elsif start == 0
634
+ elsif start == 0 && banner
439
635
  puts 'No updates were found'
440
636
  end
441
637
  end
442
638
  on :last, :outdated
443
639
  end
444
640
 
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 file
472
- op.unshift(shell_quote(basepath(file)))
473
- elsif command
474
- op << quote_option('e', command, option: false)
475
- end
476
- op.push(args) if args
477
- op.append(delim: true, escape: false, quote: false) unless op.empty?
478
- when :version
641
+ def ruby(*args, flag: nil, sync: true, banner: verbose?, with: nil, pass: PASS_RUBY[:ruby], **kwargs)
642
+ if flag == :version
479
643
  pwd_set do
480
644
  out = []
481
645
  order = { 'rbenv' => -1, 'rvm' => -1, 'asdf' => -1, 'chruby' => -1 }
@@ -514,19 +678,18 @@ module Squared
514
678
  c < d ? -1 : 1
515
679
  end
516
680
  end
517
- .append('')
681
+ .push('')
518
682
  .each do |val|
519
683
  next unless val.empty? || File.exist?(val.sub('$HOME', Dir.home))
520
684
 
521
- trim = ->(s) { s[/\A\D+\d+\.\d+(?:\.\S+)?/, 0].sub(/\A([a-z]+)-/i, '\1 ') }
685
+ trim = ->(s) { s[/^\D+\d+\.\d+(?:\.\S+)?/, 0].sub(/^([a-z]+)-/i, '\1 ') }
522
686
  ver = '.ruby-version'
523
687
  out << trim.call(case (cmd = File.basename(val))
524
688
  when 'rvm'
525
689
  `rvm current`[/^\S+/, 0]
526
690
  when 'rbenv'
527
- `rbenv version-name`.yield_self do |name|
528
- name.match?(SEM_VER) ? "ruby #{name}" : name
529
- end
691
+ name = `rbenv version-name`
692
+ name.match?(SEM_VER) ? "ruby #{name}" : name
530
693
  when 'chruby.sh'
531
694
  chruby = session_output 'source', val
532
695
  `#{chruby.with('ruby --version')}`
@@ -535,7 +698,7 @@ module Squared
535
698
  cmd = 'asdf'
536
699
  ver = '.tool-versions'
537
700
  opt = [@asdf.first]
538
- opt.unshift('--no-header') unless @@asdf[1] == 15
701
+ opt.unshift('--no-header') unless @@asdf.version == 15
539
702
  `asdf current #{opt.join(' ')}`[/^\S+\s+\S+/, 0].sub(/\s+/, ' ')
540
703
  else
541
704
  ver = nil
@@ -584,139 +747,199 @@ module Squared
584
747
  end
585
748
  return
586
749
  end
587
- run_rb(banner: false, from: :"ruby:#{flag}")
588
- end
589
-
590
- def gem!(flag, opts = [], filter: nil)
591
- cmd = gem_session
592
- case flag
593
- when :outdated
594
- cmd << gempwd << flag
595
- else
596
- cmd << flag
750
+ op = OptionPartition.new(session_opts(with, args: args, kwargs: kwargs, pass: pass), OPT_RUBY[:ruby],
751
+ ruby_session, project: self, args: true)
752
+ if (val = kwargs[:file])
753
+ op.unshift(shell_quote(basepath(val)))
754
+ elsif (val = kwargs[:command])
755
+ op << quote_option('e', val)
756
+ end
757
+ append = lambda do |file|
758
+ op.concat(args)
759
+ unless op.empty?
760
+ op.exist?(add: true) if file
761
+ op.append(delim: true, escape: false, quote: false)
762
+ end
597
763
  end
598
- list = OPT_GEM[flag] + OPT_GEM[:common]
599
- from = :"gem:#{flag}"
600
764
  case flag
601
- when :install, :update
602
- list.concat(OPT_GEM[:install_base])
765
+ when :file
766
+ append.call(false)
767
+ when :script
768
+ op.clear
769
+ else
770
+ append.call(true)
771
+ print_run(op, banner, **kwargs)
603
772
  end
604
- cmd.merge(preopts)
605
- op = OptionPartition.new(opts, list, cmd, project: self, no: OPT_GEM[:no][flag])
606
- op.each do |opt|
607
- if !opt.match?(/\A[A-Za-z\d][A-Za-z\d_.-]*\z/) && %i[install uninstall update pristine].include?(flag)
608
- op.errors << opt
609
- else
610
- op.found << opt
611
- end
773
+ run_rb(sync: sync, banner: banner, from: flag.is_a?(Symbol) ? :"ruby:#{flag}" : :ruby)
774
+ end
775
+
776
+ def gem(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
777
+ flag = flag.to_sym
778
+ if pass.nil?
779
+ pass = case flag
780
+ when :install, :update
781
+ PASS_RUBY[:gem][:install]
782
+ when :dependency, :fetch, :info, :list, :outdated, :search, :specification
783
+ PASS_RUBY[:gem][:dependency]
784
+ else
785
+ PASS_RUBY[:gem].fetch(flag, [])
786
+ end
612
787
  end
613
- op.swap
788
+ opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
614
789
  case flag
615
- when :outdated
790
+ when :build, :cert, :generate_index, :mirror, :outdated, :push, :server, :signin, :signout, :sources, :stale
791
+ opts.concat(args)
792
+ end
793
+ op = OptionPartition.new(opts, gemopts(flag), gem_session(flag, *preopts), project: self,
794
+ no: OPT_GEM[:no][flag])
795
+ from = :"gem:#{flag}"
796
+ if flag == :outdated
797
+ op.adjoin(gempwd, start: 0) if gempwd
616
798
  op.clear
617
- cmd = cmd.done
799
+ cmd = session_done op.target
618
800
  log.info cmd
619
801
  on :first, from
620
- print_item format_banner(cmd)
621
- major = 0
622
- minor = 0
623
- patch = 0
624
- update = []
802
+ banner = format_banner(cmd)
803
+ print_item banner if sync
804
+ major = [0, 0, 0]
805
+ buffer = []
806
+ filter = kwargs.fetch(:filter, {})
807
+ semver = filter[:semver]
808
+ update = if sync && filter[:select]
809
+ semver ||= 'major'
810
+ items = []
811
+ nil
812
+ elsif sync && filter[:interactive]
813
+ semver ||= 'major'
814
+ ia = true
815
+ []
816
+ elsif filter[:update]
817
+ semver ||= 'minor'
818
+ []
819
+ end
820
+ out = ->(val) { sync ? puts(val) : buffer << val }
625
821
  pwd_set(pass: !gempwd.nil?, from: from) do
626
- items = [[%w[Gem Current Latest], nil]]
822
+ rows = [[%w[Gem Current Latest], nil]]
627
823
  IO.popen(cmd).each do |line|
628
824
  if line =~ /^(\S+) \((\S+) < ([^)]+)\)$/
629
825
  cur = semscan $2
630
826
  lat = semscan $3
631
- items << [$~.to_a.drop(1), if semmajor?(cur, lat)
632
- 1
633
- else
634
- cur[2] == lat[2] ? 3 : 2
635
- end]
827
+ rows << [$~.to_a.drop(1), if semmajor?(cur, lat)
828
+ 1
829
+ else
830
+ cur[2] == lat[2] ? 3 : 2
831
+ end]
636
832
  else
637
- puts line
833
+ out.call(line)
638
834
  end
639
835
  end
640
- if items.size > 1
641
- pad = [items.size.to_s.size + 1, 3].max
836
+ if rows.size > 1
837
+ pad = [rows.size.to_s.size.succ, 3].max
642
838
  d = 0
643
839
  e = 0
644
840
  f = 0
645
841
  j = 0
646
842
  queue = nil
647
- items.each do |item|
648
- a, b, c = item.first
843
+ rows.each do |row|
844
+ a, b, c = row.first
649
845
  d = a.size if a.size > d
650
846
  e = b.size if b.size > e
651
847
  f = c.size if c.size > f
652
848
  end
653
- items.each_with_index do |item, i|
849
+ rows.each_with_index do |row, i|
654
850
  next if i == 0 && stdin?
655
851
 
656
- a, b, c = item.first
852
+ a, b, c = row.first
657
853
  if i == 0
658
854
  line = '%-*s %-*s %*s %*s' % [pad, ' #', d, a, e, b, f, c]
659
855
  n = line.size
660
- 2.times do
661
- line = sub_style(line, pat: /^(.+)(?<!\dm)(#{a}|#{c})(.*)$/, styles: theme[:header], index: 2)
662
- end
663
- queue = [line, sub_style(ARG[:BORDER][1] * n, styles: borderstyle)]
856
+ queue = if stdin?
857
+ [line, ARG[:BORDER][1] * n]
858
+ else
859
+ 2.times do
860
+ line = sub_style(line, **opt_style(theme[:header], /^(.+)(?<!\dm)(#{a}|#{c})(.*)$/, 2))
861
+ end
862
+ [line, sub_style(ARG[:BORDER][1] * n, styles: borderstyle)]
863
+ end
664
864
  else
665
865
  g = a.ljust(d)
666
866
  pat = [/^([^.]+\.)([^.]+\..+)$/, /^([^.]+\.[^.]+\.)(.+)$/]
667
867
  pre = b.start_with?('0.')
668
868
  latest = [theme[:latest]]
669
- case item.last
869
+ case row.last
670
870
  when 1
671
- case filter
871
+ case semver
672
872
  when 'major'
673
- update << a
873
+ update&.push(a)
674
874
  when 'minor', 'patch'
675
875
  next
676
876
  end
677
- g = sub_style(g, styles: theme[:major])
678
- major += 1
679
- styles = %i[green bold]
680
- pat = pre ? pat.first : nil
681
- latest << :bold
877
+ unless stdin?
878
+ g = sub_style(g, styles: theme[:major])
879
+ styles = %i[green bold]
880
+ pat = (pat.first if pre)
881
+ latest << :bold
882
+ end
883
+ major[0] += 1
682
884
  when 2
683
- case filter
885
+ case semver
684
886
  when 'major', 'minor'
685
- update << a
887
+ update&.push(a)
686
888
  when 'patch'
687
889
  next
688
890
  end
689
- g = sub_style(g, styles: theme[:active])
690
- minor += 1
691
- styles = %i[green]
692
- pat = pre ? pat.last : pat.first
891
+ unless stdin?
892
+ g = sub_style(g, styles: theme[:active])
893
+ styles = %i[green]
894
+ pat = pre ? pat.last : pat.first
895
+ end
896
+ major[1] += 1
693
897
  else
694
- case filter
898
+ case semver
695
899
  when 'major', 'minor', 'patch'
696
- update << a
900
+ update&.push(a)
901
+ end
902
+ unless stdin?
903
+ styles = %i[yellow]
904
+ pat = pat.last
697
905
  end
698
- patch += 1
699
- styles = %i[yellow]
700
- pat = pat.last
906
+ major[2] += 1
907
+ end
908
+ b = b.rjust(e)
909
+ h = c.rjust(f)
910
+ unless stdin?
911
+ b = sub_style(b, **opt_style(colormap(styles), pat, 2))
912
+ h = sub_style(h, **opt_style(latest.flatten.compact, pat, 2))
701
913
  end
702
- b = sub_style(b.rjust(e), *colormap(styles), pat: pat, index: 2)
703
- h = sub_style(c.rjust(f), styles: latest.flatten.compact, pat: pat, index: 2)
704
914
  j += 1
705
915
  if queue
706
- puts queue
916
+ out.call(queue)
707
917
  queue = nil
708
918
  end
709
- puts '%*s %s %s %s' % [pad, "#{j}.", g, b, h]
710
- update << a if filter == 'interactive' && confirm_outdated(a, c, item.last)
919
+ s = '%s %s %s' % [g, b, h]
920
+ if items
921
+ items << [s, a]
922
+ else
923
+ out.call('%*s %s' % [pad, "#{j}.", s])
924
+ end
925
+ update.delete(a) if ia && !confirm_outdated(a, c, row.last, timeout: 0)
711
926
  end
712
927
  end
713
928
  end
714
929
  end
715
- if major + minor + patch == 0
930
+ unless sync
931
+ print_item banner
932
+ puts buffer
933
+ end
934
+ if major.sum == 0
716
935
  puts 'No updates were found'
717
936
  else
718
- unless update.empty?
719
- cmd = gem_output 'update', '-f'
937
+ if items
938
+ update = choice('Select a package', items.map(&:first),
939
+ multiple: true, force: false, index: true, border: true).map! { |n| items[n.pred].last }
940
+ end
941
+ if update && !update.empty?
942
+ cmd = gem_output 'update -f'
720
943
  option('document', prefix: 'gem', ignore: false) do |val|
721
944
  cmd << case val
722
945
  when '0', 'false'
@@ -734,18 +957,40 @@ module Squared
734
957
  end
735
958
  end
736
959
  cmd.merge(update)
737
- run(cmd, banner: false, from: :'gem:update')
960
+ run(cmd, sync: sync, banner: false, from: :'gem:update')
738
961
  end
739
- print_status(major, minor, patch, from: :outdated)
962
+ print_status(*major, from: :outdated)
740
963
  end
741
964
  on :last, from
742
965
  return
966
+ end
967
+ case flag
968
+ when :check, :cleanup, :contents, :fetch, :info, :lock, :open, :owner, :pristine, :rdoc, :rebuild, :uninstall,
969
+ :unpack, :update, :yank
970
+ gems = true
971
+ op.concat(args)
972
+ when :dependency, :environment, :list, :search, :specification, :which
973
+ op.concat(args)
974
+ end
975
+ op.each do |opt|
976
+ if gems && !opt.start_with?('-') && !opt.match?(GEMNAME)
977
+ op.errors << opt
978
+ else
979
+ op.found << opt
980
+ end
981
+ end
982
+ op.swap do |a, b|
983
+ return -1 if a.start_with?('-')
984
+
985
+ b.start_with?('-') ? 1 : 0
986
+ end
987
+ case flag
743
988
  when :build
744
989
  if op.empty?
745
- raise_error('gemspec not found', hint: project) unless gemfile
990
+ raise_error Errno::ENOENT, 'gemspec', hint: project unless gemfile
746
991
  op.add_path(gemfile)
747
992
  else
748
- op.add_path(op.shift)
993
+ op.add_first(path: true)
749
994
  .clear(pass: false)
750
995
  end
751
996
  when :push
@@ -756,38 +1001,48 @@ module Squared
756
1001
  choice_index('Select a file', Dir.glob('*.gem', base: path), force: true)
757
1002
  end)
758
1003
  else
759
- file = op.shift.yield_self { |val| val.include?('.') ? val : "#{val}.gem" }
760
- raise_error("unknown args: #{op.join(', ')}", hint: flag) unless op.empty?
1004
+ file = basepath(op.shift.yield_self { |val| val.include?('.') ? val : "#{val}.gem" })
1005
+ raise_error Errno::ENOENT, file, hint: flag unless file.exist?
1006
+ raise_error ArgumentError, "unrecognized args: #{op.join(', ')}", hint: flag unless op.empty?
761
1007
  end
762
- raise_error('gem not found', hint: file) unless op.exist?(file)
763
1008
  op.add_path(file)
764
- run_rb(from: from, interactive: "Push #{sub_style(gemname, styles: theme[:active])}")
765
- return
1009
+ return run_rb(from: from, interactive: ['Push', 'N', gemname]) unless with || !banner
766
1010
  when :exec
767
1011
  min = if op.arg?('g', 'gem')
768
1012
  1
769
- elsif op.empty?
1013
+ elsif !op.empty?
1014
+ op.add_first
1015
+ 0
1016
+ elsif args.empty?
770
1017
  op << basic_option('gem', gemname)
771
1018
  1
772
1019
  else
773
- op << op.shift
774
1020
  0
775
1021
  end
1022
+ op.concat(args)
776
1023
  if (args = command_args(op.extras, min: min, force: min == 1 && op.empty?))
777
1024
  op.push(args)
778
1025
  end
779
1026
  op.append(quote: false)
780
1027
  when :update
781
- unless op.arg?('system')
782
- if op.empty?
783
- op << gemname
784
- else
785
- op.append
786
- end
1028
+ if op.arg?('system')
1029
+ op.add_first(quote: false) { |val| val if val.match?(SEM_VER) }
1030
+ else
1031
+ op.append
787
1032
  end
788
- op.clear(errors: true)
789
1033
  when :install, :uninstall, :pristine
790
- raise_error('missing gemname', hint: flag) if op.empty?
1034
+ if flag == :install
1035
+ post = if op.remove(':')
1036
+ op.concat(args)
1037
+ readline('Enter command [args]', force: true)
1038
+ elsif op.empty?
1039
+ op.concat(args)
1040
+ nil
1041
+ elsif !args.empty?
1042
+ args.join(' ')
1043
+ end
1044
+ end
1045
+ raise_error ArgumentError, 'missing gem name', hint: flag if op.empty?
791
1046
  if op.arg?('all')
792
1047
  if flag == :pristine
793
1048
  append_repeat 'skip', op.extras
@@ -796,84 +1051,240 @@ module Squared
796
1051
  op.clear
797
1052
  end
798
1053
  elsif (n = op.index { |val| val.match?(/(\A|[a-z])@\d/) })
799
- name = op.delete_at(n)
1054
+ name = op.remove_at(n)
800
1055
  pre, ver = if (n = name.index('@')) == 0
801
1056
  [gemname, name[1..-1]]
802
1057
  else
803
- [name[0, n], name[(n + 1)..-1]]
1058
+ [name[0, n], name[n.succ..-1]]
804
1059
  end
805
1060
  op.adjoin(pre, shell_option('version', ver))
806
1061
  .clear
807
- elsif flag == :install
1062
+ end
1063
+ if flag == :install
808
1064
  op.append_any
809
1065
  else
810
1066
  op.append
811
1067
  end
812
- op.clear(errors: true)
813
- op << '--' << readline('Enter command [args]', force: true) if flag == :install && op.remove(':')
814
- else
1068
+ op << '--' << post if post
1069
+ when :check, :cleanup, :contents, :fetch, :list, :lock, :rdoc
815
1070
  op.append
1071
+ when :dependency, :info, :search
1072
+ op.add_first(quote: true, expect: case flag
1073
+ when :dependency, :search then 'no pattern for gem name'
1074
+ else 'missing gem name'
1075
+ end)
1076
+ .clear
1077
+ when :environment
1078
+ unless op.empty?
1079
+ case (action = op.shift)
1080
+ when 'home', 'path', 'user_gemhome', 'version', 'remotesources', 'platform', 'credentials'
1081
+ op << action
1082
+ op.clear
1083
+ else
1084
+ raise_error ArgumentError, "unrecognized arg: #{action}", hint: flag
1085
+ end
1086
+ end
1087
+ when :open, :owner, :unpack, :yank
1088
+ op.add_first(gemname)
1089
+ .clear
1090
+ when :rebuild
1091
+ op.add_first(expect: 'missing gem name')
1092
+ .add_first(expect: 'missing gem version')
1093
+ .clear
1094
+ when :specification
1095
+ op.add_first(expect: 'missing gem name')
1096
+ .add_first
1097
+ .clear
1098
+ when :which
1099
+ op.splice(path: true) { |val| op.exist?(val) }
1100
+ .clear
1101
+ else
1102
+ op.clear
816
1103
  end
817
- run_rb(from: from)
1104
+ op.clear(errors: true) if gems
1105
+ print_run(op, banner, **kwargs)
1106
+ run_rb(sync: sync, banner: banner, from: from)
818
1107
  end
819
1108
 
820
- def bundle(flag, *args)
821
- cmd = bundle_session flag
822
- args = case flag
823
- when 'exec', 'cache', 'check'
824
- list = OPT_BUNDLE[flag.to_sym] + OPT_BUNDLE[:common]
825
- OptionPartition.new(args, list, cmd, project: self, args: flag == 'exec').extras
826
- else
827
- args.flatten
828
- end
1109
+ def bundle(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
1110
+ flag = flag.to_sym
1111
+ if pass.nil?
1112
+ pass = case flag
1113
+ when :add, :outdated, :update
1114
+ PASS_RUBY[:bundle][:update]
1115
+ else
1116
+ PASS_RUBY[:bundle].fetch(flag, [])
1117
+ end
1118
+ end
1119
+ opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
829
1120
  case flag
830
- when 'exec', 'config'
831
- cmd << readline('Enter arguments', force: true) if args.empty?
832
- when 'cache', 'check'
833
- option_clear args
834
- args.clear
1121
+ when :cache, :check, :clean, :doctor, :init, :install, :lock, :pack, :package, :platform
1122
+ pre = true
1123
+ opts.concat(args)
835
1124
  end
836
- cmd.merge(args)
837
- run(from: :"bundle:#{flag}")
838
- end
839
-
840
- def rake(*args, opts: [])
841
- op = OptionPartition.new(opts, OPT_RUBY[:rake], [(quote_option('f', rakefile) if rakefile)].compact,
842
- project: self)
843
- args.concat(op.extras)
844
- if args.empty?
845
- args << nil
1125
+ op = OptionPartition.new(opts, bundleopts(flag), bundle_session(flag),
1126
+ project: self, no: OPT_BUNDLE[:no][flag], args: flag == :exec)
1127
+ op.concat(args) unless pre
1128
+ output = false
1129
+ invalid = ->(a) { raise_error ArgumentError, "unrecognized args: #{a.join(', ')}", hint: flag }
1130
+ case flag
1131
+ when :config
1132
+ if op.empty?
1133
+ op << (val = readline('Enter arguments', force: false))
1134
+ output = val.match?(/(?:un)?set/)
1135
+ else
1136
+ a = op.dup
1137
+ b = op.shift
1138
+ c = op.shift
1139
+ d = op.shift
1140
+ e = op.arg?('global', 'local')
1141
+ op << b
1142
+ getname = -> { op << (c || readline('Enter name', force: true)) }
1143
+ case b
1144
+ when 'list'
1145
+ nil
1146
+ when 'get'
1147
+ getname.call
1148
+ when 'set'
1149
+ if e
1150
+ op << c
1151
+ c = d
1152
+ d = op.shift
1153
+ end
1154
+ getname.call
1155
+ op << (d || readline('Enter value', force: true))
1156
+ output = true
1157
+ when 'unset'
1158
+ if e
1159
+ op << c
1160
+ c = d
1161
+ end
1162
+ getname.call
1163
+ output = true
1164
+ else
1165
+ if b
1166
+ op << b
1167
+ if c
1168
+ op.add_quote(c)
1169
+ output = true
1170
+ end
1171
+ else
1172
+ invalid.call(a)
1173
+ end
1174
+ end
1175
+ op.clear
1176
+ end
1177
+ when :plugin
1178
+ a = op.dup
1179
+ case (b = op.shift)
1180
+ when 'install', 'uninstall', 'help'
1181
+ op.append
1182
+ when 'list'
1183
+ op.clear
1184
+ else
1185
+ invalid.call(a)
1186
+ end
1187
+ op.adjoin(b, with: 'plugin')
1188
+ when :exec
1189
+ if op.empty? || (op.delete(':') && op.append(quote: false))
1190
+ op << readline('Enter arguments', force: true)
1191
+ else
1192
+ op.append(quote: false)
1193
+ end
1194
+ when :binstubs, :outdated, :remove, :update
1195
+ op.append(filter: GEMNAME)
1196
+ .clear(errors: true)
1197
+ when :add, :open, :show
1198
+ op.add_first(expect: 'missing gem name')
1199
+ .clear
1200
+ when :console, :gem
1201
+ op.add_first
1202
+ .clear
846
1203
  else
847
- args.flatten!
1204
+ op.clear
848
1205
  end
849
- cmd = rake_output(*op.to_a)
850
- args.map! { |val| cmd.temp(val) }
851
- run_s(args, banner: false, from: :rake)
1206
+ print_run(op, banner, **kwargs)
1207
+ run(sync: sync, banner: banner, from: :"bundle:#{flag}").tap { |ret| success?(ret, banner, output) }
852
1208
  end
853
1209
 
854
- def irb(name = nil, opts = [], path: gemlib, args: nil)
1210
+ def rake(*args, sync: true, banner: verbose?, with: nil, pass: PASS_RUBY[:rake], **kwargs)
1211
+ opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
1212
+ op = OptionPartition.new(opts, OPT_RUBY[:rake], rake_session, project: self)
1213
+ op.adjoin(quote_option('f', rakefile)) if rakefile && !op.arg?('f', 'rakefile')
1214
+ op.concat(args)
1215
+ op.append(escape: true)
1216
+ print_run(op, banner, **kwargs)
1217
+ run(op, ({ 'BANNER' => '0' } unless banner), sync: sync, banner: false, from: :rake)
1218
+ end
1219
+
1220
+ def irb(*args, banner: verbose?, with: nil, pass: PASS_RUBY[:irb], **kwargs)
1221
+ opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
855
1222
  op = OptionPartition.new(opts, OPT_RUBY[:irb], session('irb'), project: self, first: [/\.rb$/])
856
- r = args ? [] : ['bundler/setup']
857
- r << name if name
858
- r.each { |val| op << shell_option('r', val, merge: true) }
859
- Array(path).each { |val| op << quote_option('I', val, merge: true) }
860
- if args
861
- op << '--' << args
862
- op.clear
1223
+ r = []
1224
+ r << 'bundler/setup' unless op.arg?('r')
1225
+ r << kwargs[:name] if kwargs[:name]
1226
+ r.each { |val| op.add_option('r', val, merge: true) }
1227
+ Array(kwargs.fetch(:path, gemlib)).each { |val| op << quote_option('I', val, merge: true) }
1228
+ op.concat(args)
1229
+ op.append(delim: true)
1230
+ print_run(op, banner, **kwargs)
1231
+ run(banner: false, from: :irb)
1232
+ end
1233
+
1234
+ def rbs(flag, *args, banner: verbose?, with: nil, pass: nil, **kwargs)
1235
+ case pass
1236
+ when NilClass
1237
+ pass = PASS_RUBY[:rbs]
1238
+ when Array
1239
+ pass += PASS_RUBY[:rbs]
1240
+ end
1241
+ opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
1242
+ cmd, opts = rbs_session(opts: opts)
1243
+ op = OptionPartition.new(opts, [], cmd << flag, project: self)
1244
+ case flag
1245
+ when :prototype
1246
+ sig = args.shift
1247
+ y = option('y', ignore: false)
1248
+ i = 1
1249
+ args.map { |val| basepath(val).relative_path_from(path) }.each do |file|
1250
+ dir = basepath sig, file.dirname
1251
+ dir.mkpath unless dir.exist?
1252
+ base = file.basename.to_s
1253
+ rbs = dir.join(base.stripext + '.rbs')
1254
+ status = if rbs.exist?
1255
+ case y
1256
+ when '0', 'false'
1257
+ 'ignored'
1258
+ else
1259
+ next unless y || confirm_basic('Overwrite?', rbs, 'N')
1260
+
1261
+ 'overwrite'
1262
+ end
1263
+ end
1264
+ unless status == 'ignored'
1265
+ ret = run(op.target.temp(File.extname(base) == '.rbi' ? 'rbi' : 'rb', file, '>', rbs), banner: false,
1266
+ series: true)
1267
+ if !ret
1268
+ status = 'FAIL'
1269
+ elsif File.empty?(rbs)
1270
+ status = 'empty'
1271
+ end
1272
+ end
1273
+ puts "#{i.to_s.rjust(2)}. #{rbs.relative_path_from(path)}".subhint(status)
1274
+ i += 1
1275
+ end
863
1276
  else
864
- op.append(delim: true)
1277
+ op.clear
1278
+ .append(*args)
1279
+ print_run(op, banner, **kwargs)
1280
+ run(banner: false, from: :"rbs:#{flag}")
865
1281
  end
866
- run(banner: false)
867
1282
  end
868
1283
 
869
1284
  def gemspec
870
- return @gemspec unless @gemspec.nil?
1285
+ return @gemspec || nil unless @gemspec.nil?
871
1286
 
872
- @gemspec = if gemfile
873
- Gem::Specification.load(gemfile.to_s) rescue false
874
- else
875
- false
876
- end
1287
+ @gemspec = gemfile && Gem::Specification.load(gemfile.to_s) rescue false
877
1288
  end
878
1289
 
879
1290
  def gemname
@@ -892,21 +1303,20 @@ module Squared
892
1303
  begin
893
1304
  case @autodetect
894
1305
  when 'rvm'
895
- @gemdir = pwd_set { `rvm info homes` }[/^\s+gem:\s+"(.+)"$/, 1]
1306
+ pwd_set { `rvm info homes` }[/^\s+gem:\s+"(.+)"$/, 1]
896
1307
  when 'rbenv'
897
1308
  if pwd_set { `rbenv which ruby` } =~ %r{^(.+[\\/]versions[\\/](\d\.\d)\.[^\\/]+)[\\/]bin[\\/]ruby$}
898
- @gemdir = File.join($1, 'lib/ruby/gems', "#{$2}.0")
1309
+ File.join($1, 'lib/ruby/gems', "#{$2}.0")
899
1310
  end
900
1311
  when 'asdf'
901
- @gemdir = pwd_set { `asdf where ruby` }
902
- @gemdir = @gemdir =~ /(\d\.\d)\.[^.]+$/ && File.join(@gemdir, 'lib/ruby/gems', "#{$1}.0")
1312
+ pwd_set { `asdf where ruby` }.yield_self do |val|
1313
+ val =~ /(\d\.\d)\.[^.]+$/ && File.join(val, 'lib/ruby/gems', "#{$1}.0")
1314
+ end
903
1315
  when /bundler?/
904
- @gemdir = pwd_set { `bundle env` }[/^\s+Gem Home\s+(.+)$/, 1]
905
- end
1316
+ pwd_set { `bundle env` }[/^\s+Gem Home\s+(.+)$/, 1]
1317
+ end.tap { |val| @gemdir = Pathname.new(val) if val }
906
1318
  rescue StandardError => e
907
1319
  log.debug e
908
- else
909
- @gemdir = Pathname.new(@gemdir) if @gemdir
910
1320
  end
911
1321
  return true if gemdir?
912
1322
  end
@@ -914,7 +1324,7 @@ module Squared
914
1324
 
915
1325
  set = lambda do |val, path|
916
1326
  if (ver = version) && ver != val
917
- log.warn "using version #{val} (given #{ver})"
1327
+ log.warn "using version #{val}".subhint("given #{ver}")
918
1328
  end
919
1329
  self.version = val
920
1330
  @gemdir = Pathname.new(path.strip) + gempath
@@ -923,17 +1333,17 @@ module Squared
923
1333
  opt = gempwd
924
1334
  pwd_set(pass: !opt.nil?) do
925
1335
  out = `#{gem_output(opt, 'list --local -d', gemname)}`
926
- if out =~ /#{Regexp.escape(gemname)} \(([^)]+)\)/
927
- split_escape($1)
928
- .prepend(version)
929
- .uniq
930
- .each do |val|
931
- next unless out =~ /\(#{Regexp.escape(val)}(?:,[^)]+|\b)\):([^\n]+)/
932
-
933
- set.call(val, $1)
934
- return gemdir? if @gemdir
935
- end
936
- end
1336
+ next unless out =~ /#{Regexp.escape(gemname)} \(([^)]+)\)/
1337
+
1338
+ split_escape($1)
1339
+ .unshift(version)
1340
+ .uniq
1341
+ .each do |val|
1342
+ next unless out =~ /\(#{Regexp.escape(val)}[^)]*\):([^\n]+)/
1343
+
1344
+ set.call(val, $1)
1345
+ return gemdir? if @gemdir
1346
+ end
937
1347
  end
938
1348
  @gemdir = Pathname.new(Gem.dir) + gempath
939
1349
  else
@@ -949,8 +1359,8 @@ module Squared
949
1359
  target = RUBY_VERSION.start_with?('2.6') ? RubyVM : $LOAD_PATH
950
1360
  parse.call(target.resolve_feature_path(gemname)&.last)
951
1361
  end
952
- if !@gemdir && !pwd_set { parse.call(`#{bundle_output('show', gemname)}`) }
953
- raise_error 'gems directory not found'
1362
+ unless @gemdir || pwd_set { parse.call(`#{bundle_output('show', gemname)}`) }
1363
+ raise_error Errno::ENOENT, 'gems home'
954
1364
  end
955
1365
  end
956
1366
  rescue StandardError => e
@@ -972,26 +1382,6 @@ module Squared
972
1382
  run(banner: !@session&.include?('--quiet'), **kwargs)
973
1383
  end
974
1384
 
975
- def append_bundle(opts, list, target: @session, append: nil)
976
- op = OptionPartition.new(opts, list, target, project: self)
977
- if append
978
- if append.is_a?(Regexp)
979
- op.each do |opt|
980
- if opt.match?(append)
981
- op.errors << opt
982
- else
983
- op.found << opt
984
- end
985
- end
986
- op.swap.clear(errors: true)
987
- end
988
- op.append(escape: true)
989
- else
990
- op.clear
991
- end
992
- op
993
- end
994
-
995
1385
  def ruby_session(*cmd, **kwargs)
996
1386
  session('ruby', *preopts, *cmd, **kwargs)
997
1387
  end
@@ -1015,6 +1405,13 @@ module Squared
1015
1405
  session('rake', *preopts, *cmd, **kwargs)
1016
1406
  end
1017
1407
 
1408
+ def rbs_session(*cmd, opts: nil)
1409
+ return session('rbs', *cmd) unless opts
1410
+
1411
+ op = OptionPartition.new(opts, OPT_RUBY[:rbs], project: self)
1412
+ [session('rbs', *op.to_a, *cmd), op.extras]
1413
+ end
1414
+
1018
1415
  def gem_output(*cmd, **kwargs)
1019
1416
  session_output('gem', *cmd, **kwargs)
1020
1417
  end
@@ -1050,11 +1447,11 @@ module Squared
1050
1447
  end
1051
1448
 
1052
1449
  def config_set(key, *val)
1053
- run(bundle_output('config set', key, *val), banner: false, series: false)
1450
+ run(bundle_output('config set', key, *val), banner: false, series: true)
1054
1451
  end
1055
1452
 
1056
1453
  def preopts
1057
- verbosetype > 1 && !session_arg?('quiet') ? ['--verbose'] : []
1454
+ verbose? && !session_arg?('quiet') ? ['--verbose'] : []
1058
1455
  end
1059
1456
 
1060
1457
  def variables
@@ -1062,11 +1459,10 @@ module Squared
1062
1459
  end
1063
1460
 
1064
1461
  def rakefile
1065
- return @rakefile unless @rakefile.nil?
1462
+ return @rakefile || nil unless @rakefile.nil?
1066
1463
 
1067
- @rakefile = Rake::Application::DEFAULT_RAKEFILES.find { |val| basepath(val).exist? }.yield_self do |file|
1068
- file ? basepath(file) : false
1069
- end
1464
+ file = Rake::Application::DEFAULT_RAKEFILES.find { |val| exist?(val) }
1465
+ @rakefile = file ? basepath(file) : false
1070
1466
  end
1071
1467
 
1072
1468
  def rakepwd
@@ -1088,6 +1484,38 @@ module Squared
1088
1484
  end
1089
1485
  end
1090
1486
 
1487
+ def bundleopts(*args)
1488
+ case args.first
1489
+ when :install, :update
1490
+ args << :install_a
1491
+ when :add, :plugin
1492
+ args << :common_git
1493
+ when :binstubs, :cache
1494
+ args << :common_all
1495
+ end
1496
+ OPT_BUNDLE[:common] + args.flat_map { |name| OPT_BUNDLE.fetch(name, []) }
1497
+ end
1498
+
1499
+ def gemopts(*args)
1500
+ case args.first
1501
+ when :install, :update
1502
+ args << :common_url << :common_domain << :install_a
1503
+ when :dependency, :info, :outdated, :search, :specification
1504
+ args << :common_url << :common_domain
1505
+ when :fetch, :list
1506
+ args << :common_url
1507
+ when :owner, :push, :yank
1508
+ args << :common_otp
1509
+ when :package, :pack
1510
+ args << :cache
1511
+ end
1512
+ case args.first
1513
+ when :info, :list, :search
1514
+ args << :common_all
1515
+ end
1516
+ OPT_GEM[:common] + args.flat_map { |name| OPT_GEM.fetch(name, []) }
1517
+ end
1518
+
1091
1519
  def gempwd
1092
1520
  return unless !pwd? && semgte?(Gem::VERSION, '3.4.2')
1093
1521
 
@@ -1095,7 +1523,7 @@ module Squared
1095
1523
  end
1096
1524
 
1097
1525
  def gemfile
1098
- return @gemfile unless @gemfile.nil?
1526
+ return @gemfile || nil unless @gemfile.nil?
1099
1527
 
1100
1528
  @gemfile = [project, name].map! { |val| basepath("#{val}.gemspec") }
1101
1529
  .concat(path.glob('*.gemspec'))
@@ -1108,7 +1536,7 @@ module Squared
1108
1536
  if (spec = gemspec)
1109
1537
  lib.merge(spec.require_paths || [])
1110
1538
  end
1111
- lib.select { |file| basepath(file).exist? }
1539
+ lib.select { |file| exist?(file) }
1112
1540
  end
1113
1541
  end
1114
1542