squared 0.5.3 → 0.5.5

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.
@@ -25,8 +25,8 @@ module Squared
25
25
  common: %w[no-color V|verbose retry=i].freeze,
26
26
  install: %w[frozen no-cache no-prune system binstubs=p? path=p standalone=q? target-rbconfig=p trust-policy=b
27
27
  with=q without=q].freeze,
28
- install_base: %w[full-index quiet retry gemfile=p j|jobs=i].freeze,
29
- update: %w[conservative local pre redownload ruby strict bundler=b? g|group=q source=b].freeze,
28
+ install_base: %w[force full-index quiet redownload retry gemfile=p j|jobs=i].freeze,
29
+ update: %w[conservative local pre ruby strict bundler=b? g|group=q source=b].freeze,
30
30
  outdated: %w[filter-major filter-minor filter-patch groups local parseable pre only-explicit strict
31
31
  update-strict g|group=q source=b].freeze,
32
32
  exec: %w[gemfile=p].freeze,
@@ -91,7 +91,7 @@ module Squared
91
91
  'irb' => nil
92
92
  })
93
93
 
94
- def initialize(*, autodetect: false, gemspec: nil, **kwargs)
94
+ def initialize(*, autodetect: false, gemspec: nil, asdf: 'ruby', **kwargs)
95
95
  super
96
96
  if @pass.include?(Ruby.ref)
97
97
  initialize_ref Ruby.ref
@@ -102,7 +102,13 @@ module Squared
102
102
  end
103
103
  dependfile_set GEMFILE
104
104
  @autodetect = autodetect
105
- @gemfile = path + gemspec if gemspec
105
+ @gemfile = if gemspec == false
106
+ false
107
+ elsif gemspec
108
+ path + (gemspec.include?('.') ? gemspec : "#{gemspec}.gemspec")
109
+ elsif (gemspec = path + "#{name}.gemspec").exist? || (gemspec = path + "#{project}.gemspec").exist?
110
+ gemspec
111
+ end
106
112
  return if !@output[0].nil? || !@copy.nil? || version || @autodetect || !rakefile
107
113
 
108
114
  begin
@@ -126,7 +132,7 @@ module Squared
126
132
 
127
133
  def populate(*, **)
128
134
  super
129
- return unless outdated? && ref?(Ruby.ref)
135
+ return unless (outdated? && ref?(Ruby.ref)) || @only
130
136
 
131
137
  namespace name do
132
138
  Ruby.subtasks do |action, flags|
@@ -140,21 +146,21 @@ module Squared
140
146
  format_desc action, nil, "task+,opts*|#{indexchar}index+|#,pattern*"
141
147
  task action, [:command] do |_, args|
142
148
  if args.command == '#'
143
- format_list(read_rakefile, "rake[#{indexchar}N]", 'tasks', grep: args.extras, from: rakefile,
144
- each: ->(val) { val[0] + val[1].to_s })
149
+ format_list(raketasks, "rake[#{indexchar}N]", 'tasks', grep: args.extras, from: rakefile,
150
+ each: ->(val) { val[0] + val[1].to_s })
145
151
  else
146
152
  args, opts = args.to_a.partition { |val| indexitem(val) }
147
153
  if args.empty?
148
154
  rake(opts: opts)
149
155
  else
150
- list = read_rakefile
156
+ tasks = raketasks
151
157
  while (n, pre = indexitem(args.shift))
152
- if (item = list[n - 1])
158
+ if (item = tasks[n - 1])
153
159
  cmd = pre ? "#{pre} #{item.first}" : item.first
154
160
  elsif exception
155
- indexerror n, list
161
+ indexerror n, tasks
156
162
  else
157
- log.warn "rake task #{n} of #{list.size} (out of range)"
163
+ log.warn "rake task #{n} of #{tasks.size} (out of range)"
158
164
  next
159
165
  end
160
166
  if opts.empty?
@@ -171,7 +177,7 @@ module Squared
171
177
  format_desc action, nil, 'opts*,args*|:'
172
178
  task action do |_, args|
173
179
  args = args.to_a
174
- name = basepath('lib').join("#{gemname}.rb").exist? ? gemname : nil
180
+ name = gemlib.any? { |file| basepath(file).join("#{gemname}.rb").exist? } ? gemname : nil
175
181
  irb(name, args, args: (readline('Enter file [arguments]', force: false) if args.delete(':')))
176
182
  end
177
183
  else
@@ -209,7 +215,7 @@ module Squared
209
215
  when :build, :push, :exec, :update
210
216
  format_desc(action, flag, 'opts*', after: case flag
211
217
  when :exec then 'command,args*'
212
- when :push then 'file?'
218
+ when :push then 'file?|:'
213
219
  when :update then 'name*'
214
220
  end)
215
221
  task flag do |_, args|
@@ -275,7 +281,7 @@ module Squared
275
281
  end
276
282
  end
277
283
 
278
- def copy(from: 'lib', into: @gemdir, override: false, **kwargs)
284
+ def copy(from: gemlib, into: @gemdir, override: false, **kwargs)
279
285
  glob = kwargs[:include]
280
286
  pass = kwargs[:exclude]
281
287
  if @copy && !override
@@ -426,7 +432,16 @@ module Squared
426
432
 
427
433
  def install(flag, opts = [])
428
434
  bundle_session 'install', "--#{flag}"
429
- append_bundle opts, OPT_BUNDLE[:install_base] + OPT_BUNDLE[:install] + OPT_BUNDLE[:common]
435
+ op = append_bundle opts, OPT_BUNDLE[:install_base] + OPT_BUNDLE[:install] + OPT_BUNDLE[:common]
436
+ if op.arg?('force')
437
+ op.delete('--force')
438
+ if flag != :redownload
439
+ op << '--redownload'
440
+ elsif (lock = basepath('Gemfile.lock')).exist?
441
+ config = basepath('.bundle', 'config')
442
+ lock.delete unless config.exist? && config.read.match?(/\bBUNDLE_FROZEN:\s+"true"/)
443
+ end
444
+ end
430
445
  run_rb(from: :install)
431
446
  end
432
447
 
@@ -451,16 +466,44 @@ module Squared
451
466
  when :version
452
467
  pwd_set do
453
468
  out = []
454
- [
469
+ order = { 'rvm' => -1, 'rbenv' => -1, 'chruby' => -1, 'asdf' => -1 }
470
+ ENV.fetch('PATH', '').split(':').each_with_index do |val, index|
471
+ order.each_key do |key|
472
+ if val.match?(%r{[/.]#{key}/})
473
+ order[key] = index
474
+ break
475
+ end
476
+ end
477
+ end
478
+ paths = [
455
479
  '$HOME/.rvm/bin/rvm',
456
480
  '/usr/local/rvm/bin/rvm',
457
481
  '/usr/share/rvm/bin/rvm',
458
482
  "#{ENV.fetch('RBENV_ROOT', '$HOME/.rbenv')}/bin/rbenv",
459
483
  '/usr/bin/rbenv',
460
- '/usr/local/share/chruby/chruby.sh',
461
- "#{ENV.fetch('ASDF_DATA_DIR', '$HOME/.asdf')}/plugins/ruby/bin/install",
462
- ''
463
- ].each do |val|
484
+ '/usr/local/share/chruby/chruby.sh'
485
+ ]
486
+ paths << "#{ENV.fetch('ASDF_DATA_DIR', '$HOME/.asdf')}/installs/#{@asdf.first}" if @asdf
487
+ paths.sort do |a, b|
488
+ c = -1
489
+ d = -1
490
+ order.each do |key, val|
491
+ pat = %r{/\.?#{key}}
492
+ c = val if a.match?(pat)
493
+ d = val if b.match?(pat)
494
+ end
495
+ if c == d
496
+ 0
497
+ elsif c == -1
498
+ 1
499
+ elsif d == -1
500
+ -1
501
+ else
502
+ c < d ? -1 : 1
503
+ end
504
+ end
505
+ .push('')
506
+ .each do |val|
464
507
  next unless val.empty? || File.exist?(val.sub('$HOME', Dir.home))
465
508
 
466
509
  trim = ->(s) { s[/\A\D+\d+\.\d+(?:\.\S+)?/, 0].sub(/\A([a-z]+)-/i, '\1 ') }
@@ -475,12 +518,15 @@ module Squared
475
518
  when 'chruby.sh'
476
519
  chruby = session_output 'source', val
477
520
  `#{chruby.with('ruby --version')}`
478
- when 'install'
479
- ver = '.tool-versions'
480
- `asdf current ruby`[/ruby\s+\S+/, 0].sub(/\s+/, ' ')
481
521
  else
482
- ver = nil
483
- `ruby --version`
522
+ if @asdf
523
+ cmd = 'asdf'
524
+ ver = '.tool-versions'
525
+ `asdf current #{@asdf.first}`[/\A\S+\s+\S+/, 0].sub(/\s+/, ' ')
526
+ else
527
+ ver = nil
528
+ `ruby --version`
529
+ end
484
530
  end)
485
531
  break if workspace.windows?
486
532
 
@@ -488,7 +534,7 @@ module Squared
488
534
  out << trim.call(case cmd
489
535
  when 'chruby.sh'
490
536
  `#{chruby.with('chruby --version')}`.sub(':', '')
491
- when 'install'
537
+ when 'asdf'
492
538
  "asdf #{`asdf version`.delete_prefix('v')}"
493
539
  else
494
540
  `#{cmd} --version`
@@ -500,8 +546,8 @@ module Squared
500
546
  `rbenv which ruby`
501
547
  when 'chruby.sh'
502
548
  `#{chruby.with('which ruby')}`
503
- when 'install'
504
- `asdf which ruby`
549
+ when 'asdf'
550
+ `asdf which #{@asdf.first}`
505
551
  else
506
552
  `which ruby`
507
553
  end)
@@ -566,7 +612,7 @@ module Squared
566
612
  minor = 0
567
613
  patch = 0
568
614
  update = []
569
- pwd_set(pass: !pwd.nil?, from: from) do
615
+ pwd_set(pass: !gempwd.nil?, from: from) do
570
616
  items = [[%w[Gem Current Latest], nil]]
571
617
  IO.popen(cmd.done).each do |line|
572
618
  if line =~ /^(\S+) \((\S+) < ([^)]+)\)$/
@@ -685,25 +731,25 @@ module Squared
685
731
  on :last, from
686
732
  return
687
733
  when :build
688
- if !op.empty?
689
- op << shell_quote(path + op.shift)
690
- op.clear(pass: false)
691
- elsif (file = gemfile)
692
- op << shell_quote(file)
734
+ if op.empty?
735
+ op.add_path(gemfile)
736
+ else
737
+ op.add_path(op.shift)
738
+ .clear(pass: false)
693
739
  end
694
740
  when :push
695
- if op.empty?
696
- file = path + (if (spec = gemspec)
741
+ if op.empty? || (n = op.index(':'))
742
+ file = path + (if !n && (spec = gemspec)
697
743
  "#{spec.name}-#{spec.version}.gem"
698
744
  else
699
745
  choice_index('Select a file', Dir.glob('*.gem', base: path), force: true)
700
746
  end)
701
747
  else
702
- file = path + op.shift
748
+ file = path + op.shift.yield_self { |val| val.include?('.') ? val : "#{val}.gem" }
703
749
  raise_error('gem not found', hint: file) unless file.exist?
704
750
  raise_error("unknown args: #{op.join(', ')}", hint: flag) unless op.empty?
705
751
  end
706
- op << shell_quote(file)
752
+ op.add_path(file)
707
753
  run_rb(from: from, interactive: "Push #{sub_style(gemname, styles: theme[:active])}")
708
754
  return
709
755
  when :exec
@@ -730,7 +776,7 @@ module Squared
730
776
  raise_error('missing gemname', hint: flag) if op.empty?
731
777
  case flag
732
778
  when :install, :uninstall, :pristine
733
- post = readline('Enter command [args]', force: true) if flag == :install && op.extras.delete(':')
779
+ post = readline('Enter command [args]', force: true) if flag == :install && op.remove(':')
734
780
  if op.arg?('all')
735
781
  if flag == :pristine
736
782
  append_repeat 'skip', op.extras
@@ -797,7 +843,7 @@ module Squared
797
843
  run_s(args, banner: false, from: :rake)
798
844
  end
799
845
 
800
- def irb(name = nil, opts = [], path: basepath('lib'), args: nil)
846
+ def irb(name = nil, opts = [], path: gemlib, args: nil)
801
847
  op = OptionPartition.new(opts, OPT_RUBY[:irb], session('irb'), project: self, first: [/\.rb$/])
802
848
  r = args ? [] : ['bundler/setup']
803
849
  r << name if name
@@ -815,15 +861,11 @@ module Squared
815
861
  def gemspec
816
862
  return @gemspec unless @gemspec.nil?
817
863
 
818
- begin
819
- if (file = gemfile)
820
- require 'rubygems'
821
- @gemspec = Gem::Specification.load(file.to_s)
822
- end
823
- rescue StandardError => e
824
- log.debug e
825
- end
826
- @gemspec ||= false
864
+ @gemspec = if (file = gemfile)
865
+ Gem::Specification.load(file.to_s) rescue false
866
+ else
867
+ false
868
+ end
827
869
  end
828
870
 
829
871
  def gemname
@@ -885,7 +927,6 @@ module Squared
885
927
  end
886
928
  end
887
929
  end
888
- require 'rubygems'
889
930
  @gemdir = Pathname.new(Gem.dir) + gempath
890
931
  else
891
932
  parse = lambda do |path|
@@ -940,6 +981,7 @@ module Squared
940
981
  else
941
982
  op.clear
942
983
  end
984
+ op
943
985
  end
944
986
 
945
987
  def ruby_session(*cmd, **kwargs)
@@ -981,27 +1023,10 @@ module Squared
981
1023
  session_output('rake', *cmd, **kwargs)
982
1024
  end
983
1025
 
984
- def read_rakefile
985
- @read_rakefile ||= [].tap do |ret|
986
- pwd = rakepwd
987
- pwd_set(pass: !pwd.nil?) do
988
- IO.popen(rake_output(pwd, '-AT').to_s).each do |line|
989
- next unless line =~ /^rake ((?:[^\[: ]+:?)+)(\[[^\]]+\])?/
990
-
991
- ret << [$1, $2]
992
- end
993
- end
994
- end
995
- end
996
-
997
1026
  def preopts
998
1027
  verbosetype > 1 && !session_arg?('quiet') ? ['--verbose'] : []
999
1028
  end
1000
1029
 
1001
- def gemdir?
1002
- !@gemdir.nil? && @gemdir.exist? && !@gemdir.empty?
1003
- end
1004
-
1005
1030
  def variables
1006
1031
  (super + %i[version autodetect]).freeze
1007
1032
  end
@@ -1015,13 +1040,26 @@ module Squared
1015
1040
  end
1016
1041
 
1017
1042
  def rakepwd
1018
- return if pwd? || Rake::VERSION < '13.0.4'
1043
+ return unless !pwd? && semgte?(Rake::VERSION, '13.0.4')
1019
1044
 
1020
1045
  quote_option 'C', path
1021
1046
  end
1022
1047
 
1048
+ def raketasks
1049
+ @raketasks ||= [].tap do |ret|
1050
+ opt = rakepwd
1051
+ pwd_set(pass: !opt.nil?) do
1052
+ IO.popen(rake_output(opt, '-AT').to_s).each do |line|
1053
+ next unless line =~ /^rake ((?:[^\[: ]+:?)+)(\[[^\]]+\])?/
1054
+
1055
+ ret << [$1, $2]
1056
+ end
1057
+ end
1058
+ end
1059
+ end
1060
+
1023
1061
  def gempwd
1024
- return if pwd? || Gem::VERSION < '3.4.2'
1062
+ return unless !pwd? && semgte?(Gem::VERSION, '3.4.2')
1025
1063
 
1026
1064
  quote_option 'C', path
1027
1065
  end
@@ -1034,9 +1072,23 @@ module Squared
1034
1072
  .find { |file| File.exist?(file) } || false
1035
1073
  end
1036
1074
 
1075
+ def gemlib
1076
+ @gemlib ||= begin
1077
+ lib = Set.new(['lib'])
1078
+ if (spec = gemspec)
1079
+ lib.merge(spec.require_paths || [])
1080
+ end
1081
+ lib.select { |file| basepath(file).exist? }
1082
+ end
1083
+ end
1084
+
1037
1085
  def gempath(val = version)
1038
1086
  File.join('gems', "#{gemname}-#{val}")
1039
1087
  end
1088
+
1089
+ def gemdir?
1090
+ !@gemdir.nil? && @gemdir.exist? && !@gemdir.empty?
1091
+ end
1040
1092
  end
1041
1093
 
1042
1094
  Application.implement Ruby
@@ -10,6 +10,9 @@ module Squared
10
10
  include Common::Shell
11
11
  extend Forwardable
12
12
 
13
+ OPT_VALUE = /\A([^=]+)=(.+)\z/
14
+ private_constant :OPT_VALUE
15
+
13
16
  class << self
14
17
  include Common::Format
15
18
  include Shell
@@ -45,6 +48,15 @@ module Squared
45
48
  val.map { |s| s.sub(/\A-([a-z\d])(.+)\z/i, '\1=\2').sub(/\A--?/, '') }.reject(&:empty?)
46
49
  end
47
50
 
51
+ def select(list, bare: true, no: true, single: false, double: false)
52
+ ret = bare ? list.grep_v(/=/) : list.grep(/=/).map! { |val| val.split('=', 2).first }
53
+ ret.map! { |val| val.split('|', 2).last }
54
+ ret = ret.grep_v(/^no-/) unless no
55
+ return ret if single == double
56
+
57
+ ret.select { |val| single ? val.size == 1 : val.size > 1 }
58
+ end
59
+
48
60
  def arg?(target, *args, value: false, **)
49
61
  r, s = args.partition { |val| val.is_a?(Regexp) }
50
62
  unless s.empty?
@@ -54,14 +66,21 @@ module Squared
54
66
  s = target.to_a.compact
55
67
  r.any? { |pat| s.any?(pat) }
56
68
  end
69
+
70
+ def pattern?(val)
71
+ val.match?(/(?:\A\^|\$\z)/) || val.match?(/(?:\.[*+]|\(\?:|\\[dsw]|\[.+\]|\{\d+,?\d*\})/)
72
+ end
57
73
  end
58
74
 
59
75
  attr_reader :target, :extras, :found, :errors, :values, :project, :path
60
76
 
61
77
  def_delegators :@target, :+, :-, :<<, :any?, :none?, :include?, :add, :add?, :find, :find_all, :find_index,
62
- :merge, :delete, :delete?, :delete_if, :grep, :inspect, :to_a, :to_s
78
+ :merge, :delete, :delete?, :delete_if, :grep, :grep_v, :inspect, :to_a, :to_s
63
79
  def_delegators :@extras, :empty?, :each, :each_with_index, :partition, :dup, :first, :last, :shift, :unshift,
64
- :pop, :push, :index, :delete_at, :join, :map, :map!, :select, :reject, :size
80
+ :pop, :push, :concat, :index, :delete_at, :join, :map, :map!, :select, :reject, :size
81
+
82
+ def_delegator :@extras, :delete, :remove
83
+ def_delegator :@extras, :delete_if, :remove_if
65
84
 
66
85
  def initialize(opts, list, target = Set.new, project: nil, path: nil, **kwargs, &blk)
67
86
  @target = target.is_a?(Set) ? target : target.to_set
@@ -139,35 +158,35 @@ module Squared
139
158
  skip = false
140
159
  opts.each do |opt|
141
160
  next skip = true if opt == '--'
142
- next @extras << opt if skip
161
+ next push opt if skip
143
162
 
144
163
  if single&.match?(opt)
145
- target << "-#{opt}"
164
+ add "-#{opt}"
146
165
  elsif bare.include?(opt)
147
- target << (opt.size == 1 ? "-#{opt}" : "--#{opt}")
166
+ add(opt.size == 1 ? "-#{opt}" : "--#{opt}")
148
167
  elsif opt.start_with?('no-') && no.include?(name = opt[3..-1])
149
- target << "--no-#{name}"
168
+ add "--no-#{name}"
150
169
  else
151
- if opt =~ /\A([^=]+)=(.+)\z/
170
+ if opt =~ OPT_VALUE
152
171
  key = $1
153
172
  val = $2
154
173
  merge = m.include?(key)
155
174
  if e.include?(key)
156
- target << shell_option(key, val, merge: merge)
175
+ add shell_option(key, val, merge: merge)
157
176
  elsif q.include?(key)
158
- target << quote_option(key, val, double: qq.include?(key), merge: merge)
177
+ add quote_option(key, val, double: qq.include?(key), merge: merge)
159
178
  elsif p.include?(key) && path
160
- target << quote_option(key, path + val, merge: merge)
179
+ add quote_option(key, path + val, merge: merge)
161
180
  elsif b.include?(key) || numcheck.call(key, val)
162
- target << basic_option(key, val, merge: merge)
181
+ add basic_option(key, val, merge: merge)
163
182
  elsif merge
164
- target << basic_option(key, val, merge: true)
183
+ add basic_option(key, val, merge: true)
165
184
  else
166
- @extras << opt
185
+ push opt
167
186
  end
168
187
  opt = key
169
188
  else
170
- @extras << opt
189
+ push opt
171
190
  skip = true if args
172
191
  end
173
192
  skip = true if first&.any? { |s| s.is_a?(Regexp) ? opt.match?(s) : !opt.include?(s) }
@@ -193,6 +212,22 @@ module Squared
193
212
  self
194
213
  end
195
214
 
215
+ def uniq(list)
216
+ items = map { |val| nameonly(val) }
217
+ list.reject do |val|
218
+ next true if items.include?(s = nameonly(val))
219
+
220
+ pat = /\A#{s = fill_option(s)}(?:#{s.start_with?('--') ? '[= ]' : '.*'}|\z)/
221
+ any? { |opt| opt.match?(pat) }
222
+ end
223
+ end
224
+
225
+ def uniq!(list)
226
+ n = size
227
+ concat uniq(list)
228
+ extras if size > n
229
+ end
230
+
196
231
  def clear(opts = nil, errors: false, **kwargs)
197
232
  styles = project.theme[:inline] if project
198
233
  if !opts
@@ -230,10 +265,63 @@ module Squared
230
265
  args = items[0...i] + args + items[i..-1]
231
266
  target.clear
232
267
  end
233
- merge(args)
268
+ merge args
234
269
  self
235
270
  end
236
271
 
272
+ def add_path(*args, **kwargs)
273
+ add shell_quote(path ? path.join(*args) : File.join(*args), **kwargs)
274
+ self
275
+ end
276
+
277
+ def add_quote(*args, **kwargs)
278
+ merge(args.map { |val| shell_quote(val, **kwargs) })
279
+ self
280
+ end
281
+
282
+ def splice(*exclude, quote: true, delim: true, path: false, pattern: false, &blk)
283
+ found, other = if block_given?
284
+ partition(&blk)
285
+ elsif exclude.first.is_a?(Symbol)
286
+ partition(&exclude.first)
287
+ else
288
+ partition do |val|
289
+ next false if pattern && OptionPartition.pattern?(val)
290
+
291
+ exclude.none? { |pat| val.match?(Regexp.new(pat)) }
292
+ end
293
+ end
294
+ unless found.empty?
295
+ add '--' if delim
296
+ extras.clear
297
+ concat other
298
+ if path
299
+ found.each { |val| add_path(val) }
300
+ else
301
+ merge(quote ? found.map! { |val| shell_quote(val) } : found)
302
+ end
303
+ end
304
+ self
305
+ end
306
+
307
+ def append?(key, val = nil, type: nil, **kwargs)
308
+ return false if arg?(key)
309
+
310
+ val = yield self if block_given?
311
+ return false unless val
312
+
313
+ type ||= :quote if kwargs.empty?
314
+ op << case type
315
+ when :quote
316
+ quote_option(key, val)
317
+ when :basic
318
+ basic_option(key, val)
319
+ else
320
+ shell_option(key, val, **kwargs)
321
+ end
322
+ true
323
+ end
324
+
237
325
  def reset(errors: false)
238
326
  extras.clear
239
327
  clear(errors: true) if errors
@@ -243,6 +331,12 @@ module Squared
243
331
  def arg?(*args, **kwargs)
244
332
  OptionPartition.arg?(target, *args, **kwargs)
245
333
  end
334
+
335
+ private
336
+
337
+ def nameonly(val)
338
+ val[OPT_VALUE, 1] || val
339
+ end
246
340
  end
247
341
 
248
342
  class JoinSet < Set